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}