use std::collections::HashMap;
use futures_util::Stream;
use serde::Deserialize;
use zbus::zvariant::{
DeserializeDict, ObjectPath, OwnedFd, OwnedObjectPath, OwnedValue, SerializeDict, Type, Value,
};
use crate::{
Error, WindowIdentifier,
desktop::{HandleToken, Session, SessionPortal},
proxy::Proxy,
};
#[derive(Debug, SerializeDict, Type, Default)]
#[zvariant(signature = "dict")]
struct CreateSessionOptions {
handle_token: HandleToken,
session_handle_token: HandleToken,
}
#[derive(SerializeDict, Type, Debug, Default)]
#[zvariant(signature = "dict")]
struct UsbEnumerateOptions {}
#[derive(SerializeDict, DeserializeDict, Type, Debug, Default)]
#[zvariant(signature = "dict")]
pub struct UsbDevice {
parent: Option<String>,
readable: Option<bool>,
writable: Option<bool>,
#[zvariant(rename = "device-file")]
device_file: Option<String>,
properties: Option<HashMap<String, OwnedValue>>,
}
impl UsbDevice {
pub fn parent(&self) -> Option<&str> {
self.parent.as_deref()
}
pub fn is_readable(&self) -> bool {
self.readable.unwrap_or(false)
}
pub fn is_writable(&self) -> bool {
self.writable.unwrap_or(false)
}
pub fn device_file(&self) -> Option<&str> {
self.device_file.as_deref()
}
pub fn vendor(&self) -> Option<String> {
self.properties.as_ref().and_then(|properties| {
properties
.get("ID_VENDOR_FROM_DATABASE")
.or_else(|| properties.get("ID_VENDOR_ENC"))
.and_then(|v| v.downcast_ref::<String>().ok())
})
}
pub fn model(&self) -> Option<String> {
self.properties.as_ref().and_then(|properties| {
properties
.get("ID_MODEL_FROM_DATABASE")
.or_else(|| properties.get("ID_MODEL_ENC"))
.and_then(|v| v.downcast_ref::<String>().ok())
})
}
pub fn properties(&self) -> Option<&HashMap<String, OwnedValue>> {
self.properties.as_ref()
}
}
#[derive(Debug)]
pub struct UsbError(pub Option<String>);
impl std::fmt::Display for UsbError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0.as_deref().unwrap_or(""))
}
}
impl std::error::Error for UsbError {}
impl From<AcquiredDevice> for Result<OwnedFd, UsbError> {
fn from(v: AcquiredDevice) -> Result<OwnedFd, UsbError> {
if let Some(fd) = v.fd {
if v.success {
Ok(fd)
} else {
Err(UsbError(v.error))
}
} else {
Err(UsbError(None))
}
}
}
#[derive(SerializeDict, Type, Debug, Default)]
#[zvariant(signature = "dict")]
struct AcquireDevice {
writable: bool,
}
pub struct Device(String , bool );
impl Device {
pub fn new(id: String, writable: bool) -> Device {
Device(id, writable)
}
pub fn id(&self) -> &str {
&self.0
}
pub fn is_writable(&self) -> bool {
self.1
}
}
#[derive(SerializeDict, Type, Debug, Default)]
#[zvariant(signature = "dict")]
struct AcquireOptions {
handle_token: HandleToken,
}
#[derive(DeserializeDict, Type, Debug, Default)]
#[zvariant(signature = "dict")]
struct AcquiredDevice {
success: bool,
fd: Option<OwnedFd>,
error: Option<String>,
}
#[derive(Debug, Deserialize, Type)]
pub struct UsbEvent( String, String, UsbDevice);
impl UsbEvent {
pub fn action(&self) -> &str {
&self.0
}
pub fn device_id(&self) -> &str {
&self.1
}
pub fn device(&self) -> &UsbDevice {
&self.2
}
}
#[derive(Debug, Deserialize, Type)]
pub struct UsbDeviceEvent(OwnedObjectPath, Vec<UsbEvent>);
impl UsbDeviceEvent {
pub fn session_handle(&self) -> ObjectPath<'_> {
self.0.as_ref()
}
pub fn events(&self) -> &[UsbEvent] {
&self.1
}
}
#[derive(Debug)]
#[doc(alias = "org.freedesktop.portal.Usb")]
pub struct UsbProxy(Proxy<'static>);
impl UsbProxy {
pub async fn new() -> Result<Self, Error> {
let proxy = Proxy::new_desktop("org.freedesktop.portal.Usb").await?;
Ok(Self(proxy))
}
pub async fn with_connection(connection: zbus::Connection) -> Result<Self, Error> {
let proxy =
Proxy::new_desktop_with_connection(connection, "org.freedesktop.portal.Usb").await?;
Ok(Self(proxy))
}
#[doc(alias = "CreateSession")]
pub async fn create_session(&self) -> Result<Session<Self>, Error> {
let options = CreateSessionOptions::default();
let session: OwnedObjectPath = self.0.call("CreateSession", &(&options)).await?;
Session::with_connection(self.0.connection().clone(), session).await
}
#[doc(alias = "EnumerateDevices")]
pub async fn enumerate_devices(&self) -> Result<Vec<(String, UsbDevice)>, Error> {
let options = UsbEnumerateOptions::default();
self.0.call("EnumerateDevices", &(&options)).await
}
#[doc(alias = "AcquireDevices")]
pub async fn acquire_devices(
&self,
parent_window: Option<&WindowIdentifier>,
devices: &[Device],
) -> Result<Vec<(String, Result<OwnedFd, UsbError>)>, 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| {
let device = AcquireDevice { writable: dev.1 };
(dev.0.to_string(), device)
})
.collect();
let request = self
.0
.empty_request(
&options.handle_token,
"AcquireDevices",
&(&parent_window, &acquire_devices, &options),
)
.await?;
let mut devices: Vec<(String, Result<OwnedFd, UsbError>)> = vec![];
if request.response().is_ok() {
let path = request.path();
loop {
let (mut new_devices, finished) = self.finish_acquire_devices(path).await?;
devices.append(&mut new_devices);
if finished {
break;
}
}
}
Ok(devices)
}
#[doc(alias = "FinishAcquireDevices")]
async fn finish_acquire_devices(
&self,
request_path: &ObjectPath<'_>,
) -> Result<(Vec<(String, Result<OwnedFd, UsbError>)>, bool), Error> {
let options: HashMap<&str, Value<'_>> = HashMap::new();
self.0
.call("FinishAcquireDevices", &(request_path, &options))
.await
.map(|result: (Vec<(String, AcquiredDevice)>, bool)| {
let finished = result.1;
(
result
.0
.into_iter()
.map(|item| (item.0, item.1.into()))
.collect::<Vec<_>>(),
finished,
)
})
}
#[doc(alias = "ReleaseDevices")]
pub async fn release_devices(&self, devices: &[&str]) -> Result<(), Error> {
let options: HashMap<&str, Value<'_>> = HashMap::new();
self.0.call("ReleaseDevices", &(devices, &options)).await
}
#[doc(alias = "DeviceEvents")]
pub async fn receive_device_events(
&self,
) -> Result<impl Stream<Item = UsbDeviceEvent> + use<'_>, Error> {
self.0.signal("DeviceEvents").await
}
}
impl crate::Sealed for UsbProxy {}
impl SessionPortal for UsbProxy {}