1 // Copyright 2019, The Gtk-rs Project Developers.
2 // See the COPYRIGHT file at the top-level directory of this distribution.
3 // Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>
4 
5 use gio_sys;
6 use glib_sys;
7 
8 use glib::translate::*;
9 
10 use glib::subclass::prelude::*;
11 
12 use Application;
13 use ApplicationClass;
14 
15 use libc::{c_char, c_void};
16 use std::convert;
17 use std::ffi::OsString;
18 use std::fmt;
19 use std::ops::Deref;
20 use std::ptr;
21 
22 pub struct ArgumentList {
23     pub(crate) ptr: *mut *mut *mut c_char,
24     items: Vec<OsString>,
25 }
26 
27 impl ArgumentList {
new(arguments: *mut *mut *mut c_char) -> Self28     pub(crate) fn new(arguments: *mut *mut *mut c_char) -> Self {
29         Self {
30             ptr: arguments,
31             items: unsafe { FromGlibPtrContainer::from_glib_none(ptr::read(arguments)) },
32         }
33     }
34 
refresh(&mut self)35     pub(crate) fn refresh(&mut self) {
36         self.items = unsafe { FromGlibPtrContainer::from_glib_none(ptr::read(self.ptr)) };
37     }
38 
39     // remove the item at index `idx` and shift the raw array
remove(&mut self, idx: usize)40     pub fn remove(&mut self, idx: usize) {
41         unsafe {
42             let n_args = glib_sys::g_strv_length(*self.ptr);
43             assert!((n_args as usize) == self.items.len());
44             assert!((idx as u32) < n_args);
45 
46             self.items.remove(idx);
47 
48             glib_sys::g_free(((*self.ptr).offset(idx as isize)) as *mut c_void);
49 
50             for i in (idx as u32)..n_args - 1 {
51                 ptr::write(
52                     (*self.ptr).offset(i as isize),
53                     *(*self.ptr).offset((i + 1) as isize),
54                 )
55             }
56             ptr::write((*self.ptr).offset((n_args - 1) as isize), ptr::null_mut());
57         }
58     }
59 }
60 
61 impl Deref for ArgumentList {
62     type Target = [OsString];
63 
deref(&self) -> &Self::Target64     fn deref(&self) -> &Self::Target {
65         self.items.as_slice()
66     }
67 }
68 
69 impl fmt::Debug for ArgumentList {
fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error>70     fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
71         self.items.fmt(formatter)
72     }
73 }
74 
75 impl convert::Into<Vec<OsString>> for ArgumentList {
into(self) -> Vec<OsString>76     fn into(self) -> Vec<OsString> {
77         self.items
78     }
79 }
80 
81 pub trait ApplicationImpl: ApplicationImplExt + 'static {
activate(&self, application: &Application)82     fn activate(&self, application: &Application) {
83         self.parent_activate(application)
84     }
85 
after_emit(&self, application: &Application, platform_data: &glib::Variant)86     fn after_emit(&self, application: &Application, platform_data: &glib::Variant) {
87         self.parent_after_emit(application, platform_data)
88     }
89 
before_emit(&self, application: &Application, platform_data: &glib::Variant)90     fn before_emit(&self, application: &Application, platform_data: &glib::Variant) {
91         self.parent_before_emit(application, platform_data)
92     }
93 
command_line( &self, application: &Application, command_line: &::ApplicationCommandLine, ) -> i3294     fn command_line(
95         &self,
96         application: &Application,
97         command_line: &::ApplicationCommandLine,
98     ) -> i32 {
99         self.parent_command_line(application, command_line)
100     }
101 
local_command_line( &self, application: &Application, arguments: &mut ArgumentList, ) -> Option<i32>102     fn local_command_line(
103         &self,
104         application: &Application,
105         arguments: &mut ArgumentList,
106     ) -> Option<i32> {
107         self.parent_local_command_line(application, arguments)
108     }
109 
open(&self, application: &Application, files: &[::File], hint: &str)110     fn open(&self, application: &Application, files: &[::File], hint: &str) {
111         self.parent_open(application, files, hint)
112     }
113 
quit_mainloop(&self, application: &Application)114     fn quit_mainloop(&self, application: &Application) {
115         self.parent_quit_mainloop(application)
116     }
117 
run_mainloop(&self, application: &Application)118     fn run_mainloop(&self, application: &Application) {
119         self.parent_run_mainloop(application)
120     }
121 
shutdown(&self, application: &Application)122     fn shutdown(&self, application: &Application) {
123         self.parent_shutdown(application)
124     }
125 
startup(&self, application: &Application)126     fn startup(&self, application: &Application) {
127         self.parent_startup(application)
128     }
129 }
130 
131 pub trait ApplicationImplExt {
parent_activate(&self, application: &Application)132     fn parent_activate(&self, application: &Application);
parent_after_emit(&self, application: &Application, platform_data: &glib::Variant)133     fn parent_after_emit(&self, application: &Application, platform_data: &glib::Variant);
parent_before_emit(&self, application: &Application, platform_data: &glib::Variant)134     fn parent_before_emit(&self, application: &Application, platform_data: &glib::Variant);
parent_command_line( &self, application: &Application, command_line: &::ApplicationCommandLine, ) -> i32135     fn parent_command_line(
136         &self,
137         application: &Application,
138         command_line: &::ApplicationCommandLine,
139     ) -> i32;
parent_local_command_line( &self, application: &Application, arguments: &mut ArgumentList, ) -> Option<i32>140     fn parent_local_command_line(
141         &self,
142         application: &Application,
143         arguments: &mut ArgumentList,
144     ) -> Option<i32>;
parent_open(&self, application: &Application, files: &[::File], hint: &str)145     fn parent_open(&self, application: &Application, files: &[::File], hint: &str);
parent_quit_mainloop(&self, application: &Application)146     fn parent_quit_mainloop(&self, application: &Application);
parent_run_mainloop(&self, application: &Application)147     fn parent_run_mainloop(&self, application: &Application);
parent_shutdown(&self, application: &Application)148     fn parent_shutdown(&self, application: &Application);
parent_startup(&self, application: &Application)149     fn parent_startup(&self, application: &Application);
150 }
151 
152 impl<T: ApplicationImpl + ObjectImpl> ApplicationImplExt for T {
parent_activate(&self, application: &Application)153     fn parent_activate(&self, application: &Application) {
154         unsafe {
155             let data = self.get_type_data();
156             let parent_class = data.as_ref().get_parent_class() as *mut gio_sys::GApplicationClass;
157             let f = (*parent_class)
158                 .activate
159                 .expect("No parent class implementation for \"activate\"");
160             f(application.to_glib_none().0)
161         }
162     }
163 
parent_after_emit(&self, application: &Application, platform_data: &glib::Variant)164     fn parent_after_emit(&self, application: &Application, platform_data: &glib::Variant) {
165         unsafe {
166             let data = self.get_type_data();
167             let parent_class = data.as_ref().get_parent_class() as *mut gio_sys::GApplicationClass;
168             let f = (*parent_class)
169                 .after_emit
170                 .expect("No parent class implementation for \"after_emit\"");
171             f(application.to_glib_none().0, platform_data.to_glib_none().0)
172         }
173     }
174 
parent_before_emit(&self, application: &Application, platform_data: &glib::Variant)175     fn parent_before_emit(&self, application: &Application, platform_data: &glib::Variant) {
176         unsafe {
177             let data = self.get_type_data();
178             let parent_class = data.as_ref().get_parent_class() as *mut gio_sys::GApplicationClass;
179             let f = (*parent_class)
180                 .before_emit
181                 .expect("No parent class implementation for \"before_emit\"");
182             f(application.to_glib_none().0, platform_data.to_glib_none().0)
183         }
184     }
185 
parent_command_line( &self, application: &Application, command_line: &::ApplicationCommandLine, ) -> i32186     fn parent_command_line(
187         &self,
188         application: &Application,
189         command_line: &::ApplicationCommandLine,
190     ) -> i32 {
191         unsafe {
192             let data = self.get_type_data();
193             let parent_class = data.as_ref().get_parent_class() as *mut gio_sys::GApplicationClass;
194             let f = (*parent_class)
195                 .command_line
196                 .expect("No parent class implementation for \"command_line\"");
197             f(application.to_glib_none().0, command_line.to_glib_none().0)
198         }
199     }
200 
parent_local_command_line( &self, application: &Application, arguments: &mut ArgumentList, ) -> Option<i32>201     fn parent_local_command_line(
202         &self,
203         application: &Application,
204         arguments: &mut ArgumentList,
205     ) -> Option<i32> {
206         unsafe {
207             let data = self.get_type_data();
208             let parent_class = data.as_ref().get_parent_class() as *mut gio_sys::GApplicationClass;
209             let f = (*parent_class)
210                 .local_command_line
211                 .expect("No parent class implementation for \"local_command_line\"");
212 
213             let mut exit_status = 0;
214             let res = f(
215                 application.to_glib_none().0,
216                 arguments.ptr,
217                 &mut exit_status,
218             );
219             arguments.refresh();
220 
221             match res {
222                 glib_sys::GFALSE => None,
223                 _ => Some(exit_status),
224             }
225         }
226     }
227 
parent_open(&self, application: &Application, files: &[::File], hint: &str)228     fn parent_open(&self, application: &Application, files: &[::File], hint: &str) {
229         unsafe {
230             let data = self.get_type_data();
231             let parent_class = data.as_ref().get_parent_class() as *mut gio_sys::GApplicationClass;
232             let f = (*parent_class)
233                 .open
234                 .expect("No parent class implementation for \"open\"");
235             f(
236                 application.to_glib_none().0,
237                 files.to_glib_none().0,
238                 files.len() as i32,
239                 hint.to_glib_none().0,
240             )
241         }
242     }
243 
parent_quit_mainloop(&self, application: &Application)244     fn parent_quit_mainloop(&self, application: &Application) {
245         unsafe {
246             let data = self.get_type_data();
247             let parent_class = data.as_ref().get_parent_class() as *mut gio_sys::GApplicationClass;
248             let f = (*parent_class)
249                 .quit_mainloop
250                 .expect("No parent class implementation for \"quit_mainloop\"");
251             f(application.to_glib_none().0)
252         }
253     }
254 
parent_run_mainloop(&self, application: &Application)255     fn parent_run_mainloop(&self, application: &Application) {
256         unsafe {
257             let data = self.get_type_data();
258             let parent_class = data.as_ref().get_parent_class() as *mut gio_sys::GApplicationClass;
259             let f = (*parent_class)
260                 .run_mainloop
261                 .expect("No parent class implementation for \"run_mainloop\"");
262             f(application.to_glib_none().0)
263         }
264     }
265 
parent_shutdown(&self, application: &Application)266     fn parent_shutdown(&self, application: &Application) {
267         unsafe {
268             let data = self.get_type_data();
269             let parent_class = data.as_ref().get_parent_class() as *mut gio_sys::GApplicationClass;
270             let f = (*parent_class)
271                 .shutdown
272                 .expect("No parent class implementation for \"shutdown\"");
273             f(application.to_glib_none().0)
274         }
275     }
276 
parent_startup(&self, application: &Application)277     fn parent_startup(&self, application: &Application) {
278         unsafe {
279             let data = self.get_type_data();
280             let parent_class = data.as_ref().get_parent_class() as *mut gio_sys::GApplicationClass;
281             let f = (*parent_class)
282                 .startup
283                 .expect("No parent class implementation for \"startup\"");
284             f(application.to_glib_none().0)
285         }
286     }
287 }
288 
289 unsafe impl<T: ObjectSubclass + ApplicationImpl> IsSubclassable<T> for ApplicationClass {
override_vfuncs(&mut self)290     fn override_vfuncs(&mut self) {
291         <glib::ObjectClass as IsSubclassable<T>>::override_vfuncs(self);
292         unsafe {
293             let klass = &mut *(self as *mut Self as *mut gio_sys::GApplicationClass);
294             klass.activate = Some(application_activate::<T>);
295             klass.after_emit = Some(application_after_emit::<T>);
296             klass.before_emit = Some(application_before_emit::<T>);
297             klass.command_line = Some(application_command_line::<T>);
298             klass.local_command_line = Some(application_local_command_line::<T>);
299             klass.open = Some(application_open::<T>);
300             klass.quit_mainloop = Some(application_quit_mainloop::<T>);
301             klass.run_mainloop = Some(application_run_mainloop::<T>);
302             klass.shutdown = Some(application_shutdown::<T>);
303             klass.startup = Some(application_startup::<T>);
304         }
305     }
306 }
307 
application_activate<T: ObjectSubclass>(ptr: *mut gio_sys::GApplication) where T: ApplicationImpl,308 unsafe extern "C" fn application_activate<T: ObjectSubclass>(ptr: *mut gio_sys::GApplication)
309 where
310     T: ApplicationImpl,
311 {
312     let instance = &*(ptr as *mut T::Instance);
313     let imp = instance.get_impl();
314     let wrap: Application = from_glib_borrow(ptr);
315 
316     imp.activate(&wrap)
317 }
318 
application_after_emit<T: ObjectSubclass>( ptr: *mut gio_sys::GApplication, platform_data: *mut glib_sys::GVariant, ) where T: ApplicationImpl,319 unsafe extern "C" fn application_after_emit<T: ObjectSubclass>(
320     ptr: *mut gio_sys::GApplication,
321     platform_data: *mut glib_sys::GVariant,
322 ) where
323     T: ApplicationImpl,
324 {
325     let instance = &*(ptr as *mut T::Instance);
326     let imp = instance.get_impl();
327     let wrap: Application = from_glib_borrow(ptr);
328 
329     imp.after_emit(&wrap, &from_glib_borrow(platform_data))
330 }
application_before_emit<T: ObjectSubclass>( ptr: *mut gio_sys::GApplication, platform_data: *mut glib_sys::GVariant, ) where T: ApplicationImpl,331 unsafe extern "C" fn application_before_emit<T: ObjectSubclass>(
332     ptr: *mut gio_sys::GApplication,
333     platform_data: *mut glib_sys::GVariant,
334 ) where
335     T: ApplicationImpl,
336 {
337     let instance = &*(ptr as *mut T::Instance);
338     let imp = instance.get_impl();
339     let wrap: Application = from_glib_borrow(ptr);
340 
341     imp.before_emit(&wrap, &from_glib_borrow(platform_data))
342 }
application_command_line<T: ObjectSubclass>( ptr: *mut gio_sys::GApplication, command_line: *mut gio_sys::GApplicationCommandLine, ) -> i32 where T: ApplicationImpl,343 unsafe extern "C" fn application_command_line<T: ObjectSubclass>(
344     ptr: *mut gio_sys::GApplication,
345     command_line: *mut gio_sys::GApplicationCommandLine,
346 ) -> i32
347 where
348     T: ApplicationImpl,
349 {
350     let instance = &*(ptr as *mut T::Instance);
351     let imp = instance.get_impl();
352     let wrap: Application = from_glib_borrow(ptr);
353 
354     imp.command_line(&wrap, &from_glib_borrow(command_line))
355 }
application_local_command_line<T: ObjectSubclass>( ptr: *mut gio_sys::GApplication, arguments: *mut *mut *mut c_char, exit_status: *mut i32, ) -> glib_sys::gboolean where T: ApplicationImpl,356 unsafe extern "C" fn application_local_command_line<T: ObjectSubclass>(
357     ptr: *mut gio_sys::GApplication,
358     arguments: *mut *mut *mut c_char,
359     exit_status: *mut i32,
360 ) -> glib_sys::gboolean
361 where
362     T: ApplicationImpl,
363 {
364     let instance = &*(ptr as *mut T::Instance);
365     let imp = instance.get_impl();
366     let wrap: Application = from_glib_borrow(ptr);
367 
368     let mut args = ArgumentList::new(arguments);
369     let res = imp.local_command_line(&wrap, &mut args);
370     args.refresh();
371 
372     match res {
373         Some(ret) => {
374             ptr::write(exit_status, ret);
375             glib_sys::GTRUE
376         }
377         None => glib_sys::GFALSE,
378     }
379 }
application_open<T: ObjectSubclass>( ptr: *mut gio_sys::GApplication, files: *mut *mut gio_sys::GFile, num_files: i32, hint: *const c_char, ) where T: ApplicationImpl,380 unsafe extern "C" fn application_open<T: ObjectSubclass>(
381     ptr: *mut gio_sys::GApplication,
382     files: *mut *mut gio_sys::GFile,
383     num_files: i32,
384     hint: *const c_char,
385 ) where
386     T: ApplicationImpl,
387 {
388     let instance = &*(ptr as *mut T::Instance);
389     let imp = instance.get_impl();
390     let wrap: Application = from_glib_borrow(ptr);
391 
392     let files: Vec<::File> = FromGlibContainer::from_glib_none_num(files, num_files as usize);
393     imp.open(
394         &wrap,
395         files.as_slice(),
396         &glib::GString::from_glib_borrow(hint),
397     )
398 }
application_quit_mainloop<T: ObjectSubclass>(ptr: *mut gio_sys::GApplication) where T: ApplicationImpl,399 unsafe extern "C" fn application_quit_mainloop<T: ObjectSubclass>(ptr: *mut gio_sys::GApplication)
400 where
401     T: ApplicationImpl,
402 {
403     let instance = &*(ptr as *mut T::Instance);
404     let imp = instance.get_impl();
405     let wrap: Application = from_glib_borrow(ptr);
406 
407     imp.quit_mainloop(&wrap)
408 }
application_run_mainloop<T: ObjectSubclass>(ptr: *mut gio_sys::GApplication) where T: ApplicationImpl,409 unsafe extern "C" fn application_run_mainloop<T: ObjectSubclass>(ptr: *mut gio_sys::GApplication)
410 where
411     T: ApplicationImpl,
412 {
413     let instance = &*(ptr as *mut T::Instance);
414     let imp = instance.get_impl();
415     let wrap: Application = from_glib_borrow(ptr);
416 
417     imp.run_mainloop(&wrap)
418 }
application_shutdown<T: ObjectSubclass>(ptr: *mut gio_sys::GApplication) where T: ApplicationImpl,419 unsafe extern "C" fn application_shutdown<T: ObjectSubclass>(ptr: *mut gio_sys::GApplication)
420 where
421     T: ApplicationImpl,
422 {
423     let instance = &*(ptr as *mut T::Instance);
424     let imp = instance.get_impl();
425     let wrap: Application = from_glib_borrow(ptr);
426 
427     imp.shutdown(&wrap)
428 }
application_startup<T: ObjectSubclass>(ptr: *mut gio_sys::GApplication) where T: ApplicationImpl,429 unsafe extern "C" fn application_startup<T: ObjectSubclass>(ptr: *mut gio_sys::GApplication)
430 where
431     T: ApplicationImpl,
432 {
433     let instance = &*(ptr as *mut T::Instance);
434     let imp = instance.get_impl();
435     let wrap: Application = from_glib_borrow(ptr);
436 
437     imp.startup(&wrap)
438 }
439 
440 #[cfg(test)]
441 mod tests {
442     use super::*;
443     use crate::prelude::*;
444     use glib;
445     use glib::prelude::*;
446     use glib::subclass;
447 
448     const EXIT_STATUS: i32 = 20;
449 
450     struct SimpleApplication;
451 
452     impl ObjectSubclass for SimpleApplication {
453         const NAME: &'static str = "SimpleApplication";
454         type ParentType = Application;
455         type Instance = subclass::simple::InstanceStruct<Self>;
456         type Class = subclass::simple::ClassStruct<Self>;
457 
458         glib_object_subclass!();
459 
new() -> Self460         fn new() -> Self {
461             Self
462         }
463     }
464 
465     impl ObjectImpl for SimpleApplication {
466         glib_object_impl!();
467     }
468 
469     impl ApplicationImpl for SimpleApplication {
local_command_line( &self, _application: &Application, arguments: &mut ArgumentList, ) -> Option<i32>470         fn local_command_line(
471             &self,
472             _application: &Application,
473             arguments: &mut ArgumentList,
474         ) -> Option<i32> {
475             let mut rm = Vec::new();
476 
477             for (i, line) in arguments.iter().enumerate() {
478                 // TODO: we need https://github.com/rust-lang/rust/issues/49802
479                 let l = line.clone().into_string().unwrap();
480                 if l.starts_with("--local-") {
481                     rm.push(i)
482                 }
483             }
484 
485             rm.reverse();
486 
487             for i in rm.iter() {
488                 arguments.remove(*i);
489             }
490 
491             None
492         }
493 
command_line( &self, _application: &Application, cmd_line: &::ApplicationCommandLine, ) -> i32494         fn command_line(
495             &self,
496             _application: &Application,
497             cmd_line: &::ApplicationCommandLine,
498         ) -> i32 {
499             let arguments = cmd_line.get_arguments();
500 
501             for arg in arguments {
502                 // TODO: we need https://github.com/rust-lang/rust/issues/49802
503                 let a = arg.into_string().unwrap();
504                 assert!(!a.starts_with("--local-"))
505             }
506 
507             return EXIT_STATUS;
508         }
509     }
510 
511     #[test]
test_simple_application()512     fn test_simple_application() {
513         let app = glib::Object::new(
514             SimpleApplication::get_type(),
515             &[
516                 ("application-id", &"org.gtk-rs.SimpleApplication"),
517                 ("flags", &::ApplicationFlags::empty()),
518             ],
519         )
520         .unwrap()
521         .downcast::<::Application>()
522         .unwrap();
523 
524         app.set_inactivity_timeout(10000);
525 
526         assert!(app.run(&["--local".to_string()]) == EXIT_STATUS);
527     }
528 
529 }
530