1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
// Take a look at the license at the top of the repository in the LICENSE file.

use std::{boxed::Box as Box_, mem::transmute};

use glib::{
    prelude::*,
    signal::{connect_raw, SignalHandlerId},
    translate::*,
    ExitCode, GString,
};

use crate::{Application, File};

pub trait ApplicationExtManual {
    /// Runs the application.
    ///
    /// This function is intended to be run from `main()` and its return value
    /// is intended to be returned by `main()`. Although you are expected to pass
    /// the `argc`, `argv` parameters from `main()` to this function, it is possible
    /// to pass [`None`] if `argv` is not available or commandline handling is not
    /// required. Note that on Windows, `argc` and `argv` are ignored, and
    /// `g_win32_get_command_line()` is called internally (for proper support
    /// of Unicode commandline arguments).
    ///
    /// [`Application`][crate::Application] will attempt to parse the commandline arguments. You
    /// can add commandline flags to the list of recognised options by way of
    /// `g_application_add_main_option_entries()`. After this, the
    /// [`handle-local-options`][struct@crate::Application#handle-local-options] signal is emitted, from which the
    /// application can inspect the values of its `GOptionEntrys`.
    ///
    /// [`handle-local-options`][struct@crate::Application#handle-local-options] is a good place to handle options
    /// such as `--version`, where an immediate reply from the local process is
    /// desired (instead of communicating with an already-running instance).
    /// A [`handle-local-options`][struct@crate::Application#handle-local-options] handler can stop further processing
    /// by returning a non-negative value, which then becomes the exit status of
    /// the process.
    ///
    /// What happens next depends on the flags: if
    /// [`ApplicationFlags::HANDLES_COMMAND_LINE`][crate::ApplicationFlags::HANDLES_COMMAND_LINE] was specified then the remaining
    /// commandline arguments are sent to the primary instance, where a
    /// [`command-line`][struct@crate::Application#command-line] signal is emitted. Otherwise, the
    /// remaining commandline arguments are assumed to be a list of files.
    /// If there are no files listed, the application is activated via the
    /// [`activate`][struct@crate::Application#activate] signal. If there are one or more files, and
    /// [`ApplicationFlags::HANDLES_OPEN`][crate::ApplicationFlags::HANDLES_OPEN] was specified then the files are opened
    /// via the [`open`][struct@crate::Application#open] signal.
    ///
    /// If you are interested in doing more complicated local handling of the
    /// commandline then you should implement your own [`Application`][crate::Application] subclass
    /// and override `local_command_line()`. In this case, you most likely want
    /// to return [`true`] from your `local_command_line()` implementation to
    /// suppress the default handling. See
    /// [gapplication-example-cmdline2.c][https://gitlab.gnome.org/GNOME/glib/-/blob/HEAD/gio/tests/gapplication-example-cmdline2.c]
    /// for an example.
    ///
    /// If, after the above is done, the use count of the application is zero
    /// then the exit status is returned immediately. If the use count is
    /// non-zero then the default main context is iterated until the use count
    /// falls to zero, at which point 0 is returned.
    ///
    /// If the [`ApplicationFlags::IS_SERVICE`][crate::ApplicationFlags::IS_SERVICE] flag is set, then the service will
    /// run for as much as 10 seconds with a use count of zero while waiting
    /// for the message that caused the activation to arrive. After that,
    /// if the use count falls to zero the application will exit immediately,
    /// except in the case that [`ApplicationExt::set_inactivity_timeout()`][crate::prelude::ApplicationExt::set_inactivity_timeout()] is in
    /// use.
    ///
    /// This function sets the prgname (`g_set_prgname()`), if not already set,
    /// to the basename of argv[0].
    ///
    /// Much like `g_main_loop_run()`, this function will acquire the main context
    /// for the duration that the application is running.
    ///
    /// Since 2.40, applications that are not explicitly flagged as services
    /// or launchers (ie: neither [`ApplicationFlags::IS_SERVICE`][crate::ApplicationFlags::IS_SERVICE] or
    /// [`ApplicationFlags::IS_LAUNCHER`][crate::ApplicationFlags::IS_LAUNCHER] are given as flags) will check (from the
    /// default handler for local_command_line) if "--gapplication-service"
    /// was given in the command line. If this flag is present then normal
    /// commandline processing is interrupted and the
    /// [`ApplicationFlags::IS_SERVICE`][crate::ApplicationFlags::IS_SERVICE] flag is set. This provides a "compromise"
    /// solution whereby running an application directly from the commandline
    /// will invoke it in the normal way (which can be useful for debugging)
    /// while still allowing applications to be D-Bus activated in service
    /// mode. The D-Bus service file should invoke the executable with
    /// "--gapplication-service" as the sole commandline argument. This
    /// approach is suitable for use by most graphical applications but
    /// should not be used from applications like editors that need precise
    /// control over when processes invoked via the commandline will exit and
    /// what their exit status will be.
    /// ## `argv`
    ///
    ///  the argv from `main()`, or [`None`]
    ///
    /// # Returns
    ///
    /// the exit status
    #[doc(alias = "g_application_run")]
    fn run(&self) -> ExitCode;
    #[doc(alias = "g_application_run")]
    fn run_with_args<S: AsRef<str>>(&self, args: &[S]) -> ExitCode;
    /// The ::open signal is emitted on the primary instance when there are
    /// files to open. See [`ApplicationExt::open()`][crate::prelude::ApplicationExt::open()] for more information.
    /// ## `files`
    /// an array of `GFiles`
    /// ## `hint`
    /// a hint provided by the calling instance
    fn connect_open<F: Fn(&Self, &[File], &str) + 'static>(&self, f: F) -> SignalHandlerId;

