This commit is contained in:
Brad Stein 2025-06-15 20:19:27 -05:00
parent d70a8c6e56
commit d7d6b3ab93
5 changed files with 54 additions and 37 deletions

View File

@ -1,11 +1,14 @@
// 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 tonic::Request;
use tracing::{info, warn, error};
use navka_common::navka::{relay_client::RelayClient, HidReport};
use crate::input::inputs::InputAggregator;
@ -13,6 +16,7 @@ pub struct NavkaClientApp {
aggregator: Option<InputAggregator>,
server_addr: String,
dev_mode: bool,
tx: broadcast::Sender<HidReport>,
}
impl NavkaClientApp {
@ -22,14 +26,16 @@ impl NavkaClientApp {
.nth(1)
.or_else(|| std::env::var("NAVKA_SERVER_ADDR").ok())
.unwrap_or_else(|| "http://127.0.0.1:50051".to_owned());
let (tx, _rx) = mpsc::channel::<HidReport>(64);
let mut aggregator = InputAggregator::new(dev_mode, tx);
let (tx, _) = broadcast::channel::<HidReport>(128);
let mut aggregator = InputAggregator::new(dev_mode, tx.clone());
aggregator.init()?; // discover & grab
Ok(Self {
aggregator: Some(aggregator),
server_addr: addr,
dev_mode,
tx,
})
}
@ -44,7 +50,7 @@ impl NavkaClientApp {
let suicide_future = async {
if self.dev_mode {
info!("DEV-mode: will kill itself in 30s");
tokio::time::sleep(std::time::Duration::from_secs(30)).await;
tokio::time::sleep(Duration::from_secs(30)).await;
Err::<(), _>(anyhow::anyhow!("dev-mode timer expired - goodbye cruel world..."))
} else {
futures::future::pending().await
@ -77,7 +83,7 @@ impl NavkaClientApp {
},
// reconnect loop
_ = self.reconnect_loop() => {
warn!("Reconnect loop ended?? We exit");
warn!("Reconnect loop ended??");
std::process::exit(0);
}
}
@ -98,11 +104,8 @@ impl NavkaClientApp {
}
};
// fresh channel for aggregator => we do this with new (tx, rx)
let (tx, rx) = mpsc::channel::<HidReport>(64);
let _unused = InputAggregator::new(self.dev_mode, tx.clone()); // TODO: wire this up properly
let outbound = ReceiverStream::new(rx);
// fresh reader over the *same* broadcast channel
let outbound = BroadcastStream::new(self.tx.subscribe()).filter_map(|r| async { r.ok() });
let response = match client.stream(Request::new(outbound)).await {
Ok(r) => r,
Err(e) => {
@ -113,7 +116,6 @@ impl NavkaClientApp {
};
let mut inbound = response.into_inner();
// read inbound
while let Some(res) = inbound.message().await.transpose() {
match res {
Ok(report) => {
@ -125,7 +127,7 @@ impl NavkaClientApp {
}
}
}
warn!("Inbound ended. Will try to reconnect in 1s");
warn!("Diconnected. Inbound ended. Will try to reconnect in 1s");
tokio::time::sleep(Duration::from_secs(1)).await;
}
}

View File

@ -3,7 +3,7 @@
use anyhow::{bail, Context, Result};
use evdev::{Device, EventType, KeyCode, RelativeAxisCode};
use tokio::time::{interval, Duration};
use tokio::sync::mpsc::Sender;
use tokio::sync::broadcast::Sender;
use tracing::{debug, info};
use navka_common::navka::HidReport;

View File

@ -2,8 +2,9 @@
use std::collections::HashSet;
use evdev::{Device, InputEvent, KeyCode, EventType};
use tokio::sync::mpsc::Sender;
use tokio::sync::broadcast::Sender;
use tracing::{warn, error, info, debug};
use navka_common::navka::HidReport;
use crate::input::keymap::{keycode_to_usage, is_modifier};
@ -108,10 +109,8 @@ impl KeyboardAggregator {
}
fn send_report(&self, report: [u8; 8]) {
let _ = self.tx.try_send(HidReport {
kind: Some(navka_common::navka::hid_report::Kind::KeyboardReport(
report.to_vec(),
)),
let _ = self.tx.send(HidReport {
kind: Some(hid_report::Kind::KeyboardReport(report.to_vec())),
});
}

View File

@ -1,7 +1,7 @@
// client/src/input/mouse.rs
use evdev::{Device, InputEvent, EventType, KeyCode, RelativeAxisCode};
use tokio::sync::mpsc::Sender;
use tokio::sync::broadcast::Sender;
use tracing::{error, debug};
use navka_common::navka::HidReport;
@ -55,13 +55,20 @@ impl MouseAggregator {
fn handle_event(&mut self, ev: InputEvent) {
match ev.event_type() {
EventType::RELATIVE => { /* fill dx/dy/wheel as i8 */ }
EventType::KEY => { // buttons are REPORTED as KEYS (BTN_LEFT…)
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),
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),
_ => {}
}
}
@ -70,11 +77,14 @@ impl MouseAggregator {
// whenever we changed something, emit:
let rep = [self.buttons, self.dx as u8, self.dy as u8, self.wheel as u8];
let _ = self.tx.try_send(HidReport {
kind: Some(navka_common::navka::hid_report::Kind::MouseReport(rep.to_vec())),
let _ = self.tx.send(HidReport {
kind: Some(hid_report::Kind::MouseReport(rep.to_vec())),
});
// reset deltas so we send *relative* movement
self.dx = 0; self.dy = 0; self.wheel = 0;
self.dx = 0;
self.dy = 0;
self.wheel = 0;
}
}

View File

@ -37,21 +37,26 @@ echo "$(cat /proc/sys/kernel/random/uuid)" >"$G/strings/0x409/serialnumber"
echo "Navka" >"$G/strings/0x409/manufacturer"
echo "Navka Composite" >"$G/strings/0x409/product"
# HID (boot keyboard)
# ----------------------- HID keyboard (usb0) -----------------------
mkdir -p "$G/functions/hid.usb0"
echo 1 >"$G/functions/hid.usb0/protocol"
echo 1 >"$G/functions/hid.usb0/subclass"
echo 8 >"$G/functions/hid.usb0/report_length"
printf '\x05\x01\x09\x06\xa1\x01\x05\x07\x19\xe0\x29\xe7\x15\x00\x25\x01\x75\x01\x95\x08\x81\x02\x95\x01\x75\x08\x81\x01\x95\x05\x75\x01\x05\x08\x19\x01\x29\x05\x91\x02\x95\x01\x75\x03\x91\x01\x95\x06\x75\x08\x15\x00\x25\x65\x05\x07\x19\x00\x29\x65\x81\x00\xc0' \
> "$G/functions/hid.usb0/report_desc"
printf '\x05\x01\x09\x06\xa1\x01\x05\x07\x19\xe0\x29\xe7\x15\x00\x25\x01'\
'\x75\x01\x95\x08\x81\x02\x95\x01\x75\x08\x81\x01\x95\x05\x75\x01\x05'\
'\x08\x19\x01\x29\x05\x91\x02\x95\x01\x75\x03\x91\x01\x95\x06\x75\x08'\
'\x15\x00\x25\x65\x05\x07\x19\x00\x29\x65\x81\x00\xc0' \
>"$G/functions/hid.usb0/report_desc"
# usb1 = mouse
# ----------------------- HID mouse (usb1) --------------------------
mkdir -p "$G/functions/hid.usb1"
echo 2 > "$G/functions/hid.usb1/protocol"
echo 1 > "$G/functions/hid.usb1/subclass"
echo 4 > "$G/functions/hid.usb1/report_length"
printf '\x05\x01\x09\x06\xa1\x01\x05\x07\x19\xe0\x29\xe7\x15\x00\x25\x01\x75\x01\x95\x08\x81\x02\x95\x01\x75\x08\x81\x01\x95\x05\x75\x01\x05\x08\x19\x01\x29\x05\x91\x02\x95\x01\x75\x03\x91\x01\x95\x06\x75\x08\x15\x00\x25\x65\x05\x07\x19\x00\x29\x65\x81\x00\xc0' \
> "$G/functions/hid.usb1/report_desc"
printf '\x05\x01\x09\x02\xa1\x01\x09\x01\xa1\x00\x05\x09\x19\x01\x29\x03'\
'\x15\x00\x25\x01\x95\x03\x75\x01\x81\x02\x95\x01\x75\x05\x81\x03\x05'\
'\x01\x09\x30\x09\x31\x15\x81\x25\x7f\x75\x08\x95\x02\x81\x06\xc0\xc0' \
>"$G/functions/hid.usb1/report_desc"
# # -- UAC2 Audio
# mkdir -p $G/functions/uac2.usb0
@ -59,15 +64,16 @@ printf '\x05\x01\x09\x06\xa1\x01\x05\x07\x19\xe0\x29\xe7\x15\x00\x25\x01\x75\x01
# echo 2 > $G/functions/uac2.usb0/c_ssize
# echo 2 > $G/functions/uac2.usb0/p_chmask
# -- Config
# ----------------------- configuration -----------------------------
mkdir -p "$G/configs/c.1/strings/0x409"
echo 500 > "$G/configs/c.1/MaxPower"
echo "Config 1" > "$G/configs/c.1/strings/0x409/configuration"
# -- Bindings
# 6) Finally bind to first available UDC
ln -s $G/functions/hid.usb0 $G/configs/c.1/
ln -s $G/functions/hid.usb1 $G/configs/c.1/
# ln -s $G/functions/uac2.usb0 $G/configs/c.1/
# 6) Finally bind to first available UDC
# 7) Finally bind to first available UDC
echo $(ls /sys/class/udc | head -n1) > $G/UDC
echo "[navka-core] gadget ready on USB-C port"
echo "[navka-core] gadget ready (keyboard on hidg0, mouse on hidg1)"