gio/
application.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_, mem::transmute, ops::ControlFlow};
4
5use glib::{
6    prelude::*,
7    signal::{connect_raw, SignalHandlerId},
8    translate::*,
9    ExitCode, GString,
10};
11
12use crate::{ffi, Application, ApplicationCommandLine, File};
13
14pub trait ApplicationExtManual: IsA<Application> {
15    /// Runs the application.
16    ///
17    /// This function is intended to be run from main() and its return value
18    /// is intended to be returned by main(). Although you are expected to pass
19    /// the @argc, @argv parameters from main() to this function, it is possible
20    /// to pass [`None`] if @argv is not available or commandline handling is not
21    /// required.  Note that on Windows, @argc and @argv are ignored, and
22    /// g_win32_get_command_line() is called internally (for proper support
23    /// of Unicode commandline arguments).
24    ///
25    /// #GApplication will attempt to parse the commandline arguments.  You
26    /// can add commandline flags to the list of recognised options by way of
27    /// g_application_add_main_option_entries().  After this, the
28    /// #GApplication::handle-local-options signal is emitted, from which the
29    /// application can inspect the values of its #GOptionEntrys.
30    ///
31    /// #GApplication::handle-local-options is a good place to handle options
32    /// such as `--version`, where an immediate reply from the local process is
33    /// desired (instead of communicating with an already-running instance).
34    /// A #GApplication::handle-local-options handler can stop further processing
35    /// by returning a non-negative value, which then becomes the exit status of
36    /// the process.
37    ///
38    /// What happens next depends on the flags: if
39    /// [`ApplicationFlags::HANDLES_COMMAND_LINE`][crate::ApplicationFlags::HANDLES_COMMAND_LINE] was specified then the remaining
40    /// commandline arguments are sent to the primary instance, where a
41    /// #GApplication::command-line signal is emitted.  Otherwise, the
42    /// remaining commandline arguments are assumed to be a list of files.
43    /// If there are no files listed, the application is activated via the
44    /// #GApplication::activate signal.  If there are one or more files, and
45    /// [`ApplicationFlags::HANDLES_OPEN`][crate::ApplicationFlags::HANDLES_OPEN] was specified then the files are opened
46    /// via the #GApplication::open signal.
47    ///
48    /// If you are interested in doing more complicated local handling of the
49    /// commandline then you should implement your own #GApplication subclass
50    /// and override local_command_line(). In this case, you most likely want
51    /// to return [`true`] from your local_command_line() implementation to
52    /// suppress the default handling. See
53    /// [gapplication-example-cmdline2.c][https://gitlab.gnome.org/GNOME/glib/-/blob/HEAD/gio/tests/gapplication-example-cmdline2.c]
54    /// for an example.
55    ///
56    /// If, after the above is done, the use count of the application is zero
57    /// then the exit status is returned immediately.  If the use count is
58    /// non-zero then the default main context is iterated until the use count
59    /// falls to zero, at which point 0 is returned.
60    ///
61    /// If the [`ApplicationFlags::IS_SERVICE`][crate::ApplicationFlags::IS_SERVICE] flag is set, then the service will
62    /// run for as much as 10 seconds with a use count of zero while waiting
63    /// for the message that caused the activation to arrive.  After that,
64    /// if the use count falls to zero the application will exit immediately,
65    /// except in the case that g_application_set_inactivity_timeout() is in
66    /// use.
67    ///
68    /// This function sets the prgname (g_set_prgname()), if not already set,
69    /// to the basename of argv[0].
70    ///
71    /// Much like g_main_loop_run(), this function will acquire the main context
72    /// for the duration that the application is running.
73    ///
74    /// Since 2.40, applications that are not explicitly flagged as services
75    /// or launchers (ie: neither [`ApplicationFlags::IS_SERVICE`][crate::ApplicationFlags::IS_SERVICE] or
76    /// [`ApplicationFlags::IS_LAUNCHER`][crate::ApplicationFlags::IS_LAUNCHER] are given as flags) will check (from the
77    /// default handler for local_command_line) if "--gapplication-service"
78    /// was given in the command line.  If this flag is present then normal
79    /// commandline processing is interrupted and the
80    /// [`ApplicationFlags::IS_SERVICE`][crate::ApplicationFlags::IS_SERVICE] flag is set.  This provides a "compromise"
81    /// solution whereby running an application directly from the commandline
82    /// will invoke it in the normal way (which can be useful for debugging)
83    /// while still allowing applications to be D-Bus activated in service
84    /// mode.  The D-Bus service file should invoke the executable with
85    /// "--gapplication-service" as the sole commandline argument.  This
86    /// approach is suitable for use by most graphical applications but
87    /// should not be used from applications like editors that need precise
88    /// control over when processes invoked via the commandline will exit and
89    /// what their exit status will be.
90    /// ## `argv`
91    ///
92    ///     the argv from main(), or [`None`]
93    ///
94    /// # Returns
95    ///
96    /// the exit status
97    #[doc(alias = "g_application_run")]
98    fn run(&self) -> ExitCode {
99        self.run_with_args(&std::env::args().collect::<Vec<_>>())
100    }
101
102    #[doc(alias = "g_application_run")]
103    fn run_with_args<S: AsRef<str>>(&self, args: &[S]) -> ExitCode {
104        let argv: Vec<&str> = args.iter().map(|a| a.as_ref()).collect();
105        let argc = argv.len() as i32;
106        let exit_code = unsafe {
107            ffi::g_application_run(self.as_ref().to_glib_none().0, argc, argv.to_glib_none().0)
108        };
109        ExitCode::try_from(exit_code).unwrap()
110    }
111
112    /// The ::open signal is emitted on the primary instance when there are
113    /// files to open. See g_application_open() for more information.
114    /// ## `files`
115    /// an array of #GFiles
116    /// ## `hint`
117    /// a hint provided by the calling instance
118    #[doc(alias = "open")]
119    fn connect_open<F: Fn(&Self, &[File], &str) + 'static>(&self, f: F) -> SignalHandlerId {
120        unsafe extern "C" fn open_trampoline<P, F: Fn(&P, &[File], &str) + 'static>(
121            this: *mut ffi::GApplication,
122            files: *const *mut ffi::GFile,
123            n_files: libc::c_int,
124            hint: *mut libc::c_char,
125            f: glib::ffi::gpointer,
126        ) where
127            P: IsA<Application>,
128        {
129            let f: &F = &*(f as *const F);
130            let files: Vec<File> = FromGlibContainer::from_glib_none_num(files, n_files as usize);
131            f(
132                Application::from_glib_borrow(this).unsafe_cast_ref(),
133                &files,
134                &GString::from_glib_borrow(hint),
135            )
136        }
137        unsafe {
138            let f: Box_<F> = Box_::new(f);
139            connect_raw(
140                self.as_ptr() as *mut _,
141                b"open\0".as_ptr() as *const _,
142                Some(transmute::<*const (), unsafe extern "C" fn()>(
143                    open_trampoline::<Self, F> as *const (),
144                )),
145                Box_::into_raw(f),
146            )
147        }
148    }
149
150    /// The ::command-line signal is emitted on the primary instance when
151    /// a commandline is not handled locally. See g_application_run() and
152    /// the #GApplicationCommandLine documentation for more information.
153    /// ## `command_line`
154    /// a #GApplicationCommandLine representing the
155    ///     passed commandline
156    ///
157    /// # Returns
158    ///
159    /// An integer that is set as the exit status for the calling
160    ///   process. See g_application_command_line_set_exit_status().
161    #[doc(alias = "command-line")]
162    fn connect_command_line<F: Fn(&Self, &ApplicationCommandLine) -> ExitCode + 'static>(
163        &self,
164        f: F,
165    ) -> SignalHandlerId {
166        unsafe extern "C" fn command_line_trampoline<
167            P: IsA<Application>,
168            F: Fn(&P, &ApplicationCommandLine) -> ExitCode + 'static,
169        >(
170            this: *mut ffi::GApplication,
171            command_line: *mut ffi::GApplicationCommandLine,
172            f: glib::ffi::gpointer,
173        ) -> std::ffi::c_int {
174            let f: &F = &*(f as *const F);
175            f(
176                Application::from_glib_borrow(this).unsafe_cast_ref(),
177                &from_glib_borrow(command_line),
178            )
179            .into()
180        }
181        unsafe {
182            let f: Box_<F> = Box_::new(f);
183            connect_raw(
184                self.as_ptr() as *mut _,
185                c"command-line".as_ptr() as *const _,
186                Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>(
187                    command_line_trampoline::<Self, F> as *const (),
188                )),
189                Box_::into_raw(f),
190            )
191        }
192    }
193
194    /// The ::handle-local-options signal is emitted on the local instance
195    /// after the parsing of the commandline options has occurred.
196    ///
197    /// You can add options to be recognised during commandline option
198    /// parsing using g_application_add_main_option_entries() and
199    /// g_application_add_option_group().
200    ///
201    /// Signal handlers can inspect @options (along with values pointed to
202    /// from the @arg_data of an installed #GOptionEntrys) in order to
203    /// decide to perform certain actions, including direct local handling
204    /// (which may be useful for options like --version).
205    ///
206    /// In the event that the application is marked
207    /// [`ApplicationFlags::HANDLES_COMMAND_LINE`][crate::ApplicationFlags::HANDLES_COMMAND_LINE] the "normal processing" will
208    /// send the @options dictionary to the primary instance where it can be
209    /// read with g_application_command_line_get_options_dict().  The signal
210    /// handler can modify the dictionary before returning, and the
211    /// modified dictionary will be sent.
212    ///
213    /// In the event that [`ApplicationFlags::HANDLES_COMMAND_LINE`][crate::ApplicationFlags::HANDLES_COMMAND_LINE] is not set,
214    /// "normal processing" will treat the remaining uncollected command
215    /// line arguments as filenames or URIs.  If there are no arguments,
216    /// the application is activated by g_application_activate().  One or
217    /// more arguments results in a call to g_application_open().
218    ///
219    /// If you want to handle the local commandline arguments for yourself
220    /// by converting them to calls to g_application_open() or
221    /// g_action_group_activate_action() then you must be sure to register
222    /// the application first.  You should probably not call
223    /// g_application_activate() for yourself, however: just return -1 and
224    /// allow the default handler to do it for you.  This will ensure that
225    /// the `--gapplication-service` switch works properly (i.e. no activation
226    /// in that case).
227    ///
228    /// Note that this signal is emitted from the default implementation of
229    /// local_command_line().  If you override that function and don't
230    /// chain up then this signal will never be emitted.
231    ///
232    /// You can override local_command_line() if you need more powerful
233    /// capabilities than what is provided here, but this should not
234    /// normally be required.
235    /// ## `options`
236    /// the options dictionary
237    ///
238    /// # Returns
239    ///
240    /// an exit code. If you have handled your options and want
241    /// to exit the process, return a non-negative option, 0 for success,
242    /// and a positive value for failure. To continue, return -1 to let
243    /// the default option processing continue.
244    #[doc(alias = "handle-local-options")]
245    fn connect_handle_local_options<
246        F: Fn(&Self, &glib::VariantDict) -> ControlFlow<ExitCode> + 'static,
247    >(
248        &self,
249        f: F,
250    ) -> SignalHandlerId {
251        unsafe extern "C" fn handle_local_options_trampoline<
252            P: IsA<Application>,
253            F: Fn(&P, &glib::VariantDict) -> ControlFlow<ExitCode> + 'static,
254        >(
255            this: *mut ffi::GApplication,
256            options: *mut glib::ffi::GVariantDict,
257            f: glib::ffi::gpointer,
258        ) -> std::ffi::c_int {
259            let f: &F = &*(f as *const F);
260            f(
261                Application::from_glib_borrow(this).unsafe_cast_ref(),
262                &from_glib_borrow(options),
263            )
264            .break_value()
265            .map(i32::from)
266            .unwrap_or(-1)
267        }
268        unsafe {
269            let f: Box_<F> = Box_::new(f);
270            connect_raw(
271                self.as_ptr() as *mut _,
272                c"handle-local-options".as_ptr() as *const _,
273                Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>(
274                    handle_local_options_trampoline::<Self, F> as *const (),
275                )),
276                Box_::into_raw(f),
277            )
278        }
279    }
280
281    /// Increases the use count of @self.
282    ///
283    /// Use this function to indicate that the application has a reason to
284    /// continue to run.  For example, g_application_hold() is called by GTK
285    /// when a toplevel window is on the screen.
286    ///
287    /// To cancel the hold, call g_application_release().
288    #[doc(alias = "g_application_hold")]
289    fn hold(&self) -> ApplicationHoldGuard {
290        unsafe {
291            ffi::g_application_hold(self.as_ref().to_glib_none().0);
292        }
293        ApplicationHoldGuard(self.as_ref().downgrade())
294    }
295
296    /// Increases the busy count of @self.
297    ///
298    /// Use this function to indicate that the application is busy, for instance
299    /// while a long running operation is pending.
300    ///
301    /// The busy state will be exposed to other processes, so a session shell will
302    /// use that information to indicate the state to the user (e.g. with a
303    /// spinner).
304    ///
305    /// To cancel the busy indication, use g_application_unmark_busy().
306    ///
307    /// The application must be registered before calling this function.
308    #[doc(alias = "g_application_mark_busy")]
309    fn mark_busy(&self) -> ApplicationBusyGuard {
310        unsafe {
311            ffi::g_application_mark_busy(self.as_ref().to_glib_none().0);
312        }
313        ApplicationBusyGuard(self.as_ref().downgrade())
314    }
315}
316
317impl<O: IsA<Application>> ApplicationExtManual for O {}
318
319#[derive(Debug)]
320#[must_use = "if unused the Application will immediately be released"]
321pub struct ApplicationHoldGuard(glib::WeakRef<Application>);
322
323impl Drop for ApplicationHoldGuard {
324    #[inline]
325    fn drop(&mut self) {
326        if let Some(application) = self.0.upgrade() {
327            unsafe {
328                ffi::g_application_release(application.to_glib_none().0);
329            }
330        }
331    }
332}
333
334#[derive(Debug)]
335#[must_use = "if unused the Application will immediately be unmarked busy"]
336pub struct ApplicationBusyGuard(glib::WeakRef<Application>);
337
338impl Drop for ApplicationBusyGuard {
339    #[inline]
340    fn drop(&mut self) {
341        if let Some(application) = self.0.upgrade() {
342            unsafe {
343                ffi::g_application_unmark_busy(application.to_glib_none().0);
344            }
345        }
346    }
347}