1use std::collections::HashMap;
32
33use futures_util::Stream;
34use serde::Deserialize;
35use zbus::zvariant::{
36 DeserializeDict, ObjectPath, OwnedFd, OwnedObjectPath, OwnedValue, SerializeDict, Type, Value,
37};
38
39use crate::{
40 desktop::{HandleToken, Session, SessionPortal},
41 proxy::Proxy,
42 Error, WindowIdentifier,
43};
44
45#[derive(Debug, SerializeDict, Type, Default)]
46#[zvariant(signature = "dict")]
47struct CreateSessionOptions {
48 handle_token: HandleToken,
49 session_handle_token: HandleToken,
50}
51
52#[derive(SerializeDict, Type, Debug, Default)]
54#[zvariant(signature = "dict")]
55struct UsbEnumerateOptions {}
56
57#[derive(SerializeDict, DeserializeDict, Type, Debug, Default)]
59#[zvariant(signature = "dict")]
60pub struct UsbDevice {
61 parent: Option<String>,
62 readable: Option<bool>,
64 writable: Option<bool>,
66 #[zvariant(rename = "device-file")]
68 device_file: Option<String>,
69 properties: Option<HashMap<String, OwnedValue>>,
71}
72
73impl UsbDevice {
74 pub fn parent(&self) -> Option<&str> {
76 self.parent.as_deref()
77 }
78
79 pub fn is_readable(&self) -> bool {
81 self.readable.unwrap_or(false)
82 }
83
84 pub fn is_writable(&self) -> bool {
86 self.writable.unwrap_or(false)
87 }
88
89 pub fn device_file(&self) -> Option<&str> {
91 self.device_file.as_deref()
92 }
93
94 pub fn vendor(&self) -> Option<String> {
96 self.properties.as_ref().and_then(|properties| {
97 properties
98 .get("ID_VENDOR_FROM_DATABASE")
99 .or_else(|| properties.get("ID_VENDOR_ENC"))
100 .and_then(|v| v.downcast_ref::<String>().ok())
101 })
102 }
103
104 pub fn model(&self) -> Option<String> {
106 self.properties.as_ref().and_then(|properties| {
107 properties
108 .get("ID_MODEL_FROM_DATABASE")
109 .or_else(|| properties.get("ID_MODEL_ENC"))
110 .and_then(|v| v.downcast_ref::<String>().ok())
111 })
112 }
113
114 pub fn properties(&self) -> Option<&HashMap<String, OwnedValue>> {
116 self.properties.as_ref()
117 }
118}
119
120#[derive(Debug)]
122pub struct UsbError(pub Option<String>);
123
124impl std::fmt::Display for UsbError {
125 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126 write!(f, "{}", self.0.as_deref().unwrap_or(""))
127 }
128}
129
130impl std::error::Error for UsbError {}
131
132impl From<AcquiredDevice> for Result<OwnedFd, UsbError> {
133 fn from(v: AcquiredDevice) -> Result<OwnedFd, UsbError> {
134 if let Some(fd) = v.fd {
135 if v.success {
136 Ok(fd)
137 } else {
138 Err(UsbError(v.error))
139 }
140 } else {
141 Err(UsbError(None))
142 }
143 }
144}
145
146#[derive(SerializeDict, Type, Debug, Default)]
148#[zvariant(signature = "dict")]
149struct AcquireDevice {
150 writable: bool,
151}
152
153pub struct Device(String , bool );
156
157impl Device {
158 pub fn new(id: String, writable: bool) -> Device {
160 Device(id, writable)
161 }
162
163 pub fn id(&self) -> &str {
165 &self.0
166 }
167
168 pub fn is_writable(&self) -> bool {
170 self.1
171 }
172}
173
174#[derive(SerializeDict, Type, Debug, Default)]
176#[zvariant(signature = "dict")]
177struct AcquireOptions {
178 handle_token: HandleToken,
179}
180
181#[derive(DeserializeDict, Type, Debug, Default)]
183#[zvariant(signature = "dict")]
184struct AcquiredDevice {
185 success: bool,
186 fd: Option<OwnedFd>,
187 error: Option<String>,
188}
189
190#[derive(Debug, Deserialize, Type)]
191pub struct UsbEvent(String, String, UsbDevice);
195
196impl UsbEvent {
197 pub fn action(&self) -> &str {
199 &self.0
200 }
201
202 pub fn device_id(&self) -> &str {
204 &self.1
205 }
206
207 pub fn device(&self) -> &UsbDevice {
209 &self.2
210 }
211}
212
213#[derive(Debug, Deserialize, Type)]
214pub struct UsbDeviceEvent(OwnedObjectPath, Vec<UsbEvent>);
216
217impl UsbDeviceEvent {
218 pub fn session_handle(&self) -> ObjectPath<'_> {
220 self.0.as_ref()
221 }
222
223 pub fn events(&self) -> &[UsbEvent] {
225 &self.1
226 }
227}
228
229#[derive(Debug)]
231#[doc(alias = "org.freedesktop.portal.Usb")]
232pub struct UsbProxy<'a>(Proxy<'a>);
233
234impl<'a> UsbProxy<'a> {
235 pub async fn new() -> Result<UsbProxy<'a>, Error> {
237 let proxy = Proxy::new_desktop("org.freedesktop.portal.Usb").await?;
238 Ok(Self(proxy))
239 }
240
241 #[doc(alias = "CreateSession")]
250 pub async fn create_session(&self) -> Result<Session<'a, Self>, Error> {
251 let options = CreateSessionOptions::default();
252 let session: OwnedObjectPath = self.0.call("CreateSession", &(&options)).await?;
253 Session::new(session).await
254 }
255
256 #[doc(alias = "EnumerateDevices")]
264 pub async fn enumerate_devices(&self) -> Result<Vec<(String, UsbDevice)>, Error> {
265 let options = UsbEnumerateOptions::default();
266 self.0.call("EnumerateDevices", &(&options)).await
267 }
268
269 #[doc(alias = "AcquireDevices")]
280 pub async fn acquire_devices(
281 &self,
282 parent_window: Option<&WindowIdentifier>,
283 devices: &[Device],
284 ) -> Result<Vec<(String, Result<OwnedFd, UsbError>)>, Error> {
285 let options = AcquireOptions::default();
286 let parent_window = parent_window.map(|i| i.to_string()).unwrap_or_default();
287 let acquire_devices: Vec<(String, AcquireDevice)> = devices
288 .iter()
289 .map(|dev| {
290 let device = AcquireDevice { writable: dev.1 };
291 (dev.0.to_string(), device)
292 })
293 .collect();
294 let request = self
295 .0
296 .empty_request(
297 &options.handle_token,
298 "AcquireDevices",
299 &(&parent_window, &acquire_devices, &options),
300 )
301 .await?;
302 let mut devices: Vec<(String, Result<OwnedFd, UsbError>)> = vec![];
303 if request.response().is_ok() {
304 let path = request.path();
305 loop {
306 let (mut new_devices, finished) = self.finish_acquire_devices(path).await?;
307 devices.append(&mut new_devices);
308 if finished {
309 break;
310 }
311 }
312 }
313 Ok(devices)
314 }
315
316 #[doc(alias = "FinishAcquireDevices")]
323 async fn finish_acquire_devices(
324 &self,
325 request_path: &ObjectPath<'_>,
326 ) -> Result<(Vec<(String, Result<OwnedFd, UsbError>)>, bool), Error> {
327 let options: HashMap<&str, Value<'_>> = HashMap::new();
328 self.0
329 .call("FinishAcquireDevices", &(request_path, &options))
330 .await
331 .map(|result: (Vec<(String, AcquiredDevice)>, bool)| {
332 let finished = result.1;
333 (
334 result
335 .0
336 .into_iter()
337 .map(|item| (item.0, item.1.into()))
338 .collect::<Vec<_>>(),
339 finished,
340 )
341 })
342 }
343
344 #[doc(alias = "ReleaseDevices")]
352 pub async fn release_devices(&self, devices: &[&str]) -> Result<(), Error> {
353 let options: HashMap<&str, Value<'_>> = HashMap::new();
354 self.0.call("ReleaseDevices", &(devices, &options)).await
355 }
356
357 #[doc(alias = "DeviceEvents")]
365 pub async fn receive_device_events(&self) -> Result<impl Stream<Item = UsbDeviceEvent>, Error> {
366 self.0.signal("DeviceEvents").await
367 }
368}
369
370impl crate::Sealed for UsbProxy<'_> {}
371impl SessionPortal for UsbProxy<'_> {}