[ MIRROR ] Customisable, minimalist screen locker for Wayland git.ngill.net/nlock.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163// SPDX-License-Identifier: GPL-3.0-or-later// Copyright (C) 2026, Nathan Gill
use std::{ os::fd::{AsRawFd, BorrowedFd}, sync::atomic::Ordering,};
use anyhow::{Result, anyhow};use nix::errno::Errno;use num_enum::{IntoPrimitive, TryFromPrimitive};use tracing::{debug, warn};use wayland_client::{EventQueue, QueueHandle, backend::ReadEventsGuard};
use crate::{ auth::AuthState, event_loop::{Event, EventSource}, state::NLockState,};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]#[repr(usize)]pub enum EventType { Wayland = 0, KeyboardRepeat = 1, AuthStateChanged = 2, Debug = 3, Interrupt = 4,}
impl NLockState { fn poll_events(&mut self, wayland_sock_fd: BorrowedFd<'_>) -> Result<Vec<Event>> { self.event_loop.add( EventSource::Fd(wayland_sock_fd.as_raw_fd()), EventType::Wayland.into(), )?;
let events = match self.event_loop.poll() { Ok(evs) => evs, Err(Errno::EINTR) => Vec::new(), Err(e) => return Err(anyhow!("Error during poll: {e}")), };
self.event_loop.remove(EventType::Wayland.into());
Ok(events) }
fn process_events( &mut self, events: Vec<Event>, read_guard: ReadEventsGuard, event_queue: &mut EventQueue<NLockState>, ) -> Result<()> { let mut wayland_sock_ready = false; for event in events { match event { Event::Readable { fd: _, tag } => match EventType::try_from(tag)? { EventType::Wayland => { wayland_sock_ready = true; } EventType::AuthStateChanged => match self.auth_comm.response.read() { Ok(AuthState::Idle) => { // request was ignored by backend, do nothing } Ok(AuthState::Success) => { // auth was successful, set flags for exit self.auth_state.store(AuthState::Success, Ordering::Relaxed); self.running.store(false, Ordering::Relaxed); self.state_changed.store(true, Ordering::Relaxed); } Ok(AuthState::Fail) => { // auth failed, set fail state self.auth_state.store(AuthState::Fail, Ordering::Relaxed); self.state_changed.store(true, Ordering::Relaxed); } Err(e) => { warn!("Failed to receive auth response: {e}"); } }, EventType::Debug => { if let Some(debug_comm) = &mut self.debug_comm { let _ = debug_comm.read();
debug!("Received a debug event, exiting...");
// fake a "success" and exit self.running.store(false, Ordering::Relaxed); self.state_changed.store(true, Ordering::Relaxed); } else { warn!("Received debug event, but not in debug mode, ignoring"); } } EventType::Interrupt => { // interrupt the event poll, something happened self.interrupt.read()?; } _ => {} }, Event::Timeout { tag } => { if EventType::try_from(tag)? == EventType::KeyboardRepeat { self.handle_repeat_event(); } } } }
if wayland_sock_ready { read_guard.read()?; event_queue.dispatch_pending(self)?; } else { std::mem::drop(read_guard); }
Ok(()) }
fn re_render(&mut self, qh: &QueueHandle<NLockState>) { if self.state_changed.load(Ordering::Relaxed) { // mark as dirty when state changes for (_, surface) in self.surfaces.iter_mut() { surface.tracking.dirty = true; }
self.state_changed.store(false, Ordering::Relaxed); }
if let Some(shm) = &self.shm { let auth_state = self.auth_state.clone().load(Ordering::Relaxed);
for (_, surface) in self.surfaces.iter_mut() { // only render when surface advertised as available if surface.tracking.dirty && surface.tracking.ready { surface.render( &self.config, auth_state, self.password.chars().count(), self.background_image.as_ref(), shm, qh, ); } } } }
pub fn event_loop_cycle(&mut self, event_queue: &mut EventQueue<NLockState>) -> Result<()> { event_queue.flush()?; event_queue.dispatch_pending(self)?;
let read_guard = event_queue .prepare_read() .ok_or(anyhow!("Failed to obtain Wayland event read guard"))?; let wayland_sock_fd = read_guard.connection_fd();
let events = self.poll_events(wayland_sock_fd)?; self.process_events(events, read_guard, event_queue)?; self.re_render(&event_queue.handle());
Ok(()) }}