update to edition 2024

This commit is contained in:
Brad Stein 2025-06-01 16:04:00 -05:00
parent 0daa7b0e3f
commit 6d81aa3a17
5 changed files with 98 additions and 46 deletions

View File

@ -1,7 +1,7 @@
[package]
name = "navka-client"
version = "0.1.0"
edition = "2021"
edition = "2024"
[dependencies]
tokio = { version = "1.45", features = ["full"] }

View File

@ -1,37 +1,47 @@
//! navka-client forward keyboard/mouse HID reports to navka-server
#![forbid(unsafe_code)]
use anyhow::Result;
use navka_common::navka::HidReport;
use navka_common::navka::relay_client::RelayClient;
use tokio::sync::mpsc;
use navka_common::navka::{hid_report::*, relay_client::RelayClient, HidReport};
use tokio::{sync::mpsc, time::sleep};
use tokio_stream::wrappers::ReceiverStream;
use tonic::{Request, transport::Channel};
use tonic::{transport::Channel, Request};
#[tokio::main]
async fn main() -> Result<()> {
// 1) connect to the navka-server gRPC endpoint
let channel = Channel::from_static("http://127.0.0.1:50051").connect().await?;
let mut client = RelayClient::new(channel);
// Connect to navka-server (adjust the address if server not local)
let channel = Channel::from_static("http://127.0.0.1:50051")
.connect()
.await?;
// 2) create an mpsc channel that we can push HID reports into
let (tx_request, rx_request) = mpsc::channel::<HidReport>(32);
let outbound = ReceiverStream::new(rx_request);
// mpsc channel -> ReceiverStream -> gRPC bidirectional stream
let (tx, rx) = mpsc::channel::<HidReport>(32);
let outbound = ReceiverStream::new(rx);
// 3) start the bi-directional stream (note the REQUIRED argument)
let response = client.stream(Request::new(outbound)).await?;
let mut inbound = response.into_inner(); // Streaming<HidReport>
// Kick off the RPC note: in tonic 0.11 the request object is built
// by wrapping the outbound stream in `Request::new(...)`.
let mut inbound = RelayClient::new(channel)
.stream(Request::new(outbound))
.await?
.into_inner();
// 4) demo: send “press A”, wait 100 ms, send “release”
// Example task: press and release the 'a' key once.
tokio::spawn(async move {
let press_a = HidReport { data: vec![0x00,0x00,0x04,0,0,0,0,0] };
let release = HidReport { data: vec![0x00; 8] };
// 8-byte boot-keyboard report: [mods, reserved, key1..6]
let press_a = HidReport {
data: vec![0x00, 0x00, 0x04, 0, 0, 0, 0, 0],
};
let release = HidReport { data: vec![0; 8] };
tx_request.send(press_a).await.ok();
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
tx_request.send(release).await.ok();
tx.send(press_a).await.ok();
sleep(std::time::Duration::from_millis(100)).await;
tx.send(release).await.ok();
});
// 5) print anything the server echoes back
while let Some(msg) = inbound.message().await? {
println!("🔄 got report from server: {:?}", msg.data);
// Print whatever the server echoes back.
while let Some(report) = inbound.message().await? {
println!("🔄 received: {:?}", report.data);
}
Ok(())
}

View File

@ -2,10 +2,20 @@
set -euo pipefail
# 1) Install system dependencies (Arch Linux)
sudo pacman -S --needed --noconfirm git rustup protobuf gcc ustreamer pulseaudio tailscale
sudo pacman -S --needed --noconfirm git rustup protobuf gcc pulseaudio tailscale snapd
# Enable and start snapd if not already:
sudo systemctl enable --now snapd.socket
# Create classic symlink for snap (one-time):
sudo ln -sf /var/lib/snapd/snap /snap
# Install µStreamer via Snap (if you actually need streaming)
if ! snap list | grep -q ustreamer; then
sudo snap install ustreamer
fi
# 2) Ensure Rust toolchain is present
rustup toolchain install stable
sudo install -Dm755 target/release/navka-server /usr/local/bin/navka-server
sudo install -Dm755 gadget/navka-gadget.sh /usr/local/bin/navka-gadget.sh
# 3) Determine repo URL from the current directory
REPO_DIR="$(cd "$(dirname "$0")/.." && pwd)"

View File

@ -4,6 +4,8 @@ version = "0.1.0"
edition = "2021"
[dependencies]
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
navka-common = { path = "../common" }
tokio-stream = "0.1"
tokio = { version = "1.45", features = ["full", "fs"] } # add "fs"
tokio-stream = "0.1"
tonic = { version = "0.11", features = ["transport"] }
anyhow = "1.0"
navka-common = { path = "../common" }

View File

@ -1,43 +1,73 @@
use navka_common::navka::{relay_server::{Relay, RelayServer}, HidReport};
use tokio::{fs::OpenOptions, io::AsyncWriteExt, sync::mpsc};
use tokio_stream::wrappers::ReceiverStream;
//! navka-server bridge HID reports between client (gRPC) and /dev/hidg0
#![forbid(unsafe_code)]
use anyhow::Result;
use navka_common::navka::{
hid_report::*, relay_server::Relay, HidReport, RelayServer,
};
use std::{path::Path, sync::Arc};
use tokio::{
fs::OpenOptions,
io::AsyncWriteExt,
signal,
sync::{mpsc, Mutex},
};
use tonic::{transport::Server, Request, Response, Status};
const HID_DEV: &str = "/dev/hidg0";
#[derive(Default)]
struct RelaySvc;
/// Implementation of the gRPC service generated by tonic-build.
#[derive(Debug)]
pub struct RelaySvc {
/// Shared handle to /dev/hidg0 (protected by a mutex because several
/// gRPC streams may write concurrently).
hidg: Arc<Mutex<tokio::fs::File>>,
}
#[tonic::async_trait]
impl Relay for RelaySvc {
type StreamStream = ReceiverStream<Result<HidReport, Status>>;
type StreamStream = tokio_stream::wrappers::ReceiverStream<Result<HidReport, Status>>;
async fn stream(
&self,
request: Request<tonic::Streaming<HidReport>>,
) -> Result<Response<Self::StreamStream>, Status> {
let mut inbound = request.into_inner();
let (tx, rx) = mpsc::channel(8);
// writer task: client → hidg0
// Channel well use if one day we need to send something back.
let (tx, rx) = mpsc::channel(32);
let hidg = self.hidg.clone();
tokio::spawn(async move {
let mut hid = OpenOptions::new().write(true).open(HID_DEV).await.unwrap();
while let Some(Ok(report)) = inbound.message().await {
hid.write_all(&report.data).await.unwrap();
while let Some(report) = inbound.message().await.transpose()? {
// Write raw 8-byte packet to the gadget device
hidg.lock().await.write_all(&report.data).await?;
}
Ok::<(), anyhow::Error>(())
});
// nothing to send back yet
Ok(Response::new(ReceiverStream::new(rx)))
Ok(Response::new(tokio_stream::wrappers::ReceiverStream::new(rx)))
}
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let addr = "0.0.0.0:4000".parse()?;
async fn main() -> Result<()> {
// Open /dev/hidg0 once and share it.
let hid_dev = if Path::new("/dev/hidg0").exists() {
OpenOptions::new().write(true).open("/dev/hidg0").await?
} else {
anyhow::bail!("/dev/hidg0 not found is navka-gadget running?");
};
let svc = RelayServer::new(RelaySvc {
hidg: Arc::new(Mutex::new(hid_dev)),
});
println!("🛰️ navka-server listening on 0.0.0.0:50051");
Server::builder()
.add_service(RelayServer::new(RelaySvc::default()))
.serve(addr)
.add_service(svc)
.serve_with_shutdown("0.0.0.0:50051".parse()?, async {
signal::ctrl_c().await.ok();
})
.await?;
Ok(())
}