diff --git a/crates/adapters/src/wayland/session_lock/manager/callbacks.rs b/crates/adapters/src/wayland/session_lock/manager/callbacks.rs index 644ad8d..a4492fc 100644 --- a/crates/adapters/src/wayland/session_lock/manager/callbacks.rs +++ b/crates/adapters/src/wayland/session_lock/manager/callbacks.rs @@ -4,7 +4,99 @@ use layer_shika_domain::value_objects::output_info::OutputInfo; use slint_interpreter::{ComponentInstance, Value}; use std::rc::Rc; -pub type LockCallbackHandler = Rc Value>; +pub(crate) trait FilterContext { + fn matches_filter(&self, filter: &dyn Fn(&Self) -> bool) -> bool { + filter(self) + } +} + +type FilterFn = Box bool>; + +pub(crate) struct CallbackEntry { + name: String, + handler: Handler, + filter: Option>, +} + +impl CallbackEntry { + fn new(name: impl Into, handler: Handler) -> Self { + Self { + name: name.into(), + handler, + filter: None, + } + } + + fn with_filter(name: impl Into, handler: Handler, filter: F) -> Self + where + F: Fn(&Ctx) -> bool + 'static, + { + Self { + name: name.into(), + handler, + filter: Some(Box::new(filter)), + } + } + + pub fn should_apply(&self, context: &Ctx) -> bool { + self.filter + .as_ref() + .is_none_or(|f| context.matches_filter(f.as_ref())) + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn handler(&self) -> &Handler { + &self.handler + } +} + +impl Clone for CallbackEntry { + fn clone(&self) -> Self { + Self { + name: self.name.clone(), + handler: self.handler.clone(), + filter: None, + } + } +} + +pub type CallbackHandler = Rc Value>; + +pub struct LockCallbackContext { + pub component_name: String, + pub output_handle: OutputHandle, + pub output_info: Option, + pub primary_handle: Option, + pub active_handle: Option, +} + +impl LockCallbackContext { + pub fn new( + component_name: String, + output_handle: OutputHandle, + output_info: Option, + primary_handle: Option, + active_handle: Option, + ) -> Self { + Self { + component_name, + output_handle, + output_info, + primary_handle, + active_handle, + } + } +} + +impl FilterContext for LockCallbackContext {} + +pub type LockCallbackEntry = CallbackEntry; + +pub type LockCallback = LockCallbackEntry; + pub type OutputFilter = Rc< dyn Fn( &str, @@ -15,66 +107,64 @@ pub type OutputFilter = Rc< ) -> bool, >; -#[derive(Clone)] -pub struct LockCallback { - name: String, - handler: LockCallbackHandler, - filter: Option, +pub fn create_lock_callback(name: impl Into, handler: CallbackHandler) -> LockCallback { + LockCallbackEntry::new(name, handler) } -impl LockCallback { - pub fn new(name: impl Into, handler: LockCallbackHandler) -> Self { - Self { - name: name.into(), - handler, - filter: None, - } - } - - pub fn with_filter( - name: impl Into, - handler: LockCallbackHandler, - filter: OutputFilter, - ) -> Self { - Self { - name: name.into(), - handler, - filter: Some(filter), - } - } - - pub fn should_apply( - &self, - component_name: &str, - output_handle: OutputHandle, - output_info: Option<&OutputInfo>, - primary_handle: Option, - active_handle: Option, - ) -> bool { - self.filter.as_ref().map_or_else( - || true, - |f| { - f( - component_name, - output_handle, - output_info, - primary_handle, - active_handle, - ) - }, +pub fn create_lock_callback_with_output_filter( + name: impl Into, + handler: CallbackHandler, + output_filter: F, +) -> LockCallback +where + F: Fn( + &str, + OutputHandle, + Option<&OutputInfo>, + Option, + Option, + ) -> bool + + 'static, +{ + LockCallbackEntry::with_filter(name, handler, move |ctx: &LockCallbackContext| { + output_filter( + &ctx.component_name, + ctx.output_handle, + ctx.output_info.as_ref(), + ctx.primary_handle, + ctx.active_handle, ) - } + }) +} - pub fn apply_to(&self, component: &ComponentInstance) -> Result<()> { - let handler = Rc::clone(&self.handler); +pub trait LockCallbackExt { + fn apply_to_component(&self, component: &ComponentInstance) -> Result<()>; + fn apply_with_context( + &self, + component: &ComponentInstance, + context: &LockCallbackContext, + ) -> Result<()>; +} + +impl LockCallbackExt for LockCallbackEntry { + fn apply_to_component(&self, component: &ComponentInstance) -> Result<()> { + let handler = Rc::clone(self.handler()); component - .set_callback(&self.name, move |args| handler(args)) + .set_callback(self.name(), move |args| handler(args)) .map_err(|e| LayerShikaError::InvalidInput { - message: format!("Failed to register callback '{}': {e}", self.name), + message: format!("Failed to register callback '{}': {e}", self.name()), }) } - pub const fn name(&self) -> &String { - &self.name + fn apply_with_context( + &self, + component: &ComponentInstance, + context: &LockCallbackContext, + ) -> Result<()> { + if !self.should_apply(context) { + return Ok(()); + } + + self.apply_to_component(component) } } diff --git a/crates/adapters/src/wayland/session_lock/manager/state.rs b/crates/adapters/src/wayland/session_lock/manager/state.rs index 717b6bf..55dd915 100644 --- a/crates/adapters/src/wayland/session_lock/manager/state.rs +++ b/crates/adapters/src/wayland/session_lock/manager/state.rs @@ -1,3 +1,4 @@ +use super::callbacks::{LockCallbackContext, LockCallbackExt}; use crate::errors::Result; use crate::rendering::femtovg::main_window::FemtoVGWindow; use crate::rendering::femtovg::renderable_window::RenderableWindow; @@ -51,6 +52,11 @@ pub struct ActiveLockSurface { component: Option, scale_factor: f32, has_fractional_scale: bool, + output_handle: Option, + component_name: Option, + output_info: Option, + primary_handle: Option, + active_handle: Option, } impl ActiveLockSurface { @@ -61,6 +67,11 @@ impl ActiveLockSurface { window, component: None, scale_factor: 1.0, + output_handle: None, + component_name: None, + output_info: None, + primary_handle: None, + active_handle: None, } } @@ -73,6 +84,11 @@ impl ActiveLockSurface { ) -> Result<()> { self.surface.handle_configure(serial, width, height); self.scale_factor = context.scale_factor; + self.output_handle = Some(context.output_handle); + self.component_name = Some(context.component_name.clone()); + self.output_info.clone_from(&context.output_info); + self.primary_handle = context.primary_handle; + self.active_handle = context.active_handle; let dimensions = match SurfaceDimensions::calculate(width, height, context.scale_factor) { Ok(dimensions) => dimensions, Err(err) => { @@ -103,22 +119,25 @@ impl ActiveLockSurface { self.window .window() .dispatch_event(WindowEvent::WindowActiveChanged(true)); + + let callback_context = LockCallbackContext::new( + context.component_name.clone(), + context.output_handle, + context.output_info.clone(), + context.primary_handle, + context.active_handle, + ); + for callback in &context.callbacks { - if callback.should_apply( - &context.component_name, - context.output_handle, - context.output_info.as_ref(), - context.primary_handle, - context.active_handle, - ) { - if let Err(err) = callback.apply_to(component.component_instance()) { - info!( - "Failed to register lock callback '{}': {err}", - callback.name() - ); - } else { - info!("Registered lock callback '{}'", callback.name()); - } + if let Err(err) = + callback.apply_with_context(component.component_instance(), &callback_context) + { + info!( + "Failed to register lock callback '{}': {err}", + callback.name() + ); + } else if callback.should_apply(&callback_context) { + info!("Registered lock callback '{}'", callback.name()); } else { info!( "Skipping callback '{}' due to selector filter (output {:?})", @@ -156,13 +175,33 @@ impl ActiveLockSurface { } pub fn apply_callback(&self, callback: &LockCallback) { - if let Some(component) = self.component.as_ref() { - if let Err(err) = callback.apply_to(component.component_instance()) { - info!( - "Failed to register lock callback '{}': {err}", - callback.name() - ); - } + let Some(component) = self.component.as_ref() else { + return; + }; + + let Some(component_name) = &self.component_name else { + return; + }; + + let Some(output_handle) = self.output_handle else { + return; + }; + + let callback_context = LockCallbackContext::new( + component_name.clone(), + output_handle, + self.output_info.clone(), + self.primary_handle, + self.active_handle, + ); + + if let Err(err) = + callback.apply_with_context(component.component_instance(), &callback_context) + { + info!( + "Failed to register lock callback '{}': {err}", + callback.name() + ); } } diff --git a/crates/adapters/src/wayland/surfaces/app_state.rs b/crates/adapters/src/wayland/surfaces/app_state.rs index 3cc080e..cb335d3 100644 --- a/crates/adapters/src/wayland/surfaces/app_state.rs +++ b/crates/adapters/src/wayland/surfaces/app_state.rs @@ -8,6 +8,9 @@ use crate::wayland::globals::context::GlobalContext; use crate::wayland::managed_proxies::{ManagedWlKeyboard, ManagedWlPointer}; use crate::wayland::outputs::{OutputManager, OutputMapping}; use crate::wayland::session_lock::lock_context::SessionLockContext; +use crate::wayland::session_lock::manager::callbacks::{ + create_lock_callback, create_lock_callback_with_output_filter, +}; use crate::wayland::session_lock::{LockCallback, OutputFilter, SessionLockManager}; use layer_shika_domain::entities::output_registry::OutputRegistry; use layer_shika_domain::value_objects::handle::SurfaceHandle; @@ -138,7 +141,7 @@ impl AppState { callback_name: impl Into, handler: SessionLockCallback, ) { - let callback = LockCallback::new(callback_name, handler); + let callback = create_lock_callback(callback_name, handler); if let Some(manager) = self.lock_manager.as_mut() { manager.register_callback(callback.clone()); } @@ -151,7 +154,13 @@ impl AppState { handler: SessionLockCallback, filter: OutputFilter, ) { - let callback = LockCallback::with_filter(callback_name, handler, filter); + let callback = create_lock_callback_with_output_filter( + callback_name, + handler, + move |component_name, output_handle, output_info, primary, active| { + filter(component_name, output_handle, output_info, primary, active) + }, + ); if let Some(manager) = self.lock_manager.as_mut() { manager.register_callback(callback.clone()); }