diff --git a/README.md b/README.md index dc41636..73dc94c 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,9 @@ height = 512 font_size = 24 bg_color = 0x272822ee # ~~colors are specified in 0xRRGGBBAA format # font_color = 0xf8f8f2ff +# HiDPI scaling factor; default is requested from compositor but +# fractional values are truncated, thus need to set it explicitly. +scale = 3 # ~~Block for input field [input_text] diff --git a/src/config.rs b/src/config.rs index f13bdb8..580ffd3 100644 --- a/src/config.rs +++ b/src/config.rs @@ -13,6 +13,7 @@ mod params; pub struct Config { width: Option, height: Option, + scale: Option, window_offsets: Option<(i32, i32)>, term: Option, font: Option, @@ -30,6 +31,14 @@ impl Config { pub fn disable_icons(&mut self) { self.icon = None; } + + fn scale(&self) -> u16 { + self.scale.unwrap_or(1) + } + + pub fn update_scale(&mut self, scale: u16) { + self.scale = Some(scale); + } } #[derive(Deserialize)] diff --git a/src/config/params.rs b/src/config/params.rs index e8acdc3..2df72a1 100644 --- a/src/config/params.rs +++ b/src/config/params.rs @@ -29,7 +29,8 @@ impl<'a> From<&'a Config> for InputTextParams { font: select_conf!(config, input_text, font) .map(font_by_name) .unwrap_or_else(default_font), - font_size: select_conf!(config, input_text, font_size).unwrap_or(DEFAULT_FONT_SIZE), + font_size: select_conf!(config, input_text, font_size).unwrap_or(DEFAULT_FONT_SIZE) + * config.scale(), bg_color: select_conf!(config, input_text, bg_color) .map(u32_to_solid_source) .unwrap_or_else(|| SolidSource::from_unpremultiplied_argb(0xc0, 0x75, 0x71, 0x5e)), @@ -37,9 +38,11 @@ impl<'a> From<&'a Config> for InputTextParams { .map(u32_to_solid_source) .unwrap_or_else(default_font_color), margin: select_conf!(noglob: config, input_text, margin) - .unwrap_or_else(|| Margin::all(5.0)), + .unwrap_or_else(|| Margin::all(5.0)) + * f32::from(config.scale()), padding: select_conf!(noglob: config, input_text, padding) - .unwrap_or_else(|| Padding::from_pair(1.7, -4.0)), + .unwrap_or_else(|| Padding::from_pair(1.7, -4.0)) + * f32::from(config.scale()), } } } @@ -50,7 +53,8 @@ impl<'a> From<&'a Config> for ListParams { font: select_conf!(config, list_items, font) .map(font_by_name) .unwrap_or_else(default_font), - font_size: select_conf!(config, list_items, font_size).unwrap_or(DEFAULT_FONT_SIZE), + font_size: select_conf!(config, list_items, font_size).unwrap_or(DEFAULT_FONT_SIZE) + * config.scale(), font_color: select_conf!(config, list_items, font_color) .map(u32_to_solid_source) .unwrap_or_else(default_font_color), @@ -63,15 +67,18 @@ impl<'a> From<&'a Config> for ListParams { .icon .as_ref() .map(|c| c.size.unwrap_or(DEFAULT_ICON_SIZE)) - .unwrap_or(0), + .unwrap_or(0) + * config.scale(), fallback_icon: select_conf!(noglob: config, icon, fallback_icon_path) .map(|path| Icon::load_icon(&path).expect("cannot load fallback icon")), margin: select_conf!(noglob: config, list_items, margin).unwrap_or_else(|| Margin { top: 10.0, ..Margin::from_pair(5.0, 15.0) - }), - item_spacing: select_conf!(noglob: config, list_items, item_spacing).unwrap_or(2.0), - icon_spacing: select_conf!(noglob: config, list_items, icon_spacing).unwrap_or(10.0), + }) * f32::from(config.scale()), + item_spacing: select_conf!(noglob: config, list_items, item_spacing).unwrap_or(2.0) + * f32::from(config.scale()), + icon_spacing: select_conf!(noglob: config, list_items, icon_spacing).unwrap_or(10.0) + * f32::from(config.scale()), } } } @@ -93,6 +100,7 @@ impl<'a> From<&'a Config> for SurfaceParams { width: config.width.unwrap_or(400), height: config.height.unwrap_or(512), window_offsets: config.window_offsets, + scale: config.scale, } } } @@ -100,7 +108,7 @@ impl<'a> From<&'a Config> for SurfaceParams { impl<'a> From<&'a Config> for Option { fn from(config: &'a Config) -> Option { config.icon.as_ref().map(|c| IconConfig { - icon_size: c.size.unwrap_or(DEFAULT_ICON_SIZE), + icon_size: c.size.unwrap_or(DEFAULT_ICON_SIZE) * config.scale(), theme: c .theme .as_ref() diff --git a/src/main.rs b/src/main.rs index 7a9671b..2257db9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -109,6 +109,7 @@ fn main() { let mut event_loop = calloop::EventLoop::<()>::new().unwrap(); let mut surface = surface::Surface::new(&env, config.param()); + config.update_scale(surface.scale()); let (_input, key_stream) = input::InputHandler::new(&env, &event_loop); diff --git a/src/style.rs b/src/style.rs index 84676dd..36dc0e7 100644 --- a/src/style.rs +++ b/src/style.rs @@ -1,5 +1,6 @@ use std::fmt::Display; use std::marker::PhantomData; +use std::ops::Mul; use std::str::FromStr; use serde::de::{Deserializer, Visitor}; @@ -41,6 +42,19 @@ impl Padding { } } +impl Mul for Padding { + type Output = Self; + + fn mul(self, rhs: f32) -> Self { + Self { + top: self.top * rhs, + bottom: self.bottom * rhs, + left: self.left * rhs, + right: self.right * rhs, + } + } +} + impl Margin { pub const fn all(val: f32) -> Self { Self { @@ -61,6 +75,19 @@ impl Margin { } } +impl Mul for Margin { + type Output = Self; + + fn mul(self, rhs: f32) -> Self { + Self { + top: self.top * rhs, + bottom: self.bottom * rhs, + left: self.left * rhs, + right: self.right * rhs, + } + } +} + impl<'de> Deserialize<'de> for Padding { fn deserialize(d: D) -> Result where diff --git a/src/surface.rs b/src/surface.rs index 817c1a4..b5ebddd 100644 --- a/src/surface.rs +++ b/src/surface.rs @@ -1,4 +1,5 @@ use std::cell::Cell; +use std::convert::TryInto; use std::rc::Rc; use sctk::{ @@ -25,6 +26,7 @@ pub struct Params { pub width: u32, pub height: u32, pub window_offsets: Option<(i32, i32)>, + pub scale: Option, } pub struct Surface { @@ -32,6 +34,7 @@ pub struct Surface { layer_surface: Main, next_render_event: Rc>>, pools: DoubleMemPool, + scale: u16, dimensions: (u32, u32), } @@ -51,6 +54,11 @@ impl Surface { crate::prog_name!().to_owned(), ); + let scale = params.scale.unwrap_or_else(|| { + sctk::get_surface_scale_factor(&surface) + .try_into() + .expect("invalid surface scale factor") + }); let width = params.width; let height = params.height; @@ -88,16 +96,22 @@ impl Surface { // Commit so that the server will send a configure event surface.commit(); + surface.set_buffer_scale(scale.into()); Self { surface, layer_surface, next_render_event, pools, + scale, dimensions: (width, height), } } + pub fn scale(&self) -> u16 { + self.scale + } + /// Handles any events that have occurred since the last call, redrawing if needed. /// Returns true if the surface should be dropped. pub fn handle_events(&mut self) -> EventStatus { @@ -121,8 +135,9 @@ impl Surface { return; }; - let width = self.dimensions.0; - let height = self.dimensions.1; + let scale = u32::from(self.scale); + let width = self.dimensions.0 * scale; + let height = self.dimensions.1 * scale; // First make sure the pool is the right size pool.resize((4 * width * height) as usize).unwrap();