From 8cd94cd13e5198e05f6c081ade2f85d6af25fd2c Mon Sep 17 00:00:00 2001 From: drendog Date: Mon, 17 Nov 2025 00:22:31 +0100 Subject: [PATCH] refactor: app state as single src of truth for wayland events --- .../wayland/event_handling/app_dispatcher.rs | 122 +---- .../event_handling/event_dispatcher.rs | 446 ++++++------------ .../wayland/event_handling/event_macros.rs | 20 - 3 files changed, 174 insertions(+), 414 deletions(-) diff --git a/crates/adapters/src/wayland/event_handling/app_dispatcher.rs b/crates/adapters/src/wayland/event_handling/app_dispatcher.rs index d52dfc9..a9d495a 100644 --- a/crates/adapters/src/wayland/event_handling/app_dispatcher.rs +++ b/crates/adapters/src/wayland/event_handling/app_dispatcher.rs @@ -1,13 +1,10 @@ use crate::wayland::surfaces::app_state::AppState; use crate::wayland::surfaces::display_metrics::DisplayMetrics; use log::info; -use slint::PhysicalSize; -use slint::platform::{PointerEventButton, WindowEvent}; use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::{ zwlr_layer_shell_v1::ZwlrLayerShellV1, zwlr_layer_surface_v1::{self, ZwlrLayerSurfaceV1}, }; -use wayland_client::WEnum; use wayland_client::{ Connection, Dispatch, Proxy, QueueHandle, globals::GlobalListContents, @@ -35,9 +32,6 @@ use wayland_protocols::xdg::shell::client::{ }; impl Dispatch for AppState { - #[allow(clippy::cast_possible_truncation)] - #[allow(clippy::cast_sign_loss)] - #[allow(clippy::cast_precision_loss)] fn event( state: &mut Self, layer_surface: &ZwlrLayerSurfaceV1, @@ -52,9 +46,6 @@ impl Dispatch for AppState { width, height, } => { - info!("Layer surface configured with compositor size: {width}x{height}"); - layer_surface.ack_configure(serial); - let layer_surface_id = layer_surface.id(); let Some(window) = state.get_output_by_layer_surface_mut(&layer_surface_id) else { info!( @@ -64,46 +55,13 @@ impl Dispatch for AppState { return; }; - let output_width = window.output_size().width; - let scale_factor = window.scale_factor(); - - let target_width = if width == 0 || (width == 1 && output_width > 1) { - if scale_factor > 1.0 { - (output_width as f32 / scale_factor).round() as u32 - } else { - output_width - } - } else { - width - }; - - let target_height = if height > 0 { - height - } else { - let h = window.height(); - if scale_factor > 1.0 { - (h as f32 / scale_factor).round() as u32 - } else { - h - } - }; - - let clamped_width = target_width.min(output_width); - - info!( - "Using dimensions: {}x{} (clamped from {}x{}, output: {}x{})", - clamped_width, - target_height, - target_width, - target_height, - output_width, - window.output_size().height - ); - - window.update_size(clamped_width, target_height); + window.handle_layer_surface_configure(layer_surface, serial, width, height); } zwlr_layer_surface_v1::Event::Closed => { - info!("Layer surface closed"); + let layer_surface_id = layer_surface.id(); + if let Some(window) = state.get_output_by_layer_surface_mut(&layer_surface_id) { + window.handle_layer_surface_closed(); + } } _ => {} } @@ -121,13 +79,9 @@ impl Dispatch for AppState { ) { match event { wl_output::Event::Mode { width, height, .. } => { - info!("WlOutput size changed to {width}x{height}"); - let width = width.try_into().unwrap_or_default(); - let height = height.try_into().unwrap_or_default(); - let output_id = proxy.id(); if let Some(window) = state.get_output_by_output_id_mut(&output_id) { - window.set_output_size(PhysicalSize::new(width, height)); + window.handle_output_mode(width, height); } } wl_output::Event::Description { ref description } => { @@ -182,11 +136,7 @@ impl Dispatch for AppState { let surface_id = surface.id(); if let Some(window) = state.get_output_by_surface_mut(&surface_id) { - window.set_last_pointer_serial(serial); - window.set_current_pointer_position(surface_x, surface_y); - window.set_entered_surface(&surface); - let position = window.current_pointer_position(); - window.dispatch_to_active_window(WindowEvent::PointerMoved { position }); + window.handle_pointer_enter(serial, &surface, surface_x, surface_y); if let Some(key) = state.get_key_by_surface(&surface_id).cloned() { state.set_active_output(Some(key)); @@ -194,11 +144,7 @@ impl Dispatch for AppState { } else { let key = state.get_key_by_popup(&surface_id); if let Some(window) = state.find_output_by_popup_mut(&surface_id) { - window.set_last_pointer_serial(serial); - window.set_current_pointer_position(surface_x, surface_y); - window.set_entered_surface(&surface); - let position = window.current_pointer_position(); - window.dispatch_to_active_window(WindowEvent::PointerMoved { position }); + window.handle_pointer_enter(serial, &surface, surface_x, surface_y); if let Some(key) = key { state.set_active_output(Some(key)); @@ -214,9 +160,7 @@ impl Dispatch for AppState { } => { if let Some(output_key) = state.active_output().cloned() { if let Some(window) = state.get_output_by_key_mut(&output_key) { - window.set_current_pointer_position(surface_x, surface_y); - let position = window.current_pointer_position(); - window.dispatch_to_active_window(WindowEvent::PointerMoved { position }); + window.handle_pointer_motion(surface_x, surface_y); } } } @@ -224,8 +168,7 @@ impl Dispatch for AppState { wl_pointer::Event::Leave { .. } => { if let Some(output_key) = state.active_output().cloned() { if let Some(window) = state.get_output_by_key_mut(&output_key) { - window.dispatch_to_active_window(WindowEvent::PointerExited); - window.clear_entered_surface(); + window.handle_pointer_leave(); } } state.set_active_output(None); @@ -238,21 +181,7 @@ impl Dispatch for AppState { } => { if let Some(output_key) = state.active_output().cloned() { if let Some(window) = state.get_output_by_key_mut(&output_key) { - window.set_last_pointer_serial(serial); - let position = window.current_pointer_position(); - let event = match button_state { - WEnum::Value(wl_pointer::ButtonState::Pressed) => { - WindowEvent::PointerPressed { - button: PointerEventButton::Left, - position, - } - } - _ => WindowEvent::PointerReleased { - button: PointerEventButton::Left, - position, - }, - }; - window.dispatch_to_active_window(event); + window.handle_pointer_button(serial, button_state); } } } @@ -275,7 +204,7 @@ impl Dispatch for AppState { info!("Fractional scale received: {scale_float} ({scale}x)"); for window in state.all_outputs_mut() { - window.update_scale_for_fractional_scale_object(proxy, scale); + window.handle_fractional_scale(proxy, scale); } } } @@ -291,8 +220,8 @@ impl Dispatch for AppState { _qhandle: &QueueHandle, ) { if let xdg_wm_base::Event::Ping { serial } = event { - info!("XdgWmBase ping received, sending pong with serial {serial}"); - xdg_wm_base.pong(serial); + use crate::wayland::surfaces::surface_state::WindowState; + WindowState::handle_xdg_wm_base_ping(xdg_wm_base, serial); } } } @@ -313,17 +242,11 @@ impl Dispatch for AppState { width, height, } => { - info!("XdgPopup Configure: position=({x}, {y}), size=({width}x{height})"); - let popup_id = xdg_popup.id(); for window in state.all_outputs_mut() { if let Some(popup_manager) = window.popup_manager() { - if let Some(handle) = popup_manager.find_by_xdg_popup(&popup_id) { - info!( - "Marking popup with handle {handle:?} as configured after XdgPopup::Configure" - ); - popup_manager.mark_popup_configured(handle.key()); - popup_manager.mark_all_popups_dirty(); + if popup_manager.find_by_xdg_popup(&popup_id).is_some() { + window.handle_xdg_popup_configure(xdg_popup, x, y, width, height); break; } } @@ -339,11 +262,8 @@ impl Dispatch for AppState { .as_ref() .and_then(|pm| pm.find_by_xdg_popup(&popup_id)); - if let Some(handle) = popup_handle { - info!("Destroying popup with handle {handle:?}"); - if let Some(popup_manager) = window.popup_manager() { - let _result = popup_manager.close(handle); - } + if popup_handle.is_some() { + window.handle_xdg_popup_done(xdg_popup); break; } } @@ -366,15 +286,11 @@ impl Dispatch for AppState { _qhandle: &QueueHandle, ) { if let xdg_surface::Event::Configure { serial } = event { - info!("XdgSurface Configure received, sending ack with serial {serial}"); - xdg_surface.ack_configure(serial); - let xdg_surface_id = xdg_surface.id(); for window in state.all_outputs_mut() { if let Some(popup_manager) = window.popup_manager() { if popup_manager.find_by_xdg_surface(&xdg_surface_id).is_some() { - info!("Marking all popups as dirty after Configure"); - popup_manager.mark_all_popups_dirty(); + window.handle_xdg_surface_configure(xdg_surface, serial); break; } } diff --git a/crates/adapters/src/wayland/event_handling/event_dispatcher.rs b/crates/adapters/src/wayland/event_handling/event_dispatcher.rs index bccebdd..9187d18 100644 --- a/crates/adapters/src/wayland/event_handling/event_dispatcher.rs +++ b/crates/adapters/src/wayland/event_handling/event_dispatcher.rs @@ -1,4 +1,3 @@ -use crate::impl_empty_dispatch; use crate::wayland::surfaces::surface_state::WindowState; use log::info; use slint::{ @@ -6,332 +5,197 @@ use slint::{ platform::{PointerEventButton, WindowEvent}, }; use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::{ - zwlr_layer_shell_v1::ZwlrLayerShellV1, - zwlr_layer_surface_v1::{self, ZwlrLayerSurfaceV1}, + zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, }; use wayland_client::WEnum; use wayland_client::{ - Connection, Dispatch, Proxy, QueueHandle, - globals::GlobalListContents, + Proxy, protocol::{ - wl_compositor::WlCompositor, - wl_output::{self, WlOutput}, - wl_pointer::{self, WlPointer}, - wl_registry::WlRegistry, - wl_seat::WlSeat, + wl_pointer, wl_surface::WlSurface, }, }; use wayland_protocols::wp::fractional_scale::v1::client::{ - wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1, - wp_fractional_scale_v1::{self, WpFractionalScaleV1}, -}; -use wayland_protocols::wp::viewporter::client::{ - wp_viewport::WpViewport, wp_viewporter::WpViewporter, + wp_fractional_scale_v1::WpFractionalScaleV1, }; use wayland_protocols::xdg::shell::client::{ - xdg_popup::{self, XdgPopup}, - xdg_positioner::XdgPositioner, - xdg_surface::{self, XdgSurface}, - xdg_wm_base::{self, XdgWmBase}, + xdg_popup::XdgPopup, + xdg_surface::XdgSurface, + xdg_wm_base::XdgWmBase, }; -impl Dispatch for WindowState { +impl WindowState { #[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_sign_loss)] #[allow(clippy::cast_precision_loss)] - fn event( - state: &mut Self, + pub(crate) fn handle_layer_surface_configure( + &mut self, layer_surface: &ZwlrLayerSurfaceV1, - event: zwlr_layer_surface_v1::Event, - _data: &(), - _conn: &Connection, - _queue_handle: &QueueHandle, + serial: u32, + width: u32, + height: u32, ) { - match event { - zwlr_layer_surface_v1::Event::Configure { - serial, - width, - height, - } => { - info!("Layer surface configured with compositor size: {width}x{height}"); - layer_surface.ack_configure(serial); + info!("Layer surface configured with compositor size: {width}x{height}"); + layer_surface.ack_configure(serial); - let output_width = state.output_size().width; - let scale_factor = state.scale_factor(); + let output_width = self.output_size().width; + let scale_factor = self.scale_factor(); - let target_width = if width == 0 || (width == 1 && output_width > 1) { - if scale_factor > 1.0 { - (output_width as f32 / scale_factor).round() as u32 - } else { - output_width - } - } else { - width - }; - - let target_height = if height > 0 { - height - } else { - let h = state.height(); - if scale_factor > 1.0 { - (h as f32 / scale_factor).round() as u32 - } else { - h - } - }; - - let clamped_width = target_width.min(output_width); - - info!( - "Using dimensions: {}x{} (clamped from {}x{}, output: {}x{})", - clamped_width, - target_height, - target_width, - target_height, - output_width, - state.output_size().height - ); - - state.update_size(clamped_width, target_height); + let target_width = if width == 0 || (width == 1 && output_width > 1) { + if scale_factor > 1.0 { + (output_width as f32 / scale_factor).round() as u32 + } else { + output_width } - zwlr_layer_surface_v1::Event::Closed => { - info!("Layer surface closed"); + } else { + width + }; + + let target_height = if height > 0 { + height + } else { + let h = self.height(); + if scale_factor > 1.0 { + (h as f32 / scale_factor).round() as u32 + } else { + h } - _ => {} - } + }; + + let clamped_width = target_width.min(output_width); + + info!( + "Using dimensions: {}x{} (clamped from {}x{}, output: {}x{})", + clamped_width, + target_height, + target_width, + target_height, + output_width, + self.output_size().height + ); + + self.update_size(clamped_width, target_height); } -} -impl Dispatch for WindowState { - fn event( - state: &mut Self, - _proxy: &WlOutput, - event: ::Event, - _data: &(), - _conn: &Connection, - _qhandle: &QueueHandle, + #[allow(clippy::unused_self)] + pub(crate) fn handle_layer_surface_closed(&mut self) { + info!("Layer surface closed"); + } + + pub(crate) fn handle_output_mode(&mut self, width: i32, height: i32) { + info!("WlOutput size changed to {width}x{height}"); + let width = width.try_into().unwrap_or_default(); + let height = height.try_into().unwrap_or_default(); + self.set_output_size(PhysicalSize::new(width, height)); + } + + pub(crate) fn handle_pointer_enter( + &mut self, + serial: u32, + surface: &WlSurface, + surface_x: f64, + surface_y: f64, ) { - match event { - wl_output::Event::Mode { width, height, .. } => { - info!("WlOutput size changed to {width}x{height}"); - let width = width.try_into().unwrap_or_default(); - let height = height.try_into().unwrap_or_default(); - state.set_output_size(PhysicalSize::new(width, height)); - } - wl_output::Event::Description { ref description } => { - info!("WlOutput description: {description:?}"); - } - wl_output::Event::Scale { ref factor } => { - info!("WlOutput factor scale: {factor:?}"); - } - wl_output::Event::Name { ref name } => { - info!("WlOutput name: {name:?}"); - } - wl_output::Event::Geometry { - x, - y, - physical_width, - physical_height, - subpixel, - make, - model, - transform, - } => { - info!( - "WlOutput geometry: x={x}, y={y}, physical_width={physical_width}, physical_height={physical_height}, subpixel={subpixel:?}, make={make:?}, model={model:?}, transform={transform:?}" - ); - } - wl_output::Event::Done => { - info!("WlOutput done"); - } - _ => {} - } - } -} + info!("Pointer entered surface {:?}", surface.id()); + self.set_last_pointer_serial(serial); + self.set_current_pointer_position(surface_x, surface_y); -impl Dispatch for WindowState { - fn event( - state: &mut Self, - _proxy: &WlPointer, - event: ::Event, - _data: &(), - _conn: &Connection, - _qhandle: &QueueHandle, + self.set_entered_surface(surface); + let position = self.current_pointer_position(); + + self.dispatch_to_active_window(WindowEvent::PointerMoved { position }); + } + + pub(crate) fn handle_pointer_motion(&mut self, surface_x: f64, surface_y: f64) { + self.set_current_pointer_position(surface_x, surface_y); + let position = self.current_pointer_position(); + + self.dispatch_to_active_window(WindowEvent::PointerMoved { position }); + } + + pub(crate) fn handle_pointer_leave(&mut self) { + self.dispatch_to_active_window(WindowEvent::PointerExited); + self.clear_entered_surface(); + } + + pub(crate) fn handle_pointer_button( + &mut self, + serial: u32, + button_state: WEnum, ) { - match event { - wl_pointer::Event::Enter { - serial, - surface, - surface_x, - surface_y, - } => { - info!("Pointer entered surface {:?}", surface.id()); - state.set_last_pointer_serial(serial); - state.set_current_pointer_position(surface_x, surface_y); + self.set_last_pointer_serial(serial); + let position = self.current_pointer_position(); + let event = match button_state { + WEnum::Value(wl_pointer::ButtonState::Pressed) => WindowEvent::PointerPressed { + button: PointerEventButton::Left, + position, + }, + _ => WindowEvent::PointerReleased { + button: PointerEventButton::Left, + position, + }, + }; - state.set_entered_surface(&surface); - let position = state.current_pointer_position(); - - state.dispatch_to_active_window(WindowEvent::PointerMoved { position }); - } - - wl_pointer::Event::Motion { - surface_x, - surface_y, - .. - } => { - state.set_current_pointer_position(surface_x, surface_y); - let position = state.current_pointer_position(); - - state.dispatch_to_active_window(WindowEvent::PointerMoved { position }); - } - - wl_pointer::Event::Leave { .. } => { - state.dispatch_to_active_window(WindowEvent::PointerExited); - state.clear_entered_surface(); - } - - wl_pointer::Event::Button { - serial, - state: button_state, - .. - } => { - state.set_last_pointer_serial(serial); - let position = state.current_pointer_position(); - let event = match button_state { - WEnum::Value(wl_pointer::ButtonState::Pressed) => WindowEvent::PointerPressed { - button: PointerEventButton::Left, - position, - }, - _ => WindowEvent::PointerReleased { - button: PointerEventButton::Left, - position, - }, - }; - - state.dispatch_to_active_window(event); - } - _ => {} - } + self.dispatch_to_active_window(event); } -} -impl Dispatch for WindowState { - fn event( - state: &mut Self, - proxy: &WpFractionalScaleV1, - event: wp_fractional_scale_v1::Event, - _data: &(), - _conn: &Connection, - _qhandle: &QueueHandle, - ) { - if let wp_fractional_scale_v1::Event::PreferredScale { scale } = event { - use crate::wayland::surfaces::display_metrics::DisplayMetrics; - let scale_float = DisplayMetrics::scale_factor_from_120ths(scale); - info!("Fractional scale received: {scale_float} ({scale}x)"); - state.update_scale_for_fractional_scale_object(proxy, scale); - } + pub(crate) fn handle_fractional_scale(&mut self, proxy: &WpFractionalScaleV1, scale: u32) { + use crate::wayland::surfaces::display_metrics::DisplayMetrics; + let scale_float = DisplayMetrics::scale_factor_from_120ths(scale); + info!("Fractional scale received: {scale_float} ({scale}x)"); + self.update_scale_for_fractional_scale_object(proxy, scale); } -} -impl Dispatch for WindowState { - fn event( - _state: &mut Self, - xdg_wm_base: &XdgWmBase, - event: xdg_wm_base::Event, - _data: &(), - _conn: &Connection, - _qhandle: &QueueHandle, - ) { - if let xdg_wm_base::Event::Ping { serial } = event { - info!("XdgWmBase ping received, sending pong with serial {serial}"); - xdg_wm_base.pong(serial); - } - } -} - -impl Dispatch for WindowState { - fn event( - state: &mut Self, + pub(crate) fn handle_xdg_popup_configure( + &mut self, xdg_popup: &XdgPopup, - event: xdg_popup::Event, - _data: &(), - _conn: &Connection, - _qhandle: &QueueHandle, + x: i32, + y: i32, + width: i32, + height: i32, ) { - match event { - xdg_popup::Event::Configure { - x, - y, - width, - height, - } => { - info!("XdgPopup Configure: position=({x}, {y}), size=({width}x{height})"); + info!("XdgPopup Configure: position=({x}, {y}), size=({width}x{height})"); - if let Some(popup_manager) = state.popup_manager() { - let popup_id = xdg_popup.id(); - if let Some(handle) = popup_manager.find_by_xdg_popup(&popup_id) { - info!( - "Marking popup with handle {handle:?} as configured after XdgPopup::Configure" - ); - popup_manager.mark_popup_configured(handle.key()); - popup_manager.mark_all_popups_dirty(); - } - } - } - xdg_popup::Event::PopupDone => { - info!("XdgPopup dismissed by compositor"); - let popup_id = xdg_popup.id(); - let popup_handle = state - .popup_manager() - .as_ref() - .and_then(|pm| pm.find_by_xdg_popup(&popup_id)); - - if let Some(handle) = popup_handle { - info!("Destroying popup with handle {handle:?}"); - if let Some(popup_manager) = state.popup_manager() { - let _result = popup_manager.close(handle); - } - } - } - xdg_popup::Event::Repositioned { token } => { - info!("XdgPopup repositioned with token {token}"); - } - _ => {} - } - } -} - -impl Dispatch for WindowState { - fn event( - state: &mut Self, - xdg_surface: &XdgSurface, - event: xdg_surface::Event, - _data: &(), - _conn: &Connection, - _qhandle: &QueueHandle, - ) { - if let xdg_surface::Event::Configure { serial } = event { - info!("XdgSurface Configure received, sending ack with serial {serial}"); - xdg_surface.ack_configure(serial); - - if let Some(popup_manager) = state.popup_manager() { - info!("Marking all popups as dirty after Configure"); + if let Some(popup_manager) = self.popup_manager() { + let popup_id = xdg_popup.id(); + if let Some(handle) = popup_manager.find_by_xdg_popup(&popup_id) { + info!( + "Marking popup with handle {handle:?} as configured after XdgPopup::Configure" + ); + popup_manager.mark_popup_configured(handle.key()); popup_manager.mark_all_popups_dirty(); } } } -} -impl_empty_dispatch!( - (WlRegistry, GlobalListContents), - (WlCompositor, ()), - (WlSurface, ()), - (ZwlrLayerShellV1, ()), - (WlSeat, ()), - (WpFractionalScaleManagerV1, ()), - (WpViewporter, ()), - (WpViewport, ()), - (XdgPositioner, ()) -); + pub(crate) fn handle_xdg_popup_done(&mut self, xdg_popup: &XdgPopup) { + info!("XdgPopup dismissed by compositor"); + let popup_id = xdg_popup.id(); + let popup_handle = self + .popup_manager() + .as_ref() + .and_then(|pm| pm.find_by_xdg_popup(&popup_id)); + + if let Some(handle) = popup_handle { + info!("Destroying popup with handle {handle:?}"); + if let Some(popup_manager) = self.popup_manager() { + let _result = popup_manager.close(handle); + } + } + } + + pub(crate) fn handle_xdg_surface_configure(&mut self, xdg_surface: &XdgSurface, serial: u32) { + info!("XdgSurface Configure received, sending ack with serial {serial}"); + xdg_surface.ack_configure(serial); + + if let Some(popup_manager) = self.popup_manager() { + info!("Marking all popups as dirty after Configure"); + popup_manager.mark_all_popups_dirty(); + } + } + + pub(crate) fn handle_xdg_wm_base_ping(xdg_wm_base: &XdgWmBase, serial: u32) { + info!("XdgWmBase ping received, sending pong with serial {serial}"); + xdg_wm_base.pong(serial); + } +} diff --git a/crates/adapters/src/wayland/event_handling/event_macros.rs b/crates/adapters/src/wayland/event_handling/event_macros.rs index 0bab1e4..bf15da9 100644 --- a/crates/adapters/src/wayland/event_handling/event_macros.rs +++ b/crates/adapters/src/wayland/event_handling/event_macros.rs @@ -1,23 +1,3 @@ -#[macro_export] -macro_rules! impl_empty_dispatch { - ($(($t:ty, $u:ty)),+) => { - $( - impl Dispatch<$t, $u> for WindowState { - fn event( - _state: &mut Self, - _proxy: &$t, - _event: <$t as wayland_client::Proxy>::Event, - _data: &$u, - _conn: &Connection, - _qhandle: &QueueHandle, - ) { - info!("Implement empty dispatch event for {:?}", stringify!($t)); - } - } - )+ - }; -} - #[macro_export] macro_rules! bind_globals { ($global_list:expr, $queue_handle:expr, $(($interface:ty, $name:ident, $version:expr)),+) => {