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 d7d081810..bb86c0c14 100644
--- a/ashpd-demo/build-aux/com.belmoussaoui.ashpd.demo.Devel.json
+++ b/ashpd-demo/build-aux/com.belmoussaoui.ashpd.demo.Devel.json
@@ -14,6 +14,7 @@
"--socket=wayland",
"--device=dri",
"--own-name=com.belmoussaoui.ashpd.demo",
+ "--usb=all",
"--env=RUST_LOG=ashpd_demo=debug,ashpd=debug",
"--env=G_MESSAGES_DEBUG=none",
"--env=RUST_BACKTRACE=1"
@@ -31,6 +32,16 @@
]
},
"modules": [
+ {
+ "name": "libusb",
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://github.com/libusb/libusb/releases/download/v1.0.27/libusb-1.0.27.tar.bz2",
+ "sha256": "ffaa41d741a8a3bee244ac8e54a72ea05bf2879663c098c82fc5757853441575"
+ }
+ ]
+ },
{
"name": "libshumate",
"buildsystem": "meson",
diff --git a/ashpd-demo/data/resources.gresource.xml b/ashpd-demo/data/resources.gresource.xml
index 1202c6af2..28fc4afbe 100644
--- a/ashpd-demo/data/resources.gresource.xml
+++ b/ashpd-demo/data/resources.gresource.xml
@@ -26,6 +26,8 @@
resources/ui/screenshot.ui
resources/ui/screencast.ui
resources/ui/secret.ui
+ resources/ui/usb.ui
+ resources/ui/usb_device_row.ui
resources/ui/wallpaper.ui
resources/style.css
diff --git a/ashpd-demo/data/resources/ui/usb.ui b/ashpd-demo/data/resources/ui/usb.ui
new file mode 100644
index 000000000..a168dcefa
--- /dev/null
+++ b/ashpd-demo/data/resources/ui/usb.ui
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
diff --git a/ashpd-demo/data/resources/ui/usb_device_row.ui b/ashpd-demo/data/resources/ui/usb_device_row.ui
new file mode 100644
index 000000000..420159e45
--- /dev/null
+++ b/ashpd-demo/data/resources/ui/usb_device_row.ui
@@ -0,0 +1,31 @@
+
+
+
+
+
+ false
+
+
+
+
+ 6
+ 6
+
+
+
+
+ preferences-system-sharing-symbolic
+
+
+
+
+
+ process-stop-symbolic
+
+
+
+
+
+
diff --git a/ashpd-demo/data/resources/ui/window.ui b/ashpd-demo/data/resources/ui/window.ui
index a95869152..c6fbaeed6 100644
--- a/ashpd-demo/data/resources/ui/window.ui
+++ b/ashpd-demo/data/resources/ui/window.ui
@@ -155,6 +155,12 @@
secret
+
+
+
+
+
+ usb
+
+
+
+
+
wallpaper
diff --git a/ashpd-demo/src/portals/desktop/mod.rs b/ashpd-demo/src/portals/desktop/mod.rs
index 79a12f47d..97bedde8c 100644
--- a/ashpd-demo/src/portals/desktop/mod.rs
+++ b/ashpd-demo/src/portals/desktop/mod.rs
@@ -16,6 +16,7 @@ mod remote_desktop;
mod screencast;
mod screenshot;
mod secret;
+mod usb;
mod wallpaper;
pub use account::AccountPage;
@@ -36,4 +37,5 @@ pub use remote_desktop::RemoteDesktopPage;
pub use screencast::ScreenCastPage;
pub use screenshot::ScreenshotPage;
pub use secret::SecretPage;
+pub use usb::UsbPage;
pub use wallpaper::WallpaperPage;
diff --git a/ashpd-demo/src/portals/desktop/usb.rs b/ashpd-demo/src/portals/desktop/usb.rs
new file mode 100644
index 000000000..9b02fe5d7
--- /dev/null
+++ b/ashpd-demo/src/portals/desktop/usb.rs
@@ -0,0 +1,373 @@
+// Copyright (C) 2024 GNOME Foundation
+//
+// Authors:
+// Hubert Figuière
+//
+
+use std::{collections::HashMap, os::fd::AsRawFd, sync::Arc};
+
+use adw::{prelude::*, subclass::prelude::*};
+use ashpd::{
+ desktop::{
+ usb::{Device, UsbDevice, UsbError, UsbProxy},
+ Session,
+ },
+ zbus::zvariant::{Fd, OwnedFd},
+ WindowIdentifier,
+};
+use futures_util::{lock::Mutex, StreamExt};
+use glib::clone;
+use gtk::glib;
+use rusb::UsbContext;
+
+use crate::widgets::{PortalPage, PortalPageExt, PortalPageImpl};
+
+glib::wrapper! {
+ pub struct UsbDeviceRow(ObjectSubclass)
+ @extends gtk::Widget, gtk::ListBoxRow, adw::PreferencesRow, adw::ActionRow;
+}
+
+impl UsbDeviceRow {
+ fn with_device(page: UsbPage, device_id: String, writable: bool) -> Self {
+ let obj: Self = glib::Object::new();
+
+ let imp = obj.imp();
+ imp.page.replace(Some(page));
+ imp.device_id.replace(device_id);
+ imp.writable.set(writable);
+ obj
+ }
+
+ fn acquire(&self) {
+ self.imp().checkbox.set_active(true);
+ }
+
+ fn release(&self) {
+ self.imp().checkbox.set_active(false);
+ }
+}
+
+mod imp {
+ use std::cell::{Cell, RefCell};
+
+ use super::*;
+
+ #[derive(Debug, gtk::CompositeTemplate, Default)]
+ #[template(resource = "/com/belmoussaoui/ashpd/demo/usb_device_row.ui")]
+ pub struct UsbDeviceRow {
+ #[template_child]
+ pub(super) checkbox: TemplateChild,
+ #[template_child]
+ pub(super) acquire: TemplateChild,
+ #[template_child]
+ pub(super) release: TemplateChild,
+ pub(super) page: RefCell