mirror of
https://codeberg.org/waydeer/layer-shika.git
synced 2026-01-09 23:05:54 +00:00
feat: add selector for session lock
This commit is contained in:
parent
7ace2d4d0d
commit
4c54feacbd
10 changed files with 507 additions and 15 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<ExtSessionLockSurfaceV1, ()> 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}");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<String>;
|
||||
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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<dyn Fn(&[Value]) -> Value>;
|
||||
pub type LockCallbackHandler = Rc<dyn Fn(&[Value]) -> Value>;
|
||||
pub type OutputFilter = Rc<
|
||||
dyn Fn(
|
||||
&str,
|
||||
OutputHandle,
|
||||
Option<&OutputInfo>,
|
||||
Option<OutputHandle>,
|
||||
Option<OutputHandle>,
|
||||
) -> bool,
|
||||
>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct LockCallback {
|
||||
name: String,
|
||||
handler: LockCallbackHandler,
|
||||
filter: Option<OutputFilter>,
|
||||
}
|
||||
|
||||
impl LockCallback {
|
||||
|
|
@ -41,9 +53,44 @@ impl LockCallback {
|
|||
Self {
|
||||
name: name.into(),
|
||||
handler,
|
||||
filter: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_filter(
|
||||
name: impl Into<String>,
|
||||
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<OutputHandle>,
|
||||
active_handle: Option<OutputHandle>,
|
||||
) -> 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<OutputInfo>,
|
||||
pub primary_handle: Option<OutputHandle>,
|
||||
pub active_handle: Option<OutputHandle>,
|
||||
}
|
||||
|
||||
struct LockConfigureContext {
|
||||
scale_factor: f32,
|
||||
component_definition: ComponentDefinition,
|
||||
compilation_result: Option<Rc<CompilationResult>>,
|
||||
platform: Rc<CustomSlintPlatform>,
|
||||
callbacks: Vec<LockCallback>,
|
||||
component_name: String,
|
||||
output_handle: OutputHandle,
|
||||
output_info: Option<OutputInfo>,
|
||||
primary_handle: Option<OutputHandle>,
|
||||
active_handle: Option<OutputHandle>,
|
||||
}
|
||||
|
||||
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<ObjectId> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<dyn Fn(&[slint_interpreter::Value]) -> slint_interpreter::Value>,
|
||||
filter: OutputFilter,
|
||||
) {
|
||||
self.state
|
||||
.register_session_lock_callback_with_filter(callback_name, handler, filter);
|
||||
}
|
||||
|
||||
fn session_lock_component_name(&self) -> Option<String> {
|
||||
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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<String>,
|
||||
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<String> {
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
123
crates/composition/src/lock_selection.rs
Normal file
123
crates/composition/src/lock_selection.rs
Normal file
|
|
@ -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<F, R>(&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<F, R>(&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<F>(&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<Vec<Value>, 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<SurfaceInfo> {
|
||||
self.shell.get_selected_lock_info(&self.selector)
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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::Selector>) -> 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<OutputHandle>,
|
||||
active_handle: Option<OutputHandle>| {
|
||||
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<F, R>(
|
||||
&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<F, R>(
|
||||
&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<F>(&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<crate::SurfaceInfo> {
|
||||
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 {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue