1 // Take a look at the license at the top of the repository in the LICENSE file.
2 
3 use glib::translate::*;
4 
5 use glib::subclass::prelude::*;
6 
7 use glib::{Cast, VariantDict};
8 
9 use crate::Application;
10 
11 use libc::{c_char, c_int, c_void};
12 use std::ffi::OsString;
13 use std::fmt;
14 use std::ops::Deref;
15 use std::ptr;
16 
17 pub struct ArgumentList {
18     pub(crate) ptr: *mut *mut *mut c_char,
19     items: Vec<OsString>,
20 }
21 
22 impl ArgumentList {
new(arguments: *mut *mut *mut c_char) -> Self23     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 
refresh(&mut self)30     pub(crate) fn refresh(&mut self) {
31         self.items = unsafe { FromGlibPtrContainer::from_glib_none(ptr::read(self.ptr)) };
32     }
33 
34     // remove the item at index `idx` and shift the raw array
remove(&mut self, idx: usize)35     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 
53 impl Deref for ArgumentList {
54     type Target = [OsString];
55 
deref(&self) -> &Self::Target56     fn deref(&self) -> &Self::Target {
57         self.items.as_slice()
58     }
59 }
60 
61 impl fmt::Debug for ArgumentList {
fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error>62     fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
63         self.items.fmt(formatter)
64     }
65 }
66 
67 impl From<ArgumentList> for Vec<OsString> {
from(list: ArgumentList) -> Vec<OsString>68     fn from(list: ArgumentList) -> Vec<OsString> {
69         list.items
70     }
71 }
72 
73 pub trait ApplicationImpl: ObjectImpl + ApplicationImplExt {
activate(&self, application: &Self::Type)74     fn activate(&self, application: &Self::Type) {
75         self.parent_activate(application)
76     }
77 
after_emit(&self, application: &Self::Type, platform_data: &glib::Variant)78     fn after_emit(&self, application: &Self::Type, platform_data: &glib::Variant) {
79         self.parent_after_emit(application, platform_data)
80     }
81 
before_emit(&self, application: &Self::Type, platform_data: &glib::Variant)82     fn before_emit(&self, application: &Self::Type, platform_data: &glib::Variant) {
83         self.parent_before_emit(application, platform_data)
84     }
85 
command_line( &self, application: &Self::Type, command_line: &crate::ApplicationCommandLine, ) -> i3286     fn command_line(
87         &self,
88         application: &Self::Type,
89         command_line: &crate::ApplicationCommandLine,
90     ) -> i32 {
91         self.parent_command_line(application, command_line)
92     }
93 
local_command_line( &self, application: &Self::Type, arguments: &mut ArgumentList, ) -> Option<i32>94     fn local_command_line(
95         &self,
96         application: &Self::Type,
97         arguments: &mut ArgumentList,
98     ) -> Option<i32> {
99         self.parent_local_command_line(application, arguments)
100     }
101 
open(&self, application: &Self::Type, files: &[crate::File], hint: &str)102     fn open(&self, application: &Self::Type, files: &[crate::File], hint: &str) {
103         self.parent_open(application, files, hint)
104     }
105 
quit_mainloop(&self, application: &Self::Type)106     fn quit_mainloop(&self, application: &Self::Type) {
107         self.parent_quit_mainloop(application)
108     }
109 
run_mainloop(&self, application: &Self::Type)110     fn run_mainloop(&self, application: &Self::Type) {
111         self.parent_run_mainloop(application)
112     }
113 
shutdown(&self, application: &Self::Type)114     fn shutdown(&self, application: &Self::Type) {
115         self.parent_shutdown(application)
116     }
117 
startup(&self, application: &Self::Type)118     fn startup(&self, application: &Self::Type) {
119         self.parent_startup(application)
120     }
121 
handle_local_options(&self, application: &Self::Type, options: &VariantDict) -> i32122     fn handle_local_options(&self, application: &Self::Type, options: &VariantDict) -> i32 {
123         self.parent_handle_local_options(application, options)
124     }
125 }
126 
127 pub trait ApplicationImplExt: ObjectSubclass {
parent_activate(&self, application: &Self::Type)128     fn parent_activate(&self, application: &Self::Type);
parent_after_emit(&self, application: &Self::Type, platform_data: &glib::Variant)129     fn parent_after_emit(&self, application: &Self::Type, platform_data: &glib::Variant);
parent_before_emit(&self, application: &Self::Type, platform_data: &glib::Variant)130     fn parent_before_emit(&self, application: &Self::Type, platform_data: &glib::Variant);
parent_command_line( &self, application: &Self::Type, command_line: &crate::ApplicationCommandLine, ) -> i32131     fn parent_command_line(
132         &self,
133         application: &Self::Type,
134         command_line: &crate::ApplicationCommandLine,
135     ) -> i32;
parent_local_command_line( &self, application: &Self::Type, arguments: &mut ArgumentList, ) -> Option<i32>136     fn parent_local_command_line(
137         &self,
138         application: &Self::Type,
139         arguments: &mut ArgumentList,
140     ) -> Option<i32>;
parent_open(&self, application: &Self::Type, files: &[crate::File], hint: &str)141     fn parent_open(&self, application: &Self::Type, files: &[crate::File], hint: &str);
parent_quit_mainloop(&self, application: &Self::Type)142     fn parent_quit_mainloop(&self, application: &Self::Type);
parent_run_mainloop(&self, application: &Self::Type)143     fn parent_run_mainloop(&self, application: &Self::Type);
parent_shutdown(&self, application: &Self::Type)144     fn parent_shutdown(&self, application: &Self::Type);
parent_startup(&self, application: &Self::Type)145     fn parent_startup(&self, application: &Self::Type);
parent_handle_local_options(&self, application: &Self::Type, options: &VariantDict) -> i32146     fn parent_handle_local_options(&self, application: &Self::Type, options: &VariantDict) -> i32;
147 }
148 
149 impl<T: ApplicationImpl> ApplicationImplExt for T {
parent_activate(&self, application: &Self::Type)150     fn parent_activate(&self, application: &Self::Type) {
151         unsafe {
152             let data = T::type_data();
153             let parent_class = data.as_ref().parent_class() as *mut ffi::GApplicationClass;
154             let f = (*parent_class)
155                 .activate
156                 .expect("No parent class implementation for \"activate\"");
157             f(application
158                 .unsafe_cast_ref::<Application>()
159                 .to_glib_none()
160                 .0)
161         }
162     }
163 
parent_after_emit(&self, application: &Self::Type, platform_data: &glib::Variant)164     fn parent_after_emit(&self, application: &Self::Type, platform_data: &glib::Variant) {
165         unsafe {
166             let data = T::type_data();
167             let parent_class = data.as_ref().parent_class() as *mut ffi::GApplicationClass;
168             let f = (*parent_class)
169                 .after_emit
170                 .expect("No parent class implementation for \"after_emit\"");
171             f(
172                 application
173                     .unsafe_cast_ref::<Application>()
174                     .to_glib_none()
175                     .0,
176                 platform_data.to_glib_none().0,
177             )
178         }
179     }
180 
parent_before_emit(&self, application: &Self::Type, platform_data: &glib::Variant)181     fn parent_before_emit(&self, application: &Self::Type, platform_data: &glib::Variant) {
182         unsafe {
183             let data = T::type_data();
184             let parent_class = data.as_ref().parent_class() as *mut ffi::GApplicationClass;
185             let f = (*parent_class)
186                 .before_emit
187                 .expect("No parent class implementation for \"before_emit\"");
188             f(
189                 application
190                     .unsafe_cast_ref::<Application>()
191                     .to_glib_none()
192                     .0,
193                 platform_data.to_glib_none().0,
194             )
195         }
196     }
197 
parent_command_line( &self, application: &Self::Type, command_line: &crate::ApplicationCommandLine, ) -> i32198     fn parent_command_line(
199         &self,
200         application: &Self::Type,
201         command_line: &crate::ApplicationCommandLine,
202     ) -> i32 {
203         unsafe {
204             let data = T::type_data();
205             let parent_class = data.as_ref().parent_class() as *mut ffi::GApplicationClass;
206             let f = (*parent_class)
207                 .command_line
208                 .expect("No parent class implementation for \"command_line\"");
209             f(
210                 application
211                     .unsafe_cast_ref::<Application>()
212                     .to_glib_none()
213                     .0,
214                 command_line.to_glib_none().0,
215             )
216         }
217     }
218 
parent_local_command_line( &self, application: &Self::Type, arguments: &mut ArgumentList, ) -> Option<i32>219     fn parent_local_command_line(
220         &self,
221         application: &Self::Type,
222         arguments: &mut ArgumentList,
223     ) -> Option<i32> {
224         unsafe {
225             let data = T::type_data();
226             let parent_class = data.as_ref().parent_class() as *mut ffi::GApplicationClass;
227             let f = (*parent_class)
228                 .local_command_line
229                 .expect("No parent class implementation for \"local_command_line\"");
230 
231             let mut exit_status = 0;
232             let res = f(
233                 application
234                     .unsafe_cast_ref::<Application>()
235                     .to_glib_none()
236                     .0,
237                 arguments.ptr,
238                 &mut exit_status,
239             );
240             arguments.refresh();
241 
242             match res {
243                 glib::ffi::GFALSE => None,
244                 _ => Some(exit_status),
245             }
246         }
247     }
248 
parent_open(&self, application: &Self::Type, files: &[crate::File], hint: &str)249     fn parent_open(&self, application: &Self::Type, files: &[crate::File], hint: &str) {
250         unsafe {
251             let data = T::type_data();
252             let parent_class = data.as_ref().parent_class() as *mut ffi::GApplicationClass;
253             let f = (*parent_class)
254                 .open
255                 .expect("No parent class implementation for \"open\"");
256             f(
257                 application
258                     .unsafe_cast_ref::<Application>()
259                     .to_glib_none()
260                     .0,
261                 files.to_glib_none().0,
262                 files.len() as i32,
263                 hint.to_glib_none().0,
264             )
265         }
266     }
267 
parent_quit_mainloop(&self, application: &Self::Type)268     fn parent_quit_mainloop(&self, application: &Self::Type) {
269         unsafe {
270             let data = T::type_data();
271             let parent_class = data.as_ref().parent_class() as *mut ffi::GApplicationClass;
272             let f = (*parent_class)
273                 .quit_mainloop
274                 .expect("No parent class implementation for \"quit_mainloop\"");
275             f(application
276                 .unsafe_cast_ref::<Application>()
277                 .to_glib_none()
278                 .0)
279         }
280     }
281 
parent_run_mainloop(&self, application: &Self::Type)282     fn parent_run_mainloop(&self, application: &Self::Type) {
283         unsafe {
284             let data = T::type_data();
285             let parent_class = data.as_ref().parent_class() as *mut ffi::GApplicationClass;
286             let f = (*parent_class)
287                 .run_mainloop
288                 .expect("No parent class implementation for \"run_mainloop\"");
289             f(application
290                 .unsafe_cast_ref::<Application>()
291                 .to_glib_none()
292                 .0)
293         }
294     }
295 
parent_shutdown(&self, application: &Self::Type)296     fn parent_shutdown(&self, application: &Self::Type) {
297         unsafe {
298             let data = T::type_data();
299             let parent_class = data.as_ref().parent_class() as *mut ffi::GApplicationClass;
300             let f = (*parent_class)
301                 .shutdown
302                 .expect("No parent class implementation for \"shutdown\"");
303             f(application
304                 .unsafe_cast_ref::<Application>()
305                 .to_glib_none()
306                 .0)
307         }
308     }
309 
parent_startup(&self, application: &Self::Type)310     fn parent_startup(&self, application: &Self::Type) {
311         unsafe {
312             let data = T::type_data();
313             let parent_class = data.as_ref().parent_class() as *mut ffi::GApplicationClass;
314             let f = (*parent_class)
315                 .startup
316                 .expect("No parent class implementation for \"startup\"");
317             f(application
318                 .unsafe_cast_ref::<Application>()
319                 .to_glib_none()
320                 .0)
321         }
322     }
323 
parent_handle_local_options(&self, application: &Self::Type, options: &VariantDict) -> i32324     fn parent_handle_local_options(&self, application: &Self::Type, options: &VariantDict) -> i32 {
325         unsafe {
326             let data = T::type_data();
327             let parent_class = data.as_ref().parent_class() as *mut ffi::GApplicationClass;
328             if let Some(f) = (*parent_class).handle_local_options {
329                 f(
330                     application
331                         .unsafe_cast_ref::<Application>()
332                         .to_glib_none()
333                         .0,
334                     options.to_glib_none().0,
335                 )
336             } else {
337                 // Continue default handling
338                 -1
339             }
340         }
341     }
342 }
343 
344 unsafe impl<T: ApplicationImpl> IsSubclassable<T> for Application {
class_init(class: &mut ::glib::Class<Self>)345     fn class_init(class: &mut ::glib::Class<Self>) {
346         <glib::Object as IsSubclassable<T>>::class_init(class);
347 
348         let klass = class.as_mut();
349         klass.activate = Some(application_activate::<T>);
350         klass.after_emit = Some(application_after_emit::<T>);
351         klass.before_emit = Some(application_before_emit::<T>);
352         klass.command_line = Some(application_command_line::<T>);
353         klass.local_command_line = Some(application_local_command_line::<T>);
354         klass.open = Some(application_open::<T>);
355         klass.quit_mainloop = Some(application_quit_mainloop::<T>);
356         klass.run_mainloop = Some(application_run_mainloop::<T>);
357         klass.shutdown = Some(application_shutdown::<T>);
358         klass.startup = Some(application_startup::<T>);
359         klass.handle_local_options = Some(application_handle_local_options::<T>);
360     }
361 
instance_init(instance: &mut glib::subclass::InitializingObject<T>)362     fn instance_init(instance: &mut glib::subclass::InitializingObject<T>) {
363         <glib::Object as IsSubclassable<T>>::instance_init(instance);
364     }
365 }
366 
application_activate<T: ApplicationImpl>(ptr: *mut ffi::GApplication)367 unsafe extern "C" fn application_activate<T: ApplicationImpl>(ptr: *mut ffi::GApplication) {
368     let instance = &*(ptr as *mut T::Instance);
369     let imp = instance.impl_();
370     let wrap: Borrowed<Application> = from_glib_borrow(ptr);
371 
372     imp.activate(wrap.unsafe_cast_ref())
373 }
374 
application_after_emit<T: ApplicationImpl>( ptr: *mut ffi::GApplication, platform_data: *mut glib::ffi::GVariant, )375 unsafe extern "C" fn application_after_emit<T: ApplicationImpl>(
376     ptr: *mut ffi::GApplication,
377     platform_data: *mut glib::ffi::GVariant,
378 ) {
379     let instance = &*(ptr as *mut T::Instance);
380     let imp = instance.impl_();
381     let wrap: Borrowed<Application> = from_glib_borrow(ptr);
382 
383     imp.after_emit(wrap.unsafe_cast_ref(), &from_glib_borrow(platform_data))
384 }
application_before_emit<T: ApplicationImpl>( ptr: *mut ffi::GApplication, platform_data: *mut glib::ffi::GVariant, )385 unsafe extern "C" fn application_before_emit<T: ApplicationImpl>(
386     ptr: *mut ffi::GApplication,
387     platform_data: *mut glib::ffi::GVariant,
388 ) {
389     let instance = &*(ptr as *mut T::Instance);
390     let imp = instance.impl_();
391     let wrap: Borrowed<Application> = from_glib_borrow(ptr);
392 
393     imp.before_emit(wrap.unsafe_cast_ref(), &from_glib_borrow(platform_data))
394 }
application_command_line<T: ApplicationImpl>( ptr: *mut ffi::GApplication, command_line: *mut ffi::GApplicationCommandLine, ) -> i32395 unsafe extern "C" fn application_command_line<T: ApplicationImpl>(
396     ptr: *mut ffi::GApplication,
397     command_line: *mut ffi::GApplicationCommandLine,
398 ) -> i32 {
399     let instance = &*(ptr as *mut T::Instance);
400     let imp = instance.impl_();
401     let wrap: Borrowed<Application> = from_glib_borrow(ptr);
402 
403     imp.command_line(wrap.unsafe_cast_ref(), &from_glib_borrow(command_line))
404 }
application_local_command_line<T: ApplicationImpl>( ptr: *mut ffi::GApplication, arguments: *mut *mut *mut c_char, exit_status: *mut i32, ) -> glib::ffi::gboolean405 unsafe extern "C" fn application_local_command_line<T: ApplicationImpl>(
406     ptr: *mut ffi::GApplication,
407     arguments: *mut *mut *mut c_char,
408     exit_status: *mut i32,
409 ) -> glib::ffi::gboolean {
410     let instance = &*(ptr as *mut T::Instance);
411     let imp = instance.impl_();
412     let wrap: Borrowed<Application> = from_glib_borrow(ptr);
413 
414     let mut args = ArgumentList::new(arguments);
415     let res = imp.local_command_line(wrap.unsafe_cast_ref(), &mut args);
416     args.refresh();
417 
418     match res {
419         Some(ret) => {
420             ptr::write(exit_status, ret);
421             glib::ffi::GTRUE
422         }
423         None => glib::ffi::GFALSE,
424     }
425 }
application_open<T: ApplicationImpl>( ptr: *mut ffi::GApplication, files: *mut *mut ffi::GFile, num_files: i32, hint: *const c_char, )426 unsafe extern "C" fn application_open<T: ApplicationImpl>(
427     ptr: *mut ffi::GApplication,
428     files: *mut *mut ffi::GFile,
429     num_files: i32,
430     hint: *const c_char,
431 ) {
432     let instance = &*(ptr as *mut T::Instance);
433     let imp = instance.impl_();
434     let wrap: Borrowed<Application> = from_glib_borrow(ptr);
435 
436     let files: Vec<crate::File> = FromGlibContainer::from_glib_none_num(files, num_files as usize);
437     imp.open(
438         wrap.unsafe_cast_ref(),
439         files.as_slice(),
440         &glib::GString::from_glib_borrow(hint),
441     )
442 }
application_quit_mainloop<T: ApplicationImpl>(ptr: *mut ffi::GApplication)443 unsafe extern "C" fn application_quit_mainloop<T: ApplicationImpl>(ptr: *mut ffi::GApplication) {
444     let instance = &*(ptr as *mut T::Instance);
445     let imp = instance.impl_();
446     let wrap: Borrowed<Application> = from_glib_borrow(ptr);
447 
448     imp.quit_mainloop(wrap.unsafe_cast_ref())
449 }
application_run_mainloop<T: ApplicationImpl>(ptr: *mut ffi::GApplication)450 unsafe extern "C" fn application_run_mainloop<T: ApplicationImpl>(ptr: *mut ffi::GApplication) {
451     let instance = &*(ptr as *mut T::Instance);
452     let imp = instance.impl_();
453     let wrap: Borrowed<Application> = from_glib_borrow(ptr);
454 
455     imp.run_mainloop(wrap.unsafe_cast_ref())
456 }
application_shutdown<T: ApplicationImpl>(ptr: *mut ffi::GApplication)457 unsafe extern "C" fn application_shutdown<T: ApplicationImpl>(ptr: *mut ffi::GApplication) {
458     let instance = &*(ptr as *mut T::Instance);
459     let imp = instance.impl_();
460     let wrap: Borrowed<Application> = from_glib_borrow(ptr);
461 
462     imp.shutdown(wrap.unsafe_cast_ref())
463 }
application_startup<T: ApplicationImpl>(ptr: *mut ffi::GApplication)464 unsafe extern "C" fn application_startup<T: ApplicationImpl>(ptr: *mut ffi::GApplication) {
465     let instance = &*(ptr as *mut T::Instance);
466     let imp = instance.impl_();
467     let wrap: Borrowed<Application> = from_glib_borrow(ptr);
468 
469     imp.startup(wrap.unsafe_cast_ref())
470 }
471 
application_handle_local_options<T: ApplicationImpl>( ptr: *mut ffi::GApplication, options: *mut glib::ffi::GVariantDict, ) -> c_int472 unsafe extern "C" fn application_handle_local_options<T: ApplicationImpl>(
473     ptr: *mut ffi::GApplication,
474     options: *mut glib::ffi::GVariantDict,
475 ) -> c_int {
476     let instance = &*(ptr as *mut T::Instance);
477     let imp = instance.impl_();
478     let wrap: Borrowed<Application> = from_glib_borrow(ptr);
479 
480     imp.handle_local_options(wrap.unsafe_cast_ref(), &from_glib_borrow(options))
481 }
482 
483 #[cfg(test)]
484 mod tests {
485     use super::*;
486     use crate::prelude::*;
487 
488     const EXIT_STATUS: i32 = 20;
489 
490     mod imp {
491         use super::*;
492 
493         #[derive(Default)]
494         pub struct SimpleApplication;
495 
496         #[glib::object_subclass]
497         impl ObjectSubclass for SimpleApplication {
498             const NAME: &'static str = "SimpleApplication";
499             type Type = super::SimpleApplication;
500             type ParentType = Application;
501         }
502 
503         impl ObjectImpl for SimpleApplication {}
504 
505         impl ApplicationImpl for SimpleApplication {
command_line( &self, _application: &Self::Type, cmd_line: &crate::ApplicationCommandLine, ) -> i32506             fn command_line(
507                 &self,
508                 _application: &Self::Type,
509                 cmd_line: &crate::ApplicationCommandLine,
510             ) -> i32 {
511                 let arguments = cmd_line.arguments();
512 
513                 for arg in arguments {
514                     // TODO: we need https://github.com/rust-lang/rust/issues/49802
515                     let a = arg.into_string().unwrap();
516                     assert!(!a.starts_with("--local-"))
517                 }
518 
519                 EXIT_STATUS
520             }
521 
local_command_line( &self, _application: &Self::Type, arguments: &mut ArgumentList, ) -> Option<i32>522             fn local_command_line(
523                 &self,
524                 _application: &Self::Type,
525                 arguments: &mut ArgumentList,
526             ) -> Option<i32> {
527                 let mut rm = Vec::new();
528 
529                 for (i, line) in arguments.iter().enumerate() {
530                     // TODO: we need https://github.com/rust-lang/rust/issues/49802
531                     let l = line.clone().into_string().unwrap();
532                     if l.starts_with("--local-") {
533                         rm.push(i)
534                     }
535                 }
536 
537                 rm.reverse();
538 
539                 for i in rm.iter() {
540                     arguments.remove(*i);
541                 }
542 
543                 None
544             }
545         }
546     }
547 
548     glib::wrapper! {
549         pub struct SimpleApplication(ObjectSubclass<imp::SimpleApplication>)
550         @implements crate::Application;
551     }
552 
553     #[test]
test_simple_application()554     fn test_simple_application() {
555         let app = glib::Object::new::<SimpleApplication>(&[
556             ("application-id", &"org.gtk-rs.SimpleApplication"),
557             ("flags", &crate::ApplicationFlags::empty()),
558         ])
559         .unwrap()
560         .upcast::<crate::Application>();
561 
562         app.set_inactivity_timeout(10000);
563 
564         assert_eq!(app.run_with_args(&["--local"]), EXIT_STATUS);
565     }
566 }
567