From 06453367bdec7dedbbc3c35efc3890f4e0263d7b Mon Sep 17 00:00:00 2001 From: drendog Date: Thu, 13 Nov 2025 08:59:15 +0100 Subject: [PATCH] refactor: display metrics consolidation --- adapters/src/wayland/shell_adapter.rs | 5 +- .../src/wayland/surfaces/display_metrics.rs | 139 ++++++++++++++++++ .../src/wayland/surfaces/event_context.rs | 40 ++--- adapters/src/wayland/surfaces/mod.rs | 1 + .../src/wayland/surfaces/popup_manager.rs | 48 ++++-- .../src/wayland/surfaces/surface_state.rs | 30 +++- 6 files changed, 225 insertions(+), 38 deletions(-) create mode 100644 adapters/src/wayland/surfaces/display_metrics.rs diff --git a/adapters/src/wayland/shell_adapter.rs b/adapters/src/wayland/shell_adapter.rs index 63285f4..78deca5 100644 --- a/adapters/src/wayland/shell_adapter.rs +++ b/adapters/src/wayland/shell_adapter.rs @@ -60,7 +60,10 @@ impl WaylandWindowingSystem { Rc::clone(&connection), ); - let popup_manager = Rc::new(PopupManager::new(popup_context, state.scale_factor())); + let popup_manager = Rc::new(PopupManager::new( + popup_context, + Rc::clone(state.display_metrics()), + )); let popup_service = Rc::new(PopupService::new(popup_manager)); let shared_serial = Rc::new(SharedPointerSerial::new()); diff --git a/adapters/src/wayland/surfaces/display_metrics.rs b/adapters/src/wayland/surfaces/display_metrics.rs new file mode 100644 index 0000000..7a6b581 --- /dev/null +++ b/adapters/src/wayland/surfaces/display_metrics.rs @@ -0,0 +1,139 @@ +use log::info; +use slint::PhysicalSize; +use std::cell::RefCell; +use std::rc::{Rc, Weak}; + +pub trait DisplayMetricsObserver { + fn on_scale_factor_changed(&self, new_scale: f32); + fn on_output_size_changed(&self, new_size: PhysicalSize); +} + +pub struct DisplayMetrics { + scale_factor: f32, + output_size: PhysicalSize, + surface_size: PhysicalSize, + has_fractional_scale: bool, + observers: RefCell>>, +} + +impl DisplayMetrics { + #[must_use] + pub fn new(scale_factor: f32, has_fractional_scale: bool) -> Self { + Self { + scale_factor, + output_size: PhysicalSize::new(0, 0), + surface_size: PhysicalSize::new(0, 0), + has_fractional_scale, + observers: RefCell::new(Vec::new()), + } + } + + #[must_use] + pub fn with_output_size(mut self, output_size: PhysicalSize) -> Self { + self.output_size = output_size; + self + } + + #[must_use] + pub const fn scale_factor(&self) -> f32 { + self.scale_factor + } + + #[must_use] + pub const fn output_size(&self) -> PhysicalSize { + self.output_size + } + + #[must_use] + pub const fn surface_size(&self) -> PhysicalSize { + self.surface_size + } + + #[must_use] + pub const fn has_fractional_scale(&self) -> bool { + self.has_fractional_scale + } + + pub fn register_observer(&self, observer: Weak) { + self.observers.borrow_mut().push(observer); + self.cleanup_dead_observers(); + } + + #[allow(clippy::cast_precision_loss)] + pub fn update_scale_factor(&mut self, scale_120ths: u32) -> f32 { + let new_scale_factor = scale_120ths as f32 / 120.0; + let old_scale_factor = self.scale_factor; + + if (self.scale_factor - new_scale_factor).abs() > f32::EPSILON { + info!( + "DisplayMetrics: Updating scale factor from {} to {} ({}x)", + old_scale_factor, new_scale_factor, scale_120ths + ); + self.scale_factor = new_scale_factor; + self.recalculate_surface_size(); + self.notify_scale_factor_changed(new_scale_factor); + } + + new_scale_factor + } + + pub fn update_output_size(&mut self, output_size: PhysicalSize) { + if self.output_size != output_size { + info!( + "DisplayMetrics: Updating output size from {:?} to {:?}", + self.output_size, output_size + ); + self.output_size = output_size; + self.recalculate_surface_size(); + self.notify_output_size_changed(output_size); + } + } + + pub fn update_surface_size(&mut self, surface_size: PhysicalSize) { + self.surface_size = surface_size; + } + + #[allow( + clippy::cast_possible_truncation, + clippy::cast_sign_loss, + clippy::cast_precision_loss + )] + fn recalculate_surface_size(&mut self) { + if self.output_size.width > 0 && self.output_size.height > 0 && self.scale_factor > 0.0 { + self.surface_size = PhysicalSize::new( + (self.output_size.width as f32 / self.scale_factor) as u32, + (self.output_size.height as f32 / self.scale_factor) as u32, + ); + } + } + + fn notify_scale_factor_changed(&self, new_scale: f32) { + self.observers.borrow_mut().retain(|observer| { + if let Some(obs) = observer.upgrade() { + obs.on_scale_factor_changed(new_scale); + true + } else { + false + } + }); + } + + fn notify_output_size_changed(&self, new_size: PhysicalSize) { + self.observers.borrow_mut().retain(|observer| { + if let Some(obs) = observer.upgrade() { + obs.on_output_size_changed(new_size); + true + } else { + false + } + }); + } + + fn cleanup_dead_observers(&self) { + self.observers + .borrow_mut() + .retain(|obs| obs.upgrade().is_some()); + } +} + +pub type SharedDisplayMetrics = Rc>; diff --git a/adapters/src/wayland/surfaces/event_context.rs b/adapters/src/wayland/surfaces/event_context.rs index 40ef62f..c57e4a3 100644 --- a/adapters/src/wayland/surfaces/event_context.rs +++ b/adapters/src/wayland/surfaces/event_context.rs @@ -1,10 +1,10 @@ use crate::rendering::femtovg::main_window::FemtoVGWindow; use crate::wayland::services::popup_service::{ActiveWindow, PopupService}; +use crate::wayland::surfaces::display_metrics::SharedDisplayMetrics; use crate::wayland::surfaces::event_bus::EventBus; use crate::wayland::surfaces::popup_manager::PopupManager; use crate::wayland::surfaces::window_events::{ScaleSource, WindowStateEvent}; use layer_shika_domain::value_objects::popup_request::PopupHandle; -use log::info; use slint::platform::{WindowAdapter, WindowEvent}; use slint::{LogicalPosition, PhysicalSize}; use std::cell::Cell; @@ -43,8 +43,7 @@ pub struct EventContext { main_surface_id: ObjectId, popup_service: Option>, event_bus: EventBus, - scale_factor: f32, - has_fractional_scale: bool, + display_metrics: SharedDisplayMetrics, current_pointer_position: LogicalPosition, last_pointer_serial: u32, shared_pointer_serial: Option>, @@ -55,16 +54,14 @@ impl EventContext { pub fn new( main_window: Rc, main_surface_id: ObjectId, - scale_factor: f32, - has_fractional_scale: bool, + display_metrics: SharedDisplayMetrics, ) -> Self { Self { main_window, main_surface_id, popup_service: None, event_bus: EventBus::new(), - scale_factor, - has_fractional_scale, + display_metrics, current_pointer_position: LogicalPosition::new(0.0, 0.0), last_pointer_serial: 0, shared_pointer_serial: None, @@ -95,19 +92,21 @@ impl EventContext { .map(|service| Rc::clone(service.manager())) } - pub const fn scale_factor(&self) -> f32 { - self.scale_factor + #[must_use] + pub fn scale_factor(&self) -> f32 { + self.display_metrics.borrow().scale_factor() + } + + pub const fn display_metrics(&self) -> &SharedDisplayMetrics { + &self.display_metrics } #[allow(clippy::cast_precision_loss)] pub fn update_scale_factor(&mut self, scale_120ths: u32) -> f32 { - let new_scale_factor = scale_120ths as f32 / 120.0; - let old_scale_factor = self.scale_factor; - info!( - "Updating scale factor from {} to {} ({}x)", - old_scale_factor, new_scale_factor, scale_120ths - ); - self.scale_factor = new_scale_factor; + let new_scale_factor = self + .display_metrics + .borrow_mut() + .update_scale_factor(scale_120ths); if let Some(popup_service) = &self.popup_service { popup_service.update_scale_factor(new_scale_factor); @@ -128,12 +127,15 @@ impl EventContext { #[allow(clippy::cast_possible_truncation)] pub fn set_current_pointer_position(&mut self, physical_x: f64, physical_y: f64) { - let logical_position = if self.has_fractional_scale { + let has_fractional_scale = self.display_metrics.borrow().has_fractional_scale(); + let scale_factor = self.display_metrics.borrow().scale_factor(); + + let logical_position = if has_fractional_scale { LogicalPosition::new(physical_x as f32, physical_y as f32) } else { LogicalPosition::new( - (physical_x / f64::from(self.scale_factor)) as f32, - (physical_y / f64::from(self.scale_factor)) as f32, + (physical_x / f64::from(scale_factor)) as f32, + (physical_y / f64::from(scale_factor)) as f32, ) }; self.current_pointer_position = logical_position; diff --git a/adapters/src/wayland/surfaces/mod.rs b/adapters/src/wayland/surfaces/mod.rs index f76309e..6ab6151 100644 --- a/adapters/src/wayland/surfaces/mod.rs +++ b/adapters/src/wayland/surfaces/mod.rs @@ -1,5 +1,6 @@ pub mod component_state; pub mod dimensions; +pub mod display_metrics; pub mod event_bus; pub mod event_context; pub mod event_router; diff --git a/adapters/src/wayland/surfaces/popup_manager.rs b/adapters/src/wayland/surfaces/popup_manager.rs index 34102ce..9a2bde9 100644 --- a/adapters/src/wayland/surfaces/popup_manager.rs +++ b/adapters/src/wayland/surfaces/popup_manager.rs @@ -1,6 +1,7 @@ use crate::errors::{LayerShikaError, Result}; use crate::rendering::egl::context::EGLContext; use crate::rendering::femtovg::popup_window::PopupWindow; +use crate::wayland::surfaces::display_metrics::{DisplayMetricsObserver, SharedDisplayMetrics}; use layer_shika_domain::value_objects::popup_config::PopupConfig; use layer_shika_domain::value_objects::popup_positioning_mode::PopupPositioningMode; use layer_shika_domain::value_objects::popup_request::PopupRequest; @@ -101,19 +102,17 @@ struct PendingPopup { struct PopupManagerState { popups: HashMap, - scale_factor: f32, - output_size: PhysicalSize, + display_metrics: SharedDisplayMetrics, current_popup_id: Option, pending_popup: Option, id_generator: usize, } impl PopupManagerState { - fn new(initial_scale_factor: f32) -> Self { + fn new(display_metrics: SharedDisplayMetrics) -> Self { Self { popups: HashMap::new(), - scale_factor: initial_scale_factor, - output_size: PhysicalSize::new(0, 0), + display_metrics, current_popup_id: None, pending_popup: None, id_generator: 0, @@ -128,10 +127,10 @@ pub struct PopupManager { impl PopupManager { #[must_use] - pub fn new(context: PopupContext, initial_scale_factor: f32) -> Self { + pub fn new(context: PopupContext, display_metrics: SharedDisplayMetrics) -> Self { Self { context, - state: RefCell::new(PopupManagerState::new(initial_scale_factor)), + state: RefCell::new(PopupManagerState::new(display_metrics)), } } @@ -154,20 +153,34 @@ impl PopupManager { #[must_use] pub fn scale_factor(&self) -> f32 { - self.state.borrow().scale_factor + self.state.borrow().display_metrics.borrow().scale_factor() } #[must_use] pub fn output_size(&self) -> PhysicalSize { - self.state.borrow().output_size + self.state.borrow().display_metrics.borrow().output_size() } + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] pub fn update_scale_factor(&self, scale_factor: f32) { - self.state.borrow_mut().scale_factor = scale_factor; + let scale_120ths = (scale_factor * 120.0) as u32; + self.state + .borrow() + .display_metrics + .borrow_mut() + .update_scale_factor(scale_120ths); + + for popup in self.state.borrow().popups.values() { + popup.window.set_scale_factor(scale_factor); + } } pub fn update_output_size(&self, output_size: PhysicalSize) { - self.state.borrow_mut().output_size = output_size; + self.state + .borrow() + .display_metrics + .borrow_mut() + .update_output_size(output_size); } pub fn close_current_popup(&self) { @@ -390,3 +403,16 @@ impl PopupManager { } } } + +impl DisplayMetricsObserver for PopupManager { + fn on_scale_factor_changed(&self, new_scale: f32) { + info!("PopupManager received scale factor change: {}", new_scale); + for popup in self.state.borrow().popups.values() { + popup.window.set_scale_factor(new_scale); + } + } + + fn on_output_size_changed(&self, new_size: PhysicalSize) { + info!("PopupManager received output size change: {:?}", new_size); + } +} diff --git a/adapters/src/wayland/surfaces/surface_state.rs b/adapters/src/wayland/surfaces/surface_state.rs index 4dc45a3..baeaf45 100644 --- a/adapters/src/wayland/surfaces/surface_state.rs +++ b/adapters/src/wayland/surfaces/surface_state.rs @@ -6,6 +6,7 @@ use super::rendering_state::RenderingState; use super::event_context::{EventContext, SharedPointerSerial}; use super::popup_manager::PopupManager; use super::window_renderer::WindowRendererParams; +use super::display_metrics::{DisplayMetrics, SharedDisplayMetrics}; use crate::wayland::managed_proxies::{ ManagedWlPointer, ManagedWlSurface, ManagedZwlrLayerSurfaceV1, ManagedWpFractionalScaleV1, ManagedWpViewport, @@ -28,9 +29,9 @@ pub struct WindowState { component: ComponentState, rendering: RenderingState, event_context: EventContext, + display_metrics: SharedDisplayMetrics, #[allow(dead_code)] pointer: ManagedWlPointer, - output_size: PhysicalSize, active_popup_key: RefCell>, main_surface: Rc, } @@ -90,11 +91,16 @@ impl WindowState { let size = builder.size.unwrap_or_default(); let main_surface_id = (*surface_rc).id(); + + let display_metrics = Rc::new(RefCell::new( + DisplayMetrics::new(builder.scale_factor, has_fractional_scale) + .with_output_size(builder.output_size.unwrap_or_default()), + )); + let event_context = EventContext::new( Rc::clone(&window), main_surface_id, - builder.scale_factor, - has_fractional_scale, + Rc::clone(&display_metrics), ); let rendering = RenderingState::new(WindowRendererParams { @@ -112,8 +118,8 @@ impl WindowState { component, rendering, event_context, + display_metrics, pointer, - output_size: builder.output_size.unwrap_or_default(), active_popup_key: RefCell::new(None), main_surface: surface_rc, }) @@ -151,12 +157,14 @@ impl WindowState { } pub fn set_output_size(&mut self, output_size: PhysicalSize) { - self.output_size = output_size; + self.display_metrics + .borrow_mut() + .update_output_size(output_size); self.event_context.update_output_size(output_size); } - pub const fn output_size(&self) -> PhysicalSize { - self.output_size + pub fn output_size(&self) -> PhysicalSize { + self.display_metrics.borrow().output_size() } pub const fn component_instance(&self) -> &ComponentInstance { @@ -186,6 +194,10 @@ impl WindowState { self.event_context.scale_factor() } + pub const fn display_metrics(&self) -> &SharedDisplayMetrics { + &self.display_metrics + } + pub fn last_pointer_serial(&self) -> u32 { self.event_context.last_pointer_serial() } @@ -203,6 +215,10 @@ impl WindowState { } pub fn set_popup_manager(&mut self, popup_manager: Rc) { + self.display_metrics + .borrow() + .register_observer(Rc::downgrade(&popup_manager) as _); + let popup_service = Rc::new(PopupService::new(popup_manager)); self.event_context.set_popup_service(popup_service); }