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