lesavka/client/src/input/keyboard.rs
2025-06-28 15:45:35 -05:00

88 lines
3.0 KiB
Rust

// 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<KeyboardReport>,
dev_mode: bool,
pressed_keys: HashSet<KeyCode>,
}
/*───────── 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<KeyboardReport>) -> 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<InputEvent> = 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)
}
}