2025-06-08 22:24:14 -05:00
|
|
|
|
// client/src/input/mouse.rs
|
|
|
|
|
|
|
2025-06-16 17:54:47 -05:00
|
|
|
|
use anyhow::Result;
|
2025-06-12 01:48:48 -05:00
|
|
|
|
use evdev::{Device, InputEvent, EventType, KeyCode, RelativeAxisCode};
|
2025-06-16 17:54:47 -05:00
|
|
|
|
use tokio::sync::broadcast::{self, Sender};
|
|
|
|
|
|
use tracing::{debug, error, warn};
|
2025-06-15 22:33:44 -05:00
|
|
|
|
|
2025-06-16 17:54:47 -05:00
|
|
|
|
use navka_common::navka::{HidReport, hid_report};
|
2025-06-08 22:24:14 -05:00
|
|
|
|
|
|
|
|
|
|
/// Aggregator for a single mouse device
|
|
|
|
|
|
pub struct MouseAggregator {
|
|
|
|
|
|
dev: Device,
|
2025-06-11 22:01:16 -05:00
|
|
|
|
tx: Sender<HidReport>,
|
2025-06-16 17:54:47 -05:00
|
|
|
|
dev_mode: bool,
|
|
|
|
|
|
|
2025-06-11 22:01:16 -05:00
|
|
|
|
buttons: u8,
|
|
|
|
|
|
dx: i8,
|
|
|
|
|
|
dy: i8,
|
|
|
|
|
|
wheel: i8,
|
2025-06-08 22:24:14 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl MouseAggregator {
|
2025-06-16 17:54:47 -05:00
|
|
|
|
pub fn new(dev: Device, dev_mode: bool, tx: Sender<HidReport>) -> Self {
|
2025-06-08 22:24:14 -05:00
|
|
|
|
Self {
|
|
|
|
|
|
dev,
|
2025-06-12 01:48:48 -05:00
|
|
|
|
tx,
|
2025-06-16 17:54:47 -05:00
|
|
|
|
dev_mode,
|
|
|
|
|
|
|
2025-06-12 01:48:48 -05:00
|
|
|
|
buttons: 0,
|
2025-06-08 22:24:14 -05:00
|
|
|
|
dx: 0,
|
|
|
|
|
|
dy: 0,
|
2025-06-12 01:48:48 -05:00
|
|
|
|
wheel: 0,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-16 17:54:47 -05:00
|
|
|
|
#[inline]
|
|
|
|
|
|
fn dev_log(&self, record: impl FnOnce()) {
|
|
|
|
|
|
if self.dev_mode {
|
|
|
|
|
|
record();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
|
fn set_btn(&mut self, bit: u8, val: i32) {
|
|
|
|
|
|
if val != 0 {
|
|
|
|
|
|
self.buttons |= 1 << bit;
|
2025-06-12 01:48:48 -05:00
|
|
|
|
} else {
|
2025-06-16 17:54:47 -05:00
|
|
|
|
self.buttons &= !(1 << bit);
|
2025-06-08 22:24:14 -05:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub fn process_events(&mut self) {
|
2025-06-16 17:54:47 -05:00
|
|
|
|
/* 1 ─ read a non‑blocking batch */
|
|
|
|
|
|
let events: Vec<InputEvent> = match self.dev.fetch_events() {
|
2025-06-11 00:37:01 -05:00
|
|
|
|
Ok(it) => it.collect(),
|
2025-06-16 17:54:47 -05:00
|
|
|
|
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => return,
|
|
|
|
|
|
Err(e) => { if self.dev_mode { error!("🖱️ read error: {e}"); } return; }
|
2025-06-11 00:37:01 -05:00
|
|
|
|
};
|
|
|
|
|
|
|
2025-06-16 17:54:47 -05:00
|
|
|
|
if self.dev_mode && !events.is_empty() {
|
|
|
|
|
|
self.dev_log(|| debug!("🖱️ {} events from {}", events.len(),
|
|
|
|
|
|
self.dev.name().unwrap_or("UNKNOWN")));
|
2025-06-08 22:24:14 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-16 17:54:47 -05:00
|
|
|
|
/* 2 ─ aggregate */
|
|
|
|
|
|
for ev in events {
|
|
|
|
|
|
match ev.event_type() {
|
|
|
|
|
|
/* ---------- buttons ---------- */
|
|
|
|
|
|
EventType::KEY => match ev.code() {
|
|
|
|
|
|
c if c == KeyCode::BTN_LEFT.0 => self.set_btn(0, ev.value()),
|
|
|
|
|
|
c if c == KeyCode::BTN_RIGHT.0 => self.set_btn(1, ev.value()),
|
|
|
|
|
|
c if c == KeyCode::BTN_MIDDLE.0 => self.set_btn(2, ev.value()),
|
2025-06-15 20:19:27 -05:00
|
|
|
|
_ => {}
|
2025-06-16 17:54:47 -05:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------- axes ------------ */
|
|
|
|
|
|
EventType::RELATIVE => match ev.code() {
|
|
|
|
|
|
c if c == RelativeAxisCode::REL_X.0 => {
|
|
|
|
|
|
self.dx = self.dx.saturating_add(ev.value().clamp(-127, 127) as i8);
|
|
|
|
|
|
}
|
|
|
|
|
|
c if c == RelativeAxisCode::REL_Y.0 => {
|
|
|
|
|
|
self.dy = self.dy.saturating_add(ev.value().clamp(-127, 127) as i8);
|
|
|
|
|
|
}
|
|
|
|
|
|
c if c == RelativeAxisCode::REL_WHEEL.0 => {
|
|
|
|
|
|
self.wheel = self.wheel.saturating_add(ev.value().clamp(-1, 1) as i8);
|
|
|
|
|
|
}
|
2025-06-11 22:01:16 -05:00
|
|
|
|
_ => {}
|
2025-06-16 17:54:47 -05:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- batch delimiter -------- */
|
|
|
|
|
|
EventType::SYNCHRONIZATION => {
|
|
|
|
|
|
// Any sync event is fine – we only care about boundaries
|
|
|
|
|
|
self.flush_report();
|
2025-06-08 22:24:14 -05:00
|
|
|
|
}
|
2025-06-16 17:54:47 -05:00
|
|
|
|
|
|
|
|
|
|
_ => {}
|
2025-06-08 22:24:14 -05:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-06-16 17:54:47 -05:00
|
|
|
|
}
|
2025-06-11 22:01:16 -05:00
|
|
|
|
|
2025-06-16 17:54:47 -05:00
|
|
|
|
/// Build & send HID packet, then clear deltas
|
|
|
|
|
|
fn flush_report(&mut self) {
|
|
|
|
|
|
/* Nothing changed ⇒ nothing to send */
|
|
|
|
|
|
if self.dx == 0 && self.dy == 0 && self.wheel == 0 { return; }
|
2025-06-15 20:19:27 -05:00
|
|
|
|
|
2025-06-16 17:54:47 -05:00
|
|
|
|
let report = [
|
|
|
|
|
|
self.buttons,
|
|
|
|
|
|
self.dx.clamp(-127, 127) as u8,
|
|
|
|
|
|
self.dy.clamp(-127, 127) as u8,
|
|
|
|
|
|
self.wheel as u8,
|
|
|
|
|
|
];
|
2025-06-11 22:01:16 -05:00
|
|
|
|
|
2025-06-16 17:54:47 -05:00
|
|
|
|
/* broadcast — this is non‑blocking just like `try_send` on mpsc */
|
|
|
|
|
|
if let Err(broadcast::error::SendError(_)) = self.tx.send(HidReport {
|
2025-06-15 21:39:07 -05:00
|
|
|
|
kind: Some(hid_report::Kind::MouseReport(report.to_vec())),
|
2025-06-16 17:54:47 -05:00
|
|
|
|
}) {
|
|
|
|
|
|
self.dev_log(|| warn!("❌ no HID receiver (mouse)"));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
self.dev_log(|| debug!("📤 HID mouse {:?}", report));
|
2025-06-15 21:39:07 -05:00
|
|
|
|
}
|
2025-06-16 17:54:47 -05:00
|
|
|
|
|
|
|
|
|
|
/* reset deltas for next frame */
|
|
|
|
|
|
self.dx = 0; self.dy = 0; self.wheel = 0;
|
2025-06-15 21:39:07 -05:00
|
|
|
|
}
|
2025-06-08 22:24:14 -05:00
|
|
|
|
}
|