1use std::{ffi::OsString, fmt, ops::Deref, ptr};
4
5use glib::{
6 prelude::*, subclass::prelude::*, translate::*, Error, ExitCode, Propagation, VariantDict,
7};
8use libc::{c_char, c_int, c_void};
9
10use crate::{ffi, Application, DBusConnection};
11
12pub struct ArgumentList {
13 pub(crate) ptr: *mut *mut *mut c_char,
14 items: Vec<OsString>,
15}
16
17impl ArgumentList {
18 pub(crate) fn new(arguments: *mut *mut *mut c_char) -> Self {
19 Self {
20 ptr: arguments,
21 items: unsafe { FromGlibPtrContainer::from_glib_none(ptr::read(arguments)) },
22 }
23 }
24
25 pub(crate) fn refresh(&mut self) {
26 self.items = unsafe { FromGlibPtrContainer::from_glib_none(ptr::read(self.ptr)) };
27 }
28
29 pub fn remove(&mut self, idx: usize) {
31 unsafe {
32 let n_args = glib::ffi::g_strv_length(*self.ptr) as usize;
33 assert_eq!(n_args, self.items.len());
34 assert!(idx < n_args);
35
36 self.items.remove(idx);
37
38 glib::ffi::g_free(*(*self.ptr).add(idx) as *mut c_void);
39
40 for i in idx..n_args - 1 {
41 ptr::write((*self.ptr).add(i), *(*self.ptr).add(i + 1))
42 }
43 ptr::write((*self.ptr).add(n_args - 1), ptr::null_mut());
44 }
45 }
46}
47
48impl Deref for ArgumentList {
49 type Target = [OsString];
50
51 #[inline]
52 fn deref(&self) -> &Self::Target {
53 self.items.as_slice()
54 }
55}
56
57impl fmt::Debug for ArgumentList {
58 fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
59 self.items.fmt(formatter)
60 }
61}
62
63impl From<ArgumentList> for Vec<OsString> {
64 fn from(list: ArgumentList) -> Vec<OsString> {
65 list.items
66 }
67}
68
69pub trait ApplicationImpl: ObjectImpl + ApplicationImplExt {
70 fn activate(&self) {
77 self.parent_activate()
78 }
79
80 fn after_emit(&self, platform_data: &glib::Variant) {
84 self.parent_after_emit(platform_data)
85 }
86
87 fn before_emit(&self, platform_data: &glib::Variant) {
91 self.parent_before_emit(platform_data)
92 }
93
94 fn command_line(&self, command_line: &crate::ApplicationCommandLine) -> ExitCode {
97 self.parent_command_line(command_line)
98 }
99
100 fn local_command_line(&self, arguments: &mut ArgumentList) -> Option<ExitCode> {
120 self.parent_local_command_line(arguments)
121 }
122
123 fn open(&self, files: &[crate::File], hint: &str) {
142 self.parent_open(files, hint)
143 }
144
145 fn quit_mainloop(&self) {
149 self.parent_quit_mainloop()
150 }
151
152 fn run_mainloop(&self) {
157 self.parent_run_mainloop()
158 }
159
160 fn shutdown(&self) {
163 self.parent_shutdown()
164 }
165
166 fn startup(&self) {
168 self.parent_startup()
169 }
170
171 fn handle_local_options(&self, options: &VariantDict) -> ExitCode {
174 self.parent_handle_local_options(options)
175 }
176
177 fn dbus_register(&self, connection: &DBusConnection, object_path: &str) -> Result<(), Error> {
185 self.parent_dbus_register(connection, object_path)
186 }
187
188 fn dbus_unregister(&self, connection: &DBusConnection, object_path: &str) {
192 self.parent_dbus_unregister(connection, object_path)
193 }
194
195 fn name_lost(&self) -> Propagation {
197 self.parent_name_lost()
198 }
199}
200
201mod sealed {
202 pub trait Sealed {}
203 impl<T: super::ApplicationImplExt> Sealed for T {}
204}
205
206pub trait ApplicationImplExt: sealed::Sealed + ObjectSubclass {
207 fn parent_activate(&self) {
208 unsafe {
209 let data = Self::type_data();
210 let parent_class = data.as_ref().parent_class() as *mut ffi::GApplicationClass;
211 let f = (*parent_class)
212 .activate
213 .expect("No parent class implementation for \"activate\"");
214 f(self.obj().unsafe_cast_ref::<Application>().to_glib_none().0)
215 }
216 }
217
218 fn parent_after_emit(&self, platform_data: &glib::Variant) {
219 unsafe {
220 let data = Self::type_data();
221 let parent_class = data.as_ref().parent_class() as *mut ffi::GApplicationClass;
222 let f = (*parent_class)
223 .after_emit
224 .expect("No parent class implementation for \"after_emit\"");
225 f(
226 self.obj().unsafe_cast_ref::<Application>().to_glib_none().0,
227 platform_data.to_glib_none().0,
228 )
229 }
230 }
231
232 fn parent_before_emit(&self, platform_data: &glib::Variant) {
233 unsafe {
234 let data = Self::type_data();
235 let parent_class = data.as_ref().parent_class() as *mut ffi::GApplicationClass;
236 let f = (*parent_class)
237 .before_emit
238 .expect("No parent class implementation for \"before_emit\"");
239 f(
240 self.obj().unsafe_cast_ref::<Application>().to_glib_none().0,
241 platform_data.to_glib_none().0,
242 )
243 }
244 }
245
246 fn parent_command_line(&self, command_line: &crate::ApplicationCommandLine) -> ExitCode {
247 unsafe {
248 let data = Self::type_data();
249 let parent_class = data.as_ref().parent_class() as *mut ffi::GApplicationClass;
250 let f = (*parent_class)
251 .command_line
252 .expect("No parent class implementation for \"command_line\"");
253 f(
254 self.obj().unsafe_cast_ref::<Application>().to_glib_none().0,
255 command_line.to_glib_none().0,
256 )
257 .into()
258 }
259 }
260
261 fn parent_local_command_line(&self, arguments: &mut ArgumentList) -> Option<ExitCode> {
262 unsafe {
263 let data = Self::type_data();
264 let parent_class = data.as_ref().parent_class() as *mut ffi::GApplicationClass;
265 let f = (*parent_class)
266 .local_command_line
267 .expect("No parent class implementation for \"local_command_line\"");
268
269 let mut exit_status = 0;
270 let res = f(
271 self.obj().unsafe_cast_ref::<Application>().to_glib_none().0,
272 arguments.ptr,
273 &mut exit_status,
274 );
275 arguments.refresh();
276
277 match res {
278 glib::ffi::GFALSE => None,
279 _ => Some(exit_status.into()),
280 }
281 }
282 }
283
284 fn parent_open(&self, files: &[crate::File], hint: &str) {
285 unsafe {
286 let data = Self::type_data();
287 let parent_class = data.as_ref().parent_class() as *mut ffi::GApplicationClass;
288 let f = (*parent_class)
289 .open
290 .expect("No parent class implementation for \"open\"");
291 f(
292 self.obj().unsafe_cast_ref::<Application>().to_glib_none().0,
293 files.to_glib_none().0,
294 files.len() as i32,
295 hint.to_glib_none().0,
296 )
297 }
298 }
299
300 fn parent_quit_mainloop(&self) {
301 unsafe {
302 let data = Self::type_data();
303 let parent_class = data.as_ref().parent_class() as *mut ffi::GApplicationClass;
304 let f = (*parent_class)
305 .quit_mainloop
306 .expect("No parent class implementation for \"quit_mainloop\"");
307 f(self.obj().unsafe_cast_ref::<Application>().to_glib_none().0)
308 }
309 }
310
311 fn parent_run_mainloop(&self) {
312 unsafe {
313 let data = Self::type_data();
314 let parent_class = data.as_ref().parent_class() as *mut ffi::GApplicationClass;
315 let f = (*parent_class)
316 .run_mainloop
317 .expect("No parent class implementation for \"run_mainloop\"");
318 f(self.obj().unsafe_cast_ref::<Application>().to_glib_none().0)
319 }
320 }
321
322 fn parent_shutdown(&self) {
323 unsafe {
324 let data = Self::type_data();
325 let parent_class = data.as_ref().parent_class() as *mut ffi::GApplicationClass;
326 let f = (*parent_class)
327 .shutdown
328 .expect("No parent class implementation for \"shutdown\"");
329 f(self.obj().unsafe_cast_ref::<Application>().to_glib_none().0)
330 }
331 }
332
333 fn parent_startup(&self) {
334 unsafe {
335 let data = Self::type_data();
336 let parent_class = data.as_ref().parent_class() as *mut ffi::GApplicationClass;
337 let f = (*parent_class)
338 .startup
339 .expect("No parent class implementation for \"startup\"");
340 f(self.obj().unsafe_cast_ref::<Application>().to_glib_none().0)
341 }
342 }
343
344 fn parent_handle_local_options(&self, options: &VariantDict) -> ExitCode {
345 unsafe {
346 let data = Self::type_data();
347 let parent_class = data.as_ref().parent_class() as *mut ffi::GApplicationClass;
348 if let Some(f) = (*parent_class).handle_local_options {
349 f(
350 self.obj().unsafe_cast_ref::<Application>().to_glib_none().0,
351 options.to_glib_none().0,
352 )
353 .into()
354 } else {
355 ExitCode::from(-1)
357 }
358 }
359 }
360
361 fn parent_dbus_register(
362 &self,
363 connection: &DBusConnection,
364 object_path: &str,
365 ) -> Result<(), glib::Error> {
366 unsafe {
367 let data = Self::type_data();
368 let parent_class = data.as_ref().parent_class() as *mut ffi::GApplicationClass;
369 let f = (*parent_class)
370 .dbus_register
371 .expect("No parent class implementation for \"dbus_register\"");
372 let mut err = ptr::null_mut();
373 let res = f(
374 self.obj().unsafe_cast_ref::<Application>().to_glib_none().0,
375 connection.to_glib_none().0,
376 object_path.to_glib_none().0,
377 &mut err,
378 );
379 if res == glib::ffi::GFALSE {
380 Err(from_glib_full(err))
381 } else {
382 debug_assert!(err.is_null());
383 Ok(())
384 }
385 }
386 }
387
388 fn parent_dbus_unregister(&self, connection: &DBusConnection, object_path: &str) {
389 unsafe {
390 let data = Self::type_data();
391 let parent_class = data.as_ref().parent_class() as *mut ffi::GApplicationClass;
392 let f = (*parent_class)
393 .dbus_unregister
394 .expect("No parent class implementation for \"dbus_unregister\"");
395 f(
396 self.obj().unsafe_cast_ref::<Application>().to_glib_none().0,
397 connection.to_glib_none().0,
398 object_path.to_glib_none().0,
399 );
400 }
401 }
402
403 fn parent_name_lost(&self) -> Propagation {
404 unsafe {
405 let data = Self::type_data();
406 let parent_class = data.as_ref().parent_class() as *mut ffi::GApplicationClass;
407 let f = (*parent_class)
408 .name_lost
409 .expect("No parent class implementation for \"name_lost\"");
410 Propagation::from_glib(f(self
411 .obj()
412 .unsafe_cast_ref::<Application>()
413 .to_glib_none()
414 .0))
415 }
416 }
417}
418
419impl<T: ApplicationImpl> ApplicationImplExt for T {}
420
421unsafe impl<T: ApplicationImpl> IsSubclassable<T> for Application {
422 fn class_init(class: &mut ::glib::Class<Self>) {
423 Self::parent_class_init::<T>(class);
424
425 let klass = class.as_mut();
426 klass.activate = Some(application_activate::<T>);
427 klass.after_emit = Some(application_after_emit::<T>);
428 klass.before_emit = Some(application_before_emit::<T>);
429 klass.command_line = Some(application_command_line::<T>);
430 klass.local_command_line = Some(application_local_command_line::<T>);
431 klass.open = Some(application_open::<T>);
432 klass.quit_mainloop = Some(application_quit_mainloop::<T>);
433 klass.run_mainloop = Some(application_run_mainloop::<T>);
434 klass.shutdown = Some(application_shutdown::<T>);
435 klass.startup = Some(application_startup::<T>);
436 klass.handle_local_options = Some(application_handle_local_options::<T>);
437 klass.dbus_register = Some(application_dbus_register::<T>);
438 klass.dbus_unregister = Some(application_dbus_unregister::<T>);
439 klass.name_lost = Some(application_name_lost::<T>);
440 }
441}
442
443unsafe extern "C" fn application_activate<T: ApplicationImpl>(ptr: *mut ffi::GApplication) {
444 let instance = &*(ptr as *mut T::Instance);
445 let imp = instance.imp();
446
447 imp.activate()
448}
449
450unsafe extern "C" fn application_after_emit<T: ApplicationImpl>(
451 ptr: *mut ffi::GApplication,
452 platform_data: *mut glib::ffi::GVariant,
453) {
454 let instance = &*(ptr as *mut T::Instance);
455 let imp = instance.imp();
456
457 imp.after_emit(&from_glib_borrow(platform_data))
458}
459unsafe extern "C" fn application_before_emit<T: ApplicationImpl>(
460 ptr: *mut ffi::GApplication,
461 platform_data: *mut glib::ffi::GVariant,
462) {
463 let instance = &*(ptr as *mut T::Instance);
464 let imp = instance.imp();
465
466 imp.before_emit(&from_glib_borrow(platform_data))
467}
468unsafe extern "C" fn application_command_line<T: ApplicationImpl>(
469 ptr: *mut ffi::GApplication,
470 command_line: *mut ffi::GApplicationCommandLine,
471) -> i32 {
472 let instance = &*(ptr as *mut T::Instance);
473 let imp = instance.imp();
474
475 imp.command_line(&from_glib_borrow(command_line)).into()
476}
477unsafe extern "C" fn application_local_command_line<T: ApplicationImpl>(
478 ptr: *mut ffi::GApplication,
479 arguments: *mut *mut *mut c_char,
480 exit_status: *mut i32,
481) -> glib::ffi::gboolean {
482 let instance = &*(ptr as *mut T::Instance);
483 let imp = instance.imp();
484
485 let mut args = ArgumentList::new(arguments);
486 let res = imp.local_command_line(&mut args).map(i32::from);
487 args.refresh();
488
489 match res {
490 Some(ret) => {
491 *exit_status = ret;
492 glib::ffi::GTRUE
493 }
494 None => glib::ffi::GFALSE,
495 }
496}
497unsafe extern "C" fn application_open<T: ApplicationImpl>(
498 ptr: *mut ffi::GApplication,
499 files: *mut *mut ffi::GFile,
500 num_files: i32,
501 hint: *const c_char,
502) {
503 let instance = &*(ptr as *mut T::Instance);
504 let imp = instance.imp();
505
506 let files: Vec<crate::File> = FromGlibContainer::from_glib_none_num(files, num_files as usize);
507 imp.open(files.as_slice(), &glib::GString::from_glib_borrow(hint))
508}
509unsafe extern "C" fn application_quit_mainloop<T: ApplicationImpl>(ptr: *mut ffi::GApplication) {
510 let instance = &*(ptr as *mut T::Instance);
511 let imp = instance.imp();
512
513 imp.quit_mainloop()
514}
515unsafe extern "C" fn application_run_mainloop<T: ApplicationImpl>(ptr: *mut ffi::GApplication) {
516 let instance = &*(ptr as *mut T::Instance);
517 let imp = instance.imp();
518
519 imp.run_mainloop()
520}
521unsafe extern "C" fn application_shutdown<T: ApplicationImpl>(ptr: *mut ffi::GApplication) {
522 let instance = &*(ptr as *mut T::Instance);
523 let imp = instance.imp();
524
525 imp.shutdown()
526}
527unsafe extern "C" fn application_startup<T: ApplicationImpl>(ptr: *mut ffi::GApplication) {
528 let instance = &*(ptr as *mut T::Instance);
529 let imp = instance.imp();
530
531 imp.startup()
532}
533
534unsafe extern "C" fn application_handle_local_options<T: ApplicationImpl>(
535 ptr: *mut ffi::GApplication,
536 options: *mut glib::ffi::GVariantDict,
537) -> c_int {
538 let instance = &*(ptr as *mut T::Instance);
539 let imp = instance.imp();
540
541 imp.handle_local_options(&from_glib_borrow(options)).into()
542}
543
544unsafe extern "C" fn application_dbus_register<T: ApplicationImpl>(
545 ptr: *mut ffi::GApplication,
546 connection: *mut ffi::GDBusConnection,
547 object_path: *const c_char,
548 error: *mut *mut glib::ffi::GError,
549) -> glib::ffi::gboolean {
550 let instance = &*(ptr as *mut T::Instance);
551 let imp = instance.imp();
552
553 match imp.dbus_register(
554 &from_glib_borrow(connection),
555 &glib::GString::from_glib_borrow(object_path),
556 ) {
557 Ok(()) => glib::ffi::GTRUE,
558 Err(e) => {
559 if !error.is_null() {
560 *error = e.into_glib_ptr();
561 }
562 glib::ffi::GFALSE
563 }
564 }
565}
566
567unsafe extern "C" fn application_dbus_unregister<T: ApplicationImpl>(
568 ptr: *mut ffi::GApplication,
569 connection: *mut ffi::GDBusConnection,
570 object_path: *const c_char,
571) {
572 let instance = &*(ptr as *mut T::Instance);
573 let imp = instance.imp();
574 imp.dbus_unregister(
575 &from_glib_borrow(connection),
576 &glib::GString::from_glib_borrow(object_path),
577 );
578}
579
580unsafe extern "C" fn application_name_lost<T: ApplicationImpl>(
581 ptr: *mut ffi::GApplication,
582) -> glib::ffi::gboolean {
583 let instance = &*(ptr as *mut T::Instance);
584 let imp = instance.imp();
585 imp.name_lost().into_glib()
586}
587
588#[cfg(test)]
589mod tests {
590 use super::*;
591 use crate::prelude::*;
592
593 const EXIT_STATUS: i32 = 20;
594
595 mod imp {
596 use super::*;
597
598 #[derive(Default)]
599 pub struct SimpleApplication;
600
601 #[glib::object_subclass]
602 impl ObjectSubclass for SimpleApplication {
603 const NAME: &'static str = "SimpleApplication";
604 type Type = super::SimpleApplication;
605 type ParentType = Application;
606 }
607
608 impl ObjectImpl for SimpleApplication {}
609
610 impl ApplicationImpl for SimpleApplication {
611 fn command_line(&self, _cmd_line: &crate::ApplicationCommandLine) -> ExitCode {
612 #[cfg(not(target_os = "windows"))]
613 {
614 let arguments = _cmd_line.arguments();
615
616 assert_eq!(arguments.to_vec(), &["--global-1", "--global-2"]);
621 };
622 EXIT_STATUS.into()
623 }
624
625 fn local_command_line(&self, arguments: &mut ArgumentList) -> Option<ExitCode> {
626 let mut rm = Vec::new();
627
628 for (i, line) in arguments.iter().enumerate() {
629 let l = line.to_str().unwrap();
631 if l.starts_with("--local-") {
632 rm.push(i)
633 }
634 }
635
636 rm.reverse();
637
638 for i in rm.iter() {
639 arguments.remove(*i);
640 }
641
642 None
643 }
644 }
645 }
646
647 glib::wrapper! {
648 pub struct SimpleApplication(ObjectSubclass<imp::SimpleApplication>)
649 @implements crate::Application;
650 }
651
652 #[test]
653 fn test_simple_application() {
654 let app = glib::Object::builder::<SimpleApplication>()
655 .property("application-id", "org.gtk-rs.SimpleApplication")
656 .property("flags", crate::ApplicationFlags::empty())
657 .build();
658
659 app.set_inactivity_timeout(10000);
660
661 assert_eq!(
662 app.run_with_args(&["--local-1", "--global-1", "--local-2", "--global-2"]),
663 EXIT_STATUS.into()
664 );
665 }
666}