2025-06-08 22:24:14 -05:00
|
|
|
// client/src/input/keyboard.rs
|
|
|
|
|
|
2025-06-08 04:11:58 -05:00
|
|
|
use std::collections::HashSet;
|
|
|
|
|
use evdev::{Device, InputEvent, KeyCode, EventType};
|
2025-06-15 20:19:27 -05:00
|
|
|
use tokio::sync::broadcast::Sender;
|
2025-06-11 00:37:01 -05:00
|
|
|
use tracing::{warn, error, info, debug};
|
2025-06-15 20:19:27 -05:00
|
|
|
|
2025-06-17 08:17:23 -05:00
|
|
|
use navka_common::navka::KeyboardReport;
|
2025-06-08 04:11:58 -05:00
|
|
|
|
|
|
|
|
use crate::input::keymap::{keycode_to_usage, is_modifier};
|
|
|
|
|
|
2025-06-08 22:24:14 -05:00
|
|
|
/// The aggregator logic for a single keyboard device.
|
2025-06-08 04:11:58 -05:00
|
|
|
pub struct KeyboardAggregator {
|
2025-06-08 22:24:14 -05:00
|
|
|
dev: Device,
|
2025-06-17 08:17:23 -05:00
|
|
|
tx: Sender<KeyboardReport>,
|
2025-06-11 00:37:01 -05:00
|
|
|
dev_mode: bool,
|
2025-06-08 04:11:58 -05:00
|
|
|
pressed_keys: HashSet<KeyCode>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl KeyboardAggregator {
|
2025-06-17 08:17:23 -05:00
|
|
|
pub fn new(dev: Device, dev_mode: bool, tx: Sender<KeyboardReport>) -> Self {
|
2025-06-11 00:37:01 -05:00
|
|
|
let _ = dev.set_nonblocking(true);
|
2025-06-08 04:11:58 -05:00
|
|
|
Self {
|
2025-06-08 22:24:14 -05:00
|
|
|
dev,
|
2025-06-12 01:48:48 -05:00
|
|
|
tx,
|
2025-06-11 00:37:01 -05:00
|
|
|
dev_mode,
|
2025-06-08 04:11:58 -05:00
|
|
|
pressed_keys: HashSet::new(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-16 17:54:47 -05:00
|
|
|
#[inline]
|
|
|
|
|
fn dev_log(&self, record: impl FnOnce()) {
|
|
|
|
|
if self.dev_mode {
|
|
|
|
|
record();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-08 22:24:14 -05:00
|
|
|
/// Called frequently (e.g. every ~10ms) to fetch + handle events
|
|
|
|
|
pub fn process_events(&mut self) {
|
2025-06-16 17:54:47 -05:00
|
|
|
let events: Vec<InputEvent> = match self.dev.fetch_events() {
|
2025-06-17 08:17:23 -05:00
|
|
|
Ok(it) => it.collect(),
|
|
|
|
|
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => return,
|
|
|
|
|
Err(e) => { self.d(|| error!("read err: {e}")); return }
|
2025-06-11 00:37:01 -05:00
|
|
|
};
|
2025-06-17 08:17:23 -05:00
|
|
|
|
2025-06-11 00:37:01 -05:00
|
|
|
if self.dev_mode && !events.is_empty() {
|
2025-06-17 08:17:23 -05:00
|
|
|
self.d(|| info!("{} kbd evts from {}", events.len(),
|
|
|
|
|
self.dev.name().unwrap_or("???")));
|
2025-06-11 00:37:01 -05:00
|
|
|
}
|
2025-06-08 04:11:58 -05:00
|
|
|
|
2025-06-17 08:17:23 -05:00
|
|
|
for ev in events {
|
|
|
|
|
if ev.event_type() != EventType::KEY { continue }
|
2025-06-08 04:11:58 -05:00
|
|
|
let code = KeyCode::new(ev.code());
|
2025-06-17 08:17:23 -05:00
|
|
|
match ev.value() {
|
|
|
|
|
1 => { self.pressed.insert(code); } // press
|
|
|
|
|
0 => { self.pressed.remove(&code); } // release
|
2025-06-08 04:11:58 -05:00
|
|
|
_ => {}
|
|
|
|
|
}
|
2025-06-08 22:24:14 -05:00
|
|
|
let report = self.build_report();
|
2025-06-17 08:17:23 -05:00
|
|
|
self.d(|| debug!(?report, "kbd report"));
|
2025-06-12 01:48:48 -05:00
|
|
|
self.send_report(report);
|
2025-06-17 08:17:23 -05:00
|
|
|
if self.magic() { warn!("🧙 magic chord, exiting 🪄 AVADA KEDAVRA!!! 💥💀"); std::process::exit(0) }
|
2025-06-08 04:11:58 -05:00
|
|
|
}
|
2025-06-17 08:17:23 -05:00
|
|
|
}
|
2025-06-08 04:11:58 -05:00
|
|
|
|
2025-06-08 22:24:14 -05:00
|
|
|
fn build_report(&self) -> [u8; 8] {
|
2025-06-17 08:17:23 -05:00
|
|
|
let mut out = [0u8; 8];
|
|
|
|
|
let mut mods = 0u8;
|
|
|
|
|
let mut keys = Vec::new();
|
2025-06-08 22:24:14 -05:00
|
|
|
|
2025-06-17 08:17:23 -05:00
|
|
|
for &kc in &self.pressed {
|
|
|
|
|
if let Some(m) = is_modifier(kc) { mods |= m }
|
|
|
|
|
else if let Some(u) = keycode_to_usage(kc) { keys.push(u) }
|
2025-06-08 04:11:58 -05:00
|
|
|
}
|
2025-06-17 08:17:23 -05:00
|
|
|
out[0] = mods;
|
|
|
|
|
for (i, k) in keys.into_iter().take(6).enumerate() { out[2+i] = k }
|
|
|
|
|
out
|
2025-06-08 04:11:58 -05:00
|
|
|
}
|
|
|
|
|
|
2025-06-12 01:48:48 -05:00
|
|
|
fn send_report(&self, report: [u8; 8]) {
|
2025-06-17 08:17:23 -05:00
|
|
|
let msg = KeyboardReport { data: report.to_vec() }
|
2025-06-15 21:39:07 -05:00
|
|
|
|
2025-06-15 22:33:44 -05:00
|
|
|
match self.tx.send(msg.clone()) {
|
2025-06-15 21:39:07 -05:00
|
|
|
Ok(n) => {
|
2025-06-16 17:54:47 -05:00
|
|
|
self.dev_log(|| info!("📤 sent HID report → {n} subscriber(s)"));
|
2025-06-15 21:39:07 -05:00
|
|
|
}
|
|
|
|
|
Err(e) => {
|
2025-06-16 17:54:47 -05:00
|
|
|
self.dev_log(|| warn!("❌ send failed: {e}"));
|
2025-06-15 21:39:07 -05:00
|
|
|
let _ = self.tx.send(msg);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-06-12 01:48:48 -05:00
|
|
|
}
|
2025-06-11 22:01:16 -05:00
|
|
|
|
2025-06-17 08:17:23 -05:00
|
|
|
#[inline]
|
|
|
|
|
fn magic(&self) -> bool {
|
|
|
|
|
self.pressed.contains(&KeyCode::KEY_LEFTCTRL)
|
|
|
|
|
&& self.pressed.contains(&KeyCode::KEY_ESC)
|
2025-06-08 04:11:58 -05:00
|
|
|
}
|
|
|
|
|
}
|