From a968218f6067b81571666cdb69df97e40e31be8b Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 25 Jun 2025 07:46:50 -0500 Subject: [PATCH] updated client/server for video --- client/src/app.rs | 5 +++-- scripts/install-server.sh | 36 ++++++++++++++++++++++++++---------- server/src/main.rs | 10 +++++----- server/src/usb_gadget.rs | 26 +++++++++++--------------- 4 files changed, 45 insertions(+), 32 deletions(-) diff --git a/client/src/app.rs b/client/src/app.rs index afdc8d3..46dba25 100644 --- a/client/src/app.rs +++ b/client/src/app.rs @@ -10,6 +10,7 @@ use tonic::Request; use tracing::{debug, error, info, warn}; use winit::{ event_loop::EventLoopBuilder, + platform::x11::EventLoopBuilderExtX11, platform::unix::EventLoopBuilderExtUnix, event::Event, }; @@ -79,14 +80,14 @@ impl LesavkaClientApp { .unwrap(); std::thread::spawn(move || { - let el = EventLoopBuilder::default() + let el = EventLoopBuilder::<()>::new() .with_any_thread(true) .build() .unwrap(); let win0 = MonitorWindow::new(0, &el).expect("win0"); let win1 = MonitorWindow::new(1, &el).expect("win1"); - el.run(move |_: Event<()>, _el| { + el.run(move |_: Event<'_, ()>, _| { while let Ok(pkt) = video_rx.try_recv() { match pkt.id { 0 => win0.push_packet(pkt), diff --git a/scripts/install-server.sh b/scripts/install-server.sh index ec15bbe..00b9769 100755 --- a/scripts/install-server.sh +++ b/scripts/install-server.sh @@ -29,18 +29,34 @@ echo "==> 2b. Predictable /dev names for each capture card" # ATTRS{serial}=="1200655409098", ATTR{index}=="0", \ # SYMLINK+="lesavka_l_eye" # RULES -sudo tee /etc/udev/rules.d/85-gc311.rules >/dev/null <<'RULES' -# LEFT eye – GC311 on hub‑port 1‑3 -SUBSYSTEM=="video4linux", ATTRS{idVendor}=="07ca", ATTRS{idProduct}=="3311", \ - ATTRS{index}=="0", ENV{ID_PATH_TAG}=="usb-platform-1a400000.xhci-usb-0_1_3_1_0", \ - SYMLINK+="lesavka_l_eye" -# RIGHT eye – GC311 on hub‑port 1‑4 -SUBSYSTEM=="video4linux", ATTRS{idVendor}=="07ca", ATTRS{idProduct}=="3311", \ - ATTRS{index}=="0", ENV{ID_PATH_TAG}=="usb-platform-1a400000.xhci-usb-0_1_4_1_0", \ - SYMLINK+="lesavka_r_eye" -RULES +# probe all v4l2 devices, keep only the two GC311 capture cards +mapfile -t TAGS < <( + for v in /dev/video*; do + if udevadm info -q property -n "$v" | grep -q 'ID_VENDOR_ID=07ca'; then + # extract the stable port token (ID_PATH_TAG=…) + udevadm info -q property -n "$v" | + awk -F= '/^ID_PATH_TAG=/{print $2}' + fi + done | sort +) +if [ "${#TAGS[@]}" -ne 2 ]; then + echo "❌ Exactly two GC311 devices must be attached!" >&2 + exit 1 +fi + +LEFT_TAG=${TAGS[0]} +RIGHT_TAG=${TAGS[1]} + +cat < anyhow::Result> { struct Handler { kb: Arc>, ms: Arc>, - gadget: UsbGadgetManager, + gadget: UsbGadget, } impl Handler { - fn make(gadget: UsbGadget) -> anyhow::Result { + async fn make(gadget: UsbGadget) -> anyhow::Result { let kb = OpenOptions::new().write(true).open("/dev/hidg0").await?; let ms = OpenOptions::new().write(true) .custom_flags(libc::O_NONBLOCK) @@ -191,10 +191,10 @@ async fn main() -> anyhow::Result<()> { /* auto‑cycle task */ // tokio::spawn(async { monitor_gc311_disconnect().await.ok(); }); - let gadget = UsbGadgetManager::new("lesavka"); + let gadget = UsbGadget::new("lesavka"); gadget.cycle().ok(); - let handler = Handler::make(gadget.clone())?; + let handler = Handler::make(gadget.clone()).await?; tokio::spawn({ let gadget = gadget.clone(); diff --git a/server/src/usb_gadget.rs b/server/src/usb_gadget.rs index d7b28e4..7c7903f 100644 --- a/server/src/usb_gadget.rs +++ b/server/src/usb_gadget.rs @@ -7,32 +7,28 @@ pub struct UsbGadget { udc_file: &'static str, } -impl UsbGadgetManager { - pub fn new(gadget_name: &'static str) -> Self { +impl UsbGadget { + pub fn new(name: &'static str) -> Self { // /sys/kernel/config/usb_gadget//UDC Self { udc_file: Box::leak( - format!("/sys/kernel/config/usb_gadget/{gadget_name}/UDC").into_boxed_str()) + format!("/sys/kernel/config/usb_gadget/{name}/UDC").into_boxed_str()) } } /// Force the host to re‑enumerate our HID gadget. pub fn cycle(&self) -> Result<()> { - // 1. detach info!("UDC‑cycle: detaching gadget"); - OpenOptions::new().write(true).open(self.udc_file)? - .write_all(b"")?; + OpenOptions::new().write(true).open(self.udc_file)?.write_all(b"")?; + thread::sleep(Duration::from_millis(200)); - // 2. wait ≥ 100 ms so host sees a disconnect - std::thread::sleep(Duration::from_millis(200)); - - // 3. re‑attach to **first** UDC (dwc2) - let udc = std::fs::read_dir("/sys/class/udc")? - .next().context("no UDC present")? + let udc_name = std::fs::read_dir("/sys/class/udc")? + .next() + .transpose()? + .context("no UDC present")? .file_name(); - let name = udc.to_string_lossy(); - info!("UDC‑cycle: re‑attaching to {name}"); OpenOptions::new().write(true).open(self.udc_file)? - .write_all(name.as_bytes())?; + .write_all(udc_name.to_str().unwrap().as_bytes())?; + info!("USB‑gadget cycled"); Ok(()) } }