From 4c54feacbd5bef53ae1660ffb153345064b7bf00 Mon Sep 17 00:00:00 2001 From: drendog Date: Wed, 7 Jan 2026 01:15:52 +0100 Subject: [PATCH] feat: add selector for session lock --- crates/adapters/src/lib.rs | 1 + .../wayland/event_handling/app_dispatcher.rs | 29 +++- crates/adapters/src/wayland/ops.rs | 14 ++ .../src/wayland/session_lock/lock_manager.rs | 120 +++++++++++++- crates/adapters/src/wayland/shell_adapter.rs | 23 +++ .../src/wayland/surfaces/app_state.rs | 40 ++++- crates/composition/src/lib.rs | 10 +- crates/composition/src/lock_selection.rs | 123 ++++++++++++++ crates/composition/src/session_lock.rs | 6 +- crates/composition/src/shell.rs | 156 ++++++++++++++++++ 10 files changed, 507 insertions(+), 15 deletions(-) create mode 100644 crates/composition/src/lock_selection.rs diff --git a/crates/adapters/src/lib.rs b/crates/adapters/src/lib.rs index 6f24536..5ba1a78 100644 --- a/crates/adapters/src/lib.rs +++ b/crates/adapters/src/lib.rs @@ -8,6 +8,7 @@ pub use rendering::femtovg::popup_window::PopupWindow; pub use wayland::config::{MultiSurfaceConfig, ShellSurfaceConfig, WaylandSurfaceConfig}; pub use wayland::ops::WaylandSystemOps; +pub use wayland::session_lock::lock_manager::{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/event_handling/app_dispatcher.rs b/crates/adapters/src/wayland/event_handling/app_dispatcher.rs index 6d55935..8c65b99 100644 --- a/crates/adapters/src/wayland/event_handling/app_dispatcher.rs +++ b/crates/adapters/src/wayland/event_handling/app_dispatcher.rs @@ -1,6 +1,8 @@ +use crate::wayland::session_lock::lock_manager::LockSurfaceOutputContext; use crate::wayland::surfaces::app_state::AppState; use crate::wayland::surfaces::display_metrics::DisplayMetrics; use crate::wayland::surfaces::surface_state::SurfaceState; +use layer_shika_domain::value_objects::output_handle::OutputHandle; use layer_shika_domain::value_objects::output_info::OutputGeometry; use log::{debug, info}; use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::{ @@ -650,8 +652,33 @@ impl Dispatch for AppState { } = event { let lock_surface_id = lock_surface.id(); + + let (output_handle, output_info) = if let Some(manager) = state.lock_manager() { + if let Some(output_id) = manager.find_output_id_for_lock_surface(&lock_surface_id) { + let handle = state.get_handle_by_output_id(&output_id); + let info = handle.and_then(|h| state.get_output_info(h).cloned()); + (handle.unwrap_or_else(|| OutputHandle::from_raw(0)), info) + } else { + (OutputHandle::from_raw(0), None) + } + } else { + (OutputHandle::from_raw(0), None) + }; + + let output_registry = state.output_registry(); + let primary_handle = output_registry.primary_handle(); + let active_handle = output_registry.active_handle(); + + let output_ctx = LockSurfaceOutputContext { + output_handle, + output_info, + primary_handle, + active_handle, + }; + if let Some(manager) = state.lock_manager_mut() { - if let Err(err) = manager.handle_configure(&lock_surface_id, serial, width, height) + if let Err(err) = + manager.handle_configure(&lock_surface_id, serial, width, height, output_ctx) { info!("Failed to configure session lock surface: {err}"); } diff --git a/crates/adapters/src/wayland/ops.rs b/crates/adapters/src/wayland/ops.rs index 6771b28..43b132e 100644 --- a/crates/adapters/src/wayland/ops.rs +++ b/crates/adapters/src/wayland/ops.rs @@ -1,5 +1,6 @@ use crate::errors::Result; use crate::wayland::config::ShellSurfaceConfig; +use crate::wayland::session_lock::lock_manager::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; @@ -28,6 +29,19 @@ pub trait WaylandSystemOps { fn register_session_lock_callback(&mut self, callback_name: &str, handler: SessionLockCallback); + fn register_session_lock_callback_with_filter( + &mut self, + callback_name: &str, + handler: SessionLockCallback, + filter: OutputFilter, + ); + + fn session_lock_component_name(&self) -> Option; + + fn iter_lock_surfaces(&self, f: &mut dyn FnMut(OutputHandle, &ComponentInstance)); + + fn count_lock_surfaces(&self) -> usize; + fn app_state(&self) -> &AppState; fn app_state_mut(&mut self) -> &mut AppState; diff --git a/crates/adapters/src/wayland/session_lock/lock_manager.rs b/crates/adapters/src/wayland/session_lock/lock_manager.rs index 2b93754..c077212 100644 --- a/crates/adapters/src/wayland/session_lock/lock_manager.rs +++ b/crates/adapters/src/wayland/session_lock/lock_manager.rs @@ -12,6 +12,8 @@ use crate::wayland::surfaces::pointer_utils::wayland_button_to_slint; use layer_shika_domain::surface_dimensions::SurfaceDimensions; use layer_shika_domain::value_objects::lock_config::LockConfig; use layer_shika_domain::value_objects::lock_state::LockState; +use layer_shika_domain::value_objects::output_handle::OutputHandle; +use layer_shika_domain::value_objects::output_info::OutputInfo; use log::info; use slint::{ LogicalPosition, LogicalSize, SharedString, WindowPosition, WindowSize, @@ -28,12 +30,22 @@ use wayland_client::{ use wayland_protocols::ext::session_lock::v1::client::ext_session_lock_v1::ExtSessionLockV1; use xkbcommon::xkb; -type LockCallbackHandler = Rc Value>; +pub type LockCallbackHandler = Rc Value>; +pub type OutputFilter = Rc< + dyn Fn( + &str, + OutputHandle, + Option<&OutputInfo>, + Option, + Option, + ) -> bool, +>; #[derive(Clone)] pub(crate) struct LockCallback { name: String, handler: LockCallbackHandler, + filter: Option, } impl LockCallback { @@ -41,9 +53,44 @@ impl LockCallback { 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 apply_to(&self, component: &ComponentInstance) -> Result<()> { let handler = Rc::clone(&self.handler); component @@ -62,12 +109,24 @@ struct ActiveLockSurface { has_fractional_scale: bool, } +pub struct LockSurfaceOutputContext { + pub output_handle: OutputHandle, + pub output_info: Option, + pub primary_handle: Option, + pub active_handle: Option, +} + struct LockConfigureContext { scale_factor: f32, component_definition: ComponentDefinition, compilation_result: Option>, platform: Rc, callbacks: Vec, + component_name: String, + output_handle: OutputHandle, + output_info: Option, + primary_handle: Option, + active_handle: Option, } impl ActiveLockSurface { @@ -121,13 +180,26 @@ impl ActiveLockSurface { .window() .dispatch_event(WindowEvent::WindowActiveChanged(true)); for callback in &context.callbacks { - if let Err(err) = callback.apply_to(component.component_instance()) { - info!( - "Failed to register lock callback '{}': {err}", - callback.name - ); + 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); + } } else { - info!("Registered lock callback '{}'", callback.name); + info!( + "Skipping callback '{}' due to selector filter (output {:?})", + callback.name, context.output_handle + ); } } self.component = Some(component); @@ -439,19 +511,34 @@ impl SessionLockManager { .find(|surface| surface.surface.surface_id() == *surface_id) } + 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) + .map(|(id, _)| id.clone()) + } + pub fn handle_configure( &mut self, lock_surface_id: &ObjectId, serial: u32, width: u32, height: u32, + output_ctx: LockSurfaceOutputContext, ) -> Result<()> { + let component_name = self.component_definition.name().to_string(); + let context = LockConfigureContext { scale_factor: self.config.scale_factor.value(), component_definition: self.component_definition.clone(), compilation_result: self.compilation_result.clone(), platform: Rc::clone(&self.platform), callbacks: self.callbacks.clone(), + component_name, + output_handle: output_ctx.output_handle, + output_info: output_ctx.output_info, + primary_handle: output_ctx.primary_handle, + active_handle: output_ctx.active_handle, }; let Some(surface) = self.find_surface_by_lock_surface_id_mut(lock_surface_id) else { @@ -758,4 +845,23 @@ impl SessionLockManager { window.set_position(WindowPosition::Logical(LogicalPosition::new(0., 0.))); Ok(window) } + + pub fn iter_lock_surfaces(&self, f: &mut dyn FnMut(&ObjectId, &ComponentInstance)) { + for (output_id, active_surface) in &self.lock_surfaces { + if let Some(component) = active_surface.component.as_ref() { + f(output_id, component.component_instance()); + } + } + } + + pub const fn component_name(&self) -> &ComponentDefinition { + &self.component_definition + } + + pub fn count_lock_surfaces(&self) -> usize { + self.lock_surfaces + .values() + .filter(|s| s.component.is_some()) + .count() + } } diff --git a/crates/adapters/src/wayland/shell_adapter.rs b/crates/adapters/src/wayland/shell_adapter.rs index c78ad3b..bf28425 100644 --- a/crates/adapters/src/wayland/shell_adapter.rs +++ b/crates/adapters/src/wayland/shell_adapter.rs @@ -4,6 +4,7 @@ use crate::wayland::{ managed_proxies::{ManagedWlKeyboard, ManagedWlPointer}, ops::WaylandSystemOps, outputs::{OutputManager, OutputManagerContext}, + session_lock::lock_manager::OutputFilter, surfaces::layer_surface::{SurfaceCtx, SurfaceSetupParams}, surfaces::popup_manager::{PopupContext, PopupManager}, surfaces::{ @@ -832,6 +833,28 @@ impl WaylandSystemOps for WaylandShellSystem { .register_session_lock_callback(callback_name, handler); } + fn register_session_lock_callback_with_filter( + &mut self, + callback_name: &str, + handler: Rc slint_interpreter::Value>, + filter: OutputFilter, + ) { + self.state + .register_session_lock_callback_with_filter(callback_name, handler, filter); + } + + fn session_lock_component_name(&self) -> Option { + self.state.session_lock_component_name() + } + + fn iter_lock_surfaces(&self, f: &mut dyn FnMut(OutputHandle, &ComponentInstance)) { + self.state.iter_lock_surfaces(f); + } + + fn count_lock_surfaces(&self) -> usize { + self.state.count_lock_surfaces() + } + fn app_state(&self) -> &AppState { WaylandShellSystem::app_state(self) } diff --git a/crates/adapters/src/wayland/surfaces/app_state.rs b/crates/adapters/src/wayland/surfaces/app_state.rs index 3ff930d..3ec4325 100644 --- a/crates/adapters/src/wayland/surfaces/app_state.rs +++ b/crates/adapters/src/wayland/surfaces/app_state.rs @@ -8,7 +8,7 @@ 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::lock_manager::{LockCallback, SessionLockManager}; +use crate::wayland::session_lock::lock_manager::{LockCallback, 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; @@ -145,6 +145,19 @@ impl AppState { self.lock_callbacks.push(callback); } + pub fn register_session_lock_callback_with_filter( + &mut self, + callback_name: impl Into, + handler: SessionLockCallback, + filter: OutputFilter, + ) { + let callback = LockCallback::with_filter(callback_name, handler, filter); + if let Some(manager) = self.lock_manager.as_mut() { + manager.register_callback(callback.clone()); + } + self.lock_callbacks.push(callback); + } + pub fn activate_session_lock( &mut self, component_name: &str, @@ -210,6 +223,31 @@ impl AppState { Ok(()) } + pub fn session_lock_component_name(&self) -> Option { + self.lock_manager + .as_ref() + .map(|manager| manager.component_name().name().to_string()) + } + + pub fn iter_lock_surfaces( + &self, + f: &mut dyn FnMut(OutputHandle, &slint_interpreter::ComponentInstance), + ) { + if let Some(manager) = self.lock_manager.as_ref() { + manager.iter_lock_surfaces(&mut |output_id, component| { + if let Some(handle) = self.output_mapping.get(output_id) { + f(handle, component); + } + }); + } + } + + pub fn count_lock_surfaces(&self) -> usize { + self.lock_manager + .as_ref() + .map_or(0, SessionLockManager::count_lock_surfaces) + } + fn resolve_lock_component( &self, component_name: &str, diff --git a/crates/composition/src/lib.rs b/crates/composition/src/lib.rs index 5c5b679..6fe84f1 100644 --- a/crates/composition/src/lib.rs +++ b/crates/composition/src/lib.rs @@ -2,6 +2,7 @@ mod event_loop; mod layer_surface; +mod lock_selection; mod popup; mod popup_builder; mod selection; @@ -39,6 +40,7 @@ pub use layer_shika_domain::value_objects::{ popup_size::PopupSize, }; pub use layer_surface::{LayerSurfaceHandle, ShellSurfaceConfigHandler}; +pub use lock_selection::LockSelection; pub use popup::PopupShell; pub use popup_builder::PopupBuilder; pub use selection::Selection; @@ -89,10 +91,10 @@ pub mod prelude { pub use crate::{ AnchorEdges, AnchorStrategy, CompiledUiSource, DEFAULT_COMPONENT_NAME, DEFAULT_SURFACE_NAME, EventDispatchContext, EventLoopHandle, Handle, IntoValue, - KeyboardInteractivity, Layer, LayerSurfaceHandle, Output, OutputGeometry, OutputHandle, - OutputInfo, OutputPolicy, OutputRegistry, PopupBuilder, PopupConfig, PopupHandle, - PopupPosition, PopupShell, PopupSize, PopupWindow, Result, Selection, Selector, - SessionLock, SessionLockBuilder, Shell, ShellBuilder, ShellConfig, ShellControl, + KeyboardInteractivity, Layer, LayerSurfaceHandle, LockSelection, Output, OutputGeometry, + OutputHandle, OutputInfo, OutputPolicy, OutputRegistry, PopupBuilder, PopupConfig, + PopupHandle, PopupPosition, PopupShell, PopupSize, PopupWindow, Result, Selection, + Selector, SessionLock, SessionLockBuilder, Shell, ShellBuilder, ShellConfig, ShellControl, ShellEventContext, ShellEventLoop, ShellRuntime, ShellSurfaceConfigHandler, Surface, SurfaceComponentConfig, SurfaceConfigBuilder, SurfaceControlHandle, SurfaceDefinition, SurfaceEntry, SurfaceHandle, SurfaceInfo, SurfaceMetadata, SurfaceRegistry, diff --git a/crates/composition/src/lock_selection.rs b/crates/composition/src/lock_selection.rs new file mode 100644 index 0000000..1a1ecea --- /dev/null +++ b/crates/composition/src/lock_selection.rs @@ -0,0 +1,123 @@ +// TODO: Maybe refactor to reuse the layer shell selector + +use crate::{ + Error, + selector::{Selector, SurfaceInfo}, + slint_interpreter::{ComponentInstance, Value}, +}; +use layer_shika_domain::errors::DomainError; + +/// A selection of session lock surfaces matching a selector +/// +/// Provides methods to interact with all matching lock surfaces at once. +/// Created via `Shell::select_lock()`. +pub struct LockSelection<'a> { + shell: &'a crate::Shell, + selector: Selector, +} + +impl<'a> LockSelection<'a> { + pub(crate) fn new(shell: &'a crate::Shell, selector: Selector) -> Self { + Self { shell, selector } + } + + /// Registers a callback handler for all matching lock surfaces + /// + /// Handler receives a `CallbackContext` with surface identity and shell control. + /// Callbacks are stored and applied when the lock is activated, and automatically + /// applied to new surfaces when outputs are hotplugged during an active lock. + pub fn on_callback(&mut self, callback_name: &str, handler: F) -> &mut Self + where + F: Fn(crate::CallbackContext) -> R + Clone + 'static, + R: crate::IntoValue, + { + self.shell + .on_lock_internal(&self.selector, callback_name, handler); + self + } + + /// Registers a callback handler that receives arguments for all matching lock surfaces + pub fn on_callback_with_args(&mut self, callback_name: &str, handler: F) -> &mut Self + where + F: Fn(&[Value], crate::CallbackContext) -> R + Clone + 'static, + R: crate::IntoValue, + { + self.shell + .on_lock_with_args_internal(&self.selector, callback_name, handler); + self + } + + /// Executes a function with each matching lock component instance + /// + /// Returns immediately if no lock surfaces are active. During activation, + /// this iterates over all lock component instances matching the selector. + pub fn with_component(&self, mut f: F) + where + F: FnMut(&ComponentInstance), + { + self.shell + .with_selected_lock(&self.selector, |_, component| { + f(component); + }); + } + + /// 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. + 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 + } + + /// Gets property values from all matching lock surfaces + /// + /// Returns an empty vector if the lock is inactive. + pub fn get_property(&self, name: &str) -> Result, Error> { + let mut values = Vec::new(); + let mut result = Ok(()); + self.shell + .with_selected_lock(&self.selector, |_, component| { + match component.get_property(name) { + Ok(value) => values.push(value), + Err(e) => { + log::error!("Failed to get property '{}' from lock surface: {}", name, e); + result = Err(Error::Domain(DomainError::Configuration { + message: format!("Failed to get property '{}': {}", name, e), + })); + } + } + }); + result.map(|()| values) + } + + /// Returns the number of lock surfaces matching the selector + /// + /// Returns 0 if the lock is inactive. + pub fn count(&self) -> usize { + self.shell.count_selected_lock(&self.selector) + } + + /// Checks if no lock surfaces match the selector + /// + /// Returns true if the lock is inactive or no surfaces match the selector. + pub fn is_empty(&self) -> bool { + self.count() == 0 + } + + /// Returns information about all matching lock surfaces + /// + /// Returns an empty vector if the lock is inactive. + pub fn info(&self) -> Vec { + self.shell.get_selected_lock_info(&self.selector) + } +} diff --git a/crates/composition/src/session_lock.rs b/crates/composition/src/session_lock.rs index 3249c8f..6680401 100644 --- a/crates/composition/src/session_lock.rs +++ b/crates/composition/src/session_lock.rs @@ -100,8 +100,10 @@ impl SessionLock { #[must_use] pub fn state(&self) -> LockState { if let Some(system) = self.system.upgrade() { - if let Some(state) = system.borrow().session_lock_state() { - self.state.set(state); + if let Ok(borrowed) = system.try_borrow() { + if let Some(state) = borrowed.session_lock_state() { + self.state.set(state); + } } } self.state.get() diff --git a/crates/composition/src/shell.rs b/crates/composition/src/shell.rs index 4214ea4..1d3e0a2 100644 --- a/crates/composition/src/shell.rs +++ b/crates/composition/src/shell.rs @@ -1285,6 +1285,162 @@ impl Shell { }) .collect() } + + /// Creates a selection for targeting session lock surfaces by criteria + pub fn select_lock(&self, selector: impl Into) -> crate::LockSelection<'_> { + crate::LockSelection::new(self, selector.into()) + } + + fn selector_to_output_filter(selector: &crate::Selector) -> layer_shika_adapters::OutputFilter { + let selector = selector.clone(); + + Rc::new( + move |component_name: &str, + output_handle: OutputHandle, + output_info: Option<&OutputInfo>, + primary_handle: Option, + active_handle: Option| { + let surface_info = crate::SurfaceInfo { + name: component_name.to_string(), + output: output_handle, + }; + + selector.matches(&surface_info, output_info, primary_handle, active_handle) + }, + ) + } + + pub(crate) fn on_lock_internal( + &self, + selector: &crate::Selector, + callback_name: &str, + handler: F, + ) where + F: Fn(CallbackContext) -> R + Clone + 'static, + R: IntoValue, + { + let control = self.control(); + let handler = Rc::new(handler); + let callback_name = callback_name.to_string(); + + let callback_handler = Rc::new(move |_args: &[Value]| { + let handler_rc = Rc::clone(&handler); + let control_clone = control.clone(); + let instance_id = + SurfaceInstanceId::new(SurfaceHandle::from_raw(0), OutputHandle::from_raw(0)); + let ctx = CallbackContext::new(instance_id, String::new(), control_clone); + handler_rc(ctx).into_value() + }); + + let filter = Self::selector_to_output_filter(selector); + self.inner + .borrow_mut() + .register_session_lock_callback_with_filter(&callback_name, callback_handler, filter); + } + + pub(crate) fn on_lock_with_args_internal( + &self, + selector: &crate::Selector, + callback_name: &str, + handler: F, + ) where + F: Fn(&[Value], CallbackContext) -> R + Clone + 'static, + R: IntoValue, + { + let control = self.control(); + let handler = Rc::new(handler); + let callback_name = callback_name.to_string(); + + let callback_handler = Rc::new(move |args: &[Value]| { + let handler_rc = Rc::clone(&handler); + let control_clone = control.clone(); + let instance_id = + SurfaceInstanceId::new(SurfaceHandle::from_raw(0), OutputHandle::from_raw(0)); + let ctx = CallbackContext::new(instance_id, String::new(), control_clone); + handler_rc(args, ctx).into_value() + }); + + let filter = Self::selector_to_output_filter(selector); + self.inner + .borrow_mut() + .register_session_lock_callback_with_filter(&callback_name, callback_handler, filter); + } + + pub(crate) fn with_selected_lock(&self, selector: &crate::Selector, mut f: F) + where + F: FnMut(&str, &ComponentInstance), + { + let system = self.inner.borrow(); + let (primary, active) = self.get_output_handles(); + + let Some(component_name) = system.session_lock_component_name() else { + return; + }; + + system.iter_lock_surfaces(&mut |output_handle, component| { + let surface_info = crate::SurfaceInfo { + name: component_name.clone(), + output: output_handle, + }; + + let output_info = system.app_state().get_output_info(output_handle); + + if selector.matches(&surface_info, output_info, primary, active) { + f(&component_name, component); + } + }); + } + + pub(crate) fn count_selected_lock(&self, selector: &crate::Selector) -> usize { + let system = self.inner.borrow(); + let (primary, active) = self.get_output_handles(); + + let Some(component_name) = system.session_lock_component_name() else { + return 0; + }; + + let mut count = 0; + system.iter_lock_surfaces(&mut |output_handle, _component| { + let surface_info = crate::SurfaceInfo { + name: component_name.clone(), + output: output_handle, + }; + + let output_info = system.app_state().get_output_info(output_handle); + + if selector.matches(&surface_info, output_info, primary, active) { + count += 1; + } + }); + count + } + + pub(crate) fn get_selected_lock_info( + &self, + selector: &crate::Selector, + ) -> Vec { + let system = self.inner.borrow(); + let (primary, active) = self.get_output_handles(); + + let Some(component_name) = system.session_lock_component_name() else { + return Vec::new(); + }; + + let mut info_vec = Vec::new(); + system.iter_lock_surfaces(&mut |output_handle, _component| { + let surface_info = crate::SurfaceInfo { + name: component_name.clone(), + output: output_handle, + }; + + let output_info = system.app_state().get_output_info(output_handle); + + if selector.matches(&surface_info, output_info, primary, active) { + info_vec.push(surface_info); + } + }); + info_vec + } } impl ShellRuntime for Shell {