// client/src/input/mouse.rs use evdev::{Device, InputEvent, EventType, KeyCode, RelativeAxisCode}; use tokio::sync::broadcast::Sender; use tracing::{error, debug}; use navka_common::navka::HidReport; use navka_common::navka::hid_report::Kind; /// Aggregator for a single mouse device pub struct MouseAggregator { dev: Device, tx: Sender, buttons: u8, dx: i8, dy: i8, wheel: i8, } impl MouseAggregator { pub fn new(dev: Device, tx: Sender) -> Self { Self { dev, tx, buttons: 0, dx: 0, dy: 0, wheel: 0, } } /// helper to set or clear a mouse button bit fn set_btn(&mut self, idx: usize, pressed: bool) { if pressed { self.buttons |= 1 << idx; } else { self.buttons &= !(1 << idx); } } pub fn process_events(&mut self) { let events_vec: Vec = match self.dev.fetch_events() { Ok(it) => it.collect(), Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => { return; } Err(e) => { error!("Mouse device read error: {e}"); return; } }; for ev in events_vec { self.handle_event(ev); } } fn handle_event(&mut self, ev: InputEvent) { match ev.event_type() { EventType::RELATIVE => { match RelativeAxisCode::new(ev.code()) { RelativeAxisCode::REL_X => self.dx = ev.value() as i8, RelativeAxisCode::REL_Y => self.dy = ev.value() as i8, RelativeAxisCode::REL_WHEEL => self.wheel = ev.value() as i8, _ => {} } } EventType::KEY => { let pressed = ev.value() != 0; match ev.code() { c if c == KeyCode::BTN_LEFT.0 => self.set_btn(0, pressed), c if c == KeyCode::BTN_RIGHT.0 => self.set_btn(1, pressed), c if c == KeyCode::BTN_MIDDLE.0 => self.set_btn(2, pressed), _ => {} } } _ => {} } // whenever we changed something, emit: let report = [self.buttons, self.dx as u8, self.dy as u8, self.wheel as u8]; self.send_report(report); // reset deltas so we send *relative* movement self.dx = 0; self.dy = 0; self.wheel = 0; } fn send_report(&self, report: [u8; 4]) { let msg = HidReport { kind: Some(hid_report::Kind::MouseReport(report.to_vec())), }; match self.tx.send(msg.clone()) { Ok(n) => { tracing::trace!("queued → {} receiver(s)", n); } Err(e) => { tracing::warn!("send dropped report ({e}); falling back to send()"); let _ = self.tx.send(msg); } } } }