2025-06-08 22:24:14 -05:00
|
|
|
// client/src/input/keyboard.rs
|
|
|
|
|
|
2025-06-17 20:54:31 -05:00
|
|
|
use evdev::{Device, EventType, InputEvent, KeyCode};
|
2025-12-01 11:38:51 -03:00
|
|
|
use std::{
|
|
|
|
|
collections::HashSet,
|
|
|
|
|
sync::atomic::{AtomicU32, Ordering},
|
|
|
|
|
};
|
2025-06-15 20:19:27 -05:00
|
|
|
use tokio::sync::broadcast::Sender;
|
2025-06-28 16:03:19 -05:00
|
|
|
use tracing::{debug, error, trace};
|
2025-06-15 20:19:27 -05:00
|
|
|
|
2025-06-23 07:18:26 -05:00
|
|
|
use lesavka_common::lesavka::KeyboardReport;
|
2025-06-08 04:11:58 -05:00
|
|
|
|
2025-06-17 20:54:31 -05:00
|
|
|
use super::keymap::{is_modifier, keycode_to_usage};
|
2025-06-08 04:11:58 -05:00
|
|
|
|
|
|
|
|
pub struct KeyboardAggregator {
|
2025-06-08 22:24:14 -05:00
|
|
|
dev: Device,
|
2025-12-01 11:38:51 -03:00
|
|
|
tx: Sender<KeyboardReport>,
|
2025-06-11 00:37:01 -05:00
|
|
|
dev_mode: bool,
|
2025-06-28 18:51:13 -05:00
|
|
|
sending_disabled: bool,
|
2025-06-08 04:11:58 -05:00
|
|
|
pressed_keys: HashSet<KeyCode>,
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-26 04:07:22 -05:00
|
|
|
/*───────── helpers ───────────────────────────────────────────────────*/
|
2025-06-28 15:45:35 -05:00
|
|
|
/// Monotonically-increasing ID that can be logged on server & client.
|
2025-06-26 04:07:22 -05:00
|
|
|
static SEQ: AtomicU32 = AtomicU32::new(0);
|
|
|
|
|
|
2025-06-08 04:11:58 -05:00
|
|
|
impl KeyboardAggregator {
|
2025-06-17 08:17:23 -05:00
|
|
|
pub fn new(dev: Device, dev_mode: bool, tx: Sender<KeyboardReport>) -> Self {
|
2025-06-17 20:54:31 -05:00
|
|
|
let _ = dev.set_nonblocking(true);
|
2025-12-01 11:38:51 -03:00
|
|
|
Self {
|
|
|
|
|
dev,
|
|
|
|
|
tx,
|
|
|
|
|
dev_mode,
|
|
|
|
|
sending_disabled: false,
|
|
|
|
|
pressed_keys: HashSet::new(),
|
|
|
|
|
}
|
2025-06-16 17:54:47 -05:00
|
|
|
}
|
|
|
|
|
|
2025-06-28 17:55:15 -05:00
|
|
|
pub fn set_grab(&mut self, grab: bool) {
|
2025-12-01 11:38:51 -03:00
|
|
|
let _ = if grab {
|
|
|
|
|
self.dev.grab()
|
|
|
|
|
} else {
|
|
|
|
|
self.dev.ungrab()
|
|
|
|
|
};
|
2025-06-28 17:55:15 -05:00
|
|
|
}
|
2025-06-28 16:03:19 -05:00
|
|
|
|
2025-06-28 18:51:13 -05:00
|
|
|
pub fn set_send(&mut self, send: bool) {
|
|
|
|
|
self.sending_disabled = !send;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-08 22:24:14 -05:00
|
|
|
pub fn process_events(&mut self) {
|
2025-06-17 20:54:31 -05:00
|
|
|
// --- first fetch, then log (avoids aliasing borrow) ---
|
2025-06-17 22:02:33 -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,
|
2025-12-01 11:38:51 -03:00
|
|
|
Err(e) => {
|
|
|
|
|
if self.dev_mode {
|
|
|
|
|
error!("⌨️❌ read error: {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-12-01 11:38:51 -03:00
|
|
|
trace!(
|
|
|
|
|
"⌨️ {} 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 {
|
2025-12-01 11:38:51 -03:00
|
|
|
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() {
|
2025-12-01 11:38:51 -03:00
|
|
|
1 => {
|
|
|
|
|
self.pressed_keys.insert(code);
|
|
|
|
|
} // press
|
|
|
|
|
0 => {
|
|
|
|
|
self.pressed_keys.remove(&code);
|
|
|
|
|
} // release
|
2025-06-08 04:11:58 -05:00
|
|
|
_ => {}
|
|
|
|
|
}
|
2025-06-17 20:54:31 -05:00
|
|
|
|
2025-06-08 22:24:14 -05:00
|
|
|
let report = self.build_report();
|
2025-06-28 15:45:35 -05:00
|
|
|
// Generate a local sequence number for debugging/log-merge only.
|
2025-06-26 04:07:22 -05:00
|
|
|
let id = SEQ.fetch_add(1, Ordering::Relaxed);
|
2025-12-01 11:38:51 -03:00
|
|
|
if self.dev_mode {
|
|
|
|
|
debug!(seq = id, ?report, "kbd");
|
|
|
|
|
}
|
2025-06-28 18:51:13 -05:00
|
|
|
if !self.sending_disabled {
|
2025-12-01 11:38:51 -03:00
|
|
|
let _ = self.tx.send(KeyboardReport {
|
|
|
|
|
data: report.to_vec(),
|
|
|
|
|
});
|
2025-06-28 18:51:13 -05:00
|
|
|
}
|
2025-06-08 04:11:58 -05:00
|
|
|
}
|
2025-06-17 20:54:31 -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 20:54:31 -05:00
|
|
|
for &kc in &self.pressed_keys {
|
2025-12-01 11:38:51 -03:00
|
|
|
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 20:54:31 -05:00
|
|
|
|
2025-06-17 08:17:23 -05:00
|
|
|
out[0] = mods;
|
2025-12-01 11:38:51 -03:00
|
|
|
for (i, k) in keys.into_iter().take(6).enumerate() {
|
|
|
|
|
out[2 + i] = k
|
|
|
|
|
}
|
2025-06-17 08:17:23 -05:00
|
|
|
out
|
2025-06-08 04:11:58 -05:00
|
|
|
}
|
|
|
|
|
|
2025-12-01 11:38:51 -03:00
|
|
|
pub fn has_key(&self, kc: KeyCode) -> bool {
|
|
|
|
|
self.pressed_keys.contains(&kc)
|
|
|
|
|
}
|
2025-06-28 15:45:35 -05:00
|
|
|
|
|
|
|
|
pub fn magic_grab(&self) -> bool {
|
|
|
|
|
self.has_key(KeyCode::KEY_LEFTCTRL)
|
|
|
|
|
&& self.has_key(KeyCode::KEY_LEFTSHIFT)
|
|
|
|
|
&& self.has_key(KeyCode::KEY_G)
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-29 04:54:39 -05:00
|
|
|
pub fn magic_left(&self) -> bool {
|
|
|
|
|
self.has_key(KeyCode::KEY_LEFTCTRL)
|
|
|
|
|
&& self.has_key(KeyCode::KEY_LEFTSHIFT)
|
|
|
|
|
&& self.has_key(KeyCode::KEY_LEFT)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn magic_right(&self) -> bool {
|
|
|
|
|
self.has_key(KeyCode::KEY_LEFTCTRL)
|
|
|
|
|
&& self.has_key(KeyCode::KEY_LEFTSHIFT)
|
|
|
|
|
&& self.has_key(KeyCode::KEY_RIGHT)
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-28 15:45:35 -05:00
|
|
|
pub fn magic_kill(&self) -> bool {
|
2025-12-01 11:38:51 -03:00
|
|
|
self.has_key(KeyCode::KEY_LEFTCTRL) && self.has_key(KeyCode::KEY_ESC)
|
2025-06-08 04:11:58 -05:00
|
|
|
}
|
|
|
|
|
}
|
2025-06-29 03:46:34 -05:00
|
|
|
|
|
|
|
|
impl Drop for KeyboardAggregator {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
let _ = self.dev.ungrab();
|
|
|
|
|
let _ = self.tx.send(KeyboardReport {
|
|
|
|
|
data: [0; 8].into(),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|