diff --git a/adapters/src/wayland/event_handling/event_dispatcher.rs b/adapters/src/wayland/event_handling/event_dispatcher.rs index da70646..7f3e140 100644 --- a/adapters/src/wayland/event_handling/event_dispatcher.rs +++ b/adapters/src/wayland/event_handling/event_dispatcher.rs @@ -269,30 +269,30 @@ impl Dispatch for WindowState { } => { info!("XdgPopup Configure: position=({x}, {y}), size=({width}x{height})"); - if let Some(popup_manager) = &state.popup_manager() { + if let Some(popup_service) = state.popup_service() { let popup_id = xdg_popup.id(); - if let Some(key) = popup_manager.find_popup_key_by_xdg_popup_id(&popup_id) { + if let Some(handle) = popup_service.find_by_xdg_popup(&popup_id) { info!( - "Marking popup with key {key} as configured after XdgPopup::Configure" + "Marking popup with handle {handle:?} as configured after XdgPopup::Configure" ); - popup_manager.mark_popup_configured(key); - popup_manager.mark_all_popups_dirty(); + popup_service.mark_popup_configured(handle); + popup_service.manager().mark_all_popups_dirty(); } } } xdg_popup::Event::PopupDone => { info!("XdgPopup dismissed by compositor"); let popup_id = xdg_popup.id(); - let popup_key = state - .popup_manager() + let popup_handle = state + .popup_service() .as_ref() - .and_then(|pm| pm.find_popup_key_by_xdg_popup_id(&popup_id)); + .and_then(|ps| ps.find_by_xdg_popup(&popup_id)); - if let Some(key) = popup_key { - info!("Destroying popup with key {key}"); - state.clear_active_window_if_popup(key); - if let Some(popup_manager) = &state.popup_manager() { - popup_manager.destroy_popup(key); + if let Some(handle) = popup_handle { + info!("Destroying popup with handle {handle:?}"); + state.clear_active_window_if_popup(handle.key()); + if let Some(popup_service) = state.popup_service() { + let _result = popup_service.close(handle); } } } @@ -317,9 +317,9 @@ impl Dispatch for WindowState { info!("XdgSurface Configure received, sending ack with serial {serial}"); xdg_surface.ack_configure(serial); - if let Some(popup_manager) = &state.popup_manager() { + if let Some(popup_service) = state.popup_service() { info!("Marking all popups as dirty after Configure"); - popup_manager.mark_all_popups_dirty(); + popup_service.manager().mark_all_popups_dirty(); } } } diff --git a/adapters/src/wayland/mod.rs b/adapters/src/wayland/mod.rs index de30dab..dd79beb 100644 --- a/adapters/src/wayland/mod.rs +++ b/adapters/src/wayland/mod.rs @@ -3,5 +3,6 @@ pub mod connection; pub mod event_handling; pub mod globals; pub mod managed_proxies; +pub mod services; pub mod shell_adapter; pub mod surfaces; diff --git a/adapters/src/wayland/services/mod.rs b/adapters/src/wayland/services/mod.rs new file mode 100644 index 0000000..0a517e2 --- /dev/null +++ b/adapters/src/wayland/services/mod.rs @@ -0,0 +1 @@ +pub mod popup_service; diff --git a/adapters/src/wayland/services/popup_service.rs b/adapters/src/wayland/services/popup_service.rs new file mode 100644 index 0000000..9da9589 --- /dev/null +++ b/adapters/src/wayland/services/popup_service.rs @@ -0,0 +1,196 @@ +use crate::errors::Result; +use crate::rendering::femtovg::popup_window::PopupWindow; +use layer_shika_domain::value_objects::popup_request::{PopupHandle, PopupRequest}; +use log::info; +use slint::PhysicalSize; +use std::cell::{Cell, RefCell}; +use std::rc::Rc; +use wayland_client::{backend::ObjectId, protocol::wl_surface::WlSurface, Proxy}; +use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1; + +use super::super::surfaces::popup_manager::PopupManager; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ActiveWindow { + Main, + Popup(usize), +} + +pub struct PopupService { + manager: Rc, + active_window: RefCell>, + scale_factor: Cell, +} + +impl PopupService { + #[must_use] + pub fn new(manager: Rc) -> Self { + let scale_factor = manager.scale_factor(); + Self { + manager, + active_window: RefCell::new(None), + scale_factor: Cell::new(scale_factor), + } + } + + pub fn show(&self, request: PopupRequest, width: f32, height: f32) { + self.manager.set_pending_popup(request, width, height); + } + + pub fn close(&self, handle: PopupHandle) -> Result<()> { + let key = handle.key(); + self.clear_active_window_if_popup(key); + self.manager.destroy_popup(key); + Ok(()) + } + + pub fn close_current(&self) { + if let Some(key) = self.manager.current_popup_key() { + self.clear_active_window_if_popup(key); + } + self.manager.close_current_popup(); + } + + #[must_use] + pub fn find_by_surface(&self, surface_id: &ObjectId) -> Option { + self.manager + .find_popup_key_by_surface_id(surface_id) + .map(PopupHandle::new) + } + + #[must_use] + pub fn find_by_fractional_scale(&self, fractional_scale_id: &ObjectId) -> Option { + self.manager + .find_popup_key_by_fractional_scale_id(fractional_scale_id) + .map(PopupHandle::new) + } + + #[must_use] + pub fn find_by_xdg_popup(&self, xdg_popup_id: &ObjectId) -> Option { + self.manager + .find_popup_key_by_xdg_popup_id(xdg_popup_id) + .map(PopupHandle::new) + } + + #[must_use] + pub fn find_by_xdg_surface(&self, xdg_surface_id: &ObjectId) -> Option { + self.manager + .find_popup_key_by_xdg_surface_id(xdg_surface_id) + .map(PopupHandle::new) + } + + #[must_use] + pub fn get_popup_window(&self, handle: PopupHandle) -> Option> { + self.manager.get_popup_window(handle.key()) + } + + pub fn update_scale_factor(&self, scale_factor: f32) { + self.scale_factor.set(scale_factor); + self.manager.update_scale_factor(scale_factor); + self.manager.mark_all_popups_dirty(); + } + + pub fn update_output_size(&self, output_size: PhysicalSize) { + self.manager.update_output_size(output_size); + } + + #[must_use] + pub fn scale_factor(&self) -> f32 { + self.scale_factor.get() + } + + pub fn find_window_for_surface(&self, surface: &WlSurface, main_surface_id: &ObjectId) { + let surface_id = surface.id(); + + if *main_surface_id == surface_id { + *self.active_window.borrow_mut() = Some(ActiveWindow::Main); + return; + } + + if let Some(popup_key) = self.manager.find_popup_key_by_surface_id(&surface_id) { + *self.active_window.borrow_mut() = Some(ActiveWindow::Popup(popup_key)); + return; + } + + *self.active_window.borrow_mut() = None; + } + + #[must_use] + pub fn active_window(&self) -> Option { + *self.active_window.borrow() + } + + pub fn clear_active_window(&self) { + *self.active_window.borrow_mut() = None; + } + + pub fn clear_active_window_if_popup(&self, popup_key: usize) { + if *self.active_window.borrow() == Some(ActiveWindow::Popup(popup_key)) { + *self.active_window.borrow_mut() = None; + } + } + + #[allow(clippy::cast_precision_loss)] + pub fn update_scale_for_fractional_scale_object( + &self, + fractional_scale_proxy: &WpFractionalScaleV1, + scale_120ths: u32, + ) { + let fractional_scale_id = fractional_scale_proxy.id(); + + if let Some(popup_key) = + self.manager + .find_popup_key_by_fractional_scale_id(&fractional_scale_id) + { + if let Some(popup_window) = self.manager.get_popup_window(popup_key) { + 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(); + } + } + } + + pub fn render_popups(&self) -> Result<()> { + self.manager.render_popups() + } + + pub fn mark_popup_configured(&self, handle: PopupHandle) { + self.manager.mark_popup_configured(handle.key()); + } + + pub fn update_popup_viewport( + &self, + handle: PopupHandle, + logical_width: i32, + logical_height: i32, + ) { + self.manager + .update_popup_viewport(handle.key(), logical_width, logical_height); + } + + #[must_use] + pub fn get_popup_info(&self, handle: PopupHandle) -> Option<(PopupRequest, u32)> { + self.manager.get_popup_info(handle.key()) + } + + #[must_use] + pub fn manager(&self) -> &Rc { + &self.manager + } + + #[must_use] + pub fn has_xdg_shell(&self) -> bool { + self.manager.has_xdg_shell() + } + + #[must_use] + pub fn current_popup_key(&self) -> Option { + self.manager.current_popup_key() + } + + #[must_use] + pub fn take_pending_popup(&self) -> Option<(PopupRequest, f32, f32)> { + self.manager.take_pending_popup() + } +} diff --git a/adapters/src/wayland/shell_adapter.rs b/adapters/src/wayland/shell_adapter.rs index 0f2e2b7..a1b1ccc 100644 --- a/adapters/src/wayland/shell_adapter.rs +++ b/adapters/src/wayland/shell_adapter.rs @@ -1,6 +1,7 @@ use crate::wayland::{ config::{LayerSurfaceParams, WaylandWindowConfig}, globals::context::GlobalContext, + services::popup_service::PopupService, surfaces::layer_surface::{SurfaceCtx, SurfaceSetupParams}, surfaces::popup_manager::{CreatePopupParams, PopupContext, PopupManager}, surfaces::{ @@ -38,7 +39,7 @@ pub struct WaylandWindowingSystem { connection: Rc, event_queue: EventQueue, event_loop: EventLoop<'static, WindowState>, - popup_manager: Rc, + popup_service: Rc, } impl WaylandWindowingSystem { @@ -60,10 +61,11 @@ impl WaylandWindowingSystem { ); let popup_manager = Rc::new(PopupManager::new(popup_context, state.scale_factor())); + let popup_service = Rc::new(PopupService::new(popup_manager)); let shared_serial = Rc::new(SharedPointerSerial::new()); Self::setup_popup_creator( - &popup_manager, + &popup_service, &platform, &state, &event_queue, @@ -75,12 +77,12 @@ impl WaylandWindowingSystem { connection, event_queue, event_loop, - popup_manager, + popup_service, }) .map(|mut system| { system .state - .set_popup_manager(Rc::clone(&system.popup_manager)); + .set_popup_service(Rc::clone(&system.popup_service)); system.state.set_shared_pointer_serial(shared_serial); system }) @@ -156,20 +158,20 @@ impl WaylandWindowingSystem { } fn setup_popup_creator( - popup_manager: &Rc, + popup_service: &Rc, platform: &Rc, state: &WindowState, event_queue: &EventQueue, shared_serial: &Rc, ) { - if !popup_manager.has_xdg_shell() { + if !popup_service.has_xdg_shell() { info!("xdg-shell not available, popups will not be supported"); return; } info!("Setting up popup creator with xdg-shell support"); - let popup_manager_clone = Rc::clone(popup_manager); + let popup_service_clone = Rc::clone(popup_service); let layer_surface = state.layer_surface(); let queue_handle = event_queue.handle(); let serial_holder = Rc::clone(shared_serial); @@ -179,7 +181,7 @@ impl WaylandWindowingSystem { let serial = serial_holder.get(); - let (params, request) = if let Some((request, width, height)) = popup_manager_clone.take_pending_popup() { + let (params, request) = if let Some((request, width, height)) = popup_service_clone.take_pending_popup() { log::info!( "Using popup request: component='{}', position=({}, {}), size={}x{}, mode={:?}", request.component, @@ -206,7 +208,8 @@ impl WaylandWindowingSystem { )); }; - let popup_window = popup_manager_clone + let popup_window = popup_service_clone + .manager() .create_popup( &queue_handle, &layer_surface, @@ -276,12 +279,12 @@ impl WaylandWindowingSystem { let event_queue = &mut self.event_queue; let connection = &self.connection; - let popup_manager = Rc::clone(&self.popup_manager); + let popup_service = Rc::clone(&self.popup_service); self.event_loop .run(None, &mut self.state, move |shared_data| { if let Err(e) = - Self::process_events(connection, event_queue, shared_data, &popup_manager) + Self::process_events(connection, event_queue, shared_data, &popup_service) { error!("Error processing events: {e}"); } @@ -311,7 +314,7 @@ impl WaylandWindowingSystem { connection: &Connection, event_queue: &mut EventQueue, shared_data: &mut WindowState, - popup_manager: &PopupManager, + popup_service: &PopupService, ) -> Result<()> { if let Some(guard) = event_queue.prepare_read() { guard @@ -330,7 +333,7 @@ impl WaylandWindowingSystem { message: e.to_string(), })?; - popup_manager + popup_service .render_popups() .map_err(|e| RenderingError::Operation { message: e.to_string(), diff --git a/adapters/src/wayland/surfaces/surface_state.rs b/adapters/src/wayland/surfaces/surface_state.rs index 61b0117..71accfa 100644 --- a/adapters/src/wayland/surfaces/surface_state.rs +++ b/adapters/src/wayland/surfaces/surface_state.rs @@ -6,12 +6,14 @@ use crate::wayland::managed_proxies::{ ManagedWlPointer, ManagedWlSurface, ManagedZwlrLayerSurfaceV1, ManagedWpFractionalScaleV1, ManagedWpViewport, }; +use crate::wayland::services::popup_service::{ActiveWindow, PopupService}; use crate::rendering::femtovg::main_window::FemtoVGWindow; use crate::errors::{LayerShikaError, Result}; use core::result::Result as CoreResult; use layer_shika_domain::errors::DomainError; use layer_shika_domain::ports::windowing::RuntimeStatePort; use layer_shika_domain::surface_dimensions::SurfaceDimensions; +use layer_shika_domain::value_objects::popup_request::PopupHandle; use log::{error, info}; use slint::{LogicalPosition, PhysicalSize, ComponentHandle}; use slint::platform::{WindowAdapter, WindowEvent}; @@ -54,12 +56,6 @@ enum ScalingMode { Integer, } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum ActiveWindow { - Main, - Popup(usize), -} - pub struct WindowState { component_instance: ComponentInstance, compilation_result: Option>, @@ -72,7 +68,7 @@ pub struct WindowState { window: Rc, height: u32, exclusive_zone: i32, - popup_manager: Option>, + popup_service: Option>, size: PhysicalSize, logical_size: PhysicalSize, output_size: PhysicalSize, @@ -80,7 +76,6 @@ pub struct WindowState { last_pointer_serial: u32, shared_pointer_serial: Option>, scale_factor: f32, - active_window: Option, } impl WindowState { @@ -150,7 +145,7 @@ impl WindowState { window, height: builder.height, exclusive_zone: builder.exclusive_zone, - popup_manager: None, + popup_service: None, size: builder.size.unwrap_or_default(), logical_size: PhysicalSize::default(), output_size: builder.output_size.unwrap_or_default(), @@ -158,7 +153,6 @@ impl WindowState { last_pointer_serial: 0, shared_pointer_serial: None, scale_factor: builder.scale_factor, - active_window: None, }) } @@ -184,7 +178,8 @@ impl WindowState { ))); } ScalingMode::FractionalOnly => { - self.window.set_scale_factor(dimensions.buffer_scale() as f32); + self.window + .set_scale_factor(dimensions.buffer_scale() as f32); self.window .set_size(slint::WindowSize::Logical(slint::LogicalSize::new( dimensions.logical_width() as f32, @@ -193,8 +188,9 @@ impl WindowState { } ScalingMode::Integer => { self.window.set_scale_factor(self.scale_factor); - self.window - .set_size(slint::WindowSize::Physical(dimensions.to_slint_physical_size())); + self.window.set_size(slint::WindowSize::Physical( + dimensions.to_slint_physical_size(), + )); } } } @@ -295,8 +291,8 @@ impl WindowState { pub fn set_output_size(&mut self, output_size: PhysicalSize) { self.output_size = output_size; - if let Some(popup_manager) = &self.popup_manager { - popup_manager.update_output_size(output_size); + if let Some(popup_service) = &self.popup_service { + popup_service.update_output_size(output_size); } } @@ -327,8 +323,8 @@ impl WindowState { ); self.scale_factor = new_scale_factor; - if let Some(popup_manager) = &self.popup_manager { - popup_manager.update_scale_factor(new_scale_factor); + if let Some(popup_service) = &self.popup_service { + popup_service.update_scale_factor(new_scale_factor); } let current_logical_size = self.logical_size; @@ -356,41 +352,35 @@ impl WindowState { self.shared_pointer_serial = Some(shared_serial); } + pub fn set_popup_service(&mut self, popup_service: Rc) { + self.popup_service = Some(popup_service); + } + pub fn set_popup_manager(&mut self, popup_manager: Rc) { - self.popup_manager = Some(popup_manager); + self.popup_service = Some(Rc::new(PopupService::new(popup_manager))); } pub fn find_window_for_surface(&mut self, surface: &WlSurface) { - let surface_id = surface.id(); - - if (**self.surface.inner()).id() == surface_id { - self.active_window = Some(ActiveWindow::Main); - return; + if let Some(popup_service) = &self.popup_service { + popup_service.find_window_for_surface(surface, &(**self.surface.inner()).id()); } - - if let Some(popup_manager) = &self.popup_manager { - if let Some(popup_key) = popup_manager.find_popup_key_by_surface_id(&surface_id) { - self.active_window = Some(ActiveWindow::Popup(popup_key)); - return; - } - } - - self.active_window = None; } pub fn dispatch_to_active_window(&self, event: WindowEvent) { - match self.active_window { - Some(ActiveWindow::Main) => { - self.window.window().dispatch_event(event); - } - Some(ActiveWindow::Popup(index)) => { - if let Some(popup_manager) = &self.popup_manager { - if let Some(popup_window) = popup_manager.get_popup_window(index) { + if let Some(popup_service) = &self.popup_service { + match popup_service.active_window() { + Some(ActiveWindow::Main) => { + self.window.window().dispatch_event(event); + } + Some(ActiveWindow::Popup(index)) => { + if let Some(popup_window) = + popup_service.get_popup_window(PopupHandle::new(index)) + { popup_window.dispatch_event(event); } } + None => {} } - None => {} } } @@ -409,32 +399,32 @@ impl WindowState { } } - if let Some(popup_manager) = &self.popup_manager { - if let Some(popup_key) = - popup_manager.find_popup_key_by_fractional_scale_id(&fractional_scale_id) - { - if let Some(popup_window) = popup_manager.get_popup_window(popup_key) { - 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(); - } - } + if let Some(popup_service) = &self.popup_service { + popup_service + .update_scale_for_fractional_scale_object(fractional_scale_proxy, scale_120ths); } } pub fn clear_active_window(&mut self) { - self.active_window = None; - } - - pub fn clear_active_window_if_popup(&mut self, popup_key: usize) { - if self.active_window == Some(ActiveWindow::Popup(popup_key)) { - self.active_window = None; + if let Some(popup_service) = &self.popup_service { + popup_service.clear_active_window(); } } - pub const fn popup_manager(&self) -> &Option> { - &self.popup_manager + pub fn clear_active_window_if_popup(&mut self, popup_key: usize) { + if let Some(popup_service) = &self.popup_service { + popup_service.clear_active_window_if_popup(popup_key); + } + } + + pub const fn popup_service(&self) -> &Option> { + &self.popup_service + } + + pub fn popup_manager(&self) -> Option> { + self.popup_service + .as_ref() + .map(|service| Rc::clone(service.manager())) } }