From 3aee67385f6886ff357b43b9dadbd80066792e55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hubert=20Figui=C3=A8re?= Date: Mon, 20 May 2024 21:38:11 -0400 Subject: [PATCH] demo+usb: Display the device description obtained from the acquired device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added rusb to the crates. Added libusb to the manifest. Signed-off-by: Hubert Figuière --- ashpd-demo/Cargo.lock | 29 ++++ ashpd-demo/Cargo.toml | 1 + .../com.belmoussaoui.ashpd.demo.Devel.json | 13 ++ ashpd-demo/src/portals/desktop/usb.rs | 152 ++++++++++-------- src/desktop/usb.rs | 7 +- 5 files changed, 136 insertions(+), 66 deletions(-) diff --git a/ashpd-demo/Cargo.lock b/ashpd-demo/Cargo.lock index bae07d545..df35148dd 100644 --- a/ashpd-demo/Cargo.lock +++ b/ashpd-demo/Cargo.lock @@ -78,6 +78,7 @@ dependencies = [ "gtk4", "libadwaita", "libshumate", + "rusb", "serde", "tracing", "tracing-subscriber", @@ -1590,6 +1591,18 @@ dependencies = [ "system-deps 6.2.2", ] +[[package]] +name = "libusb1-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da050ade7ac4ff1ba5379af847a10a10a8e284181e060105bf8d86960ce9ce0f" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -1982,6 +1995,16 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "rusb" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab9f9ff05b63a786553a4c02943b74b34a988448671001e9a27e2f0565cc05a4" +dependencies = [ + "libc", + "libusb1-sys", +] + [[package]] name = "rustc-hash" version = "1.1.0" @@ -2349,6 +2372,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version-compare" version = "0.2.0" diff --git a/ashpd-demo/Cargo.toml b/ashpd-demo/Cargo.toml index 4e2baa9ae..1cb3073ba 100644 --- a/ashpd-demo/Cargo.toml +++ b/ashpd-demo/Cargo.toml @@ -14,6 +14,7 @@ gettext-rs = {version = "0.7", features = ["gettext-system"]} gst = {package = "gstreamer", version = "0.23"} gst4gtk = {package = "gst-plugin-gtk4", version = "0.13", features = ["wayland", "x11egl", "x11glx", "gtk_v4_14"]} gtk = {package = "gtk4", version = "0.9", features = ["v4_14"]} +rusb = "0.9.4" serde = {version = "1.0", features = ["derive"]} shumate = {version = "0.6", package = "libshumate"} tracing = "0.1" diff --git a/ashpd-demo/build-aux/com.belmoussaoui.ashpd.demo.Devel.json b/ashpd-demo/build-aux/com.belmoussaoui.ashpd.demo.Devel.json index 66e9bd38d..644cf8b54 100644 --- a/ashpd-demo/build-aux/com.belmoussaoui.ashpd.demo.Devel.json +++ b/ashpd-demo/build-aux/com.belmoussaoui.ashpd.demo.Devel.json @@ -32,6 +32,19 @@ ] }, "modules": [ + { + "name": "libusb", + "config-opts": [ + "--disable-udev" + ], + "sources": [ + { + "type": "archive", + "url": "https://github.com/libusb/libusb/releases/download/v1.0.23/libusb-1.0.23.tar.bz2", + "sha256": "db11c06e958a82dac52cf3c65cb4dd2c3f339c8a988665110e0d24d19312ad8d" + } + ] + }, { "name": "libshumate", "buildsystem": "meson", diff --git a/ashpd-demo/src/portals/desktop/usb.rs b/ashpd-demo/src/portals/desktop/usb.rs index 30d0a202d..80c1193c9 100644 --- a/ashpd-demo/src/portals/desktop/usb.rs +++ b/ashpd-demo/src/portals/desktop/usb.rs @@ -4,16 +4,9 @@ // Hubert Figuière // -use std::cell::RefCell; -use std::collections::HashMap; -use std::sync::Arc; +use std::{cell::RefCell, collections::HashMap, os::fd::AsRawFd, sync::Arc}; use adw::{prelude::*, subclass::prelude::*}; -use futures_util::{lock::Mutex, StreamExt}; -use glib::clone; -use gtk::glib; - -use ashpd::zbus::zvariant::ObjectPath; use ashpd::{ desktop::{ usb::{Device, UsbOptions, UsbProxy}, @@ -131,6 +124,22 @@ mod imp { } } + fn usb_describe_device(fd: &dyn AsRawFd) -> ashpd::Result { + let context = rusb::Context::new() + .map_err(|_| ashpd::PortalError::Failed("rusb Context".to_string()))?; + let handle = unsafe { context.open_device_with_fd(fd.as_raw_fd()) } + .map_err(|_| ashpd::PortalError::Failed("open USB device".to_string()))?; + let device = handle.device(); + let device_desc = device.device_descriptor().unwrap(); + Ok(format!( + "Bus {:03} Device {:03} ID {:04x}:{:04x}", + device.bus_number(), + device.address(), + device_desc.vendor_id(), + device_desc.product_id() + )) + } + pub(super) async fn refresh_devices(&self) -> ashpd::Result<()> { let page = self.obj(); @@ -152,54 +161,16 @@ mod imp { row.connect_share_clicked(clone!( #[strong] page, - move |row| { + move |widget| { glib::spawn_future_local(clone!( - #[strong] - row, #[strong] device_id, #[strong] + widget, + #[strong] page, async move { - let root = row.native().unwrap(); - let identifier = WindowIdentifier::from_native(&root).await; - let usb = UsbProxy::new().await.unwrap(); - let result = usb - .acquire_devices(&identifier, &[(&device_id, device_writable)]) - .await; - match result { - Ok(resp) => { - if resp.response().is_ok() { - loop { - let result = usb.finish_acquire_devices().await; - match result { - Ok(result) => { - println!("result {result:?}"); - if !result.1 { - continue; - } - for device in &result.0 { - page.imp().acquired_device(&device.0); - } - } - Err(err) => { - tracing::error!( - "Finish acquire device error: {err}" - ); - page.error(&format!( - "Finish acquire device error: {err}" - )); - } - } - break; - } - } - } - Err(err) => { - tracing::error!("Acquire device error: {err}"); - page.error(&format!("Acquire device error: {err}")); - } - } + page.imp().share(&widget, &device_id, device_writable).await } )); } @@ -214,19 +185,7 @@ mod imp { device_id, #[strong] page, - async move { - let usb = UsbProxy::new().await.unwrap(); - let result = usb.release_devices(&[&device_id]).await; - println!("{result:?}"); - match result { - Ok(_) => {} - Err(err) => { - tracing::error!("Acquire device error: {err}"); - page.error(&format!("Acquire device error: {err}")); - } - } - page.imp().released_device(&device_id); - } + async move { page.imp().unshare(&device_id).await } )); } )); @@ -235,6 +194,73 @@ mod imp { Ok(()) } + async fn finish_acquire_device(&self, usb: &UsbProxy<'_>, object_path: &ObjectPath<'_>) { + loop { + let result = usb.finish_acquire_devices(object_path).await; + match result { + Ok(result) => { + println!("result {result:?}"); + if !result.1 { + continue; + } + for device in &result.0 { + if let Ok(fd) = &device.1 { + match Self::usb_describe_device(&Fd::from(fd)) { + Ok(describe) => self.obj().info(&describe), + Err(err) => self.obj().info(&err.to_string()), + } + } + self.acquired_device(&device.0); + } + } + Err(err) => { + tracing::error!("Finish acquire device error: {err}"); + self.obj() + .error(&format!("Finish acquire device error: {err}")); + } + } + break; + } + } + + async fn share(&self, widget: >k::Button, device_id: &String, device_writable: bool) { + let root = widget.native().unwrap(); + let identifier = WindowIdentifier::from_native(&root).await; + let usb = UsbProxy::new().await.unwrap(); + let result = usb + .acquire_devices( + identifier.as_ref(), + &[Device(device_id.to_string(), device_writable)], + ) + .await; + match result { + Ok(resp) => { + if resp.response().is_ok() { + let path = resp.path(); + self.finish_acquire_device(&usb, path).await; + } + } + Err(err) => { + tracing::error!("Acquire device error: {err}"); + self.obj().error(&format!("Acquire device error: {err}")); + } + } + } + + async fn unshare(&self, device_id: &str) { + let usb = UsbProxy::new().await.unwrap(); + let result = usb.release_devices(&[device_id]).await; + println!("{result:?}"); + match result { + Ok(_) => {} + Err(err) => { + tracing::error!("Acquire device error: {err}"); + self.obj().error(&format!("Acquire device error: {err}")); + } + } + self.released_device(device_id); + } + pub(super) async fn start_session(&self) -> ashpd::Result<()> { let usb = UsbProxy::new().await?; let session = usb.create_session().await?; diff --git a/src/desktop/usb.rs b/src/desktop/usb.rs index 8984749fb..6af4f8142 100644 --- a/src/desktop/usb.rs +++ b/src/desktop/usb.rs @@ -211,10 +211,11 @@ impl<'a> UsbProxy<'a> { #[doc(alias = "AcquireDevices")] pub async fn acquire_devices( &self, - parent_window: &WindowIdentifier, - devices: &[(&String, bool)], + parent_window: Option<&WindowIdentifier>, + devices: &[Device], ) -> Result, Error> { let options = AcquireOptions::default(); + let parent_window = parent_window.map(|i| i.to_string()).unwrap_or_default(); let acquire_devices: Vec<(String, AcquireDevice)> = devices .iter() .map(|dev| { @@ -226,7 +227,7 @@ impl<'a> UsbProxy<'a> { .empty_request( &options.handle_token, "AcquireDevices", - &(parent_window, &acquire_devices, &options), + &(&parent_window, &acquire_devices, &options), ) .await }