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 prelude::*,
7 signal::{connect_raw, SignalHandlerId},
8 translate::*,
9 ExitCode, GString,
10};
11
12use crate::{ffi, Application, ApplicationCommandLine, 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
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 let f: &F = &*(f as *const F);
130 let files: Vec<File> = FromGlibContainer::from_glib_none_num(files, n_files as usize);
131 f(
132 Application::from_glib_borrow(this).unsafe_cast_ref(),
133 &files,
134 &GString::from_glib_borrow(hint),
135 )
136 }
137 unsafe {
138 let f: Box_<F> = Box_::new(f);
139 connect_raw(
140 self.as_ptr() as *mut _,
141 b"open\0".as_ptr() as *const _,
142 Some(transmute::<*const (), unsafe extern "C" fn()>(
143 open_trampoline::<Self, F> as *const (),
144 )),
145 Box_::into_raw(f),
146 )
147 }
148 }
149
150 /// The ::command-line signal is emitted on the primary instance when
151 /// a commandline is not handled locally. See g_application_run() and
152 /// the #GApplicationCommandLine documentation for more information.
153 /// ## `command_line`
154 /// a #GApplicationCommandLine representing the
155 /// passed commandline
156 ///
157 /// # Returns
158 ///
159 /// An integer that is set as the exit status for the calling
160 /// process. See g_application_command_line_set_exit_status().
161 #[doc(alias = "command-line")]
162 fn connect_command_line<F: Fn(&Self, &ApplicationCommandLine) -> ExitCode + 'static>(
163 &self,
164 f: F,
165 ) -> SignalHandlerId {
166 unsafe extern "C" fn command_line_trampoline<
167 P: IsA<Application>,
168 F: Fn(&P, &ApplicationCommandLine) -> ExitCode + 'static,
169 >(
170 this: *mut ffi::GApplication,
171 command_line: *mut ffi::GApplicationCommandLine,
172 f: glib::ffi::gpointer,
173 ) -> std::ffi::c_int {
174 let f: &F = &*(f as *const F);
175 f(
176 Application::from_glib_borrow(this).unsafe_cast_ref(),
177 &from_glib_borrow(command_line),
178 )
179 .into()
180 }
181 unsafe {
182 let f: Box_<F> = Box_::new(f);
183 connect_raw(
184 self.as_ptr() as *mut _,
185 c"command-line".as_ptr() as *const _,
186 Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>(
187 command_line_trampoline::<Self, F> as *const (),
188 )),
189 Box_::into_raw(f),
190 )
191 }
192 }
193
194 /// The ::handle-local-options signal is emitted on the local instance
195 /// after the parsing of the commandline options has occurred.
196 ///
197 /// You can add options to be recognised during commandline option
198 /// parsing using g_application_add_main_option_entries() and
199 /// g_application_add_option_group().
200 ///
201 /// Signal handlers can inspect @options (along with values pointed to
202 /// from the @arg_data of an installed #GOptionEntrys) in order to
203 /// decide to perform certain actions, including direct local handling
204 /// (which may be useful for options like --version).
205 ///
206 /// In the event that the application is marked
207 /// [`ApplicationFlags::HANDLES_COMMAND_LINE`][crate::ApplicationFlags::HANDLES_COMMAND_LINE] the "normal processing" will
208 /// send the @options dictionary to the primary instance where it can be
209 /// read with g_application_command_line_get_options_dict(). The signal
210 /// handler can modify the dictionary before returning, and the
211 /// modified dictionary will be sent.
212 ///
213 /// In the event that [`ApplicationFlags::HANDLES_COMMAND_LINE`][crate::ApplicationFlags::HANDLES_COMMAND_LINE] is not set,
214 /// "normal processing" will treat the remaining uncollected command
215 /// line arguments as filenames or URIs. If there are no arguments,
216 /// the application is activated by g_application_activate(). One or
217 /// more arguments results in a call to g_application_open().
218 ///
219 /// If you want to handle the local commandline arguments for yourself
220 /// by converting them to calls to g_application_open() or
221 /// g_action_group_activate_action() then you must be sure to register
222 /// the application first. You should probably not call
223 /// g_application_activate() for yourself, however: just return -1 and
224 /// allow the default handler to do it for you. This will ensure that
225 /// the `--gapplication-service` switch works properly (i.e. no activation
226 /// in that case).
227 ///
228 /// Note that this signal is emitted from the default implementation of
229 /// local_command_line(). If you override that function and don't
230 /// chain up then this signal will never be emitted.
231 ///
232 /// You can override local_command_line() if you need more powerful
233 /// capabilities than what is provided here, but this should not
234 /// normally be required.
235 /// ## `options`
236 /// the options dictionary
237 ///
238 /// # Returns
239 ///
240 /// an exit code. If you have handled your options and want
241 /// to exit the process, return a non-negative option, 0 for success,
242 /// and a positive value for failure. To continue, return -1 to let
243 /// the default option processing continue.
244 #[doc(alias = "handle-local-options")]
245 fn connect_handle_local_options<
246 F: Fn(&Self, &glib::VariantDict) -> ControlFlow<ExitCode> + 'static,
247 >(
248 &self,
249 f: F,
250 ) -> SignalHandlerId {
251 unsafe extern "C" fn handle_local_options_trampoline<
252 P: IsA<Application>,
253 F: Fn(&P, &glib::VariantDict) -> ControlFlow<ExitCode> + 'static,
254 >(
255 this: *mut ffi::GApplication,
256 options: *mut glib::ffi::GVariantDict,
257 f: glib::ffi::gpointer,
258 ) -> std::ffi::c_int {
259 let f: &F = &*(f as *const F);
260 f(
261 Application::from_glib_borrow(this).unsafe_cast_ref(),
262 &from_glib_borrow(options),
263 )
264 .break_value()
265 .map(i32::from)
266 .unwrap_or(-1)
267 }
268 unsafe {
269 let f: Box_<F> = Box_::new(f);
270 connect_raw(
271 self.as_ptr() as *mut _,
272 c"handle-local-options".as_ptr() as *const _,
273 Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>(
274 handle_local_options_trampoline::<Self, F> as *const (),
275 )),
276 Box_::into_raw(f),
277 )
278 }
279 }
280
281 /// Increases the use count of @self.
282 ///
283 /// Use this function to indicate that the application has a reason to
284 /// continue to run. For example, g_application_hold() is called by GTK
285 /// when a toplevel window is on the screen.
286 ///
287 /// To cancel the hold, call g_application_release().
288 #[doc(alias = "g_application_hold")]
289 fn hold(&self) -> ApplicationHoldGuard {
290 unsafe {
291 ffi::g_application_hold(self.as_ref().to_glib_none().0);
292 }
293 ApplicationHoldGuard(self.as_ref().downgrade())
294 }
295
296 /// Increases the busy count of @self.
297 ///
298 /// Use this function to indicate that the application is busy, for instance
299 /// while a long running operation is pending.
300 ///
301 /// The busy state will be exposed to other processes, so a session shell will
302 /// use that information to indicate the state to the user (e.g. with a
303 /// spinner).
304 ///
305 /// To cancel the busy indication, use g_application_unmark_busy().
306 ///
307 /// The application must be registered before calling this function.
308 #[doc(alias = "g_application_mark_busy")]
309 fn mark_busy(&self) -> ApplicationBusyGuard {
310 unsafe {
311 ffi::g_application_mark_busy(self.as_ref().to_glib_none().0);
312 }
313 ApplicationBusyGuard(self.as_ref().downgrade())
314 }
315}
316
317impl<O: IsA<Application>> ApplicationExtManual for O {}
318
319#[derive(Debug)]
320#[must_use = "if unused the Application will immediately be released"]
321pub struct ApplicationHoldGuard(glib::WeakRef<Application>);
322
323impl Drop for ApplicationHoldGuard {
324 #[inline]
325 fn drop(&mut self) {
326 if let Some(application) = self.0.upgrade() {
327 unsafe {
328 ffi::g_application_release(application.to_glib_none().0);
329 }
330 }
331 }
332}
333
334#[derive(Debug)]
335#[must_use = "if unused the Application will immediately be unmarked busy"]
336pub struct ApplicationBusyGuard(glib::WeakRef<Application>);
337
338impl Drop for ApplicationBusyGuard {
339 #[inline]
340 fn drop(&mut self) {
341 if let Some(application) = self.0.upgrade() {
342 unsafe {
343 ffi::g_application_unmark_busy(application.to_glib_none().0);
344 }
345 }
346 }
347}