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