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