    /// Increases the use count of `self`.
    ///
    /// Use this function to indicate that the application has a reason to
    /// continue to run. For example, [`ApplicationExtManual::hold()`][crate::prelude::ApplicationExtManual::hold()] is called by GTK+
    /// when a toplevel window is on the screen.
    ///
    /// To cancel the hold, call [`ApplicationExtManual::release()`][crate::prelude::ApplicationExtManual::release()].
    #[doc(alias = "g_application_hold")]
    fn hold(&self) -> ApplicationHoldGuard;

    /// Increases the busy count of `self`.
    ///
    /// Use this function to indicate that the application is busy, for instance
    /// while a long running operation is pending.
    ///
    /// The busy state will be exposed to other processes, so a session shell will
    /// use that information to indicate the state to the user (e.g. with a
    /// spinner).
    ///
    /// To cancel the busy indication, use [`ApplicationExtManual::unmark_busy()`][crate::prelude::ApplicationExtManual::unmark_busy()].
    ///
    /// The application must be registered before calling this function.
    #[doc(alias = "g_application_mark_busy")]
    fn mark_busy(&self) -> ApplicationBusyGuard;
}

impl<O: IsA<Application>> ApplicationExtManual for O {
    fn run(&self) -> ExitCode {
        self.run_with_args(&std::env::args().collect::<Vec<_>>())
    }

    fn run_with_args<S: AsRef<str>>(&self, args: &[S]) -> ExitCode {
        let argv: Vec<&str> = args.iter().map(|a| a.as_ref()).collect();
        let argc = argv.len() as i32;
        let exit_code = unsafe {
            ffi::g_application_run(self.as_ref().to_glib_none().0, argc, argv.to_glib_none().0)
        };
        ExitCode::from(exit_code)
    }

    fn connect_open<F: Fn(&Self, &[File], &str) + 'static>(&self, f: F) -> SignalHandlerId {
        unsafe extern "C" fn open_trampoline<P, F: Fn(&P, &[File], &str) + 'static>(
            this: *mut ffi::GApplication,
            files: *const *mut ffi::GFile,
            n_files: libc::c_int,
            hint: *mut libc::c_char,
            f: glib::ffi::gpointer,
        ) where
            P: IsA<Application>,
        {
            let f: &F = &*(f as *const F);
            let files: Vec<File> = FromGlibContainer::from_glib_none_num(files, n_files as usize);
            f(
                Application::from_glib_borrow(this).unsafe_cast_ref(),
                &files,
                &GString::from_glib_borrow(hint),
            )
        }
        unsafe {
            let f: Box_<F> = Box_::new(f);
            connect_raw(
                self.as_ptr() as *mut _,
                b"open\0".as_ptr() as *const _,
                Some(transmute::<_, unsafe extern "C" fn()>(
                    open_trampoline::<Self, F> as *const (),
                )),
                Box_::into_raw(f),
            )
        }
    }

    fn hold(&self) -> ApplicationHoldGuard {
        unsafe {
            ffi::g_application_hold(self.as_ref().to_glib_none().0);
        }
        ApplicationHoldGuard(self.as_ref().downgrade())
    }

    fn mark_busy(&self) -> ApplicationBusyGuard {
        unsafe {
            ffi::g_application_mark_busy(self.as_ref().to_glib_none().0);
        }
        ApplicationBusyGuard(self.as_ref().downgrade())
    }
}

#[derive(Debug)]
#[must_use = "if unused the Application will immediately be released"]
pub struct ApplicationHoldGuard(glib::WeakRef<Application>);

impl Drop for ApplicationHoldGuard {
    #[inline]
    fn drop(&mut self) {
        if let Some(application) = self.0.upgrade() {
            unsafe {
                ffi::g_application_release(application.to_glib_none().0);
            }
        }
    }
}

#[derive(Debug)]
#[must_use = "if unused the Application will immediately be unmarked busy"]
pub struct ApplicationBusyGuard(glib::WeakRef<Application>);

impl Drop for ApplicationBusyGuard {
    #[inline]
    fn drop(&mut self) {
        if let Some(application) = self.0.upgrade() {
            unsafe {
                ffi::g_application_unmark_busy(application.to_glib_none().0);
            }
        }
    }
}