Skip to main content

glycin_utils/
dbus_loader_api.rs

1// Copyright (c) 2024 GNOME Foundation Inc.
2
3use 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}