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}