From 57aeeaa882067218de83084fcebc6a7c4d4fdb2e Mon Sep 17 00:00:00 2001 From: Dorota Czaplejewicz Date: Wed, 6 Apr 2022 08:40:59 +0000 Subject: [PATCH 1/4] output: Store physical size --- src/outputs.rs | 44 +++++++++++++++++++++++++++++++++++--------- src/state.rs | 3 ++- src/util.rs | 6 ++++++ 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/src/outputs.rs b/src/outputs.rs index 13c1b5d3..cb17efa8 100644 --- a/src/outputs.rs +++ b/src/outputs.rs @@ -126,7 +126,7 @@ pub mod c { outputs: COutputs, wl_output: WlOutput, _x: i32, _y: i32, - _phys_width: i32, _phys_height: i32, + phys_width: i32, phys_height: i32, _subpixel: i32, _make: *const c_char, _model: *const c_char, transform: i32, @@ -144,7 +144,19 @@ pub mod c { .find_output_mut(wl_output) .map(|o| &mut o.pending); match output_state { - Some(state) => { state.transform = Some(transform) }, + Some(state) => { + fn maybe_mm(value: i32) -> Option { + if value == 0 { None } + else { Some(Millimeter(value)) } + } + state.geometry = Some(Geometry { + phys_size: GSize { + width: maybe_mm(phys_width), + height: maybe_mm(phys_height), + }, + transform, + }); + }, None => log_print!( logging::Level::Warning, "Got geometry on unknown output", @@ -286,13 +298,17 @@ pub mod c { // TODO: handle unregistration } + /// Generic size -#[derive(Clone)] -pub struct Size { - pub width: u32, - pub height: u32, +#[derive(Clone, Copy, Debug)] +pub struct GSize { + pub width: Unit, + pub height: Unit, } +/// Unspecified size (TODO: transitional, remove) +pub type Size = GSize; + /// wl_output mode #[derive(Clone, Copy, Debug)] pub struct Mode { @@ -300,10 +316,20 @@ pub struct Mode { height: i32, } +#[derive(Clone, Copy, Debug)] +pub struct Millimeter(pub i32); + +/// All geometry parameters +#[derive(Clone, Copy, Debug)] +pub struct Geometry { + pub transform: c::Transform, + pub phys_size: GSize>, +} + #[derive(Clone, Copy, Debug)] pub struct OutputState { pub current_mode: Option, - pub transform: Option, + pub geometry: Option, pub scale: i32, } @@ -317,7 +343,7 @@ impl OutputState { fn uninitialized() -> OutputState { OutputState { current_mode: None, - transform: None, + geometry: None, scale: 1, } } @@ -327,7 +353,7 @@ impl OutputState { match self { OutputState { current_mode: Some(Mode { width, height } ), - transform: Some(transform), + geometry: Some(Geometry { transform, .. } ), scale: _, } => Some( match transform { diff --git a/src/state.rs b/src/state.rs index 5254a940..58d8e50e 100644 --- a/src/state.rs +++ b/src/state.rs @@ -10,6 +10,7 @@ use crate::imservice::{ ContentHint, ContentPurpose }; use crate::main::{ Commands, PanelCommand, PixelSize }; use crate::outputs; use crate::outputs::{OutputId, OutputState}; +use crate::util::Rational; use std::cmp; use std::collections::HashMap; use std::time::Instant; @@ -341,7 +342,7 @@ pub mod test { id, OutputState { current_mode: None, - transform: None, + geometry: None, scale: 1, }, ); diff --git a/src/util.rs b/src/util.rs index d5109066..f4eb4f52 100644 --- a/src/util.rs +++ b/src/util.rs @@ -157,6 +157,12 @@ pub fn find_max_double(iterator: I, get: F) .0 } +#[derive(Debug, Clone, Copy)] +pub struct Rational { + pub numerator: i32, + pub denominator: u32, +} + /// Compares pointers but not internal values of Rc pub struct Pointer(pub Rc); From 6528879feda035bab4184c3ca21f821269d8cf45 Mon Sep 17 00:00:00 2001 From: Dorota Czaplejewicz Date: Wed, 6 Apr 2022 08:59:04 +0000 Subject: [PATCH 2/4] state: Derive panel size from physical click target size --- src/outputs.rs | 71 ++++++++++++++++++++++++++++++++++++++----------- src/state.rs | 72 +++++++++++++++++++++++++++++++++++++++----------- src/util.rs | 47 ++++++++++++++++++++++++++++++-- 3 files changed, 157 insertions(+), 33 deletions(-) diff --git a/src/outputs.rs b/src/outputs.rs index cb17efa8..1125afaa 100644 --- a/src/outputs.rs +++ b/src/outputs.rs @@ -4,9 +4,11 @@ /*! Managing Wayland outputs */ +use std::ops; use std::vec::Vec; use crate::event_loop; use ::logging; +use crate::util::DivCeil; // traits use ::logging::Warn; @@ -319,6 +321,20 @@ pub struct Mode { #[derive(Clone, Copy, Debug)] pub struct Millimeter(pub i32); +impl DivCeil for Millimeter { + type Output = Millimeter; + fn div_ceil(self, other: i32) -> Self { + Self(self.0.div_ceil(other)) + } +} + +impl ops::Mul for Millimeter { + type Output = Self; + fn mul(self, m: i32) -> Self { + Self(self.0 * m as i32) + } +} + /// All geometry parameters #[derive(Clone, Copy, Debug)] pub struct Geometry { @@ -348,28 +364,51 @@ impl OutputState { } } - pub fn get_pixel_size(&self) -> Option { + fn transform_size( + width: T, + height: T, + transform: self::c::Transform, + ) -> GSize { use self::c::Transform; + + match transform { + Transform::Normal + | Transform::Rotated180 + | Transform::Flipped + | Transform::FlippedRotated180 => GSize { + width, + height, + }, + _ => GSize { + width: height, + height: width, + }, + } + } + + /// Return resolution adjusted for current transform + pub fn get_pixel_size(&self) -> Option { match self { OutputState { current_mode: Some(Mode { width, height } ), geometry: Some(Geometry { transform, .. } ), scale: _, - } => Some( - match transform { - Transform::Normal - | Transform::Rotated180 - | Transform::Flipped - | Transform::FlippedRotated180 => Size { - width: *width as u32, - height: *height as u32, - }, - _ => Size { - width: *height as u32, - height: *width as u32, - }, - } - ), + } => Some(Self::transform_size(*width as u32, *height as u32, *transform)), + OutputState { + current_mode: Some(Mode { width, height } ), + .. + } => Some(Size { width: *width as u32, height: *height as u32 } ), + _ => None, + } + } + + /// Return physical dimensions adjusted for current transform + pub fn get_physical_size(&self) -> Option>> { + match self { + OutputState { + geometry: Some(Geometry { transform, phys_size } ), + .. + } => Some(Self::transform_size(phys_size.width, phys_size.height, *transform)), _ => None, } } diff --git a/src/state.rs b/src/state.rs index 58d8e50e..f3fbe91a 100644 --- a/src/state.rs +++ b/src/state.rs @@ -9,7 +9,7 @@ use crate::animation; use crate::imservice::{ ContentHint, ContentPurpose }; use crate::main::{ Commands, PanelCommand, PixelSize }; use crate::outputs; -use crate::outputs::{OutputId, OutputState}; +use crate::outputs::{Millimeter, OutputId, OutputState}; use crate::util::Rational; use std::cmp; use std::collections::HashMap; @@ -242,23 +242,65 @@ impl Application { fn get_preferred_height(output: &OutputState) -> Option { output.get_pixel_size() .map(|px_size| { + // Assume isotropy. + // Pixels/mm. + let density = output.get_physical_size() + .and_then(|size| size.width) + .map(|width| Rational { + numerator: px_size.width as i32, + denominator: width.0 as u32, + }) + // Whatever the Librem 5 has, + // as a good default. + .unwrap_or(Rational { + numerator: 720, + denominator: 65, + }); + + // Based on what works on the L5. + // Exceeding that probably wastes space. Reducing makes typing harder. + const IDEAL_TARGET_SIZE: Rational = Rational { + numerator: Millimeter(948), + denominator: 100, + }; + + // TODO: calculate based on selected layout + const ROW_COUNT: u32 = 4; + let height = { - if px_size.width > px_size.height { - px_size.width / 5 - } else { - let abstract_width - = PixelSize { - scale_factor: output.scale as u32, - pixels: px_size.width, - } - .as_scaled_ceiling(); - if (abstract_width < 540) && (px_size.width > 0) { - px_size.width * 7 / 12 // to match 360×210 + let ideal_height = IDEAL_TARGET_SIZE * ROW_COUNT as i32; + let ideal_height_px = (ideal_height * density).ceil().0 as u32; + + // Reduce height to match what the layout can fill. + // For this, we need to guess if normal or wide will be picked up. + // This must match `eek_gtk_keyboard.c::get_type`. + // TODO: query layout database and choose one directly + let abstract_width + = PixelSize { + scale_factor: output.scale as u32, + pixels: px_size.width, + } + .as_scaled_ceiling(); + + let height_as_widths = { + if abstract_width < 540 { + // Normal + Rational { + numerator: 210, + denominator: 360, + } } else { - // Here we switch to wide layout, less height needed - px_size.width * 7 / 22 + // Wide + Rational { + numerator: 172, + denominator: 540, + } } - } + }; + cmp::min( + ideal_height_px, + (height_as_widths * px_size.width as i32).ceil() as u32, + ) }; PixelSize { scale_factor: output.scale as u32, diff --git a/src/util.rs b/src/util.rs index f4eb4f52..4a8305ed 100644 --- a/src/util.rs +++ b/src/util.rs @@ -7,6 +7,7 @@ use ::float_ord::FloatOrd; use std::borrow::Borrow; use std::hash::{ Hash, Hasher }; use std::iter::FromIterator; +use std::ops::Mul; pub mod c { use super::*; @@ -157,12 +158,54 @@ pub fn find_max_double(iterator: I, get: F) .0 } +pub trait DivCeil { + type Output; + fn div_ceil(self, rhs: Rhs) -> Self::Output; +} + +/// Newer Rust introduces this natively, +/// but we don't always have newer Rust. +impl DivCeil for i32 { + type Output = Self; + fn div_ceil(self, other: i32) -> Self::Output { + let d = self / other; + let m = self % other; + if m == 0 { d } else { d + 1} + } +} + #[derive(Debug, Clone, Copy)] -pub struct Rational { - pub numerator: i32, +pub struct Rational { + pub numerator: T, pub denominator: u32, } +impl> Rational { + pub fn ceil(self) -> U { + self.numerator.div_ceil(self.denominator as i32) + } +} + +impl> Mul for Rational { + type Output = Self; + fn mul(self, m: i32) -> Self { + Self { + numerator: self.numerator * m, + denominator: self.denominator, + } + } +} + +impl> Mul> for Rational { + type Output = Self; + fn mul(self, m: Rational) -> Self { + Self { + numerator: self.numerator * m.numerator, + denominator: self.denominator * m.denominator, + } + } +} + /// Compares pointers but not internal values of Rc pub struct Pointer(pub Rc); From 14d7d5d4e05e89c1019ec1fe2f81a9f685ac4a61 Mon Sep 17 00:00:00 2001 From: Dorota Czaplejewicz Date: Wed, 6 Apr 2022 16:03:31 +0000 Subject: [PATCH 3/4] Clean up size types --- src/outputs.rs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/outputs.rs b/src/outputs.rs index 1125afaa..22de38c5 100644 --- a/src/outputs.rs +++ b/src/outputs.rs @@ -152,7 +152,7 @@ pub mod c { else { Some(Millimeter(value)) } } state.geometry = Some(Geometry { - phys_size: GSize { + phys_size: Size { width: maybe_mm(phys_width), height: maybe_mm(phys_height), }, @@ -303,13 +303,12 @@ pub mod c { /// Generic size #[derive(Clone, Copy, Debug)] -pub struct GSize { +pub struct Size { pub width: Unit, pub height: Unit, } -/// Unspecified size (TODO: transitional, remove) -pub type Size = GSize; +pub type PixelSize = Size; /// wl_output mode #[derive(Clone, Copy, Debug)] @@ -339,7 +338,7 @@ impl ops::Mul for Millimeter { #[derive(Clone, Copy, Debug)] pub struct Geometry { pub transform: c::Transform, - pub phys_size: GSize>, + pub phys_size: Size>, } #[derive(Clone, Copy, Debug)] @@ -368,18 +367,18 @@ impl OutputState { width: T, height: T, transform: self::c::Transform, - ) -> GSize { + ) -> Size { use self::c::Transform; match transform { Transform::Normal | Transform::Rotated180 | Transform::Flipped - | Transform::FlippedRotated180 => GSize { + | Transform::FlippedRotated180 => Size { width, height, }, - _ => GSize { + _ => Size { width: height, height: width, }, @@ -387,7 +386,7 @@ impl OutputState { } /// Return resolution adjusted for current transform - pub fn get_pixel_size(&self) -> Option { + pub fn get_pixel_size(&self) -> Option { match self { OutputState { current_mode: Some(Mode { width, height } ), @@ -397,13 +396,13 @@ impl OutputState { OutputState { current_mode: Some(Mode { width, height } ), .. - } => Some(Size { width: *width as u32, height: *height as u32 } ), + } => Some(PixelSize { width: *width as u32, height: *height as u32 } ), _ => None, } } /// Return physical dimensions adjusted for current transform - pub fn get_physical_size(&self) -> Option>> { + pub fn get_physical_size(&self) -> Option>> { match self { OutputState { geometry: Some(Geometry { transform, phys_size } ), From 397f5e126e05c1ebe7cbaa03c4235ae64eb45a41 Mon Sep 17 00:00:00 2001 From: Dorota Czaplejewicz Date: Thu, 7 Apr 2022 14:30:46 +0000 Subject: [PATCH 4/4] state: Add sizing unit test --- src/outputs.rs | 4 ++-- src/state.rs | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/outputs.rs b/src/outputs.rs index 22de38c5..a6a8944d 100644 --- a/src/outputs.rs +++ b/src/outputs.rs @@ -313,8 +313,8 @@ pub type PixelSize = Size; /// wl_output mode #[derive(Clone, Copy, Debug)] pub struct Mode { - width: i32, - height: i32, + pub width: i32, + pub height: i32, } #[derive(Clone, Copy, Debug)] diff --git a/src/state.rs b/src/state.rs index f3fbe91a..d5d4437a 100644 --- a/src/state.rs +++ b/src/state.rs @@ -566,4 +566,29 @@ pub mod test { ); } + + #[test] + fn size_l5() { + use crate::outputs::{Mode, Geometry, c, Size}; + assert_eq!( + Application::get_preferred_height(&OutputState { + current_mode: Some(Mode { + width: 720, + height: 1440, + }), + geometry: Some(Geometry{ + transform: c::Transform::Normal, + phys_size: Size { + width: Some(Millimeter(65)), + height: Some(Millimeter(130)), + }, + }), + scale: 2, + }), + Some(PixelSize { + scale_factor: 2, + pixels: 420, + }), + ); + } }