refactor: extract popup service

This commit is contained in:
drendog 2025-11-09 17:15:10 +01:00
parent acece2dbf3
commit 6346c1ab96
Signed by: dwenya
GPG key ID: 8DD77074645332D0
6 changed files with 278 additions and 87 deletions

View file

@ -269,30 +269,30 @@ impl Dispatch<XdgPopup, ()> for WindowState {
} => { } => {
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() { if let Some(popup_service) = state.popup_service() {
let popup_id = xdg_popup.id(); 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!( 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_service.mark_popup_configured(handle);
popup_manager.mark_all_popups_dirty(); popup_service.manager().mark_all_popups_dirty();
} }
} }
} }
xdg_popup::Event::PopupDone => { xdg_popup::Event::PopupDone => {
info!("XdgPopup dismissed by compositor"); info!("XdgPopup dismissed by compositor");
let popup_id = xdg_popup.id(); let popup_id = xdg_popup.id();
let popup_key = state let popup_handle = state
.popup_manager() .popup_service()
.as_ref() .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 { if let Some(handle) = popup_handle {
info!("Destroying popup with key {key}"); info!("Destroying popup with handle {handle:?}");
state.clear_active_window_if_popup(key); state.clear_active_window_if_popup(handle.key());
if let Some(popup_manager) = &state.popup_manager() { if let Some(popup_service) = state.popup_service() {
popup_manager.destroy_popup(key); let _result = popup_service.close(handle);
} }
} }
} }
@ -317,9 +317,9 @@ impl Dispatch<XdgSurface, ()> for WindowState {
info!("XdgSurface Configure received, sending ack with serial {serial}"); info!("XdgSurface Configure received, sending ack with serial {serial}");
xdg_surface.ack_configure(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"); info!("Marking all popups as dirty after Configure");
popup_manager.mark_all_popups_dirty(); popup_service.manager().mark_all_popups_dirty();
} }
} }
} }

View file

@ -3,5 +3,6 @@ pub mod connection;
pub mod event_handling; pub mod event_handling;
pub mod globals; pub mod globals;
pub mod managed_proxies; pub mod managed_proxies;
pub mod services;
pub mod shell_adapter; pub mod shell_adapter;
pub mod surfaces; pub mod surfaces;

View file

@ -0,0 +1 @@
pub mod popup_service;

View file

