HID fix
This commit is contained in:
parent
dd9df940db
commit
1110c59f1e
@ -6,7 +6,7 @@ use anyhow::Result;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::sync::broadcast;
|
use tokio::sync::broadcast;
|
||||||
use tokio_stream::{wrappers::BroadcastStream, StreamExt};
|
use tokio_stream::{wrappers::BroadcastStream, StreamExt};
|
||||||
use tonic::Request;
|
use tonic::{Request, transport::Channel};
|
||||||
use tracing::{debug, error, info, warn, trace};
|
use tracing::{debug, error, info, warn, trace};
|
||||||
use winit::{
|
use winit::{
|
||||||
event_loop::EventLoopBuilder,
|
event_loop::EventLoopBuilder,
|
||||||
@ -29,10 +29,10 @@ pub struct LesavkaClientApp {
|
|||||||
|
|
||||||
impl LesavkaClientApp {
|
impl LesavkaClientApp {
|
||||||
pub fn new() -> Result<Self> {
|
pub fn new() -> Result<Self> {
|
||||||
let dev_mode = std::env::var("NAVKA_DEV_MODE").is_ok();
|
let dev_mode = std::env::var("LESAVKA_DEV_MODE").is_ok();
|
||||||
let server_addr = std::env::args()
|
let server_addr = std::env::args()
|
||||||
.nth(1)
|
.nth(1)
|
||||||
.or_else(|| std::env::var("NAVKA_SERVER_ADDR").ok())
|
.or_else(|| std::env::var("LESAVKA_SERVER_ADDR").ok())
|
||||||
.unwrap_or_else(|| "http://127.0.0.1:50051".into());
|
.unwrap_or_else(|| "http://127.0.0.1:50051".into());
|
||||||
|
|
||||||
let (kbd_tx, _) = broadcast::channel::<KeyboardReport>(1024);
|
let (kbd_tx, _) = broadcast::channel::<KeyboardReport>(1024);
|
||||||
@ -47,6 +47,19 @@ impl LesavkaClientApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(&mut self) -> Result<()> {
|
pub async fn run(&mut self) -> Result<()> {
|
||||||
|
// ---- build two channels ------------------------------------------------
|
||||||
|
let hid_ep: Channel = Channel::from_shared(self.server_addr.clone())
|
||||||
|
.unwrap()
|
||||||
|
.tcp_nodelay(true)
|
||||||
|
.concurrency_limit(1)
|
||||||
|
.http2_keep_alive_interval(Duration::from_secs(15))
|
||||||
|
.connect_lazy();
|
||||||
|
|
||||||
|
let vid_ep: Channel = Channel::from_shared(self.server_addr.clone())
|
||||||
|
.unwrap()
|
||||||
|
.tcp_nodelay(true)
|
||||||
|
.connect_lazy();
|
||||||
|
|
||||||
/* detach the aggregator before spawn so `self` is not moved */
|
/* detach the aggregator before spawn so `self` is not moved */
|
||||||
let aggregator = self.aggregator.take().expect("InputAggregator present");
|
let aggregator = self.aggregator.take().expect("InputAggregator present");
|
||||||
let agg_task = tokio::spawn(async move {
|
let agg_task = tokio::spawn(async move {
|
||||||
@ -55,8 +68,8 @@ impl LesavkaClientApp {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/* two networking tasks */
|
/* two networking tasks */
|
||||||
let kbd_loop = self.stream_loop_keyboard();
|
let kbd_loop = self.stream_loop_keyboard(hid_ep.clone());
|
||||||
let mou_loop = self.stream_loop_mouse();
|
let mou_loop = self.stream_loop_mouse(hid_ep.clone());
|
||||||
|
|
||||||
/* optional suicide timer */
|
/* optional suicide timer */
|
||||||
let suicide = async {
|
let suicide = async {
|
||||||
@ -93,7 +106,7 @@ impl LesavkaClientApp {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
let vid_loop = Self::video_loop(self.server_addr.clone(), video_tx);
|
let vid_loop = Self::video_loop(vid_ep.clone(), video_tx);
|
||||||
|
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
_ = kbd_loop => unreachable!(),
|
_ = kbd_loop => unreachable!(),
|
||||||
@ -109,79 +122,76 @@ impl LesavkaClientApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*──────────────── keyboard stream ───────────────*/
|
/*──────────────── keyboard stream ───────────────*/
|
||||||
async fn stream_loop_keyboard(&self) {
|
async fn stream_loop_keyboard(&self, ep: Channel) {
|
||||||
loop {
|
loop {
|
||||||
info!("⌨️ connect {}", self.server_addr);
|
info!("⌨️ connect {}", self.server_addr);
|
||||||
let mut cli = match RelayClient::connect(self.server_addr.clone()).await {
|
// let mut cli = match RelayClient::connect(self.server_addr.clone()).await {
|
||||||
Ok(c) => c,
|
// Ok(c) => c,
|
||||||
Err(e) => { error!("connect: {e}"); Self::delay().await; continue }
|
// Err(e) => { error!("connect: {e}"); Self::delay().await; continue }
|
||||||
};
|
// };
|
||||||
|
let mut cli = RelayClient::new(ep.clone());
|
||||||
|
|
||||||
let outbound = BroadcastStream::new(self.kbd_tx.subscribe()).filter_map(|r| r.ok());
|
let outbound = BroadcastStream::new(self.kbd_tx.subscribe()).filter_map(|r| r.ok());
|
||||||
let resp = match cli.stream_keyboard(Request::new(outbound)).await {
|
let resp = match cli.stream_keyboard(Request::new(outbound)).await {
|
||||||
Ok(r) => r, Err(e) => { error!("stream_keyboard: {e}"); Self::delay().await; continue }
|
Ok(r) => r, Err(e) => { error!("stream_keyboard: {e}"); Self::delay().await; continue }
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut inbound = resp.into_inner();
|
// let mut inbound = resp.into_inner();
|
||||||
while let Some(m) = inbound.message().await.transpose() {
|
// while let Some(m) = inbound.message().await.transpose() {
|
||||||
match m {
|
// match m {
|
||||||
Ok(r) => trace!("kbd echo {} B", r.data.len()),
|
// Ok(r) => trace!("kbd echo {} B", r.data.len()),
|
||||||
Err(e) => { error!("kbd inbound: {e}"); break }
|
// Err(e) => { error!("kbd inbound: {e}"); break }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
drop(resp);
|
||||||
warn!("⌨️ disconnected");
|
warn!("⌨️ disconnected");
|
||||||
Self::delay().await;
|
Self::delay().await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*──────────────── mouse stream ──────────────────*/
|
/*──────────────── mouse stream ──────────────────*/
|
||||||
async fn stream_loop_mouse(&self) {
|
async fn stream_loop_mouse(&self, ep: Channel) {
|
||||||
loop {
|
loop {
|
||||||
info!("🖱️ connect {}", self.server_addr);
|
info!("🖱️ connect {}", self.server_addr);
|
||||||
let mut cli = match RelayClient::connect(self.server_addr.clone()).await {
|
// let mut cli = match RelayClient::connect(self.server_addr.clone()).await {
|
||||||
Ok(c) => c,
|
// Ok(c) => c,
|
||||||
Err(e) => { error!("connect: {e}"); Self::delay().await; continue }
|
// Err(e) => { error!("connect: {e}"); Self::delay().await; continue }
|
||||||
};
|
// };
|
||||||
|
let mut cli = RelayClient::new(ep.clone());
|
||||||
|
|
||||||
let outbound = BroadcastStream::new(self.mou_tx.subscribe()).filter_map(|r| r.ok());
|
let outbound = BroadcastStream::new(self.mou_tx.subscribe()).filter_map(|r| r.ok());
|
||||||
let resp = match cli.stream_mouse(Request::new(outbound)).await {
|
let resp = match cli.stream_mouse(Request::new(outbound)).await {
|
||||||
Ok(r) => r, Err(e) => { error!("stream_mouse: {e}"); Self::delay().await; continue }
|
Ok(r) => r, Err(e) => { error!("stream_mouse: {e}"); Self::delay().await; continue }
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut inbound = resp.into_inner();
|
// let mut inbound = resp.into_inner();
|
||||||
while let Some(m) = inbound.message().await.transpose() {
|
// while let Some(m) = inbound.message().await.transpose() {
|
||||||
match m {
|
// match m {
|
||||||
Ok(r) => trace!("mouse echo {} B", r.data.len()),
|
// Ok(r) => trace!("mouse echo {} B", r.data.len()),
|
||||||
Err(e) => { error!("mouse inbound: {e}"); break }
|
// Err(e) => { error!("mouse inbound: {e}"); break }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
drop(resp);
|
||||||
warn!("🖱️ disconnected");
|
warn!("🖱️ disconnected");
|
||||||
Self::delay().await;
|
Self::delay().await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*──────────────── monitor stream ────────────────*/
|
/*──────────────── monitor stream ────────────────*/
|
||||||
async fn video_loop(addr: String, tx: tokio::sync::mpsc::UnboundedSender<VideoPacket>) {
|
async fn video_loop(ep: Channel, tx: tokio::sync::mpsc::UnboundedSender<VideoPacket>) {
|
||||||
loop {
|
loop {
|
||||||
match RelayClient::connect(addr.clone()).await {
|
let mut cli = RelayClient::new(ep.clone());
|
||||||
Ok(mut cli) => {
|
for monitor_id in 0..=1 {
|
||||||
for monitor_id in 0..=1 {
|
let req = MonitorRequest { id: monitor_id, max_bitrate: 6_000 };
|
||||||
let req = MonitorRequest { id: monitor_id, max_bitrate: 6_000 };
|
if let Ok(mut stream) = cli.capture_video(Request::new(req)).await {
|
||||||
match cli.capture_video(Request::new(req)).await {
|
while let Some(pkt) = stream.get_mut().message().await.transpose() {
|
||||||
Ok(mut stream) => {
|
match pkt {
|
||||||
while let Some(pkt) = stream.get_mut().message().await.transpose() {
|
Ok(p) => { let _ = tx.send(p); }
|
||||||
match pkt {
|
Err(e) => { error!("video {monitor_id}: {e}"); break }
|
||||||
Ok(p) => { let _ = tx.send(p); },
|
|
||||||
Err(e) => { error!("video {monitor_id}: {e}"); break }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(e) => error!("video {monitor_id}: {e}"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => error!("video connect: {e}"),
|
|
||||||
}
|
|
||||||
tokio::time::sleep(Duration::from_secs(2)).await;
|
tokio::time::sleep(Duration::from_secs(2)).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
use std::{collections::HashSet, sync::atomic::{AtomicU32, Ordering}};
|
use std::{collections::HashSet, sync::atomic::{AtomicU32, Ordering}};
|
||||||
use evdev::{Device, EventType, InputEvent, KeyCode};
|
use evdev::{Device, EventType, InputEvent, KeyCode};
|
||||||
use tokio::sync::broadcast::Sender;
|
use tokio::sync::broadcast::Sender;
|
||||||
use tracing::{debug, error, warn};
|
use tracing::{debug, error, warn, trace};
|
||||||
|
|
||||||
use lesavka_common::lesavka::KeyboardReport;
|
use lesavka_common::lesavka::KeyboardReport;
|
||||||
|
|
||||||
|
|||||||
@ -2,14 +2,18 @@
|
|||||||
|
|
||||||
use evdev::{Device, EventType, InputEvent, KeyCode, RelativeAxisCode};
|
use evdev::{Device, EventType, InputEvent, KeyCode, RelativeAxisCode};
|
||||||
use tokio::sync::broadcast::{self, Sender};
|
use tokio::sync::broadcast::{self, Sender};
|
||||||
use tracing::{debug, error, warn};
|
use std::time::{Duration, Instant};
|
||||||
|
use tracing::{debug, error, warn, trace};
|
||||||
|
|
||||||
use lesavka_common::lesavka::MouseReport;
|
use lesavka_common::lesavka::MouseReport;
|
||||||
|
|
||||||
|
const SEND_INTERVAL: Duration = Duration::from_micros(2000);
|
||||||
|
|
||||||
pub struct MouseAggregator {
|
pub struct MouseAggregator {
|
||||||
dev: Device,
|
dev: Device,
|
||||||
tx: Sender<MouseReport>,
|
tx: Sender<MouseReport>,
|
||||||
dev_mode: bool,
|
dev_mode: bool,
|
||||||
|
next_send: Instant,
|
||||||
|
|
||||||
buttons: u8,
|
buttons: u8,
|
||||||
last_buttons: u8,
|
last_buttons: u8,
|
||||||
@ -20,7 +24,7 @@ pub struct MouseAggregator {
|
|||||||
|
|
||||||
impl MouseAggregator {
|
impl MouseAggregator {
|
||||||
pub fn new(dev: Device, dev_mode: bool, tx: Sender<MouseReport>) -> Self {
|
pub fn new(dev: Device, dev_mode: bool, tx: Sender<MouseReport>) -> Self {
|
||||||
Self { dev, tx, dev_mode, buttons:0, last_buttons:0, dx:0, dy:0, wheel:0 }
|
Self { dev, tx, dev_mode, next_send: Instant::now(), buttons:0, last_buttons:0, dx:0, dy:0, wheel:0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline] fn slog(&self, f: impl FnOnce()) { if self.dev_mode { f() } }
|
#[inline] fn slog(&self, f: impl FnOnce()) { if self.dev_mode { f() } }
|
||||||
@ -60,9 +64,8 @@ impl MouseAggregator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn flush(&mut self) {
|
fn flush(&mut self) {
|
||||||
if self.dx==0 && self.dy==0 && self.wheel==0 && self.buttons==self.last_buttons {
|
if Instant::now() < self.next_send { return; }
|
||||||
return;
|
self.next_send += SEND_INTERVAL;
|
||||||
}
|
|
||||||
|
|
||||||
let pkt = [
|
let pkt = [
|
||||||
self.buttons,
|
self.buttons,
|
||||||
|
|||||||
@ -21,7 +21,7 @@ fn ensure_runtime_dir() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
ensure_runtime_dir();
|
ensure_runtime_dir();
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ async fn main() -> Result<()> {
|
|||||||
.with_thread_ids(true)
|
.with_thread_ids(true)
|
||||||
.with_file(true);
|
.with_file(true);
|
||||||
|
|
||||||
let dev_mode = env::var("NAVKA_DEV_MODE").is_ok();
|
let dev_mode = env::var("LESAVKA_DEV_MODE").is_ok();
|
||||||
let mut _guard: Option<WorkerGuard> = None; // keep guard alive
|
let mut _guard: Option<WorkerGuard> = None; // keep guard alive
|
||||||
|
|
||||||
/*------------- subscriber setup -----------------------------------*/
|
/*------------- subscriber setup -----------------------------------*/
|
||||||
|
|||||||
@ -25,14 +25,14 @@ impl MonitorWindow {
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
// appsrc -> decode -> convert -> autovideosink
|
// appsrc -> decode -> convert -> autovideosink
|
||||||
let desc = if std::env::var_os("XDG_RUNTIME_DIR").is_some() {
|
let desc = if std::env::var_os("LESAVKA_HW_DEC").is_some() {
|
||||||
// graphical
|
"appsrc name=src is-live=true format=time do-timestamp=true ! queue ! \
|
||||||
|
h264parse ! vaapih264dec low-latency=true ! videoconvert ! \
|
||||||
|
autovideosink sync=false"
|
||||||
|
} else {
|
||||||
|
// fallback
|
||||||
"appsrc name=src is-live=true format=time do-timestamp=true ! \
|
"appsrc name=src is-live=true format=time do-timestamp=true ! \
|
||||||
queue ! h264parse ! decodebin ! videoconvert ! autovideosink sync=false"
|
queue ! h264parse ! decodebin ! videoconvert ! autovideosink sync=false"
|
||||||
} else {
|
|
||||||
// headless / debugging over ssh
|
|
||||||
"appsrc name=src is-live=true format=time do-timestamp=true ! \
|
|
||||||
fakesink sync=false"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let pipeline = gst::parse::launch(desc)?
|
let pipeline = gst::parse::launch(desc)?
|
||||||
|
|||||||
@ -38,8 +38,8 @@ User=root
|
|||||||
Group=root
|
Group=root
|
||||||
|
|
||||||
Environment=RUST_LOG=debug
|
Environment=RUST_LOG=debug
|
||||||
Environment=NAVKA_DEV_MODE=1
|
Environment=LESAVKA_DEV_MODE=1
|
||||||
Environment=NAVKA_SERVER_ADDR=http://64.25.10.31:50051
|
Environment=LESAVKA_SERVER_ADDR=http://64.25.10.31:50051
|
||||||
|
|
||||||
ExecStart=/usr/local/bin/lesavka-client
|
ExecStart=/usr/local/bin/lesavka-client
|
||||||
Restart=no
|
Restart=no
|
||||||
|
|||||||
@ -9,11 +9,10 @@ use tokio::{fs::{OpenOptions}, io::AsyncWriteExt, sync::Mutex};
|
|||||||
use tokio_stream::{wrappers::ReceiverStream};
|
use tokio_stream::{wrappers::ReceiverStream};
|
||||||
use tonic::{transport::Server, Request, Response, Status};
|
use tonic::{transport::Server, Request, Response, Status};
|
||||||
use anyhow::Context as _;
|
use anyhow::Context as _;
|
||||||
use tracing::{info, trace, warn, error};
|
use tracing::{info, trace, error};
|
||||||
use tracing_subscriber::{filter::EnvFilter, fmt, prelude::*};
|
use tracing_subscriber::{filter::EnvFilter, fmt, prelude::*};
|
||||||
use tracing_appender::non_blocking;
|
use tracing_appender::non_blocking;
|
||||||
use tracing_appender::non_blocking::WorkerGuard;
|
use tracing_appender::non_blocking::WorkerGuard;
|
||||||
use udev::{MonitorBuilder};
|
|
||||||
|
|
||||||
use lesavka_server::{usb_gadget::UsbGadget, video};
|
use lesavka_server::{usb_gadget::UsbGadget, video};
|
||||||
|
|
||||||
@ -144,31 +143,48 @@ impl Relay for Handler {
|
|||||||
.map_err(|e| Status::internal(e.to_string()))?;
|
.map_err(|e| Status::internal(e.to_string()))?;
|
||||||
tokio::time::sleep(Duration::from_millis(500)).await;
|
tokio::time::sleep(Duration::from_millis(500)).await;
|
||||||
}
|
}
|
||||||
let (tx, rx) = tokio::sync::mpsc::channel(32);
|
let (tx, _rx) =
|
||||||
|
tokio::sync::mpsc::channel::<Result<KeyboardReport, Status>>(32);
|
||||||
let kb = self.kb.clone();
|
let kb = self.kb.clone();
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let mut s = req.into_inner();
|
let mut s = req.into_inner();
|
||||||
while let Some(pkt) = s.next().await.transpose()? {
|
while let Some(pkt) = s.next().await.transpose()? {
|
||||||
// kb.lock().await.write_all(&pkt.data).await?;
|
// kb.lock().await.write_all(&pkt.data).await?;
|
||||||
loop {
|
const SPINS: usize = 20;
|
||||||
match kb.lock().await.write_all(&pkt.data).await {
|
for _ in 0..SPINS {
|
||||||
Ok(()) => {
|
match kb.lock().await.write(&pkt.data).await {
|
||||||
trace!("⌨️ wrote {}", pkt.data.iter()
|
// Ok(n) if n == pkt.data.len() => {
|
||||||
.map(|b| format!("{b:02X}")).collect::<Vec<_>>().join(" "));
|
// trace!("⌨️ wrote {}", pkt.data.iter()
|
||||||
|
// .map(|b| format!("{b:02X}")).collect::<Vec<_>>().join(" "));
|
||||||
|
// break;
|
||||||
|
// },
|
||||||
|
// Ok(_) | Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {
|
||||||
|
// std::hint::spin_loop();
|
||||||
|
// continue; // try again
|
||||||
|
// }
|
||||||
|
// Err(e)
|
||||||
|
// if matches!(e.raw_os_error(),
|
||||||
|
// Some(libc::EBUSY) | // still opening
|
||||||
|
// Some(libc::ENODEV) | // gadget not‑yet configured
|
||||||
|
// Some(libc::EPIPE) | // host vanished
|
||||||
|
// Some(libc::EINVAL) | // host hasn’t accepted EP config yet
|
||||||
|
// Some(libc::EAGAIN)) // non‑blocking
|
||||||
|
// => {
|
||||||
|
// tokio::time::sleep(Duration::from_millis(10)).await;
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// Err(e) => return Err(Status::internal(e.to_string())),
|
||||||
|
Ok(n) if n == pkt.data.len() => { // success
|
||||||
|
trace!("⌨️ wrote {}", pkt.data.iter().map(|b| format!("{b:02X}")).collect::<Vec<_>>().join(" "));
|
||||||
break;
|
break;
|
||||||
},
|
}
|
||||||
Err(e)
|
Ok(_) => continue,
|
||||||
if matches!(e.raw_os_error(),
|
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {
|
||||||
Some(libc::EBUSY) | // still opening
|
std::hint::spin_loop();
|
||||||
Some(libc::ENODEV) | // gadget not‑yet configured
|
|
||||||
Some(libc::EPIPE) | // host vanished
|
|
||||||
Some(libc::EINVAL) | // host hasn’t accepted EP config yet
|
|
||||||
Some(libc::EAGAIN)) // non‑blocking
|
|
||||||
=> {
|
|
||||||
tokio::time::sleep(Duration::from_millis(10)).await;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
Err(ref e) if e.kind() == std::io::ErrorKind::BrokenPipe => break,
|
||||||
Err(e) => return Err(Status::internal(e.to_string())),
|
Err(e) => return Err(Status::internal(e.to_string())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -177,14 +193,17 @@ impl Relay for Handler {
|
|||||||
Ok::<(), Status>(())
|
Ok::<(), Status>(())
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(Response::new(ReceiverStream::new(rx)))
|
let (_noop_tx, empty_rx) =
|
||||||
|
tokio::sync::mpsc::channel::<Result<KeyboardReport, Status>>(1);
|
||||||
|
Ok(Response::new(ReceiverStream::new(empty_rx)))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn stream_mouse(
|
async fn stream_mouse(
|
||||||
&self,
|
&self,
|
||||||
req: Request<tonic::Streaming<MouseReport>>,
|
req: Request<tonic::Streaming<MouseReport>>,
|
||||||
) -> Result<Response<Self::StreamMouseStream>, Status> {
|
) -> Result<Response<Self::StreamMouseStream>, Status> {
|
||||||
let (tx, rx) = tokio::sync::mpsc::channel(4096); // higher burst
|
let (tx, _rx) =
|
||||||
|
tokio::sync::mpsc::channel::<Result<MouseReport, Status>>(4096);
|
||||||
let ms = self.ms.clone();
|
let ms = self.ms.clone();
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
@ -192,23 +211,38 @@ impl Relay for Handler {
|
|||||||
let mut boot_mode = true;
|
let mut boot_mode = true;
|
||||||
while let Some(pkt) = s.next().await.transpose()? {
|
while let Some(pkt) = s.next().await.transpose()? {
|
||||||
loop {
|
loop {
|
||||||
match ms.lock().await.write_all(&pkt.data).await {
|
match ms.lock().await.write(&pkt.data).await {
|
||||||
Ok(()) => {
|
// Ok(n) if n == pkt.data.len() => {
|
||||||
trace!("🖱️ wrote {}", pkt.data.iter()
|
// trace!("🖱️ wrote {}", pkt.data.iter()
|
||||||
.map(|b| format!("{b:02X}")).collect::<Vec<_>>().join(" "));
|
// .map(|b| format!("{b:02X}")).collect::<Vec<_>>().join(" "));
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// Ok(_) | Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {
|
||||||
|
// std::hint::spin_loop();
|
||||||
|
// continue; // try again
|
||||||
|
// }
|
||||||
|
// Err(e)
|
||||||
|
// if matches!(e.raw_os_error(),
|
||||||
|
// Some(libc::EBUSY) | // still opening
|
||||||
|
// Some(libc::ENODEV) | // gadget not‑yet configured
|
||||||
|
// Some(libc::EPIPE) | // host vanished
|
||||||
|
// Some(libc::EINVAL) | // host hasn’t accepted EP config yet
|
||||||
|
// Some(libc::EAGAIN)) // non‑blocking
|
||||||
|
// => {
|
||||||
|
// tokio::time::sleep(Duration::from_millis(10)).await;
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// Err(e) => return Err(Status::internal(e.to_string())),
|
||||||
|
Ok(n) if n == pkt.data.len() => { // success
|
||||||
|
trace!("⌨️ wrote {}", pkt.data.iter().map(|b| format!("{b:02X}")).collect::<Vec<_>>().join(" "));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Err(e)
|
Ok(_) => continue,
|
||||||
if matches!(e.raw_os_error(),
|
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {
|
||||||
Some(libc::EBUSY) | // still opening
|
std::hint::spin_loop();
|
||||||
Some(libc::ENODEV) | // gadget not‑yet configured
|
|
||||||
Some(libc::EPIPE) | // host vanished
|
|
||||||
Some(libc::EINVAL) | // host hasn’t accepted EP config yet
|
|
||||||
Some(libc::EAGAIN)) // non‑blocking
|
|
||||||
=> {
|
|
||||||
tokio::time::sleep(Duration::from_millis(10)).await;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
Err(ref e) if e.kind() == std::io::ErrorKind::BrokenPipe => break,
|
||||||
Err(e) => return Err(Status::internal(e.to_string())),
|
Err(e) => return Err(Status::internal(e.to_string())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -217,7 +251,9 @@ impl Relay for Handler {
|
|||||||
Ok::<(), Status>(())
|
Ok::<(), Status>(())
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(Response::new(ReceiverStream::new(rx)))
|
let (_noop_tx, empty_rx) =
|
||||||
|
tokio::sync::mpsc::channel::<Result<MouseReport, Status>>(1);
|
||||||
|
Ok(Response::new(ReceiverStream::new(empty_rx)))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn capture_video(
|
async fn capture_video(
|
||||||
@ -294,6 +330,7 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
info!("🌐 lesavka‑server listening on 0.0.0.0:50051");
|
info!("🌐 lesavka‑server listening on 0.0.0.0:50051");
|
||||||
|
|
||||||
if let Err(e) = Server::builder()
|
if let Err(e) = Server::builder()
|
||||||
|
.tcp_nodelay(true)
|
||||||
.add_service(RelayServer::new(handler))
|
.add_service(RelayServer::new(handler))
|
||||||
.serve(([0, 0, 0, 0], 50051).into())
|
.serve(([0, 0, 0, 0], 50051).into())
|
||||||
.await
|
.await
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user