// 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> { 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, read_guard: ReadEventsGuard, event_queue: &mut EventQueue, ) -> 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) { 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) -> 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(()) } }