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};
4
5use glib::{
6    prelude::*,
7    signal::{connect_raw, SignalHandlerId},
8    translate::*,
9    ExitCode, GString,
10};
11
12use crate::{ffi, Application, File};
13
14mod sealed {
15    pub trait Sealed {}
16    impl<T: super::IsA<super::Application>> Sealed for T {}
17}
18
19pub trait ApplicationExtManual: sealed::Sealed + IsA<Application> {
20    /// Runs the application.
21    ///
22    /// This function is intended to be run from main() and its return value
23    /// is intended to be returned by main(). Although you are expected to pass
24    /// the @argc, @argv parameters from main() to this function, it is possible
25    /// to pass [`None`] if @argv is not available or commandline handling is not
26    /// required.  Note that on Windows, @argc and @argv are ignored, and
27    /// g_win32_get_command_line() is called internally (for proper support
28    /// of Unicode commandline arguments).
29    ///
30    /// #GApplication will attempt to parse the commandline arguments.  You
31    /// can add commandline flags to the list of recognised options by way of
32    /// g_application_add_main_option_entries().  After this, the
33    /// #GApplication::handle-local-options signal is emitted, from which the
34    /// application can inspect the values of its #GOptionEntrys.
35    ///
36    /// #GApplication::handle-local-options is a good place to handle options
37    /// such as `--version`, where an immediate reply from the local process is
38    /// desired (instead of communicating with an already-running instance).
39    /// A #GApplication::handle-local-options handler can stop further processing
40    /// by returning a non-negative value, which then becomes the exit status of
41    /// the process.
42    ///
43    /// What happens next depends on the flags: if
44    /// [`ApplicationFlags::HANDLES_COMMAND_LINE`][crate::ApplicationFlags::HANDLES_COMMAND_LINE] was specified then the remaining
45    /// commandline arguments are sent to the primary instance, where a
46    /// #GApplication::command-line signal is emitted.  Otherwise, the
47    /// remaining commandline arguments are assumed to be a list of files.
48    /// If there are no files listed, the application is activated via the
49    /// #GApplication::activate signal.  If there are one or more files, and
50    /// [`ApplicationFlags::HANDLES_OPEN`][crate::ApplicationFlags::HANDLES_OPEN] was specified then the files are opened
51    /// via the #GApplication::open signal.
52    ///
53    /// If you are interested in doing more complicated local handling of the
54    /// commandline then you should implement your own #GApplication subclass
55    /// and override local_command_line(). In this case, you most likely want
56    /// to return [`true`] from your local_command_line() implementation to
57    /// suppress the default handling. See
58    /// [gapplication-example-cmdline2.c][https://gitlab.gnome.org/GNOME/glib/-/blob/HEAD/gio/tests/gapplication-example-cmdline2.c]
59    /// for an example.
60    ///
61    /// If, after the above is done, the use count of the application is zero
62    /// then the exit status is returned immediately.  If the use count is
63    /// non-zero then the default main context is iterated until the use count
64    /// falls to zero, at which point 0 is returned.
65    ///
66    /// If the [`ApplicationFlags::IS_SERVICE`][crate::ApplicationFlags::IS_SERVICE] flag is set, then the service will
67    /// run for as much as 10 seconds with a use count of zero while waiting
68    /// for the message that caused the activation to arrive.  After that,
69    /// if the use count falls to zero the application will exit immediately,
70    /// except in the case that g_application_set_inactivity_timeout() is in
71    /// use.
72    ///
73    /// This function sets the prgname (g_set_prgname()), if not already set,
74    /// to the basename of argv[0].
75    ///
76    /// Much like g_main_loop_run(), this function will acquire the main context
77    /// for the duration that the application is running.
78    ///
79    /// Since 2.40, applications that are not explicitly flagged as services
80    /// or launchers (ie: neither [`ApplicationFlags::IS_SERVICE`][crate::ApplicationFlags::IS_SERVICE] or
81    /// [`ApplicationFlags::IS_LAUNCHER`][crate::ApplicationFlags::IS_LAUNCHER] are given as flags) will check (from the
82    /// default handler for local_command_line) if "--gapplication-service"
83    /// was given in the command line.  If this flag is present then normal
84    /// commandline processing is interrupted and the
85    /// [`ApplicationFlags::IS_SERVICE`][crate::ApplicationFlags::IS_SERVICE] flag is set.  This provides a "compromise"
86    /// solution whereby running an application directly from the commandline
87    /// will invoke it in the normal way (which can be useful for debugging)
88    /// while still allowing applications to be D-Bus activated in service
89    /// mode.  The D-Bus service file should invoke the executable with
90    /// "--gapplication-service" as the sole commandline argument.  This
91    /// approach is suitable for use by most graphical applications but
92    /// should not be used from applications like editors that need precise
93    /// control over when processes invoked via the commandline will exit and
94    /// what their exit status will be.
95    /// ## `argv`
96    ///
97    ///     the argv from main(), or [`None`]
98    ///
99    /// # Returns
100    ///
101    /// the exit status
102    #[doc(alias = "g_application_run")]
103    fn run(&self) -> ExitCode {
104        self.run_with_args(&std::env::args().collect::<Vec<_>>())
105    }
106    #[doc(alias = "g_application_run")]
107    fn run_with_args<S: AsRef<str>>(&self, args: &[S]) -> ExitCode {
108        let argv: Vec<&str> = args.iter().map(|a| a.as_ref()).collect();
109        let argc = argv.len() as i32;
110        let exit_code = unsafe {
111            ffi::g_application_run(self.as_ref().to_glib_none().0, argc, argv.to_glib_none().0)
112        };
113        ExitCode::from(exit_code)
114    }
115    /// The ::open signal is emitted on the primary instance when there are
116    /// files to open. See g_application_open() for more information.
117    /// ## `files`
118    /// an array of #GFiles
119    /// ## `hint`
120    /// a hint provided by the calling instance
121    fn connect_open<F: Fn(&Self, &[File], &str) + 'static>(&self, f: F) -> SignalHandlerId {
122        unsafe extern "C" fn open_trampoline<P, F: Fn(&P, &[File], &str) + 'static>(
123            this: *mut ffi::GApplication,
124            files: *const *mut ffi::GFile,
125            n_files: libc::c_int,
126            hint: *mut libc::c_char,
127            f: glib::ffi::gpointer,
128        ) where
129            P: IsA<Application>,
130        {
131            let f: &F = &*(f as *const F);
132            let files: Vec<File> = 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        unsafe {
140            let f: Box_<F> = Box_::new(f);
141            connect_raw(
142                self.as_ptr() as *mut _,
143                b"open\0".as_ptr() as *const _,
144                Some(transmute::<*const (), unsafe extern "C" fn()>(
145                    open_trampoline::<Self, F> as *const (),
146                )),
147                Box_::into_raw(f),
148            )
149        }
150    }
151
152    /// Increases the use count of @self.
153    ///
154    /// Use this function to indicate that the application has a reason to
155    /// continue to run.  For example, g_application_hold() is called by GTK
156    /// when a toplevel window is on the screen.
157    ///
158    /// To cancel the hold, call g_application_release().
159    #[doc(alias = "g_application_hold")]
160    fn hold(&self) -> ApplicationHoldGuard {
161        unsafe {
162            ffi::g_application_hold(self.as_ref().to_glib_none().0);
163        }
164        ApplicationHoldGuard(self.as_ref().downgrade())
165    }
166
167    /// Increases the busy count of @self.
168    ///
169    /// Use this function to indicate that the application is busy, for instance
170    /// while a long running operation is pending.
171    ///
172    /// The busy state will be exposed to other processes, so a session shell will
173    /// use that information to indicate the state to the user (e.g. with a
174    /// spinner).
175    ///
176    /// To cancel the busy indication, use g_application_unmark_busy().
177    ///
178    /// The application must be registered before calling this function.
179    #[doc(alias = "g_application_mark_busy")]
180    fn mark_busy(&self) -> ApplicationBusyGuard {
181        unsafe {
182            ffi::g_application_mark_busy(self.as_ref().to_glib_none().0);
183        }
184        ApplicationBusyGuard(self.as_ref().downgrade())
185    }
186}
187
188impl<O: IsA<Application>> ApplicationExtManual for O {}
189
190#[derive(Debug)]
191#[must_use = "if unused the Application will immediately be released"]
192pub struct ApplicationHoldGuard(glib::WeakRef<Application>);
193
194impl Drop for ApplicationHoldGuard {
195    #[inline]
196    fn drop(&mut self) {
197        if let Some(application) = self.0.upgrade() {
198            unsafe {
199                ffi::g_application_release(application.to_glib_none().0);
200            }
201        }
202    }
203}
204
205#[derive(Debug)]
206#[must_use = "if unused the Application will immediately be unmarked busy"]
207pub struct ApplicationBusyGuard(glib::WeakRef<Application>);
208
209impl Drop for ApplicationBusyGuard {
210    #[inline]
211    fn drop(&mut self) {
212        if let Some(application) = self.0.upgrade() {
213            unsafe {
214                ffi::g_application_unmark_busy(application.to_glib_none().0);
215            }
216        }
217    }
218}