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    ExitCode, GString,
7    prelude::*,
8    signal::{SignalHandlerId, connect_raw},
9    translate::*,
10};
11
12use crate::{Application, ApplicationCommandLine, File, ffi};
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            unsafe {
130                let f: &F = &*(f as *const F);
131                let files: Vec<File> =
132                    FromGlibContainer::from_glib_none_num(files, n_files as usize);
133                f(
134                    Application::from_glib_borrow(this).unsafe_cast_ref(),
135                    &files,
136                    &GString::from_glib_borrow(hint),
137                )
138            }
139        }
140        unsafe {
141            let f: Box_<F> = Box_::new(f);
142            connect_raw(
143                self.as_ptr() as *mut _,
144                b"open\0".as_ptr() as *const _,
145                Some(transmute::<*const (), unsafe extern "C" fn()>(
146                    open_trampoline::<Self, F> as *const (),
147                )),
148                Box_::into_raw(f),
149            )
150        }
151    }
152
153    /// The ::command-line signal is emitted on the primary instance when
154    /// a commandline is not handled locally. See g_application_run() and
155    /// the #GApplicationCommandLine documentation for more information.
156    /// ## `command_line`
157    /// a #GApplicationCommandLine representing the
158    ///     passed commandline
159    ///
160    /// # Returns
161    ///
162    /// An integer that is set as the exit status for the calling
163    ///   process. See g_application_command_line_set_exit_status().
164    #[doc(alias = "command-line")]
165    fn connect_command_line<F: Fn(&Self, &ApplicationCommandLine) -> ExitCode + 'static>(
166        &self,
167        f: F,
168    ) -> SignalHandlerId {
169        unsafe extern "C" fn command_line_trampoline<
170            P: IsA<Application>,
171            F: Fn(&P, &ApplicationCommandLine) -> ExitCode + 'static,
172        >(
173            this: *mut ffi::GApplication,
174            command_line: *mut ffi::GApplicationCommandLine,
175            f: glib::ffi::gpointer,
176        ) -> std::ffi::c_int {
177            unsafe {
178                let f: &F = &*(f as *const F);
179                f(
180                    Application::from_glib_borrow(this).unsafe_cast_ref(),
181                    &from_glib_borrow(command_line),
182                )
183                .into()
184            }
185        }
186        unsafe {
187            let f: Box_<F> = Box_::new(f);
188            connect_raw(
189                self.as_ptr() as *mut _,
190                c"command-line".as_ptr() as *const _,
191                Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>(
192                    command_line_trampoline::<Self, F> as *const (),
193                )),
194                Box_::into_raw(f),
195            )
196        }
197    }
198
199    /// The ::handle-local-options signal is emitted on the local instance
200    /// after the parsing of the commandline options has occurred.
201    ///
202    /// You can add options to be recognised during commandline option
203    /// parsing using g_application_add_main_option_entries() and
204    /// g_application_add_option_group().
205    ///
206    /// Signal handlers can inspect @options (along with values pointed to
207    /// from the @arg_data of an installed #GOptionEntrys) in order to
208    /// decide to perform certain actions, including direct local handling
209    /// (which may be useful for options like --version).
210    ///
211    /// In the event that the application is marked
212    /// [`ApplicationFlags::HANDLES_COMMAND_LINE`][crate::ApplicationFlags::HANDLES_COMMAND_LINE] the "normal processing" will
213    /// send the @options dictionary to the primary instance where it can be
214    /// read with g_application_command_line_get_options_dict().  The signal
215    /// handler can modify the dictionary before returning, and the
216    /// modified dictionary will be sent.
217    ///
218    /// In the event that [`ApplicationFlags::HANDLES_COMMAND_LINE`][crate::ApplicationFlags::HANDLES_COMMAND_LINE] is not set,
219    /// "normal processing" will treat the remaining uncollected command
220    /// line arguments as filenames or URIs.  If there are no arguments,
221    /// the application is activated by g_application_activate().  One or
222    /// more arguments results in a call to g_application_open().
223    ///
224    /// If you want to handle the local commandline arguments for yourself
225    /// by converting them to calls to g_application_open() or
226    /// g_action_group_activate_action() then you must be sure to register
227    /// the application first.  You should probably not call
228    /// g_application_activate() for yourself, however: just return -1 and
229    /// allow the default handler to do it for you.  This will ensure that
230    /// the `--gapplication-service` switch works properly (i.e. no activation
231    /// in that case).
232    ///
233    /// Note that this signal is emitted from the default implementation of
234    /// local_command_line().  If you override that function and don't
235    /// chain up then this signal will never be emitted.
236    ///
237    /// You can override local_command_line() if you need more powerful
238    /// capabilities than what is provided here, but this should not
239    /// normally be required.
240    /// ## `options`
241    /// the options dictionary
242    ///
243    /// # Returns
244    ///
245    /// an exit code. If you have handled your options and want
246    /// to exit the process, return a non-negative option, 0 for success,
247    /// and a positive value for failure. To continue, return -1 to let
248    /// the default option processing continue.
249    #[doc(alias = "handle-local-options")]
250    fn connect_handle_local_options<
251        F: Fn(&Self, &glib::VariantDict) -> ControlFlow<ExitCode> + 'static,
252    >(
253        &self,
254        f: F,
255    ) -> SignalHandlerId {
256        unsafe extern "C" fn handle_local_options_trampoline<
257            P: IsA<Application>,
258            F: Fn(&P, &glib::VariantDict) -> ControlFlow<ExitCode> + 'static,
259        >(
260            this: *mut ffi::GApplication,
261            options: *mut glib::ffi::GVariantDict,
262            f: glib::ffi::gpointer,
263        ) -> std::ffi::c_int {
264            unsafe {
265                let f: &F = &*(f as *const F);
266                f(
267                    Application::from_glib_borrow(this).unsafe_cast_ref(),
268                    &from_glib_borrow(options),
269                )
270                .break_value()
271                .map(i32::from)
272                .unwrap_or(-1)
273            }
274        }
275        unsafe {
276            let f: Box_<F> = Box_::new(f);
277            connect_raw(
278                self.as_ptr() as *mut _,
279                c"handle-local-options".as_ptr() as *const _,
280                Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>(
281                    handle_local_options_trampoline::<Self, F> as *const (),
282                )),
283                Box_::into_raw(f),
284            )
285        }
286    }
287
288    /// Increases the use count of @self.
289    ///
290    /// Use this function to indicate that the application has a reason to
291    /// continue to run.  For example, g_application_hold() is called by GTK
292    /// when a toplevel window is on the screen.
293    ///
294    /// To cancel the hold, call g_application_release().
295    #[doc(alias = "g_application_hold")]
296    fn hold(&self) -> ApplicationHoldGuard {
297        unsafe {
298            ffi::g_application_hold(self.as_ref().to_glib_none().0);
299        }
300        ApplicationHoldGuard(self.as_ref().downgrade())
301    }
302
303    /// Increases the busy count of @self.
304    ///
305    /// Use this function to indicate that the application is busy, for instance
306    /// while a long running operation is pending.
307    ///
308    /// The busy state will be exposed to other processes, so a session shell will
309    /// use that information to indicate the state to the user (e.g. with a
310    /// spinner).
311    ///
312    /// To cancel the busy indication, use g_application_unmark_busy().
313    ///
314    /// The application must be registered before calling this function.
315    #[doc(alias = "g_application_mark_busy")]
316    fn mark_busy(&self) -> ApplicationBusyGuard {
317        unsafe {
318            ffi::g_application_mark_busy(self.as_ref().to_glib_none().0);
319        }
320        ApplicationBusyGuard(self.as_ref().downgrade())
321    }
322}
323
324impl<O: IsA<Application>> ApplicationExtManual for O {}
325
326#[derive(Debug)]
327#[must_use = "if unused the Application will immediately be released"]
328pub struct ApplicationHoldGuard(glib::WeakRef<Application>);
329
330impl Drop for ApplicationHoldGuard {
331    #[inline]
332    fn drop(&mut self) {
333        if let Some(application) = self.0.upgrade() {
334            unsafe {
335                ffi::g_application_release(application.to_glib_none().0);
336            }
337        }
338    }
339}
340
341#[derive(Debug)]
342#[must_use = "if unused the Application will immediately be unmarked busy"]
343pub struct ApplicationBusyGuard(glib::WeakRef<Application>);
344
345impl Drop for ApplicationBusyGuard {
346    #[inline]
347    fn drop(&mut self) {
348        if let Some(application) = self.0.upgrade() {
349            unsafe {
350                ffi::g_application_unmark_busy(application.to_glib_none().0);
351            }
352        }
353    }
354}