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-08 22:24:14 -05:00
|
|
|
use tracing::warn;
|
2025-06-08 04:11:58 -05:00
|
|
|
|
|
|
|
|
use navka_common::navka::HidReport;
|
|
|
|
|
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-08 04:11:58 -05:00
|
|
|
pressed_keys: HashSet<KeyCode>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl KeyboardAggregator {
|
2025-06-08 22:24:14 -05:00
|
|
|
pub fn new(dev: Device) -> Self {
|
2025-06-08 04:11:58 -05:00
|
|
|
Self {
|
2025-06-08 22:24:14 -05:00
|
|
|
dev,
|
2025-06-08 04:11:58 -05:00
|
|
|
pressed_keys: HashSet::new(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-08 22:24:14 -05:00
|
|
|
/// Called frequently (e.g. every ~10ms) to fetch + handle events
|
|
|
|
|
pub fn process_events(&mut self) {
|
|
|
|
|
match self.dev.fetch_events() {
|
|
|
|
|
Ok(events) => {
|
|
|
|
|
for ev in events {
|
|
|
|
|
self.handle_event(ev);
|
2025-06-08 04:11:58 -05:00
|
|
|
}
|
|
|
|
|
}
|
2025-06-08 22:24:14 -05:00
|
|
|
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
|
|
|
|
|
// nothing
|
2025-06-08 04:11:58 -05:00
|
|
|
}
|
2025-06-08 22:24:14 -05:00
|
|
|
Err(e) => {
|
|
|
|
|
tracing::error!("Keyboard device read error: {e}");
|
2025-06-08 04:11:58 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-08 22:24:14 -05:00
|
|
|
fn handle_event(&mut self, ev: InputEvent) {
|
2025-06-08 04:11:58 -05:00
|
|
|
if ev.event_type() == EventType::KEY {
|
|
|
|
|
let code = KeyCode::new(ev.code());
|
2025-06-08 22:24:14 -05:00
|
|
|
match ev.value() {
|
2025-06-08 04:11:58 -05:00
|
|
|
1 => { self.pressed_keys.insert(code); }
|
|
|
|
|
0 => { self.pressed_keys.remove(&code); }
|
|
|
|
|
2 => { /* repeats, if needed */ }
|
|
|
|
|
_ => {}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-08 22:24:14 -05:00
|
|
|
let report = self.build_report();
|
|
|
|
|
// TODO: send this somewhere (e.g. an mpsc::Sender)
|
|
|
|
|
// For now, just log:
|
|
|
|
|
tracing::debug!(?report, "Keyboard HID report");
|
|
|
|
|
// optional: magic chord
|
|
|
|
|
if self.is_magic_chord() {
|
|
|
|
|
warn!("Magic chord pressed => exit aggregator??");
|
|
|
|
|
// Or do something else
|
|
|
|
|
}
|
2025-06-08 04:11:58 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-08 22:24:14 -05:00
|
|
|
fn build_report(&self) -> [u8; 8] {
|
|
|
|
|
let mut bytes = [0u8; 8];
|
|
|
|
|
|
2025-06-08 04:11:58 -05:00
|
|
|
let mut normal_keys = Vec::new();
|
2025-06-08 22:24:14 -05:00
|
|
|
let mut modifiers = 0u8;
|
|
|
|
|
|
|
|
|
|
for &kc in &self.pressed_keys {
|
|
|
|
|
if let Some(m) = is_modifier(kc) {
|
|
|
|
|
modifiers |= m;
|
|
|
|
|
} else if let Some(usage) = keycode_to_usage(kc) {
|
2025-06-08 04:11:58 -05:00
|
|
|
normal_keys.push(usage);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-06-08 22:24:14 -05:00
|
|
|
|
|
|
|
|
bytes[0] = modifiers;
|
|
|
|
|
for (i, keycode) in normal_keys.into_iter().take(6).enumerate() {
|
|
|
|
|
bytes[2 + i] = keycode;
|
2025-06-08 04:11:58 -05:00
|
|
|
}
|
2025-06-08 22:24:14 -05:00
|
|
|
bytes
|
2025-06-08 04:11:58 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn is_magic_chord(&self) -> bool {
|
2025-06-08 22:24:14 -05:00
|
|
|
// example logic
|
|
|
|
|
self.pressed_keys.contains(&KeyCode::KEY_LEFTCTRL) &&
|
|
|
|
|
self.pressed_keys.contains(&KeyCode::KEY_ESC)
|
2025-06-08 04:11:58 -05:00
|
|
|
}
|
|
|
|
|
}
|