gio/
app_info.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::boxed::Box as Box_;
4use std::pin::Pin;
5use std::ptr;
6
7use glib::prelude::*;
8use glib::translate::*;
9
10use crate::{ffi, AppInfo, AppLaunchContext, Cancellable};
11
12mod sealed {
13    pub trait Sealed {}
14    impl<T: super::IsA<super::AppInfo>> Sealed for T {}
15}
16
17pub trait AppInfoExtManual: sealed::Sealed + IsA<AppInfo> + 'static {
18    /// Async version of [`AppInfoExt::launch_uris()`][crate::prelude::AppInfoExt::launch_uris()].
19    ///
20    /// The @callback is invoked immediately after the application launch, but it
21    /// waits for activation in case of D-Bus–activated applications and also provides
22    /// extended error information for sandboxed applications, see notes for
23    /// [`AppInfo::launch_default_for_uri_async()`][crate::AppInfo::launch_default_for_uri_async()].
24    /// ## `uris`
25    /// a list of URIs to launch.
26    /// ## `context`
27    /// the launch context
28    /// ## `cancellable`
29    /// a [`Cancellable`][crate::Cancellable]
30    /// ## `callback`
31    /// a [type@Gio.AsyncReadyCallback] to call
32    ///   when the request is done
33    #[cfg_attr(docsrs, doc(cfg(feature = "v2_60")))]
34    #[doc(alias = "g_app_info_launch_uris_async")]
35    fn launch_uris_async<
36        P: IsA<AppLaunchContext>,
37        Q: IsA<Cancellable>,
38        R: FnOnce(Result<(), glib::Error>) + 'static,
39    >(
40        &self,
41        uris: &[&str],
42        context: Option<&P>,
43        cancellable: Option<&Q>,
44        callback: R,
45    ) {
46        let main_context = glib::MainContext::ref_thread_default();
47        let is_main_context_owner = main_context.is_owner();
48        let has_acquired_main_context = (!is_main_context_owner)
49            .then(|| main_context.acquire().ok())
50            .flatten();
51        assert!(
52            is_main_context_owner || has_acquired_main_context.is_some(),
53            "Async operations only allowed if the thread is owning the MainContext"
54        );
55
56        let user_data: Box_<(glib::thread_guard::ThreadGuard<R>, *mut *mut libc::c_char)> =
57            Box_::new((
58                glib::thread_guard::ThreadGuard::new(callback),
59                uris.to_glib_full(),
60            ));
61        unsafe extern "C" fn launch_uris_async_trampoline<
62            R: FnOnce(Result<(), glib::Error>) + 'static,
63        >(
64            _source_object: *mut glib::gobject_ffi::GObject,
65            res: *mut ffi::GAsyncResult,
66            user_data: glib::ffi::gpointer,
67        ) {
68            let mut error = ptr::null_mut();
69            let _ = ffi::g_app_info_launch_uris_finish(_source_object as *mut _, res, &mut error);
70            let result = if error.is_null() {
71                Ok(())
72            } else {
73                Err(from_glib_full(error))
74            };
75            let callback: Box_<(glib::thread_guard::ThreadGuard<R>, *mut *mut libc::c_char)> =
76                Box_::from_raw(user_data as *mut _);
77            let (callback, uris) = *callback;
78            let callback = callback.into_inner();
79            callback(result);
80            glib::ffi::g_strfreev(uris);
81        }
82        let callback = launch_uris_async_trampoline::<R>;
83        unsafe {
84            ffi::g_app_info_launch_uris_async(
85                self.as_ref().to_glib_none().0,
86                uris.to_glib_none().0,
87                context.map(|p| p.as_ref()).to_glib_none().0,
88                cancellable.map(|p| p.as_ref()).to_glib_none().0,
89                Some(callback),
90                Box_::into_raw(user_data) as *mut _,
91            );
92        }
93    }
94
95    #[cfg_attr(docsrs, doc(cfg(feature = "v2_60")))]
96    fn launch_uris_future<P: IsA<AppLaunchContext> + Clone + 'static>(
97        &self,
98        uris: &[&str],
99        context: Option<&P>,
100    ) -> Pin<Box_<dyn std::future::Future<Output = Result<(), glib::Error>> + 'static>> {
101        let uris = uris.iter().copied().map(String::from).collect::<Vec<_>>();
102        let context = context.map(ToOwned::to_owned);
103        Box_::pin(crate::GioFuture::new(
104            self,
105            move |obj, cancellable, send| {
106                let uris = uris
107                    .iter()
108                    .map(::std::borrow::Borrow::borrow)
109                    .collect::<Vec<_>>();
110                obj.launch_uris_async(
111                    uris.as_ref(),
112                    context.as_ref().map(::std::borrow::Borrow::borrow),
113                    Some(cancellable),
114                    move |res| {
115                        send.resolve(res);
116                    },
117                );
118            },
119        ))
120    }
121}
122
123impl<O: IsA<AppInfo>> AppInfoExtManual for O {}