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