From b14cb51f869c38e36e9c32a01b64fb6bcadaf28a Mon Sep 17 00:00:00 2001 From: drendog Date: Sun, 26 Oct 2025 02:53:19 +0100 Subject: [PATCH] fix: fractional scaling on popup --- src/windowing/popup.rs | 17 ++++++++++++++ src/windowing/popup_manager.rs | 38 +++++++++++++++++++++++++++---- src/windowing/state/dispatches.rs | 4 ++-- src/windowing/state/mod.rs | 34 +++++++++++++++++++++++++++ src/windowing/system.rs | 6 ++--- 5 files changed, 88 insertions(+), 11 deletions(-) diff --git a/src/windowing/popup.rs b/src/windowing/popup.rs index d8bcf09..4181168 100644 --- a/src/windowing/popup.rs +++ b/src/windowing/popup.rs @@ -72,6 +72,23 @@ impl PopupSurface { Rc::new(vp.get_viewport(&surface, params.queue_handle, ())) }); + #[allow(clippy::cast_possible_wrap)] + #[allow(clippy::cast_precision_loss)] + #[allow(clippy::cast_possible_truncation)] + if let Some(ref vp) = viewport { + let logical_width = (params.size.width as f32 / params.scale_factor) as i32; + let logical_height = (params.size.height as f32 / params.scale_factor) as i32; + info!( + "Setting viewport destination to logical size: {}x{} (physical: {}x{}, scale: {})", + logical_width, + logical_height, + params.size.width, + params.size.height, + params.scale_factor + ); + vp.set_destination(logical_width, logical_height); + } + surface.set_buffer_scale(1); surface.commit(); diff --git a/src/windowing/popup_manager.rs b/src/windowing/popup_manager.rs index 5d680bc..aa37721 100644 --- a/src/windowing/popup_manager.rs +++ b/src/windowing/popup_manager.rs @@ -57,30 +57,44 @@ struct ActivePopup { pub struct PopupManager { context: PopupContext, popups: RefCell>, + current_scale_factor: RefCell, } impl PopupManager { - pub const fn new(context: PopupContext) -> Self { + pub const fn new(context: PopupContext, initial_scale_factor: f32) -> Self { Self { context, popups: RefCell::new(Vec::new()), + current_scale_factor: RefCell::new(initial_scale_factor), } } + pub fn update_scale_factor(&self, scale_factor: f32) { + *self.current_scale_factor.borrow_mut() = scale_factor; + } + pub fn create_popup( &self, queue_handle: &QueueHandle, parent_layer_surface: &ZwlrLayerSurfaceV1, last_pointer_serial: u32, - scale_factor: f32, ) -> Result> { let xdg_wm_base = self.context.xdg_wm_base.as_ref().ok_or_else(|| { LayerShikaError::WaylandProtocol("xdg-shell not available for popups".into()) })?; - info!("Creating popup window"); + let scale_factor = *self.current_scale_factor.borrow(); + info!("Creating popup window with scale factor {scale_factor}"); - let popup_size = PhysicalSize::new(360, 524); + let logical_size = slint::LogicalSize::new(360.0, 524.0); + #[allow(clippy::cast_possible_truncation)] + #[allow(clippy::cast_sign_loss)] + let popup_size = PhysicalSize::new( + (logical_size.width * scale_factor) as u32, + (logical_size.height * scale_factor) as u32, + ); + + info!("Popup logical size: {logical_size:?}, physical size: {popup_size:?}"); let popup_surface = PopupSurface::create(&super::popup::PopupSurfaceParams { compositor: &self.context.compositor, @@ -107,8 +121,8 @@ impl PopupManager { .map_err(|e| LayerShikaError::FemtoVGRendererCreation(e.to_string()))?; let popup_window = PopupWindow::new(renderer); - popup_window.set_size(WindowSize::Physical(popup_size)); popup_window.set_scale_factor(scale_factor); + popup_window.set_size(WindowSize::Logical(logical_size)); info!("Popup window created successfully"); @@ -151,6 +165,20 @@ impl PopupManager { None } + pub fn find_popup_index_by_fractional_scale_id( + &self, + fractional_scale_id: &ObjectId, + ) -> Option { + for (index, popup) in self.popups.borrow().iter().enumerate() { + if let Some(ref fs) = popup.surface.fractional_scale { + if fs.id() == *fractional_scale_id { + return Some(index); + } + } + } + None + } + pub fn get_popup_window(&self, index: usize) -> Option> { self.popups .borrow() diff --git a/src/windowing/state/dispatches.rs b/src/windowing/state/dispatches.rs index 6f4a747..1022a81 100644 --- a/src/windowing/state/dispatches.rs +++ b/src/windowing/state/dispatches.rs @@ -218,7 +218,7 @@ impl Dispatch for WindowState { impl Dispatch for WindowState { fn event( state: &mut Self, - _proxy: &WpFractionalScaleV1, + proxy: &WpFractionalScaleV1, event: wp_fractional_scale_v1::Event, _data: &(), _conn: &Connection, @@ -228,7 +228,7 @@ impl Dispatch for WindowState { #[allow(clippy::cast_precision_loss)] let scale_float = scale as f32 / 120.0; info!("Fractional scale received: {scale_float} ({scale}x)"); - state.update_scale_factor(scale); + state.update_scale_for_fractional_scale_object(proxy, scale); } } } diff --git a/src/windowing/state/mod.rs b/src/windowing/state/mod.rs index 44545f1..50577d9 100644 --- a/src/windowing/state/mod.rs +++ b/src/windowing/state/mod.rs @@ -6,6 +6,7 @@ use slint::platform::{WindowAdapter, WindowEvent}; use slint_interpreter::ComponentInstance; use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1; use wayland_client::{protocol::{wl_output::WlOutput, wl_surface::WlSurface}, Proxy}; +use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1; use crate::rendering::femtovg_window::FemtoVGWindow; use crate::errors::{LayerShikaError, Result}; use crate::windowing::surface_dimensions::SurfaceDimensions; @@ -269,6 +270,10 @@ impl WindowState { ); self.scale_factor = new_scale_factor; + if let Some(popup_manager) = &self.popup_manager { + popup_manager.update_scale_factor(new_scale_factor); + } + let current_logical_size = self.logical_size; if current_logical_size.width > 0 && current_logical_size.height > 0 { self.update_size(current_logical_size.width, current_logical_size.height); @@ -324,4 +329,33 @@ impl WindowState { None => {} } } + + #[allow(clippy::cast_precision_loss)] + pub fn update_scale_for_fractional_scale_object( + &mut self, + fractional_scale_proxy: &WpFractionalScaleV1, + scale_120ths: u32, + ) { + let fractional_scale_id = fractional_scale_proxy.id(); + + if let Some(ref main_fractional_scale) = self.fractional_scale { + if (**main_fractional_scale.inner()).id() == fractional_scale_id { + self.update_scale_factor(scale_120ths); + return; + } + } + + if let Some(popup_manager) = &self.popup_manager { + if let Some(popup_index) = + popup_manager.find_popup_index_by_fractional_scale_id(&fractional_scale_id) + { + if let Some(popup_window) = popup_manager.get_popup_window(popup_index) { + let new_scale_factor = scale_120ths as f32 / 120.0; + info!("Updating popup scale factor to {new_scale_factor} ({scale_120ths}x)"); + popup_window.set_scale_factor(new_scale_factor); + popup_window.request_redraw(); + } + } + } + } } diff --git a/src/windowing/system.rs b/src/windowing/system.rs index c24483e..5b72caf 100644 --- a/src/windowing/system.rs +++ b/src/windowing/system.rs @@ -52,7 +52,7 @@ impl WindowingSystem { Rc::clone(&connection), ); - let popup_manager = Rc::new(PopupManager::new(popup_context)); + let popup_manager = Rc::new(PopupManager::new(popup_context, state.scale_factor())); Self::setup_popup_creator(&popup_manager, &platform, &state, &event_queue); @@ -156,14 +156,12 @@ impl WindowingSystem { let popup_manager_clone = Rc::clone(popup_manager); let layer_surface = state.layer_surface(); let queue_handle = event_queue.handle(); - let scale_factor = state.scale_factor(); - let last_serial = state.last_pointer_serial(); platform.set_popup_creator(move || { info!("Popup creator called! Creating popup window..."); let result = popup_manager_clone - .create_popup(&queue_handle, &layer_surface, last_serial, scale_factor) + .create_popup(&queue_handle, &layer_surface, 0) .map(|w| w as Rc) .map_err(|e| PlatformError::Other(format!("Failed to create popup: {e}")));