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
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    #[doc(alias = "g_application_run")]
102    fn run_with_args<S: AsRef<str>>(&self, args: &[S]) -> ExitCode {
103        let argv: Vec<&str> = args.iter().map(|a| a.as_ref()).collect();
104        let argc = argv.len() as i32;
105        let exit_code = unsafe {
106            ffi::g_application_run(self.as_ref().to_glib_none().0, argc, argv.to_glib_none().0)
107        };
108        ExitCode::from(exit_code)
109    }
110    /// The ::open signal is emitted on the primary instance when there are
111    /// files to open. See g_application_open() for more information.
112    /// ## `files`
113    /// an array of #GFiles
114    /// ## `hint`
115    /// a hint provided by the calling instance
116    fn connect_open<F: Fn(&Self, &[File], &str) + 'static>(&self, f: F) -> SignalHandlerId {
117        unsafe extern "C" fn open_trampoline<P, F: Fn(&P, &[File], &str) + 'static>(
118            this: *mut ffi::GApplication,
119            files: *const *mut ffi::GFile,
120            n_files: libc::c_int,
121            hint: *mut libc::c_char,
122            f: glib::ffi::gpointer,
123        ) where
124            P: IsA<Application>,
125        {
126            let f: &F = &*(f as *const F);
127            let files: Vec<File> = FromGlibContainer::from_glib_none_num(files, n_files as usize);
128            f(
129                Application::from_glib_borrow(this).unsafe_cast_ref(),
130                &files,
131                &GString::from_glib_borrow(hint),
132            )
133        }
134        unsafe {
135            let f: Box_<F> = Box_::new(f);
136            connect_raw(
137                self.as_ptr() as *mut _,
138                b"open\0".as_ptr() as *const _,
139                Some(transmute::<*const (), unsafe extern "C" fn()>(
140                    open_trampoline::<Self, F> as *const (),
141                )),
142                Box_::into_raw(f),
143            )
144        }
145    }
146
147    /// Increases the use count of @self.
148    ///
149    /// Use this function to indicate that the application has a reason to
150    /// continue to run.  For example, g_application_hold() is called by GTK
151    /// when a toplevel window is on the screen.
152    ///
153    /// To cancel the hold, call g_application_release().
154    #[doc(alias = "g_application_hold")]
155    fn hold(&self) -> ApplicationHoldGuard {
156        unsafe {
157            ffi::g_application_hold(self.as_ref().to_glib_none().0);
158        }
159        ApplicationHoldGuard(self.as_ref().downgrade())
160    }
161
162    /// Increases the busy count of @self.
163    ///
164    /// Use this function to indicate that the application is busy, for instance
165    /// while a long running operation is pending.
166    ///
167    /// The busy state will be exposed to other processes, so a session shell will
168    /// use that information to indicate the state to the user (e.g. with a
169    /// spinner).
170    ///
171    /// To cancel the busy indication, use g_application_unmark_busy().
172    ///
173    /// The application must be registered before calling this function.
174    #[doc(alias = "g_application_mark_busy")]
175    fn mark_busy(&self) -> ApplicationBusyGuard {
176        unsafe {
177            ffi::g_application_mark_busy(self.as_ref().to_glib_none().0);
178        }
179        ApplicationBusyGuard(self.as_ref().downgrade())
180    }
181}
182
183impl<O: IsA<Application>> ApplicationExtManual for O {}
184
185#[derive(Debug)]
186#[must_use = "if unused the Application will immediately be released"]
187pub struct ApplicationHoldGuard(glib::WeakRef<Application>);
188
189impl Drop for ApplicationHoldGuard {
190    #[inline]
191    fn drop(&mut self) {
192        if let Some(application) = self.0.upgrade() {
193            unsafe {
194                ffi::g_application_release(application.to_glib_none().0);
195            }
196        }
197    }
198}
199
200#[derive(Debug)]
201#[must_use = "if unused the Application will immediately be unmarked busy"]
202pub struct ApplicationBusyGuard(glib::WeakRef<Application>);
203
204impl Drop for ApplicationBusyGuard {
205    #[inline]
206    fn drop(&mut self) {
207        if let Some(application) = self.0.upgrade() {
208            unsafe {
209                ffi::g_application_unmark_busy(application.to_glib_none().0);
210            }
211        }
212    }
213}