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
14mod sealed {
15 pub trait Sealed {}
16 impl<T: super::IsA<super::Application>> Sealed for T {}
17}
18
19pub trait ApplicationExtManual: sealed::Sealed + IsA<Application> {
20 /// Runs the application.
21 ///
22 /// This function is intended to be run from main() and its return value
23 /// is intended to be returned by main(). Although you are expected to pass
24 /// the @argc, @argv parameters from main() to this function, it is possible
25 /// to pass [`None`] if @argv is not available or commandline handling is not
26 /// required. Note that on Windows, @argc and @argv are ignored, and
27 /// g_win32_get_command_line() is called internally (for proper support
28 /// of Unicode commandline arguments).
29 ///
30 /// #GApplication will attempt to parse the commandline arguments. You
31 /// can add commandline flags to the list of recognised options by way of
32 /// g_application_add_main_option_entries(). After this, the
33 /// #GApplication::handle-local-options signal is emitted, from which the
34 /// application can inspect the values of its #GOptionEntrys.
35 ///
36 /// #GApplication::handle-local-options is a good place to handle options
37 /// such as `--version`, where an immediate reply from the local process is
38 /// desired (instead of communicating with an already-running instance).
39 /// A #GApplication::handle-local-options handler can stop further processing
40 /// by returning a non-negative value, which then becomes the exit status of
41 /// the process.
42 ///
43 /// What happens next depends on the flags: if
44 /// [`ApplicationFlags::HANDLES_COMMAND_LINE`][crate::ApplicationFlags::HANDLES_COMMAND_LINE] was specified then the remaining
45 /// commandline arguments are sent to the primary instance, where a
46 /// #GApplication::command-line signal is emitted. Otherwise, the
47 /// remaining commandline arguments are assumed to be a list of files.
48 /// If there are no files listed, the application is activated via the
49 /// #GApplication::activate signal. If there are one or more files, and
50 /// [`ApplicationFlags::HANDLES_OPEN`][crate::ApplicationFlags::HANDLES_OPEN] was specified then the files are opened
51 /// via the #GApplication::open signal.
52 ///
53 /// If you are interested in doing more complicated local handling of the
54 /// commandline then you should implement your own #GApplication subclass
55 /// and override local_command_line(). In this case, you most likely want
56 /// to return [`true`] from your local_command_line() implementation to
57 /// suppress the default handling. See
58 /// [gapplication-example-cmdline2.c][https://gitlab.gnome.org/GNOME/glib/-/blob/HEAD/gio/tests/gapplication-example-cmdline2.c]
59 /// for an example.
60 ///
61 /// If, after the above is done, the use count of the application is zero
62 /// then the exit status is returned immediately. If the use count is
63 /// non-zero then the default main context is iterated until the use count
64 /// falls to zero, at which point 0 is returned.
65 ///
66 /// If the [`ApplicationFlags::IS_SERVICE`][crate::ApplicationFlags::IS_SERVICE] flag is set, then the service will
67 /// run for as much as 10 seconds with a use count of zero while waiting
68 /// for the message that caused the activation to arrive. After that,
69 /// if the use count falls to zero the application will exit immediately,
70 /// except in the case that g_application_set_inactivity_timeout() is in
71 /// use.
72 ///
73 /// This function sets the prgname (g_set_prgname()), if not already set,
74 /// to the basename of argv[0].
75 ///
76 /// Much like g_main_loop_run(), this function will acquire the main context
77 /// for the duration that the application is running.
78 ///
79 /// Since 2.40, applications that are not explicitly flagged as services
80 /// or launchers (ie: neither [`ApplicationFlags::IS_SERVICE`][crate::ApplicationFlags::IS_SERVICE] or
81 /// [`ApplicationFlags::IS_LAUNCHER`][crate::ApplicationFlags::IS_LAUNCHER] are given as flags) will check (from the
82 /// default handler for local_command_line) if "--gapplication-service"
83 /// was given in the command line. If this flag is present then normal
84 /// commandline processing is interrupted and the
85 /// [`ApplicationFlags::IS_SERVICE`][crate::ApplicationFlags::IS_SERVICE] flag is set. This provides a "compromise"
86 /// solution whereby running an application directly from the commandline
87 /// will invoke it in the normal way (which can be useful for debugging)
88 /// while still allowing applications to be D-Bus activated in service
89 /// mode. The D-Bus service file should invoke the executable with
90 /// "--gapplication-service" as the sole commandline argument. This
91 /// approach is suitable for use by most graphical applications but
92 /// should not be used from applications like editors that need precise
93 /// control over when processes invoked via the commandline will exit and
94 /// what their exit status will be.
95 /// ## `argv`
96 ///
97 /// the argv from main(), or [`None`]
98 ///
99 /// # Returns
100 ///
101 /// the exit status
102 #[doc(alias = "g_application_run")]
103 fn run(&self) -> ExitCode {
104 self.run_with_args(&std::env::args().collect::<Vec<_>>())
105 }
106 #[doc(alias = "g_application_run")]
107 fn run_with_args<S: AsRef<str>>(&self, args: &[S]) -> ExitCode {
108 let argv: Vec<&str> = args.iter().map(|a| a.as_ref()).collect();
109 let argc = argv.len() as i32;
110 let exit_code = unsafe {
111 ffi::g_application_run(self.as_ref().to_glib_none().0, argc, argv.to_glib_none().0)
112 };
113 ExitCode::from(exit_code)
114 }
115 /// The ::open signal is emitted on the primary instance when there are
116 /// files to open. See g_application_open() for more information.
117 /// ## `files`
118 /// an array of #GFiles
119 /// ## `hint`
120 /// a hint provided by the calling instance
121 fn connect_open<F: Fn(&Self, &[File], &str) + 'static>(&self, f: F) -> SignalHandlerId {
122 unsafe extern "C" fn open_trampoline<P, F: Fn(&P, &[File], &str) + 'static>(
123 this: *mut ffi::GApplication,
124 files: *const *mut ffi::GFile,
125 n_files: libc::c_int,
126 hint: *mut libc::c_char,
127 f: glib::ffi::gpointer,
128 ) where
129 P: IsA<Application>,
130 {
131 let f: &F = &*(f as *const F);
132 let files: Vec<File> = 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 unsafe {
140 let f: Box_<F> = Box_::new(f);
141 connect_raw(
142 self.as_ptr() as *mut _,
143 b"open\0".as_ptr() as *const _,
144 Some(transmute::<*const (), unsafe extern "C" fn()>(
145 open_trampoline::<Self, F> as *const (),
146 )),
147 Box_::into_raw(f),
148 )
149 }
150 }
151
152 /// Increases the use count of @self.
153 ///
154 /// Use this function to indicate that the application has a reason to
155 /// continue to run. For example, g_application_hold() is called by GTK
156 /// when a toplevel window is on the screen.
157 ///
158 /// To cancel the hold, call g_application_release().
159 #[doc(alias = "g_application_hold")]
160 fn hold(&self) -> ApplicationHoldGuard {
161 unsafe {
162 ffi::g_application_hold(self.as_ref().to_glib_none().0);
163 }
164 ApplicationHoldGuard(self.as_ref().downgrade())
165 }
166
167 /// Increases the busy count of @self.
168 ///
169 /// Use this function to indicate that the application is busy, for instance
170 /// while a long running operation is pending.
171 ///
172 /// The busy state will be exposed to other processes, so a session shell will
173 /// use that information to indicate the state to the user (e.g. with a
174 /// spinner).
175 ///
176 /// To cancel the busy indication, use g_application_unmark_busy().
177 ///
178 /// The application must be registered before calling this function.
179 #[doc(alias = "g_application_mark_busy")]
180 fn mark_busy(&self) -> ApplicationBusyGuard {
181 unsafe {
182 ffi::g_application_mark_busy(self.as_ref().to_glib_none().0);
183 }
184 ApplicationBusyGuard(self.as_ref().downgrade())
185 }
186}
187
188impl<O: IsA<Application>> ApplicationExtManual for O {}
189
190#[derive(Debug)]
191#[must_use = "if unused the Application will immediately be released"]
192pub struct ApplicationHoldGuard(glib::WeakRef<Application>);
193
194impl Drop for ApplicationHoldGuard {
195 #[inline]
196 fn drop(&mut self) {
197 if let Some(application) = self.0.upgrade() {
198 unsafe {
199 ffi::g_application_release(application.to_glib_none().0);
200 }
201 }
202 }
203}
204
205#[derive(Debug)]
206#[must_use = "if unused the Application will immediately be unmarked busy"]
207pub struct ApplicationBusyGuard(glib::WeakRef<Application>);
208
209impl Drop for ApplicationBusyGuard {
210 #[inline]
211 fn drop(&mut self) {
212 if let Some(application) = self.0.upgrade() {
213 unsafe {
214 ffi::g_application_unmark_busy(application.to_glib_none().0);
215 }
216 }
217 }
218}