From a2d6dd5f9f67bfb551097264ac8ea87113600e4d Mon Sep 17 00:00:00 2001 From: drendog Date: Sun, 18 Jan 2026 06:15:19 +0100 Subject: [PATCH] fix: session lock selectors, lost filter during clone, and wrong surface id --- crates/adapters/src/lib.rs | 4 +- crates/adapters/src/wayland/ops.rs | 4 +- .../wayland/session_lock/manager/callbacks.rs | 118 +++++++++++++++++- .../src/wayland/session_lock/manager/mod.rs | 19 ++- .../src/wayland/session_lock/manager/state.rs | 57 ++++++++- .../adapters/src/wayland/session_lock/mod.rs | 5 +- crates/adapters/src/wayland/shell_adapter.rs | 6 +- .../src/wayland/surfaces/app_state.rs | 17 ++- crates/composition/src/lock_selection.rs | 17 +-- crates/composition/src/shell.rs | 21 ++++ 10 files changed, 245 insertions(+), 23 deletions(-) diff --git a/crates/adapters/src/lib.rs b/crates/adapters/src/lib.rs index 9583629..89c499c 100644 --- a/crates/adapters/src/lib.rs +++ b/crates/adapters/src/lib.rs @@ -8,7 +8,9 @@ pub use rendering::femtovg::popup_window::PopupWindow; pub use wayland::config::{MultiSurfaceConfig, ShellSurfaceConfig, WaylandSurfaceConfig}; pub use wayland::ops::WaylandSystemOps; -pub use wayland::session_lock::{LockSurfaceOutputContext, OutputFilter}; +pub use wayland::session_lock::{ + create_lock_property_operation_with_output_filter, LockSurfaceOutputContext, OutputFilter, +}; pub use wayland::shell_adapter::WaylandShellSystem; pub use wayland::surfaces::app_state::AppState; pub use wayland::surfaces::popup_manager::PopupManager; diff --git a/crates/adapters/src/wayland/ops.rs b/crates/adapters/src/wayland/ops.rs index 19ae3a5..af24b40 100644 --- a/crates/adapters/src/wayland/ops.rs +++ b/crates/adapters/src/wayland/ops.rs @@ -1,6 +1,6 @@ use crate::errors::Result; use crate::wayland::config::ShellSurfaceConfig; -use crate::wayland::session_lock::OutputFilter; +use crate::wayland::session_lock::{LockPropertyOperation, OutputFilter}; use crate::wayland::surfaces::app_state::AppState; use layer_shika_domain::value_objects::lock_config::LockConfig; use layer_shika_domain::value_objects::lock_state::LockState; @@ -38,6 +38,8 @@ pub trait WaylandSystemOps { filter: OutputFilter, ); + fn register_session_lock_property_operation(&mut self, property_operation: LockPropertyOperation); + fn session_lock_component_name(&self) -> Option; fn iter_lock_surfaces(&self, f: &mut dyn FnMut(OutputHandle, &ComponentInstance)); diff --git a/crates/adapters/src/wayland/session_lock/manager/callbacks.rs b/crates/adapters/src/wayland/session_lock/manager/callbacks.rs index a4492fc..161b8eb 100644 --- a/crates/adapters/src/wayland/session_lock/manager/callbacks.rs +++ b/crates/adapters/src/wayland/session_lock/manager/callbacks.rs @@ -10,7 +10,7 @@ pub(crate) trait FilterContext { } } -type FilterFn = Box bool>; +type FilterFn = Rc bool>; pub(crate) struct CallbackEntry { name: String, @@ -34,7 +34,7 @@ impl CallbackEntry { Self { name: name.into(), handler, - filter: Some(Box::new(filter)), + filter: Some(Rc::new(filter)), } } @@ -58,7 +58,7 @@ impl Clone for CallbackEntry { Self { name: self.name.clone(), handler: self.handler.clone(), - filter: None, + filter: self.filter.clone(), } } } @@ -168,3 +168,115 @@ impl LockCallbackExt for LockCallbackEntry { self.apply_to_component(component) } } + +pub struct LockPropertyOperation { + name: String, + value: Value, + filter: Option>, +} + +impl LockPropertyOperation { + pub fn new(name: impl Into, value: Value) -> Self { + Self { + name: name.into(), + value, + filter: None, + } + } + + pub fn with_filter(name: impl Into, value: Value, filter: F) -> Self + where + F: Fn(&LockCallbackContext) -> bool + 'static, + { + Self { + name: name.into(), + value, + filter: Some(Rc::new(filter)), + } + } + + pub fn should_apply(&self, context: &LockCallbackContext) -> bool { + self.filter + .as_ref() + .is_none_or(|f| context.matches_filter(f.as_ref())) + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn value(&self) -> &Value { + &self.value + } +} + +impl Clone for LockPropertyOperation { + fn clone(&self) -> Self { + Self { + name: self.name.clone(), + value: self.value.clone(), + filter: self.filter.clone(), + } + } +} + +pub fn create_lock_property_operation(name: impl Into, value: Value) -> LockPropertyOperation { + LockPropertyOperation::new(name, value) +} + +pub fn create_lock_property_operation_with_output_filter( + name: impl Into, + value: Value, + output_filter: F, +) -> LockPropertyOperation +where + F: Fn( + &str, + OutputHandle, + Option<&OutputInfo>, + Option, + Option, + ) -> bool + + 'static, +{ + LockPropertyOperation::with_filter(name, value, move |ctx: &LockCallbackContext| { + output_filter( + &ctx.component_name, + ctx.output_handle, + ctx.output_info.as_ref(), + ctx.primary_handle, + ctx.active_handle, + ) + }) +} + +pub trait LockPropertyOperationExt { + fn apply_to_component(&self, component: &ComponentInstance) -> Result<()>; + fn apply_with_context( + &self, + component: &ComponentInstance, + context: &LockCallbackContext, + ) -> Result<()>; +} + +impl LockPropertyOperationExt for LockPropertyOperation { + fn apply_to_component(&self, component: &ComponentInstance) -> Result<()> { + component + .set_property(self.name(), self.value().clone()) + .map_err(|e| LayerShikaError::InvalidInput { + message: format!("Failed to set property '{}': {e}", 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/mod.rs b/crates/adapters/src/wayland/session_lock/manager/mod.rs index 049bafc..18ab4a3 100644 --- a/crates/adapters/src/wayland/session_lock/manager/mod.rs +++ b/crates/adapters/src/wayland/session_lock/manager/mod.rs @@ -23,7 +23,10 @@ use wayland_client::{ }; use wayland_protocols::ext::session_lock::v1::client::ext_session_lock_v1::ExtSessionLockV1; -pub use callbacks::{LockCallback, OutputFilter}; +pub use callbacks::{ + create_lock_property_operation_with_output_filter, LockCallback, LockPropertyOperation, + OutputFilter, +}; pub use state::{ActiveLockSurface, LockConfigureContext, LockSurfaceOutputContext}; use self::input_handling::InputState; @@ -39,6 +42,7 @@ pub struct SessionLockManager { compilation_result: Option>, platform: Rc, callbacks: Vec, + property_operations: Vec, input_state: InputState, } @@ -61,6 +65,7 @@ impl SessionLockManager { compilation_result, platform, callbacks: Vec::new(), + property_operations: Vec::new(), input_state: InputState::new(), } } @@ -219,7 +224,7 @@ impl SessionLockManager { pub fn find_output_id_for_lock_surface(&self, lock_surface_id: &ObjectId) -> Option { self.lock_surfaces .iter() - .find(|(_, surface)| surface.surface().surface_id() == *lock_surface_id) + .find(|(_, surface)| surface.surface().lock_surface_id() == *lock_surface_id) .map(|(id, _)| id.clone()) } @@ -239,6 +244,7 @@ impl SessionLockManager { compilation_result: self.compilation_result.clone(), platform: Rc::clone(&self.platform), callbacks: self.callbacks.clone(), + property_operations: self.property_operations.clone(), component_name, output_handle: output_ctx.output_handle, output_info: output_ctx.output_info, @@ -269,6 +275,7 @@ impl SessionLockManager { compilation_result: self.compilation_result.clone(), platform: Rc::clone(&self.platform), callbacks: self.callbacks.clone(), + property_operations: self.property_operations.clone(), component_name, output_handle: output_ctx.output_handle, output_info: output_ctx.output_info, @@ -298,6 +305,7 @@ impl SessionLockManager { compilation_result: self.compilation_result.clone(), platform: Rc::clone(&self.platform), callbacks: self.callbacks.clone(), + property_operations: self.property_operations.clone(), component_name: component_name.clone(), output_handle, output_info: surface.output_info.clone(), @@ -323,6 +331,13 @@ impl SessionLockManager { self.callbacks.push(callback); } + pub(crate) fn register_property_operation(&mut self, property_operation: LockPropertyOperation) { + for (_, surface) in &self.lock_surfaces { + surface.apply_property_operation(&property_operation); + } + self.property_operations.push(property_operation); + } + pub fn handle_fractional_scale(&mut self, fractional_scale_id: &ObjectId, scale_120ths: u32) { for (_, surface) in &mut self.lock_surfaces { let matches = surface diff --git a/crates/adapters/src/wayland/session_lock/manager/state.rs b/crates/adapters/src/wayland/session_lock/manager/state.rs index 9980db2..ef37408 100644 --- a/crates/adapters/src/wayland/session_lock/manager/state.rs +++ b/crates/adapters/src/wayland/session_lock/manager/state.rs @@ -1,4 +1,4 @@ -use super::callbacks::{LockCallbackContext, LockCallbackExt}; +use super::callbacks::{LockCallbackContext, LockCallbackExt, LockPropertyOperationExt}; use crate::errors::Result; use crate::rendering::femtovg::main_window::FemtoVGWindow; use crate::rendering::femtovg::renderable_window::RenderableWindow; @@ -39,6 +39,7 @@ pub struct LockConfigureContext { pub compilation_result: Option>, pub platform: Rc, pub callbacks: Vec, + pub property_operations: Vec, pub component_name: String, pub output_handle: OutputHandle, pub output_info: Option, @@ -175,6 +176,29 @@ impl ActiveLockSurface { ); } } + + for property_op in &context.property_operations { + if property_op.should_apply(&callback_context) { + if let Err(err) = + property_op.apply_to_component(component.component_instance()) + { + info!( + "Failed to set lock property '{}': {err}", + property_op.name() + ); + } else { + info!("Set lock property '{}' on output {:?}", property_op.name(), context.output_handle); + } + } else { + info!( + "Skipping property '{}' due to selector filter (output {:?}, primary={:?})", + property_op.name(), + context.output_handle, + context.primary_handle + ); + } + } + self.component = Some(component); self.pending_component_initialization = false; @@ -233,6 +257,37 @@ impl ActiveLockSurface { } } + pub fn apply_property_operation(&self, property_op: &super::callbacks::LockPropertyOperation) { + 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) = + property_op.apply_with_context(component.component_instance(), &callback_context) + { + info!( + "Failed to set lock property '{}': {err}", + property_op.name() + ); + } + } + fn scaling_mode(&self) -> LockScalingMode { if self.surface.has_fractional_scale() && self.surface.has_viewport() { LockScalingMode::FractionalWithViewport diff --git a/crates/adapters/src/wayland/session_lock/mod.rs b/crates/adapters/src/wayland/session_lock/mod.rs index f4a98e8..a0a261f 100644 --- a/crates/adapters/src/wayland/session_lock/mod.rs +++ b/crates/adapters/src/wayland/session_lock/mod.rs @@ -2,4 +2,7 @@ pub mod lock_context; pub mod lock_surface; pub mod manager; -pub use manager::{LockCallback, LockSurfaceOutputContext, OutputFilter, SessionLockManager}; +pub use manager::{ + create_lock_property_operation_with_output_filter, LockCallback, LockPropertyOperation, + LockSurfaceOutputContext, OutputFilter, SessionLockManager, +}; diff --git a/crates/adapters/src/wayland/shell_adapter.rs b/crates/adapters/src/wayland/shell_adapter.rs index 29199ce..07168ca 100644 --- a/crates/adapters/src/wayland/shell_adapter.rs +++ b/crates/adapters/src/wayland/shell_adapter.rs @@ -5,7 +5,7 @@ use crate::wayland::{ ops::WaylandSystemOps, outputs::{OutputManager, OutputManagerContext}, rendering::RenderableSet, - session_lock::OutputFilter, + session_lock::{LockPropertyOperation, OutputFilter}, surfaces::layer_surface::{SurfaceCtx, SurfaceSetupParams}, surfaces::popup_manager::{PopupContext, PopupManager}, surfaces::{ @@ -902,6 +902,10 @@ impl WaylandSystemOps for WaylandShellSystem { .register_session_lock_callback_with_filter(callback_name, handler, filter); } + fn register_session_lock_property_operation(&mut self, property_operation: LockPropertyOperation) { + self.state.register_session_lock_property_operation(property_operation); + } + fn session_lock_component_name(&self) -> Option { self.state.session_lock_component_name() } diff --git a/crates/adapters/src/wayland/surfaces/app_state.rs b/crates/adapters/src/wayland/surfaces/app_state.rs index 2dbfb8a..59de79b 100644 --- a/crates/adapters/src/wayland/surfaces/app_state.rs +++ b/crates/adapters/src/wayland/surfaces/app_state.rs @@ -14,7 +14,7 @@ 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 crate::wayland::session_lock::{LockCallback, LockPropertyOperation, OutputFilter, SessionLockManager}; use layer_shika_domain::entities::output_registry::OutputRegistry; use layer_shika_domain::value_objects::handle::SurfaceHandle; use layer_shika_domain::value_objects::lock_config::LockConfig; @@ -71,6 +71,7 @@ pub struct AppState { keyboard_state: KeyboardState, lock_manager: Option, lock_callbacks: Vec, + lock_property_operations: Vec, queue_handle: Option>, } @@ -101,6 +102,7 @@ impl AppState { keyboard_state: KeyboardState::new(), lock_manager: None, lock_callbacks: Vec::new(), + lock_property_operations: Vec::new(), queue_handle: None, } } @@ -176,6 +178,16 @@ impl AppState { self.lock_callbacks.push(callback); } + pub fn register_session_lock_property_operation( + &mut self, + property_operation: LockPropertyOperation, + ) { + if let Some(manager) = self.lock_manager.as_mut() { + manager.register_property_operation(property_operation.clone()); + } + self.lock_property_operations.push(property_operation); + } + pub fn activate_session_lock( &mut self, component_name: &str, @@ -212,6 +224,9 @@ impl AppState { for callback in self.lock_callbacks.iter().cloned() { manager.register_callback(callback); } + for property_op in self.lock_property_operations.iter().cloned() { + manager.register_property_operation(property_op); + } let outputs = self.collect_session_lock_outputs(); manager.activate(outputs, queue_handle)?; diff --git a/crates/composition/src/lock_selection.rs b/crates/composition/src/lock_selection.rs index 1a1ecea..60e4684 100644 --- a/crates/composition/src/lock_selection.rs +++ b/crates/composition/src/lock_selection.rs @@ -63,20 +63,13 @@ impl<'a> LockSelection<'a> { /// Sets a property value on all matching lock surfaces /// - /// If the lock is inactive, this operation succeeds silently with no effect. - /// If the lock is active, the property is set on all matching component instances. + /// If the lock is inactive, the property operation is stored and will be applied + /// when the lock is activated. If the lock is active, the property is set immediately + /// on all matching component instances. pub fn set_property(&self, name: &str, value: &Value) -> Result<(), Error> { - let mut result = Ok(()); self.shell - .with_selected_lock(&self.selector, |_, component| { - if let Err(e) = component.set_property(name, value.clone()) { - log::error!("Failed to set property '{}' on lock surface: {}", name, e); - result = Err(Error::Domain(DomainError::Configuration { - message: format!("Failed to set property '{}': {}", name, e), - })); - } - }); - result + .register_lock_property_internal(&self.selector, name, value.clone()); + Ok(()) } /// Gets property values from all matching lock surfaces diff --git a/crates/composition/src/shell.rs b/crates/composition/src/shell.rs index e09af96..72443de 100644 --- a/crates/composition/src/shell.rs +++ b/crates/composition/src/shell.rs @@ -1387,6 +1387,27 @@ impl Shell { .register_session_lock_callback_with_filter(&callback_name, callback_handler, filter); } + pub(crate) fn register_lock_property_internal( + &self, + selector: &crate::Selector, + property_name: &str, + value: Value, + ) { + use layer_shika_adapters::create_lock_property_operation_with_output_filter; + + let filter = Self::selector_to_output_filter(selector); + let property_operation = create_lock_property_operation_with_output_filter( + property_name, + value, + move |component_name, output_handle, output_info, primary, active| { + filter(component_name, output_handle, output_info, primary, active) + }, + ); + self.inner + .borrow_mut() + .register_session_lock_property_operation(property_operation); + } + pub(crate) fn with_selected_lock(&self, selector: &crate::Selector, mut f: F) where F: FnMut(&str, &ComponentInstance),