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);