layer_shika_composition/
session_lock.rs

1use crate::IntoValue;
2use crate::calloop::channel;
3use crate::slint_interpreter::Value;
4use crate::system::{SessionLockCommand, ShellCommand};
5use crate::{Error, Result};
6use layer_shika_adapters::WaylandSystemOps;
7use layer_shika_domain::dimensions::ScaleFactor;
8use layer_shika_domain::errors::DomainError;
9use layer_shika_domain::value_objects::lock_config::LockConfig;
10use layer_shika_domain::value_objects::lock_state::LockState;
11use layer_shika_domain::value_objects::margins::Margins;
12use layer_shika_domain::value_objects::output_policy::OutputPolicy;
13use std::cell::RefCell;
14use std::rc::Rc;
15use std::rc::Weak;
16
17pub struct SessionLock {
18    system: Weak<RefCell<dyn WaylandSystemOps>>,
19    component_name: String,
20    config: LockConfig,
21    command_sender: channel::Sender<ShellCommand>,
22}
23
24impl SessionLock {
25    pub(crate) fn new(
26        system: Weak<RefCell<dyn WaylandSystemOps>>,
27        component_name: String,
28        config: LockConfig,
29        command_sender: channel::Sender<ShellCommand>,
30    ) -> Self {
31        Self {
32            system,
33            component_name,
34            config,
35            command_sender,
36        }
37    }
38
39    pub fn activate(&self) -> Result<()> {
40        log::info!("Session lock activation called - queuing SessionLockCommand::Activate");
41
42        self.command_sender
43            .send(ShellCommand::SessionLock(SessionLockCommand::Activate {
44                component_name: self.component_name.clone(),
45                config: self.config.clone(),
46            }))
47            .map_err(|e| {
48                Error::Domain(DomainError::InvalidInput {
49                    message: format!("Failed to send session lock command: {e:?}"),
50                })
51            })?;
52
53        log::info!("SessionLockCommand::Activate queued successfully");
54        Ok(())
55    }
56
57    pub fn deactivate(&self) -> Result<()> {
58        log::info!("Session lock deactivation called - queuing SessionLockCommand::Deactivate");
59
60        self.command_sender
61            .send(ShellCommand::SessionLock(SessionLockCommand::Deactivate))
62            .map_err(|e| {
63                Error::Domain(DomainError::InvalidInput {
64                    message: format!("Failed to send session lock command: {e:?}"),
65                })
66            })?;
67
68        log::info!("SessionLockCommand::Deactivate queued successfully");
69        Ok(())
70    }
71
72    /// Registers a callback handler on the lock screen component.
73    ///
74    /// The callback must exist in the Slint component, for example:
75    /// `callback unlock_requested(string)`.
76    pub fn on_callback<F, R>(&self, callback_name: &str, handler: F) -> Result<()>
77    where
78        F: Fn() -> R + Clone + 'static,
79        R: IntoValue,
80    {
81        self.on_callback_with_args(callback_name, move |_args| handler().into_value())
82    }
83
84    /// Registers a callback handler that receives Slint arguments.
85    pub fn on_callback_with_args<F, R>(&self, callback_name: &str, handler: F) -> Result<()>
86    where
87        F: Fn(&[Value]) -> R + Clone + 'static,
88        R: IntoValue,
89    {
90        let system = self.system.upgrade().ok_or(Error::SystemDropped)?;
91        let handler = Rc::new(move |args: &[Value]| handler(args).into_value());
92        system
93            .borrow_mut()
94            .register_session_lock_callback(callback_name, handler);
95        Ok(())
96    }
97
98    #[must_use]
99    pub fn state(&self) -> LockState {
100        let Some(system) = self.system.upgrade() else {
101            return LockState::Inactive;
102        };
103
104        let Ok(borrowed) = system.try_borrow() else {
105            return LockState::Inactive;
106        };
107
108        borrowed.session_lock_state().unwrap_or(LockState::Inactive)
109    }
110
111    #[must_use]
112    pub fn is_locked(&self) -> bool {
113        self.state() == LockState::Locked
114    }
115
116    #[must_use]
117    pub fn component_name(&self) -> &str {
118        &self.component_name
119    }
120}
121
122pub struct SessionLockBuilder {
123    component_name: String,
124    config: LockConfig,
125}
126
127impl SessionLockBuilder {
128    #[must_use]
129    pub fn new(component_name: impl Into<String>) -> Self {
130        Self {
131            component_name: component_name.into(),
132            config: LockConfig::default(),
133        }
134    }
135
136    #[must_use]
137    pub fn scale_factor(mut self, factor: impl TryInto<ScaleFactor, Error = DomainError>) -> Self {
138        self.config.scale_factor = factor.try_into().unwrap_or_default();
139        self
140    }
141
142    #[must_use]
143    pub fn margin(mut self, margin: impl Into<Margins>) -> Self {
144        self.config.margin = margin.into();
145        self
146    }
147
148    #[must_use]
149    pub fn namespace(mut self, namespace: impl Into<String>) -> Self {
150        self.config.namespace = namespace.into();
151        self
152    }
153
154    #[must_use]
155    pub fn output_policy(mut self, policy: OutputPolicy) -> Self {
156        self.config.output_policy = policy;
157        self
158    }
159
160    pub(crate) fn build(
161        self,
162        system: Weak<RefCell<dyn WaylandSystemOps>>,
163        command_sender: channel::Sender<ShellCommand>,
164    ) -> SessionLock {
165        SessionLock::new(system, self.component_name, self.config, command_sender)
166    }
167
168    #[must_use]
169    pub fn component_name(&self) -> &str {
170        &self.component_name
171    }
172}