// client/src/input/keyboard.rs use std::{collections::HashSet, sync::atomic::{AtomicU32, Ordering}}; use evdev::{Device, EventType, InputEvent, KeyCode}; use tokio::sync::broadcast::Sender; use tracing::{debug, error, warn, trace}; use lesavka_common::lesavka::KeyboardReport; use super::keymap::{is_modifier, keycode_to_usage}; pub struct KeyboardAggregator { dev: Device, tx: Sender, dev_mode: bool, pressed_keys: HashSet, } /*───────── helpers ───────────────────────────────────────────────────*/ /// Monotonically-increasing ID that can be logged on server & client. static SEQ: AtomicU32 = AtomicU32::new(0); impl KeyboardAggregator { pub fn new(dev: Device, dev_mode: bool, tx: Sender) -> Self { let _ = dev.set_nonblocking(true); Self { dev, tx, dev_mode, pressed_keys: HashSet::new(), released: false} } pub fn process_events(&mut self) { // --- first fetch, then log (avoids aliasing borrow) --- let events: Vec = match self.dev.fetch_events() { Ok(it) => it.collect(), Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => return, Err(e) => { if self.dev_mode { error!("read error: {e}"); } return } }; if self.dev_mode && !events.is_empty() { trace!("{} kbd evts from {}", events.len(), self.dev.name().unwrap_or("?")); } for ev in events { if ev.event_type() != EventType::KEY { continue } let code = KeyCode::new(ev.code()); match ev.value() { 1 => { self.pressed_keys.insert(code); } // press 0 => { self.pressed_keys.remove(&code); } // release _ => {} } let report = self.build_report(); // Generate a local sequence number for debugging/log-merge only. let id = SEQ.fetch_add(1, Ordering::Relaxed); if self.dev_mode { debug!(seq = id, ?report, "kbd"); } let _ = self.tx.send(KeyboardReport { data: report.to_vec() }); } } fn build_report(&self) -> [u8; 8] { let mut out = [0u8; 8]; let mut mods = 0u8; let mut keys = Vec::new(); for &kc in &self.pressed_keys { if let Some(m) = is_modifier(kc) { mods |= m } else if let Some(u) = keycode_to_usage(kc) { keys.push(u) } } out[0] = mods; for (i, k) in keys.into_iter().take(6).enumerate() { out[2+i] = k } out } pub fn has_key(&self, kc: KeyCode) -> bool { self.pressed_keys.contains(&kc) } pub fn magic_grab(&self) -> bool { self.has_key(KeyCode::KEY_LEFTCTRL) && self.has_key(KeyCode::KEY_LEFTSHIFT) && self.has_key(KeyCode::KEY_G) } pub fn magic_kill(&self) -> bool { self.has_key(KeyCode::KEY_LEFTCTRL) && self.has_key(KeyCode::KEY_ESC) } }