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}