Skip to main content

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