From fe9df4fd1b4147c85a9383d76f148354952c0417 Mon Sep 17 00:00:00 2001 From: drendog Date: Tue, 11 Nov 2025 20:54:28 +0100 Subject: [PATCH] refactor: event-driven state mutation --- adapters/src/wayland/surfaces/event_bus.rs | 42 +++++++++++++++ .../src/wayland/surfaces/interaction_state.rs | 27 +++++++++- adapters/src/wayland/surfaces/mod.rs | 2 + adapters/src/wayland/surfaces/popup_state.rs | 17 +++++- .../src/wayland/surfaces/rendering_state.rs | 13 +++++ .../src/wayland/surfaces/surface_state.rs | 41 +++++++++++---- .../src/wayland/surfaces/window_events.rs | 52 +++++++++++++++++++ 7 files changed, 182 insertions(+), 12 deletions(-) create mode 100644 adapters/src/wayland/surfaces/event_bus.rs create mode 100644 adapters/src/wayland/surfaces/window_events.rs diff --git a/adapters/src/wayland/surfaces/event_bus.rs b/adapters/src/wayland/surfaces/event_bus.rs new file mode 100644 index 0000000..44f7fe1 --- /dev/null +++ b/adapters/src/wayland/surfaces/event_bus.rs @@ -0,0 +1,42 @@ +use super::window_events::WindowStateEvent; +use std::cell::RefCell; +use std::rc::Rc; + +type EventHandlerFn = Box; + +#[derive(Clone)] +pub struct EventBus { + handlers: Rc>>, +} + +impl Default for EventBus { + fn default() -> Self { + Self::new() + } +} + +impl EventBus { + pub fn new() -> Self { + Self { + handlers: Rc::new(RefCell::new(Vec::new())), + } + } + + pub fn subscribe(&self, handler: F) + where + F: Fn(&WindowStateEvent) + 'static, + { + self.handlers.borrow_mut().push(Box::new(handler)); + } + + pub fn publish(&self, event: &WindowStateEvent) { + let handlers = self.handlers.borrow(); + for handler in handlers.iter() { + handler(event); + } + } + + pub fn clear(&self) { + self.handlers.borrow_mut().clear(); + } +} diff --git a/adapters/src/wayland/surfaces/interaction_state.rs b/adapters/src/wayland/surfaces/interaction_state.rs index 19828b0..1e666b8 100644 --- a/adapters/src/wayland/surfaces/interaction_state.rs +++ b/adapters/src/wayland/surfaces/interaction_state.rs @@ -1,7 +1,9 @@ use crate::wayland::managed_proxies::ManagedWlPointer; use crate::wayland::services::popup_service::PopupService; +use crate::wayland::surfaces::event_bus::EventBus; use crate::wayland::surfaces::event_router::EventRouter; use crate::wayland::surfaces::scale_coordinator::{ScaleCoordinator, SharedPointerSerial}; +use crate::wayland::surfaces::window_events::{ScaleSource, WindowStateEvent}; use slint::LogicalPosition; use slint::platform::WindowEvent; use std::rc::Rc; @@ -12,6 +14,7 @@ pub struct InteractionState { pointer: ManagedWlPointer, event_router: EventRouter, scale_coordinator: ScaleCoordinator, + event_bus: EventBus, } impl InteractionState { @@ -25,13 +28,24 @@ impl InteractionState { pointer, event_router, scale_coordinator, + event_bus: EventBus::new(), } } + pub fn set_event_bus(&mut self, event_bus: EventBus) { + self.event_bus = event_bus; + } + #[allow(clippy::cast_possible_truncation)] pub fn set_current_pointer_position(&mut self, physical_x: f64, physical_y: f64) { self.scale_coordinator .set_current_pointer_position(physical_x, physical_y); + + self.event_bus + .publish(&WindowStateEvent::PointerPositionChanged { + physical_x, + physical_y, + }); } pub fn current_pointer_position(&self) -> LogicalPosition { @@ -44,6 +58,9 @@ impl InteractionState { pub fn set_last_pointer_serial(&mut self, serial: u32) { self.scale_coordinator.set_last_pointer_serial(serial); + + self.event_bus + .publish(&WindowStateEvent::PointerSerialUpdated { serial }); } pub fn set_shared_pointer_serial(&mut self, shared_serial: Rc) { @@ -65,6 +82,14 @@ impl InteractionState { #[allow(clippy::cast_precision_loss)] pub fn update_scale_factor(&mut self, scale_120ths: u32) -> f32 { - self.scale_coordinator.update_scale_factor(scale_120ths) + let new_scale = self.scale_coordinator.update_scale_factor(scale_120ths); + + self.event_bus + .publish(&WindowStateEvent::ScaleFactorChanged { + new_scale, + source: ScaleSource::FractionalScale, + }); + + new_scale } } diff --git a/adapters/src/wayland/surfaces/mod.rs b/adapters/src/wayland/surfaces/mod.rs index 3da3df6..fb1fe31 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 event_bus; pub mod event_router; pub mod interaction_state; pub mod layer_surface; @@ -10,4 +11,5 @@ pub mod rendering_state; pub mod scale_coordinator; pub mod surface_builder; pub mod surface_state; +pub mod window_events; pub mod window_renderer; diff --git a/adapters/src/wayland/surfaces/popup_state.rs b/adapters/src/wayland/surfaces/popup_state.rs index 84b3f5c..80b5ecb 100644 --- a/adapters/src/wayland/surfaces/popup_state.rs +++ b/adapters/src/wayland/surfaces/popup_state.rs @@ -1,11 +1,14 @@ use crate::wayland::services::popup_service::PopupService; +use crate::wayland::surfaces::event_bus::EventBus; use crate::wayland::surfaces::popup_manager::PopupManager; +use crate::wayland::surfaces::window_events::WindowStateEvent; use slint::PhysicalSize; use std::rc::Rc; use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1; pub struct PopupState { popup_service: Option>, + event_bus: EventBus, } impl Default for PopupState { @@ -16,24 +19,36 @@ impl Default for PopupState { impl PopupState { #[must_use] - pub const fn new() -> Self { + pub fn new() -> Self { Self { popup_service: None, + event_bus: EventBus::new(), } } + pub fn set_event_bus(&mut self, event_bus: EventBus) { + self.event_bus = event_bus; + } + pub fn set_popup_service(&mut self, popup_service: Rc) { self.popup_service = Some(popup_service); + self.event_bus + .publish(&WindowStateEvent::PopupConfigurationChanged); } pub fn set_popup_manager(&mut self, popup_manager: Rc) { self.popup_service = Some(Rc::new(PopupService::new(popup_manager))); + self.event_bus + .publish(&WindowStateEvent::PopupConfigurationChanged); } pub fn update_output_size(&self, output_size: PhysicalSize) { if let Some(popup_service) = &self.popup_service { popup_service.update_output_size(output_size); } + + self.event_bus + .publish(&WindowStateEvent::OutputSizeChanged { output_size }); } pub fn update_scale_factor(&self, scale_factor: f32) { diff --git a/adapters/src/wayland/surfaces/rendering_state.rs b/adapters/src/wayland/surfaces/rendering_state.rs index 91f9335..c6a3843 100644 --- a/adapters/src/wayland/surfaces/rendering_state.rs +++ b/adapters/src/wayland/surfaces/rendering_state.rs @@ -2,12 +2,15 @@ use std::rc::Rc; use crate::errors::Result; use crate::rendering::femtovg::main_window::FemtoVGWindow; use crate::wayland::surfaces::window_renderer::{WindowRenderer, WindowRendererParams}; +use crate::wayland::surfaces::event_bus::EventBus; +use crate::wayland::surfaces::window_events::WindowStateEvent; use slint::PhysicalSize; use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1; use crate::wayland::managed_proxies::ManagedWpFractionalScaleV1; pub struct RenderingState { renderer: WindowRenderer, + event_bus: EventBus, } impl RenderingState { @@ -15,15 +18,25 @@ impl RenderingState { pub fn new(params: WindowRendererParams) -> Self { Self { renderer: WindowRenderer::new(params), + event_bus: EventBus::new(), } } + pub fn set_event_bus(&mut self, event_bus: EventBus) { + self.event_bus = event_bus; + } + pub fn render_frame_if_dirty(&self) -> Result<()> { self.renderer.render_frame_if_dirty() } pub fn update_size(&mut self, width: u32, height: u32, scale_factor: f32) { self.renderer.update_size(width, height, scale_factor); + + self.event_bus.publish(&WindowStateEvent::SizeChanged { + logical_width: width, + logical_height: height, + }); } pub const fn size(&self) -> PhysicalSize { diff --git a/adapters/src/wayland/surfaces/surface_state.rs b/adapters/src/wayland/surfaces/surface_state.rs index c783dec..5b90d15 100644 --- a/adapters/src/wayland/surfaces/surface_state.rs +++ b/adapters/src/wayland/surfaces/surface_state.rs @@ -9,6 +9,7 @@ use super::popup_state::PopupState; use super::popup_manager::PopupManager; use super::scale_coordinator::{ScaleCoordinator, SharedPointerSerial}; use super::window_renderer::WindowRendererParams; +use super::event_bus::EventBus; use crate::wayland::managed_proxies::{ ManagedWlPointer, ManagedWlSurface, ManagedZwlrLayerSurfaceV1, ManagedWpFractionalScaleV1, ManagedWpViewport, @@ -35,6 +36,8 @@ pub struct WindowState { output_size: PhysicalSize, active_popup_key: RefCell>, main_surface: Rc, + #[allow(dead_code)] + event_bus: EventBus, } impl WindowState { @@ -91,7 +94,21 @@ impl WindowState { let has_fractional_scale = fractional_scale.is_some(); let size = builder.size.unwrap_or_default(); - let rendering = RenderingState::new(WindowRendererParams { + let main_surface_id = (*surface_rc).id(); + let event_router = EventRouter::new(Rc::clone(&window), main_surface_id); + let scale_coordinator = ScaleCoordinator::new(builder.scale_factor, has_fractional_scale); + + let mut interaction = InteractionState::new(pointer, event_router, scale_coordinator); + + let mut popup = PopupState::new(); + + let event_bus = EventBus::new(); + + let event_bus_clone_for_rendering = event_bus.clone(); + let event_bus_clone_for_interaction = event_bus.clone(); + let event_bus_clone_for_popup = event_bus.clone(); + + let mut rendering = RenderingState::new(WindowRendererParams { window: Rc::clone(&window), surface, layer_surface, @@ -102,15 +119,11 @@ impl WindowState { size, }); - let main_surface_id = (*surface_rc).id(); - let event_router = EventRouter::new(Rc::clone(&window), main_surface_id); - let scale_coordinator = ScaleCoordinator::new(builder.scale_factor, has_fractional_scale); + rendering.set_event_bus(event_bus_clone_for_rendering); + interaction.set_event_bus(event_bus_clone_for_interaction); + popup.set_event_bus(event_bus_clone_for_popup); - let interaction = InteractionState::new(pointer, event_router, scale_coordinator); - - let popup = PopupState::new(); - - Ok(Self { + let mut instance = Self { component, rendering, interaction, @@ -118,9 +131,17 @@ impl WindowState { output_size: builder.output_size.unwrap_or_default(), active_popup_key: RefCell::new(None), main_surface: surface_rc, - }) + event_bus, + }; + + instance.setup_event_handlers(); + + Ok(instance) } + #[allow(clippy::unused_self)] + fn setup_event_handlers(&mut self) {} + pub fn update_size(&mut self, width: u32, height: u32) { let scale_factor = self.interaction.scale_factor(); self.rendering.update_size(width, height, scale_factor); diff --git a/adapters/src/wayland/surfaces/window_events.rs b/adapters/src/wayland/surfaces/window_events.rs new file mode 100644 index 0000000..f4b2549 --- /dev/null +++ b/adapters/src/wayland/surfaces/window_events.rs @@ -0,0 +1,52 @@ +use slint::PhysicalSize; +use wayland_client::backend::ObjectId; + +#[derive(Debug, Clone)] +pub enum WindowStateEvent { + ScaleFactorChanged { + new_scale: f32, + source: ScaleSource, + }, + + SizeChanged { + logical_width: u32, + logical_height: u32, + }, + + OutputSizeChanged { + output_size: PhysicalSize, + }, + + PointerPositionChanged { + physical_x: f64, + physical_y: f64, + }, + + PointerSerialUpdated { + serial: u32, + }, + + SurfaceEntered { + surface_id: ObjectId, + }, + + SurfaceExited, + + RenderRequested, + + PopupConfigurationChanged, +} + +#[derive(Debug, Clone, Copy)] +pub enum ScaleSource { + FractionalScale, + IntegerScale, +} + +pub trait WindowStateEventHandler { + fn handle_event(&mut self, event: &WindowStateEvent); +} + +pub trait WindowStateEventEmitter { + fn emit_event(&self, event: WindowStateEvent); +}