glycin_utils/
dbus_loader_api.rs1use std::marker::PhantomData;
4use std::os::fd::OwnedFd;
5use std::os::unix::net::UnixStream;
6use std::sync::{Arc, Mutex, MutexGuard};
7
8use futures_util::FutureExt;
9use zbus::zvariant::OwnedObjectPath;
10
11use crate::dbus_types::*;
12use crate::error::*;
13
14pub trait LoaderImplementation: Send + Sync + Sized + 'static {
15 fn init(
16 stream: UnixStream,
17 mime_type: String,
18 details: InitializationDetails,
19 ) -> Result<(Self, ImageDetails), ProcessError>;
20
21 fn frame(&mut self, frame_request: FrameRequest) -> Result<Frame, ProcessError>;
22}
23
24pub struct Loader<T: LoaderImplementation> {
25 pub loader: PhantomData<T>,
26 pub image_id: Mutex<u64>,
27}
28
29#[zbus::interface(name = "org.gnome.glycin.Loader")]
30impl<T: LoaderImplementation> Loader<T> {
31 async fn init(
32 &self,
33 init_request: InitRequest,
34 #[zbus(connection)] dbus_connection: &zbus::Connection,
35 ) -> Result<RemoteImage, RemoteError> {
36 let fd = OwnedFd::from(init_request.fd);
37 let stream = UnixStream::from(fd);
38
39 let (loader_state, image_info) =
40 T::init(stream, init_request.mime_type, init_request.details)
41 .map_err(|x| x.into_loader_error())?;
42
43 let image_id = {
44 let lock = self.image_id.lock();
45 let mut image_id = match lock {
46 Ok(id) => id,
47 Err(err) => return Err(RemoteError::InternalLoaderError(err.to_string())),
48 };
49 let id = *image_id;
50 *image_id = id + 1;
51 id
52 };
53
54 let path = OwnedObjectPath::try_from(format!("/org/gnome/glycin/image/{image_id}"))
55 .internal_error()
56 .map_err(|x| x.into_loader_error())?;
57
58 let dbus_image = RemoteImage::new(image_info, path.clone());
59
60 dbus_connection
61 .object_server()
62 .at(
63 &path,
64 Image {
65 loader_implementation: Arc::new(Mutex::new(Box::new(loader_state))),
66 path: path.clone(),
67 dropped: Default::default(),
68 },
69 )
70 .await
71 .internal_error()
72 .map_err(|x| x.into_loader_error())?;
73
74 Ok(dbus_image)
75 }
76}
77
78pub struct Image<T: LoaderImplementation> {
79 pub loader_implementation: Arc<Mutex<Box<T>>>,
80 pub path: OwnedObjectPath,
81 dropped: async_lock::OnceCell<()>,
82}
83
84impl<T: LoaderImplementation> Image<T> {
85 pub fn get_loader_state(&self) -> Result<MutexGuard<'_, Box<T>>, RemoteError> {
86 self.loader_implementation.lock().map_err(|err| {
87 RemoteError::InternalLoaderError(format!(
88 "Failed to lock loader state for operation: {err}"
89 ))
90 })
91 }
92}
93
94#[zbus::interface(name = "org.gnome.glycin.Image")]
95impl<T: LoaderImplementation> Image<T> {
96 async fn frame(&self, frame_request: FrameRequest) -> Result<Frame, RemoteError> {
97 let loader_implementation = self.loader_implementation.clone();
98 let mut frame_request = blocking::unblock(move || {
99 let mut loader_implementation = loader_implementation.lock().map_err(|err| {
100 RemoteError::InternalLoaderError(format!(
101 "Failed to lock loader state for operation: {err}"
102 ))
103 })?;
104
105 loader_implementation
106 .frame(frame_request)
107 .map_err(|x| x.into_loader_error())
108 })
109 .fuse();
110
111 futures_util::select! {
112 result = frame_request => result,
113 _ = self.dropped.wait().fuse() => Err(RemoteError::Aborted),
114 }
115 }
116
117 async fn done(
118 &self,
119 #[zbus(object_server)] object_server: &zbus::ObjectServer,
120 ) -> Result<(), RemoteError> {
121 log::debug!("Disconnecting {}", self.path);
122 let removed = object_server.remove::<Image<T>, _>(&self.path).await?;
123 if removed {
124 log::debug!("Removed {}", self.path);
125 } else {
126 log::error!("Failed to remove {}", self.path);
127 }
128 let _ = self.dropped.set(()).await;
129 Ok(())
130 }
131}