@ -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<PopupManager>,
active_window: RefCell<Option<ActiveWindow>>,
scale_factor: Cell<f32>,
}
impl PopupService {
#[must_use]
pub fn new(manager: Rc<PopupManager>) -> 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<PopupHandle> {
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<PopupHandle> {
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<PopupHandle> {
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<PopupHandle> {
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<Rc<PopupWindow>> {
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<ActiveWindow> {
*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<PopupManager> {
&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<usize> {
self.manager.current_popup_key()
}
#[must_use]
pub fn take_pending_popup(&self) -> Option<(PopupRequest, f32, f32)> {
self.manager.take_pending_popup()
}
}

View file

@ -1,6 +1,7 @@
use crate::wayland::{ use crate::wayland::{
config::{LayerSurfaceParams, WaylandWindowConfig}, config::{LayerSurfaceParams, WaylandWindowConfig},
globals::context::GlobalContext, globals::context::GlobalContext,
services::popup_service::PopupService,
surfaces::layer_surface::{SurfaceCtx, SurfaceSetupParams}, surfaces::layer_surface::{SurfaceCtx, SurfaceSetupParams},
surfaces::popup_manager::{CreatePopupParams, PopupContext, PopupManager}, surfaces::popup_manager::{CreatePopupParams, PopupContext, PopupManager},
surfaces::{ surfaces::{
@ -38,7 +39,7 @@ pub struct WaylandWindowingSystem {
connection: Rc<Connection>, connection: Rc<Connection>,
event_queue: EventQueue<WindowState>, event_queue: EventQueue<WindowState>,
event_loop: EventLoop<'static, WindowState>, event_loop: EventLoop<'static, WindowState>,
popup_manager: Rc<PopupManager>, popup_service: Rc<PopupService>,
} }
impl WaylandWindowingSystem { impl WaylandWindowingSystem {
@ -60,10 +61,11 @@ impl WaylandWindowingSystem {
); );
let popup_manager = Rc::new(PopupManager::new(popup_context, state.scale_factor())); 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()); let shared_serial = Rc::new(SharedPointerSerial::new());
Self::setup_popup_creator( Self::setup_popup_creator(
&popup_manager, &popup_service,
&platform, &platform,
&state, &state,
&event_queue, &event_queue,
@ -75,12 +77,12 @@ impl WaylandWindowingSystem {
connection, connection,
event_queue, event_queue,
event_loop, event_loop,
popup_manager, popup_service,
}) })
.map(|mut system| { .map(|mut system| {
system system
.state .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.state.set_shared_pointer_serial(shared_serial);
system system
}) })
@ -156,20 +158,20 @@ impl WaylandWindowingSystem {
} }
fn setup_popup_creator( fn setup_popup_creator(
popup_manager: &Rc<PopupManager>, popup_service: &Rc<PopupService>,
platform: &Rc<CustomSlintPlatform>, platform: &Rc<CustomSlintPlatform>,
state: &WindowState, state: &WindowState,
event_queue: &EventQueue<WindowState>, event_queue: &EventQueue<WindowState>,
shared_serial: &Rc<SharedPointerSerial>, shared_serial: &Rc<SharedPointerSerial>,
) { ) {
if !popup_manager.has_xdg_shell() { if !popup_service.has_xdg_shell() {
info!("xdg-shell not available, popups will not be supported"); info!("xdg-shell not available, popups will not be supported");
return; return;
} }
info!("Setting up popup creator with xdg-shell support"); 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 layer_surface = state.layer_surface();
let queue_handle = event_queue.handle(); let queue_handle = event_queue.handle();
let serial_holder = Rc::clone(shared_serial); let serial_holder = Rc::clone(shared_serial);
@ -179,7 +181,7 @@ impl WaylandWindowingSystem {
let serial = serial_holder.get(); 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!( log::info!(
"Using popup request: component='{}', position=({}, {}), size={}x{}, mode={:?}", "Using popup request: component='{}', position=({}, {}), size={}x{}, mode={:?}",
request.component, request.component,
@ -206,7 +208,8 @@ impl WaylandWindowingSystem {
)); ));
}; };
let popup_window = popup_manager_clone let popup_window = popup_service_clone
.manager()
.create_popup( .create_popup(
&queue_handle, &queue_handle,
&layer_surface, &layer_surface,
@ -276,12 +279,12 @@ impl WaylandWindowingSystem {
let event_queue = &mut self.event_queue; let event_queue = &mut self.event_queue;
let connection = &self.connection; let connection = &self.connection;
let popup_manager = Rc::clone(&self.popup_manager); let popup_service = Rc::clone(&self.popup_service);
self.event_loop self.event_loop
.run(None, &mut self.state, move |shared_data| { .run(None, &mut self.state, move |shared_data| {
if let Err(e) = 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}"); error!("Error processing events: {e}");
} }
@ -311,7 +314,7 @@ impl WaylandWindowingSystem {
connection: &Connection, connection: &Connection,
event_queue: &mut EventQueue<WindowState>, event_queue: &mut EventQueue<WindowState>,
shared_data: &mut WindowState, shared_data: &mut WindowState,
popup_manager: &PopupManager, popup_service: &PopupService,
) -> Result<()> { ) -> Result<()> {
if let Some(guard) = event_queue.prepare_read() { if let Some(guard) = event_queue.prepare_read() {
guard guard
@ -330,7 +333,7 @@ impl WaylandWindowingSystem {
message: e.to_string(), message: e.to_string(),
})?; })?;
popup_manager popup_service
.render_popups() .render_popups()
.map_err(|e| RenderingError::Operation { .map_err(|e| RenderingError::Operation {
message: e.to_string(), message: e.to_string(),

View file

@ -6,12 +6,14 @@ use crate::wayland::managed_proxies::{
ManagedWlPointer, ManagedWlSurface, ManagedZwlrLayerSurfaceV1, ManagedWlPointer, ManagedWlSurface, ManagedZwlrLayerSurfaceV1,
ManagedWpFractionalScaleV1, ManagedWpViewport, ManagedWpFractionalScaleV1, ManagedWpViewport,
}; };
use crate::wayland::services::popup_service::{ActiveWindow, PopupService};
use crate::rendering::femtovg::main_window::FemtoVGWindow; use crate::rendering::femtovg::main_window::FemtoVGWindow;
use crate::errors::{LayerShikaError, Result}; use crate::errors::{LayerShikaError, Result};
use core::result::Result as CoreResult; use core::result::Result as CoreResult;
use layer_shika_domain::errors::DomainError; use layer_shika_domain::errors::DomainError;
use layer_shika_domain::ports::windowing::RuntimeStatePort; use layer_shika_domain::ports::windowing::RuntimeStatePort;
use layer_shika_domain::surface_dimensions::SurfaceDimensions; use layer_shika_domain::surface_dimensions::SurfaceDimensions;
use layer_shika_domain::value_objects::popup_request::PopupHandle;
use log::{error, info}; use log::{error, info};
use slint::{LogicalPosition, PhysicalSize, ComponentHandle}; use slint::{LogicalPosition, PhysicalSize, ComponentHandle};
use slint::platform::{WindowAdapter, WindowEvent}; use slint::platform::{WindowAdapter, WindowEvent};
@ -54,12 +56,6 @@ enum ScalingMode {
Integer, Integer,
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ActiveWindow {
Main,
Popup(usize),
}
pub struct WindowState { pub struct WindowState {
component_instance: ComponentInstance, component_instance: ComponentInstance,
compilation_result: Option<Rc<CompilationResult>>, compilation_result: Option<Rc<CompilationResult>>,
@ -72,7 +68,7 @@ pub struct WindowState {
window: Rc<FemtoVGWindow>, window: Rc<FemtoVGWindow>,
height: u32, height: u32,
exclusive_zone: i32, exclusive_zone: i32,
popup_manager: Option<Rc<PopupManager>>, popup_service: Option<Rc<PopupService>>,
size: PhysicalSize, size: PhysicalSize,
logical_size: PhysicalSize, logical_size: PhysicalSize,
output_size: PhysicalSize, output_size: PhysicalSize,
@ -80,7 +76,6 @@ pub struct WindowState {
last_pointer_serial: u32, last_pointer_serial: u32,
shared_pointer_serial: Option<Rc<SharedPointerSerial>>, shared_pointer_serial: Option<Rc<SharedPointerSerial>>,
scale_factor: f32, scale_factor: f32,
active_window: Option<ActiveWindow>,
} }
impl WindowState { impl WindowState {
@ -150,7 +145,7 @@ impl WindowState {
window, window,
height: builder.height, height: builder.height,
exclusive_zone: builder.exclusive_zone, exclusive_zone: builder.exclusive_zone,
popup_manager: None, popup_service: None,
size: builder.size.unwrap_or_default(), size: builder.size.unwrap_or_default(),
logical_size: PhysicalSize::default(), logical_size: PhysicalSize::default(),
output_size: builder.output_size.unwrap_or_default(), output_size: builder.output_size.unwrap_or_default(),
@ -158,7 +153,6 @@ impl WindowState {
last_pointer_serial: 0, last_pointer_serial: 0,
shared_pointer_serial: None, shared_pointer_serial: None,
scale_factor: builder.scale_factor, scale_factor: builder.scale_factor,
active_window: None,
}) })
} }
@ -184,7 +178,8 @@ impl WindowState {
))); )));
} }
ScalingMode::FractionalOnly => { ScalingMode::FractionalOnly => {
self.window.set_scale_factor(dimensions.buffer_scale() as f32); self.window
.set_scale_factor(dimensions.buffer_scale() as f32);
self.window self.window
.set_size(slint::WindowSize::Logical(slint::LogicalSize::new( .set_size(slint::WindowSize::Logical(slint::LogicalSize::new(
dimensions.logical_width() as f32, dimensions.logical_width() as f32,
@ -193,8 +188,9 @@ impl WindowState {
} }
ScalingMode::Integer => { ScalingMode::Integer => {
self.window.set_scale_factor(self.scale_factor); self.window.set_scale_factor(self.scale_factor);
self.window self.window.set_size(slint::WindowSize::Physical(
.set_size(slint::WindowSize::Physical(dimensions.to_slint_physical_size())); dimensions.to_slint_physical_size(),
));
} }
} }
} }
@ -295,8 +291,8 @@ impl WindowState {
pub fn set_output_size(&mut self, output_size: PhysicalSize) { pub fn set_output_size(&mut self, output_size: PhysicalSize) {
self.output_size = output_size; self.output_size = output_size;
if let Some(popup_manager) = &self.popup_manager { if let Some(popup_service) = &self.popup_service {
popup_manager.update_output_size(output_size); popup_service.update_output_size(output_size);
} }
} }
@ -327,8 +323,8 @@ impl WindowState {
); );
self.scale_factor = new_scale_factor; self.scale_factor = new_scale_factor;
if let Some(popup_manager) = &self.popup_manager { if let Some(popup_service) = &self.popup_service {
popup_manager.update_scale_factor(new_scale_factor); popup_service.update_scale_factor(new_scale_factor);
} }
let current_logical_size = self.logical_size; let current_logical_size = self.logical_size;
@ -356,41 +352,35 @@ impl WindowState {
self.shared_pointer_serial = Some(shared_serial); self.shared_pointer_serial = Some(shared_serial);
} }
pub fn set_popup_service(&mut self, popup_service: Rc<PopupService>) {
self.popup_service = Some(popup_service);
}
pub fn set_popup_manager(&mut self, popup_manager: Rc<PopupManager>) { pub fn set_popup_manager(&mut self, popup_manager: Rc<PopupManager>) {
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) { pub fn find_window_for_surface(&mut self, surface: &WlSurface) {
let surface_id = surface.id(); if let Some(popup_service) = &self.popup_service {
popup_service.find_window_for_surface(surface, &(**self.surface.inner()).id());
if (**self.surface.inner()).id() == surface_id {
self.active_window = Some(ActiveWindow::Main);
return;
} }
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) { pub fn dispatch_to_active_window(&self, event: WindowEvent) {
match self.active_window { if let Some(popup_service) = &self.popup_service {
Some(ActiveWindow::Main) => { match popup_service.active_window() {
self.window.window().dispatch_event(event); Some(ActiveWindow::Main) => {
} self.window.window().dispatch_event(event);
Some(ActiveWindow::Popup(index)) => { }
if let Some(popup_manager) = &self.popup_manager { Some(ActiveWindow::Popup(index)) => {
if let Some(popup_window) = popup_manager.get_popup_window(index) { if let Some(popup_window) =
popup_service.get_popup_window(PopupHandle::new(index))
{
popup_window.dispatch_event(event); 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_service) = &self.popup_service {
if let Some(popup_key) = popup_service
popup_manager.find_popup_key_by_fractional_scale_id(&fractional_scale_id) .update_scale_for_fractional_scale_object(fractional_scale_proxy, scale_120ths);
{
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();
}
}
} }
} }
pub fn clear_active_window(&mut self) { pub fn clear_active_window(&mut self) {
self.active_window = None; if let Some(popup_service) = &self.popup_service {
} popup_service.clear_active_window();
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;
} }
} }
pub const fn popup_manager(&self) -> &Option<Rc<PopupManager>> { pub fn clear_active_window_if_popup(&mut self, popup_key: usize) {
&self.popup_manager if let Some(popup_service) = &self.popup_service {
popup_service.clear_active_window_if_popup(popup_key);
}
}
pub const fn popup_service(&self) -> &Option<Rc<PopupService>> {
&self.popup_service
}
pub fn popup_manager(&self) -> Option<Rc<PopupManager>> {
self.popup_service
.as_ref()
.map(|service| Rc::clone(service.manager()))
} }
} }