established full keyboard functionality, working on mouse
This commit is contained in:
parent
5cc1e92de0
commit
d5fe1898a4
@ -1,16 +1,17 @@
|
||||
[package]
|
||||
name = "navka_client"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1.45", features = ["full", "fs"] }
|
||||
tokio = { version = "1.45", features = ["full", "fs", "rt-multi-thread", "macros", "sync", "time"] }
|
||||
tonic = { version = "0.13", features = ["transport"] }
|
||||
tokio-stream = { version = "0.1", features = ["sync"] }
|
||||
anyhow = "1.0"
|
||||
navka_common = { path = "../common" }
|
||||
tracing = { version = "0.1", features = ["std"] }
|
||||
tracing-subscriber = "0.3"
|
||||
tracing-subscriber = { version = "0.3", features = ["fmt", "env-filter"] }
|
||||
tracing-appender = "0.2"
|
||||
futures = "0.3"
|
||||
evdev = "0.13"
|
||||
|
||||
|
||||
@ -1,11 +1,9 @@
|
||||
// client/src/app.rs
|
||||
#![forbid(unsafe_code)]
|
||||
use anyhow::Result;
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use std::time::Duration;
|
||||
use tokio::{sync::mpsc, sync::broadcast, task::JoinHandle};
|
||||
use tokio_stream::StreamExt as _;
|
||||
use tokio_stream::wrappers::{ReceiverStream, BroadcastStream};
|
||||
use tokio::{sync::broadcast, task::JoinHandle};
|
||||
use tokio_stream::{wrappers::BroadcastStream, StreamExt};
|
||||
use tonic::Request;
|
||||
use tracing::{info, warn, error, debug};
|
||||
|
||||
@ -21,6 +19,7 @@ pub struct NavkaClientApp {
|
||||
|
||||
impl NavkaClientApp {
|
||||
pub fn new() -> Result<Self> {
|
||||
info!("Creating navka-client app!");
|
||||
let dev_mode = std::env::var("NAVKA_DEV_MODE").is_ok();
|
||||
let addr = std::env::args()
|
||||
.nth(1)
|
||||
@ -40,6 +39,7 @@ impl NavkaClientApp {
|
||||
}
|
||||
|
||||
pub async fn run(&mut self) -> Result<()> {
|
||||
info!("Running navka-client app!");
|
||||
// spawn aggregator
|
||||
let mut aggregator = std::mem::take(&mut self.aggregator).expect("aggregator must exist");
|
||||
let aggregator_task: JoinHandle<Result<()>> = tokio::spawn(async move {
|
||||
@ -105,8 +105,7 @@ impl NavkaClientApp {
|
||||
};
|
||||
|
||||
// fresh reader over the *same* broadcast channel
|
||||
let mut rx = self.tx.subscribe();
|
||||
let outbound = BroadcastStream::new(rx.clone()).filter_map(|r| async { r.ok() });
|
||||
let outbound = BroadcastStream::new(self.tx.subscribe()).filter_map(|r| r.ok());
|
||||
|
||||
info!("🛫 spawning stream()");
|
||||
let response = match client.stream(Request::new(outbound)).await {
|
||||
|
||||
@ -84,7 +84,7 @@ impl InputAggregator {
|
||||
info!("Grabbed mouse {:?}", dev.name().unwrap_or("UNKNOWN"));
|
||||
|
||||
// let mouse_agg = MouseAggregator::new(dev);np
|
||||
let mouse_agg = MouseAggregator::new(dev, self.tx.clone());
|
||||
let mouse_agg = MouseAggregator::new(dev, self.dev_mode, self.tx.clone());
|
||||
self.mice.push(mouse_agg);
|
||||
found_any = true;
|
||||
continue;
|
||||
|
||||
@ -5,8 +5,7 @@ use evdev::{Device, InputEvent, KeyCode, EventType};
|
||||
use tokio::sync::broadcast::Sender;
|
||||
use tracing::{warn, error, info, debug};
|
||||
|
||||
use navka_common::navka::HidReport;
|
||||
use navka_common::navka::hid_report::Kind;
|
||||
use navka_common::navka::{HidReport, hid_report};
|
||||
|
||||
use crate::input::keymap::{keycode_to_usage, is_modifier};
|
||||
|
||||
@ -29,31 +28,44 @@ impl KeyboardAggregator {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn dev_log(&self, record: impl FnOnce()) {
|
||||
if self.dev_mode {
|
||||
record();
|
||||
}
|
||||
}
|
||||
|
||||
/// Called frequently (e.g. every ~10ms) to fetch + handle events
|
||||
pub fn process_events(&mut self) {
|
||||
let events: Vec<InputEvent> = {
|
||||
match self.dev.fetch_events() {
|
||||
Ok(it) => it.collect(),
|
||||
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => return,
|
||||
Err(e) => {
|
||||
// Fetch once. Any borrow of `self.dev` ends right here.
|
||||
let events: Vec<InputEvent> = match self.dev.fetch_events() {
|
||||
Ok(iter) => iter.collect(),
|
||||
|
||||
// Would-block → nothing new
|
||||
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => return,
|
||||
|
||||
// Any other error → log without touching `self.dev`
|
||||
Err(e) => {
|
||||
if self.dev_mode {
|
||||
error!("Keyboard device read error: {e}");
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Safe: no mutable borrow is active now
|
||||
if self.dev_mode && !events.is_empty() {
|
||||
info!(
|
||||
self.dev_log(|| info!(
|
||||
"Got {} events from dev: {}",
|
||||
events.len(),
|
||||
self.dev.name().unwrap_or("???")
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
for ev in events {
|
||||
self.handle_event(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_event(&mut self, ev: InputEvent) {
|
||||
if ev.event_type() == EventType::KEY {
|
||||
@ -61,10 +73,10 @@ impl KeyboardAggregator {
|
||||
let val = ev.value(); // 1 = press, 0 = release, 2 = repeat
|
||||
|
||||
if self.dev_mode {
|
||||
info!(
|
||||
self.dev_log(|| info!(
|
||||
"Keyboard event: code={:?}, value={}, name={:?}",
|
||||
code, val, self.dev.name()
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
match val {
|
||||
@ -77,12 +89,10 @@ impl KeyboardAggregator {
|
||||
let report = self.build_report();
|
||||
// TODO: send this somewhere (e.g. an mpsc::Sender)
|
||||
// For now, just log:
|
||||
debug!(?report, "Keyboard HID report");
|
||||
self.dev_log(|| debug!(?report, "Keyboard HID report"));
|
||||
self.send_report(report);
|
||||
// optional: magic chord
|
||||
if self.is_magic_chord() {
|
||||
warn!("Magic chord pressed => exit aggregator??");
|
||||
// Or do something else
|
||||
self.dev_log(|| warn!("Magic chord pressed => AVADA KEDAVA!!!"));
|
||||
std::process::exit(0);
|
||||
}
|
||||
}
|
||||
@ -116,10 +126,10 @@ impl KeyboardAggregator {
|
||||
|
||||
match self.tx.send(msg.clone()) {
|
||||
Ok(n) => {
|
||||
info!("📤 sent HID report → {n} subscriber(s)");
|
||||
self.dev_log(|| info!("📤 sent HID report → {n} subscriber(s)"));
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::warn!("❌ send failed: {e}");
|
||||
self.dev_log(|| warn!("❌ send failed: {e}"));
|
||||
let _ = self.tx.send(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ use evdev::KeyCode;
|
||||
/// Return Some(usage) if we have a known mapping from evdev::KeyCode -> HID usage code
|
||||
pub fn keycode_to_usage(key: KeyCode) -> Option<u8> {
|
||||
match key {
|
||||
// Letters
|
||||
// --- Letters ------------------------------------------------------
|
||||
KeyCode::KEY_A => Some(0x04),
|
||||
KeyCode::KEY_B => Some(0x05),
|
||||
KeyCode::KEY_C => Some(0x06),
|
||||
@ -33,7 +33,7 @@ pub fn keycode_to_usage(key: KeyCode) -> Option<u8> {
|
||||
KeyCode::KEY_Y => Some(0x1C),
|
||||
KeyCode::KEY_Z => Some(0x1D),
|
||||
|
||||
// Number row
|
||||
// --- Number row ---------------------------------------------------
|
||||
KeyCode::KEY_1 => Some(0x1E),
|
||||
KeyCode::KEY_2 => Some(0x1F),
|
||||
KeyCode::KEY_3 => Some(0x20),
|
||||
@ -45,7 +45,7 @@ pub fn keycode_to_usage(key: KeyCode) -> Option<u8> {
|
||||
KeyCode::KEY_9 => Some(0x26),
|
||||
KeyCode::KEY_0 => Some(0x27),
|
||||
|
||||
// Common punctuation
|
||||
// --- Common punctuation -------------------------------------------
|
||||
KeyCode::KEY_ENTER => Some(0x28),
|
||||
KeyCode::KEY_ESC => Some(0x29),
|
||||
KeyCode::KEY_BACKSPACE => Some(0x2A),
|
||||
@ -63,7 +63,42 @@ pub fn keycode_to_usage(key: KeyCode) -> Option<u8> {
|
||||
KeyCode::KEY_DOT => Some(0x37),
|
||||
KeyCode::KEY_SLASH => Some(0x38),
|
||||
|
||||
// Function keys
|
||||
// --- Navigation / editing cluster ---------------------------------
|
||||
KeyCode::KEY_SYSRQ => Some(0x46), // Print‑Screen
|
||||
KeyCode::KEY_SCROLLLOCK => Some(0x47),
|
||||
KeyCode::KEY_PAUSE => Some(0x48),
|
||||
KeyCode::KEY_INSERT => Some(0x49),
|
||||
KeyCode::KEY_HOME => Some(0x4A),
|
||||
KeyCode::KEY_PAGEUP => Some(0x4B),
|
||||
KeyCode::KEY_DELETE => Some(0x4C),
|
||||
KeyCode::KEY_END => Some(0x4D),
|
||||
KeyCode::KEY_PAGEDOWN => Some(0x4E),
|
||||
KeyCode::KEY_RIGHT => Some(0x4F),
|
||||
KeyCode::KEY_LEFT => Some(0x50),
|
||||
KeyCode::KEY_DOWN => Some(0x51),
|
||||
KeyCode::KEY_UP => Some(0x52),
|
||||
|
||||
// --- Keypad / Num‑lock block --------------------------------------
|
||||
KeyCode::KEY_NUMLOCK => Some(0x53),
|
||||
KeyCode::KEY_KPSLASH => Some(0x54),
|
||||
KeyCode::KEY_KPASTERISK => Some(0x55),
|
||||
KeyCode::KEY_KPMINUS => Some(0x56),
|
||||
KeyCode::KEY_KPPLUS => Some(0x57),
|
||||
KeyCode::KEY_KPENTER => Some(0x58),
|
||||
KeyCode::KEY_KP1 => Some(0x59),
|
||||
KeyCode::KEY_KP2 => Some(0x5A),
|
||||
KeyCode::KEY_KP3 => Some(0x5B),
|
||||
KeyCode::KEY_KP4 => Some(0x5C),
|
||||
KeyCode::KEY_KP5 => Some(0x5D),
|
||||
KeyCode::KEY_KP6 => Some(0x5E),
|
||||
KeyCode::KEY_KP7 => Some(0x5F),
|
||||
KeyCode::KEY_KP8 => Some(0x60),
|
||||
KeyCode::KEY_KP9 => Some(0x61),
|
||||
KeyCode::KEY_KP0 => Some(0x62),
|
||||
KeyCode::KEY_KPDOT => Some(0x63),
|
||||
KeyCode::KEY_KPEQUAL => Some(0x67),
|
||||
|
||||
// --- Function keys ------------------------------------------------
|
||||
KeyCode::KEY_CAPSLOCK => Some(0x39),
|
||||
KeyCode::KEY_F1 => Some(0x3A),
|
||||
KeyCode::KEY_F2 => Some(0x3B),
|
||||
@ -78,6 +113,10 @@ pub fn keycode_to_usage(key: KeyCode) -> Option<u8> {
|
||||
KeyCode::KEY_F11 => Some(0x44),
|
||||
KeyCode::KEY_F12 => Some(0x45),
|
||||
|
||||
// --- Misc ---------------------------------------------------------
|
||||
KeyCode::KEY_102ND => Some(0x64), // “<>” on ISO boards
|
||||
KeyCode::KEY_MENU => Some(0x65), // Application / Compose
|
||||
|
||||
// We'll handle modifiers (ctrl, shift, alt, meta) in `is_modifier()`
|
||||
_ => None,
|
||||
}
|
||||
|
||||
@ -1,16 +1,18 @@
|
||||
// client/src/input/mouse.rs
|
||||
|
||||
use anyhow::Result;
|
||||
use evdev::{Device, InputEvent, EventType, KeyCode, RelativeAxisCode};
|
||||
use tokio::sync::broadcast::Sender;
|
||||
use tracing::{error, debug};
|
||||
use tokio::sync::broadcast::{self, Sender};
|
||||
use tracing::{debug, error, warn};
|
||||
|
||||
use navka_common::navka::HidReport;
|
||||
use navka_common::navka::hid_report::Kind;
|
||||
use navka_common::navka::{HidReport, hid_report};
|
||||
|
||||
/// Aggregator for a single mouse device
|
||||
pub struct MouseAggregator {
|
||||
dev: Device,
|
||||
tx: Sender<HidReport>,
|
||||
dev_mode: bool,
|
||||
|
||||
buttons: u8,
|
||||
dx: i8,
|
||||
dy: i8,
|
||||
@ -18,10 +20,12 @@ pub struct MouseAggregator {
|
||||
}
|
||||
|
||||
impl MouseAggregator {
|
||||
pub fn new(dev: Device, tx: Sender<HidReport>) -> Self {
|
||||
pub fn new(dev: Device, dev_mode: bool, tx: Sender<HidReport>) -> Self {
|
||||
Self {
|
||||
dev,
|
||||
tx,
|
||||
dev_mode,
|
||||
|
||||
buttons: 0,
|
||||
dx: 0,
|
||||
dy: 0,
|
||||
@ -29,77 +33,93 @@ impl MouseAggregator {
|
||||
}
|
||||
}
|
||||
|
||||
/// helper to set or clear a mouse button bit
|
||||
fn set_btn(&mut self, idx: usize, pressed: bool) {
|
||||
if pressed {
|
||||
self.buttons |= 1 << idx;
|
||||
#[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;
|
||||
} else {
|
||||
self.buttons &= !(1 << idx);
|
||||
self.buttons &= !(1 << bit);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_events(&mut self) {
|
||||
let events_vec: Vec<InputEvent> = match self.dev.fetch_events() {
|
||||
/* 1 ─ read a non‑blocking batch */
|
||||
let events: Vec<InputEvent> = 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;
|
||||
}
|
||||
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => return,
|
||||
Err(e) => { if self.dev_mode { error!("🖱️ read error: {e}"); } return; }
|
||||
};
|
||||
|
||||
for ev in events_vec {
|
||||
self.handle_event(ev);
|
||||
if self.dev_mode && !events.is_empty() {
|
||||
self.dev_log(|| debug!("🖱️ {} events from {}", events.len(),
|
||||
self.dev.name().unwrap_or("UNKNOWN")));
|
||||
}
|
||||
|
||||
/* 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()),
|
||||
_ => {}
|
||||
},
|
||||
|
||||
/* ----------- 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);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
|
||||
/* ---- batch delimiter -------- */
|
||||
EventType::SYNCHRONIZATION => {
|
||||
// Any sync event is fine – we only care about boundaries
|
||||
self.flush_report();
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
/// 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; }
|
||||
|
||||
// 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);
|
||||
let report = [
|
||||
self.buttons,
|
||||
self.dx.clamp(-127, 127) as u8,
|
||||
self.dy.clamp(-127, 127) as u8,
|
||||
self.wheel as u8,
|
||||
];
|
||||
|
||||
// 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 {
|
||||
/* broadcast — this is non‑blocking just like `try_send` on mpsc */
|
||||
if let Err(broadcast::error::SendError(_)) = self.tx.send(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);
|
||||
}
|
||||
}) {
|
||||
self.dev_log(|| warn!("❌ no HID receiver (mouse)"));
|
||||
} else {
|
||||
self.dev_log(|| debug!("📤 HID mouse {:?}", report));
|
||||
}
|
||||
|
||||
/* reset deltas for next frame */
|
||||
self.dx = 0; self.dy = 0; self.wheel = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,56 +4,65 @@
|
||||
|
||||
use anyhow::Result;
|
||||
use navka_client::NavkaClientApp;
|
||||
use std::env;
|
||||
use std::fs::OpenOptions;
|
||||
use tracing_subscriber::fmt;
|
||||
use tracing_subscriber::prelude::__tracing_subscriber_SubscriberExt;
|
||||
use tracing_subscriber::{fmt, EnvFilter};
|
||||
use std::{env, fs::OpenOptions, path::Path};
|
||||
use tracing_appender::non_blocking;
|
||||
use tracing_appender::non_blocking::WorkerGuard;
|
||||
use tracing_subscriber::{filter::EnvFilter, fmt, prelude::*};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
tracing_subscriber::fmt()
|
||||
.with_env_filter(
|
||||
// honour RUST_LOG but fall back to very chatty defaults
|
||||
EnvFilter::try_from_default_env().unwrap_or_else(|_| {
|
||||
EnvFilter::new(
|
||||
"navka_client=trace,\
|
||||
navka_server=trace,\
|
||||
tonic=debug,\
|
||||
h2=debug,\
|
||||
tower=debug",
|
||||
)
|
||||
}),
|
||||
)
|
||||
.with_target(true)
|
||||
.with_thread_ids(true)
|
||||
.with_file(true)
|
||||
.init();
|
||||
/*------------- common filter & stderr layer ------------------------*/
|
||||
let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| {
|
||||
EnvFilter::new(
|
||||
"navka_client=trace,\
|
||||
navka_server=trace,\
|
||||
tonic=debug,\
|
||||
h2=debug,\
|
||||
tower=debug",
|
||||
)
|
||||
});
|
||||
|
||||
let stderr_layer = fmt::layer()
|
||||
.with_target(true)
|
||||
.with_thread_ids(true)
|
||||
.with_file(true);
|
||||
|
||||
let dev_mode = env::var("NAVKA_DEV_MODE").is_ok();
|
||||
let mut _guard: Option<WorkerGuard> = None; // keep guard alive
|
||||
|
||||
/*------------- subscriber setup -----------------------------------*/
|
||||
if dev_mode {
|
||||
let log_path = Path::new("/tmp").join("navka-client.log");
|
||||
|
||||
// file → non‑blocking writer (+ guard)
|
||||
let file = OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open("/tmp/navka-client.log")?;
|
||||
.open(&log_path)?;
|
||||
let (file_writer, guard) = non_blocking(file);
|
||||
_guard = Some(guard);
|
||||
|
||||
let subscriber = tracing_subscriber::registry()
|
||||
.with(fmt::layer())
|
||||
.with(
|
||||
fmt::layer()
|
||||
.with_writer(file)
|
||||
.with_ansi(false)
|
||||
.with_target(true)
|
||||
.with_level(true),
|
||||
);
|
||||
let file_layer = fmt::layer()
|
||||
.with_writer(file_writer)
|
||||
.with_ansi(false)
|
||||
.with_target(true)
|
||||
.with_level(true);
|
||||
|
||||
tracing::subscriber::set_global_default(subscriber)?;
|
||||
tracing::info!("navka-client starting in dev mode: logs -> /tmp/navka-client.log");
|
||||
tracing_subscriber::registry()
|
||||
.with(env_filter)
|
||||
.with(stderr_layer)
|
||||
.with(file_layer)
|
||||
.init();
|
||||
|
||||
tracing::info!("navka-client running in DEV mode → {}", log_path.display());
|
||||
} else {
|
||||
tracing_subscriber::fmt::init();
|
||||
tracing_subscriber::registry()
|
||||
.with(env_filter)
|
||||
.with(stderr_layer)
|
||||
.init();
|
||||
}
|
||||
|
||||
/*------------- run the actual application -------------------------*/
|
||||
let mut app = NavkaClientApp::new()?;
|
||||
app.run().await
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,25 +39,42 @@ impl Relay for Handler {
|
||||
while let Some(msg) = in_stream.next().await.transpose()? {
|
||||
info!("📥 packet received");
|
||||
match msg.kind {
|
||||
/* ───────────── KEYBOARD ───────────── */
|
||||
Some(hid_report::Kind::KeyboardReport(ref v)) if v.len() == 8 => {
|
||||
kb.lock().await.write_all(v).await?;
|
||||
trace!(" └─ wrote 8 B to /dev/hidg0");
|
||||
let mut f = kb.lock().await;
|
||||
match f.write_all(v).await {
|
||||
Ok(()) => info!("⌨️ HID report forwarded → /dev/hidg0"),
|
||||
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
|
||||
trace!("⌨️ /dev/hidg0 busy ({e}); dropped packet");
|
||||
}
|
||||
Err(e) => {
|
||||
error!("⌨️ write error to /dev/hidg0: {e}");
|
||||
continue; // drop this packet, keep stream alive
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ─────────────── MOUSE ─────────────── */
|
||||
Some(hid_report::Kind::MouseReport(ref v)) if v.len() == 4 => {
|
||||
ms.lock().await.write_all(v).await?;
|
||||
trace!(" └─ wrote 4 B to /dev/hidg1");
|
||||
let mut f = ms.lock().await;
|
||||
match f.write_all(v).await {
|
||||
Ok(()) => info!("🖱️ HID report forwarded → /dev/hidg1"),
|
||||
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
|
||||
trace!("🖱️ /dev/hidg1 busy ({e}); dropped packet");
|
||||
}
|
||||
Err(e) => {
|
||||
error!("🖱️ write error to /dev/hidg1: {e}");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ────────────── BAD PACKET ──────────── */
|
||||
_ => {
|
||||
error!(?msg.kind, "⚠️ malformed packet");
|
||||
let _bad_len = match &msg.kind {
|
||||
Some(hid_report::Kind::KeyboardReport(v)) => v.len(),
|
||||
Some(hid_report::Kind::MouseReport(v)) => v.len(),
|
||||
_ => 0,
|
||||
};
|
||||
continue;
|
||||
}
|
||||
}
|
||||
info!("HID report forwarded");
|
||||
let _ = tx.send(Ok(msg)).await;
|
||||
}
|
||||
info!("🔚 client stream closed");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user