update to edition 2024
This commit is contained in:
parent
0daa7b0e3f
commit
6d81aa3a17
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "navka-client"
|
name = "navka-client"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tokio = { version = "1.45", features = ["full"] }
|
tokio = { version = "1.45", features = ["full"] }
|
||||||
|
|||||||
@ -1,37 +1,47 @@
|
|||||||
|
//! navka-client – forward keyboard/mouse HID reports to navka-server
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use navka_common::navka::HidReport;
|
use navka_common::navka::{hid_report::*, relay_client::RelayClient, HidReport};
|
||||||
use navka_common::navka::relay_client::RelayClient;
|
use tokio::{sync::mpsc, time::sleep};
|
||||||
use tokio::sync::mpsc;
|
|
||||||
use tokio_stream::wrappers::ReceiverStream;
|
use tokio_stream::wrappers::ReceiverStream;
|
||||||
use tonic::{Request, transport::Channel};
|
use tonic::{transport::Channel, Request};
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
// 1) connect to the navka-server gRPC endpoint
|
// Connect to navka-server (adjust the address if server not local)
|
||||||
let channel = Channel::from_static("http://127.0.0.1:50051").connect().await?;
|
let channel = Channel::from_static("http://127.0.0.1:50051")
|
||||||
let mut client = RelayClient::new(channel);
|
.connect()
|
||||||
|
.await?;
|
||||||
|
|
||||||
// 2) create an mpsc channel that we can push HID reports into
|
// mpsc channel -> ReceiverStream -> gRPC bidirectional stream
|
||||||
let (tx_request, rx_request) = mpsc::channel::<HidReport>(32);
|
let (tx, rx) = mpsc::channel::<HidReport>(32);
|
||||||
let outbound = ReceiverStream::new(rx_request);
|
let outbound = ReceiverStream::new(rx);
|
||||||
|
|
||||||
// 3) start the bi-directional stream (note the REQUIRED argument)
|
// Kick off the RPC – note: in tonic 0.11 the request object is built
|
||||||
let response = client.stream(Request::new(outbound)).await?;
|
// by wrapping the outbound stream in `Request::new(...)`.
|
||||||
let mut inbound = response.into_inner(); // Streaming<HidReport>
|
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 {
|
tokio::spawn(async move {
|
||||||
let press_a = HidReport { data: vec![0x00,0x00,0x04,0,0,0,0,0] };
|
// 8-byte boot-keyboard report: [mods, reserved, key1..6]
|
||||||
let release = HidReport { data: vec![0x00; 8] };
|
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();
|
tx.send(press_a).await.ok();
|
||||||
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
sleep(std::time::Duration::from_millis(100)).await;
|
||||||
tx_request.send(release).await.ok();
|
tx.send(release).await.ok();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 5) print anything the server echoes back
|
// Print whatever the server echoes back.
|
||||||
while let Some(msg) = inbound.message().await? {
|
while let Some(report) = inbound.message().await? {
|
||||||
println!("🔄 got report from server: {:?}", msg.data);
|
println!("🔄 received: {:?}", report.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,10 +2,20 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# 1) Install system dependencies (Arch Linux)
|
# 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
|
# 2) Ensure Rust toolchain is present
|
||||||
rustup toolchain install stable
|
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
|
# 3) Determine repo URL from the current directory
|
||||||
REPO_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
REPO_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
|||||||
@ -4,6 +4,8 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
|
tokio = { version = "1.45", features = ["full", "fs"] } # add "fs"
|
||||||
navka-common = { path = "../common" }
|
tokio-stream = "0.1"
|
||||||
tokio-stream = "0.1"
|
tonic = { version = "0.11", features = ["transport"] }
|
||||||
|
anyhow = "1.0"
|
||||||
|
navka-common = { path = "../common" }
|
||||||
|
|||||||
@ -1,43 +1,73 @@
|
|||||||
use navka_common::navka::{relay_server::{Relay, RelayServer}, HidReport};
|
//! navka-server – bridge HID reports between client (gRPC) and /dev/hidg0
|
||||||
use tokio::{fs::OpenOptions, io::AsyncWriteExt, sync::mpsc};
|
#![forbid(unsafe_code)]
|
||||||
use tokio_stream::wrappers::ReceiverStream;
|
|
||||||
|
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};
|
use tonic::{transport::Server, Request, Response, Status};
|
||||||
|
|
||||||
const HID_DEV: &str = "/dev/hidg0";
|
/// Implementation of the gRPC service generated by tonic-build.
|
||||||
|
#[derive(Debug)]
|
||||||
#[derive(Default)]
|
pub struct RelaySvc {
|
||||||
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]
|
#[tonic::async_trait]
|
||||||
impl Relay for RelaySvc {
|
impl Relay for RelaySvc {
|
||||||
type StreamStream = ReceiverStream<Result<HidReport, Status>>;
|
type StreamStream = tokio_stream::wrappers::ReceiverStream<Result<HidReport, Status>>;
|
||||||
|
|
||||||
async fn stream(
|
async fn stream(
|
||||||
&self,
|
&self,
|
||||||
request: Request<tonic::Streaming<HidReport>>,
|
request: Request<tonic::Streaming<HidReport>>,
|
||||||
) -> Result<Response<Self::StreamStream>, Status> {
|
) -> Result<Response<Self::StreamStream>, Status> {
|
||||||
let mut inbound = request.into_inner();
|
let mut inbound = request.into_inner();
|
||||||
let (tx, rx) = mpsc::channel(8);
|
|
||||||
|
|
||||||
// writer task: client → hidg0
|
// Channel we’ll 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 {
|
tokio::spawn(async move {
|
||||||
let mut hid = OpenOptions::new().write(true).open(HID_DEV).await.unwrap();
|
while let Some(report) = inbound.message().await.transpose()? {
|
||||||
while let Some(Ok(report)) = inbound.message().await {
|
// Write raw 8-byte packet to the gadget device
|
||||||
hid.write_all(&report.data).await.unwrap();
|
hidg.lock().await.write_all(&report.data).await?;
|
||||||
}
|
}
|
||||||
|
Ok::<(), anyhow::Error>(())
|
||||||
});
|
});
|
||||||
|
|
||||||
// nothing to send back yet
|
Ok(Response::new(tokio_stream::wrappers::ReceiverStream::new(rx)))
|
||||||
Ok(Response::new(ReceiverStream::new(rx)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> Result<()> {
|
||||||
let addr = "0.0.0.0:4000".parse()?;
|
// 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()
|
Server::builder()
|
||||||
.add_service(RelayServer::new(RelaySvc::default()))
|
.add_service(svc)
|
||||||
.serve(addr)
|
.serve_with_shutdown("0.0.0.0:50051".parse()?, async {
|
||||||
|
signal::ctrl_c().await.ok();
|
||||||
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user