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