1use crate::{
2 SpellAssociatedNew,
3 configure::{LayerConf, set_up_tracing},
4 slint_adapter::{SpellLockShell, SpellMultiWinHandler, SpellSkiaWinAdapter},
5 wayland_adapter::{
6 common::PointerState,
7 lock::{self, wayland::SpellSlintLock},
8 },
9};
10use i_slint_core::items::MouseCursor;
11use nonstick::{
12 AuthnFlags, ConversationAdapter, Result as PamResult, Transaction, TransactionBuilder,
13};
14use slint::PhysicalSize;
15use smithay_client_toolkit::{
16 compositor::CompositorState,
17 delegate_compositor, delegate_keyboard, delegate_output, delegate_pointer, delegate_registry,
18 delegate_seat, delegate_session_lock, delegate_shm, delegate_touch,
19 output::{self, OutputState},
20 reexports::{
21 calloop::{
22 self, EventLoop, LoopHandle, RegistrationToken,
23 channel::{self, Sender},
24 },
25 calloop_wayland_source::WaylandSource,
26 client::{
27 Connection, QueueHandle,
28 globals::registry_queue_init,
29 protocol::{wl_keyboard::WlKeyboard, wl_shm, wl_touch::WlTouch},
30 },
31 },
32 registry::RegistryState,
33 seat::{SeatState, pointer::cursor_shape::CursorShapeManager},
34 session_lock::{SessionLock, SessionLockState, SessionLockSurface},
35 shm::{
36 Shm,
37 slot::{Buffer, Slot, SlotPool},
38 },
39};
40use std::{cell::RefCell, process::Command, rc::Rc};
41use tracing::{Level, info, span, warn};
42
43mod input;
44mod internal;
45mod nonstick_impl;
46mod wayland;
47
48pub struct SpellLock {
95 loop_handle: LoopHandle<'static, SpellLock>,
96 conn: Connection,
97 compositor_state: CompositorState,
98 registry_state: RegistryState,
99 output_state: OutputState,
100 keyboard_state: Option<WlKeyboard>,
101 pointer_state: PointerState,
102 touch_state: Option<WlTouch>,
103 seat_state: SeatState,
104 shm: Shm,
105 session_lock: Option<SessionLock>,
106 lock_surfaces: Vec<SessionLockSurface>,
107 slint_part: Option<SpellSlintLock>,
108 is_locked: bool,
109 pub span: span::Span,
111 unlock_screen: Sender<bool>,
112 event_loop: Rc<RefCell<EventLoop<'static, SpellLock>>>,
114 backspace: Option<RegistrationToken>,
115}
116
117impl std::fmt::Debug for SpellLock {
118 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119 f.debug_struct("SpellLock")
120 .field("is_locked", &self.is_locked)
121 .finish()
122 }
123}
124
125impl SpellLock {
126 pub fn invoke_lock_spell() -> Self {
129 let conn = Connection::connect_to_env().unwrap();
130 let _ = set_up_tracing("SpellLock");
131 let (globals, mut event_queue) = registry_queue_init(&conn).unwrap();
132 let qh: QueueHandle<SpellLock> = event_queue.handle();
133 let registry_state = RegistryState::new(&globals);
134 let shm = Shm::bind(&globals, &qh).unwrap();
135 let event_loop: EventLoop<'static, SpellLock> =
136 EventLoop::try_new().expect("Failed to initialize the event loop!");
137 let output_state = OutputState::new(&globals, &qh);
138 let session_lock_state = SessionLockState::new(&globals, &qh);
139 let compositor_state =
140 CompositorState::bind(&globals, &qh).expect("Faild to create compositor state");
141 let cursor_manager =
142 CursorShapeManager::bind(&globals, &qh).expect("cursor shape is not available");
143 let mut win_handler_vec: Vec<(String, (u32, u32))> = Vec::new();
144 let lock_surfaces = Vec::new();
145
146 let pointer_state = PointerState {
147 pointer: None,
148 pointer_data: None,
149 cursor_shape: cursor_manager,
150 last_cursor_enter_serial: None,
151 current_wayland_cursor: MouseCursor::Default,
152 };
153 let (sender, rx) = channel::channel::<bool>();
154 let mut spell_lock = SpellLock {
155 loop_handle: event_loop.handle().clone(),
156 conn: conn.clone(),
157 compositor_state,
158 output_state,
159 keyboard_state: None,
160 touch_state: None,
161 pointer_state,
162 registry_state,
163 seat_state: SeatState::new(&globals, &qh),
164 slint_part: None,
165 shm,
166 session_lock: None,
167 lock_surfaces,
168 unlock_screen: sender,
169 span: span!(Level::INFO, "lock", name = "lock-screen",),
170 is_locked: true,
171 event_loop: Rc::new(RefCell::new(event_loop)),
172 backspace: None,
173 };
174
175 let _ = event_queue.roundtrip(&mut spell_lock);
176
177 let session_lock = Some(
178 session_lock_state
179 .lock(&qh)
180 .expect("ext-session-lock not supported"),
181 );
182
183 spell_lock.session_lock = session_lock;
184 for output in spell_lock.output_state.outputs() {
185 let output_info: output::OutputInfo = spell_lock.output_state.info(&output).unwrap();
186 let output_name: String = output_info.name.unwrap_or_else(|| "SomeOutput".to_string());
187 let dimensions = (
188 output_info.logical_size.unwrap().0 as u32,
189 output_info.logical_size.unwrap().1 as u32,
190 );
191 win_handler_vec.push((output_name, dimensions));
192
193 let session_lock = spell_lock.session_lock.as_ref().unwrap();
194 let surface = spell_lock.compositor_state.create_surface(&qh);
195
196 let lock_surface = session_lock.create_lock_surface(surface, &output, &qh);
199 spell_lock.lock_surfaces.push(lock_surface);
200 }
201 let multi_handler = SpellMultiWinHandler::new_lock(win_handler_vec);
202 let sizes: Vec<PhysicalSize> = multi_handler
203 .borrow()
204 .windows
205 .iter()
206 .map(|(_, conf)| {
207 if let LayerConf::Lock(width, height) = conf {
208 PhysicalSize {
209 width: *width,
210 height: *height,
211 }
212 } else {
213 panic!("Shouldn't enter here");
214 }
215 })
216 .collect();
217
218 let mut pool = SlotPool::new(
219 (sizes[0].width * sizes[0].height * 4) as usize,
220 &spell_lock.shm,
221 )
222 .expect("Couldn't create pool");
223 let mut buffer_slots: Vec<RefCell<Slot>> = Vec::new();
224 let buffers: Vec<Buffer> = sizes
225 .iter()
226 .map(|physical_size| {
227 let stride = physical_size.width as i32 * 4;
228 let (wayland_buffer, _) = pool
229 .create_buffer(
230 physical_size.width as i32,
231 physical_size.height as i32,
232 stride,
233 wl_shm::Format::Argb8888,
234 )
235 .expect("Creating Buffer");
236 buffer_slots.push(RefCell::new(wayland_buffer.slot()));
237 wayland_buffer
238 })
239 .collect();
240 let (slint_event_sender, slint_event_receiver) =
241 calloop::channel::channel::<Box<dyn FnOnce() + Send>>();
242 let pool: Rc<RefCell<SlotPool>> = Rc::new(RefCell::new(pool));
243 let mut adapters: Vec<Rc<SpellSkiaWinAdapter>> = Vec::new();
244 buffer_slots
245 .into_iter()
246 .enumerate()
247 .for_each(|(index, slot)| {
248 let adapter = SpellSkiaWinAdapter::new(
249 pool.clone(),
250 slot,
251 sizes[index].width,
252 sizes[index].height,
253 );
254 adapters.push(adapter);
255 });
256
257 multi_handler.borrow_mut().adapter = adapters.clone();
258 spell_lock.slint_part = Some(SpellSlintLock {
259 adapters,
260 size: sizes,
261 wayland_buffer: buffers,
262 });
263
264 spell_lock.set_event_sources(slint_event_receiver, rx);
265 let _ = slint::platform::set_platform(Box::new(SpellLockShell::new(
266 multi_handler,
267 slint_event_sender,
268 )));
269
270 WaylandSource::new(spell_lock.conn.clone(), event_queue)
271 .insert(spell_lock.loop_handle.clone())
272 .unwrap();
273 spell_lock
274 }
275
276 fn unlock_finger(&mut self, error_callback: Box<dyn FnOnce() + Send>) {
277 let sender = self.unlock_screen.clone();
278 let span = self.span.clone();
279 std::thread::spawn(move || {
280 let _guard = span.enter();
281 fn unlock_internal(sender: Sender<bool>) -> PamResult<()> {
282 let finger = lock::nonstick_impl::FingerprintInfo;
283 let output = Command::new("sh")
284 .arg("-c")
285 .arg("last | awk '{print $1}' | sort | uniq -c | sort -nr")
286 .output()
287 .expect("Couldn't retrive username");
288
289 let val = String::from_utf8_lossy(&output.stdout);
290 let val_2 = val.split('\n').collect::<Vec<_>>()[0].trim();
291 let user_name = val_2.split(" ").collect::<Vec<_>>()[1].to_string();
292
293 let mut txn = TransactionBuilder::new_with_service("login")
294 .username(user_name)
295 .build(finger.into_conversation())?;
296 txn.authenticate(AuthnFlags::empty())?;
299 txn.account_management(AuthnFlags::empty())?;
300 if let Err(err) = sender.send(true) {
301 warn!("Error sending unlock via sender: {err}");
302 }
303 Ok(())
304 }
305 if let Err(err) = unlock_internal(sender) {
306 warn!("{:?}", err);
307 error_callback();
308 } else {
309 info!("Password passed");
310 }
311 });
312 }
313
314 fn unlock(
315 &mut self,
316 username: Option<&str>,
317 password: &str,
318 on_unlock_callback: Box<dyn FnOnce()>,
319 ) -> PamResult<()> {
320 let user_name;
321 if let Some(username) = username {
322 user_name = username.to_string();
323 } else {
324 let output = Command::new("sh")
325 .arg("-c")
326 .arg("last | awk '{print $1}' | sort | uniq -c | sort -nr")
327 .output()
328 .expect("Couldn't retrive username");
329
330 let val = String::from_utf8_lossy(&output.stdout);
331 let val_2 = val.split('\n').collect::<Vec<_>>()[0].trim();
332 user_name = val_2.split(" ").collect::<Vec<_>>()[1].to_string();
333 }
334
335 let user_pass = lock::nonstick_impl::UsernamePassConvo {
336 username: user_name.clone(),
337 password: password.into(),
338 };
339
340 let mut txn = TransactionBuilder::new_with_service("login")
341 .username(user_name)
342 .build(user_pass.into_conversation())?;
343 txn.authenticate(AuthnFlags::empty())?;
346 txn.account_management(AuthnFlags::empty())?;
347
348 on_unlock_callback();
349 if let Some(locked_val) = self.session_lock.take() {
350 locked_val.unlock();
351 } else {
352 warn!("Authentication verified but couldn't unlock");
353 }
354 self.is_locked = false;
355 self.conn.roundtrip().unwrap();
356 Ok(())
357 }
358
359 pub fn get_handler(&self) -> LockHandle {
363 LockHandle(self.loop_handle.clone())
364 }
365}
366
367impl SpellAssociatedNew for SpellLock {
368 fn on_call(&mut self) -> Result<(), Box<dyn std::error::Error>> {
369 let event_loop = self.event_loop.clone();
370 event_loop
371 .borrow_mut()
372 .dispatch(std::time::Duration::from_millis(1), self)?;
373 Ok(())
374 }
375
376 fn is_locked(&self) -> bool {
377 self.is_locked
378 }
379
380 fn get_span(&self) -> span::Span {
381 self.span.clone()
382 }
383}
384
385delegate_keyboard!(SpellLock);
386delegate_compositor!(SpellLock);
387delegate_output!(SpellLock);
388delegate_shm!(SpellLock);
389delegate_registry!(SpellLock);
390delegate_pointer!(SpellLock);
391delegate_touch!(SpellLock);
392delegate_session_lock!(SpellLock);
393delegate_seat!(SpellLock);
394
395#[derive(Debug, Clone)]
398pub struct LockHandle(LoopHandle<'static, SpellLock>);
399
400impl LockHandle {
401 pub fn unlock(
406 &self,
407 username: Option<String>,
408 password: String,
409 on_err_callback: Box<dyn FnOnce()>,
410 on_unlock_callback: Box<dyn FnOnce()>,
411 ) {
412 self.0.insert_idle(move |app_data| {
413 if app_data
414 .unlock(username.as_deref(), &password, on_unlock_callback)
415 .is_err()
416 {
417 on_err_callback();
418 }
419 });
420 }
421
422 pub fn verify_fingerprint(&self, error_callback: Box<dyn FnOnce() + Send>) {
426 self.0.insert_idle(move |app_data| {
427 app_data.unlock_finger(error_callback);
428 });
429 }
430}