1// Take a look at the license at the top of the repository in the LICENSE file.
2
3// rustdoc-stripper-ignore-next
4//! Module that contains all types needed for creating a direct subclass of `GObject`
5//! or implementing virtual methods of it.
6
7use std::{mem, ptr};
8
9use super::{prelude::*, Signal};
10use crate::{prelude::*, translate::*, Cast, Object, ParamSpec, Slice, Value};
11
12// rustdoc-stripper-ignore-next
13/// Trait for implementors of `glib::Object` subclasses.
14///
15/// This allows overriding the virtual methods of `glib::Object`. Except for
16/// `finalize` as implementing `Drop` would allow the same behavior.
17pub trait ObjectImpl: ObjectSubclass + ObjectImplExt {
18 // rustdoc-stripper-ignore-next
19 /// Properties installed for this type.
20 fn properties() -> &'static [ParamSpec] {
21 &[]
22 }
23
24 // rustdoc-stripper-ignore-next
25 /// Signals installed for this type.
26 fn signals() -> &'static [Signal] {
27 &[]
28 }
29
30 // rustdoc-stripper-ignore-next
31 /// Property setter.
32 ///
33 /// This is called whenever the property of this specific subclass with the
34 /// given index is set. The new value is passed as `glib::Value`.
35 ///
36 /// `value` is guaranteed to be of the correct type for the given property.
37 fn set_property(&self, _id: usize, _value: &Value, _pspec: &ParamSpec) {
38 unimplemented!()
39 }
40
41 // rustdoc-stripper-ignore-next
42 /// Property getter.
43 ///
44 /// This is called whenever the property value of the specific subclass with the
45 /// given index should be returned.
46 ///
47 /// The returned `Value` must be of the correct type for the given property.
48 #[doc(alias = "get_property")]
49 fn property(&self, _id: usize, _pspec: &ParamSpec) -> Value {
50 unimplemented!()
51 }
52
53 // rustdoc-stripper-ignore-next
54 /// Constructed.
55 ///
56 /// This is called once construction of the instance is finished.
57 ///
58 /// Should chain up to the parent class' implementation.
59 fn constructed(&self) {
60 self.parent_constructed();
61 }
62
63 // rustdoc-stripper-ignore-next
64 /// Disposes of the object.
65 ///
66 /// When `dispose()` ends, the object should not hold any reference to any other member object.
67 /// The object is also expected to be able to answer client method invocations (with possibly an
68 /// error code but no memory violation) until it is dropped. `dispose()` can be executed more
69 /// than once.
70 fn dispose(&self) {}
71
72 // rustdoc-stripper-ignore-next
73 /// Function to be called when property change is notified for with
74 /// `self.notify("property")`.
75 fn notify(&self, pspec: &ParamSpec) {
76 self.parent_notify(pspec)
77 }
78
79 fn dispatch_properties_changed(&self, pspecs: &[ParamSpec]) {
80 self.parent_dispatch_properties_changed(pspecs)
81 }
82}
83
84#[doc(alias = "get_property")]
85unsafe extern "C" fn property<T: ObjectImpl>(
86 obj: *mut gobject_ffi::GObject,
87 id: u32,
88 value: *mut gobject_ffi::GValue,
89 pspec: *mut gobject_ffi::GParamSpec,
90) {
91 let instance: &::Instance = &*(obj as *mut T::Instance);
92 let imp: &T = instance.imp();
93
94 let v: Value = imp.property(id as usize, &from_glib_borrow(ptr:pspec));
95
96 // We first unset the value we get passed in, in case it contained
97 // any previous data. Then we directly overwrite it with our new
98 // value, and pass ownership of the contained data to the C GValue
99 // by forgetting it on the Rust side.
100 //
101 // Without this, by using the GValue API, we would have to create
102 // a copy of the value when setting it on the destination just to
103 // immediately free the original value afterwards.
104 gobject_ffi::g_value_unset(value);
105 let v: ManuallyDrop = mem::ManuallyDrop::new(v);
106 ptr::write(dst:value, src:ptr::read(src:v.to_glib_none().0));
107}
108
109unsafe extern "C" fn set_property<T: ObjectImpl>(
110 obj: *mut gobject_ffi::GObject,
111 id: u32,
112 value: *mut gobject_ffi::GValue,
113 pspec: *mut gobject_ffi::GParamSpec,
114) {
115 let instance: &::Instance = &*(obj as *mut T::Instance);
116 let imp: &T = instance.imp();
117 imp.set_property(
118 id as usize,
119 &*(value as *mut Value),
120 &from_glib_borrow(ptr:pspec),
121 );
122}
123
124unsafe extern "C" fn constructed<T: ObjectImpl>(obj: *mut gobject_ffi::GObject) {
125 let instance: &::Instance = &*(obj as *mut T::Instance);
126 let imp: &T = instance.imp();
127
128 imp.constructed();
129}
130
131unsafe extern "C" fn notify<T: ObjectImpl>(
132 obj: *mut gobject_ffi::GObject,
133 pspec: *mut gobject_ffi::GParamSpec,
134) {
135 let instance: &::Instance = &*(obj as *mut T::Instance);
136 let imp: &T = instance.imp();
137 imp.notify(&from_glib_borrow(ptr:pspec));
138}
139
140unsafe extern "C" fn dispatch_properties_changed<T: ObjectImpl>(
141 obj: *mut gobject_ffi::GObject,
142 n_pspecs: u32,
143 pspecs: *mut *mut gobject_ffi::GParamSpec,
144) {
145 let instance: &::Instance = &*(obj as *mut T::Instance);
146 let imp: &T = instance.imp();
147 imp.dispatch_properties_changed(pspecs:Slice::from_glib_borrow_num(ptr:pspecs, len:n_pspecs as _));
148}
149
150unsafe extern "C" fn dispose<T: ObjectImpl>(obj: *mut gobject_ffi::GObject) {
151 let instance: &::Instance = &*(obj as *mut T::Instance);
152 let imp: &T = instance.imp();
153
154 imp.dispose();
155
156 // Chain up to the parent's dispose.
157 let data: NonNull = T::type_data();
158 let parent_class: *mut GObjectClass = data.as_ref().parent_class() as *mut gobject_ffi::GObjectClass;
159 if let Some(ref func: &unsafe fn(*mut GObject)) = (*parent_class).dispose {
160 func(obj);
161 }
162}
163
164// rustdoc-stripper-ignore-next
165/// Trait containing only the property related functions of `ObjectImpl`.
166/// Implemented by the `Props` macro.
167/// When implementing `ObjectImpl` you may want to delegate the function calls to this trait.
168pub trait DerivedObjectProperties: ObjectSubclass {
169 // rustdoc-stripper-ignore-next
170 /// Properties installed for this type.
171 fn derived_properties() -> &'static [ParamSpec] {
172 &[]
173 }
174
175 // rustdoc-stripper-ignore-next
176 /// Similar to [`ObjectImpl`](trait.ObjectImpl.html) but auto-generated by the [`Properties`] macro
177 /// to allow handling more complex use-cases.
178 fn derived_set_property(&self, _id: usize, _value: &Value, _pspec: &ParamSpec) {
179 unimplemented!()
180 }
181
182 // rustdoc-stripper-ignore-next
183 /// Similar to [`ObjectImpl`](trait.ObjectImpl.html) but auto-generated by the [`Properties`] macro
184 /// to allow handling more complex use-cases.
185 fn derived_property(&self, _id: usize, _pspec: &ParamSpec) -> Value {
186 unimplemented!()
187 }
188}
189
190// rustdoc-stripper-ignore-next
191/// Extension trait for `glib::Object`'s class struct.
192///
193/// This contains various class methods and allows subclasses to override signal class handlers.
194pub unsafe trait ObjectClassSubclassExt: Sized + 'static {
195 fn override_signal_class_handler<F>(&mut self, name: &str, class_handler: F)
196 where
197 F: Fn(&super::SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
198 {
199 unsafe {
200 super::types::signal_override_class_handler(
201 name,
202 *(self as *mut _ as *mut ffi::GType),
203 class_handler,
204 );
205 }
206 }
207}
208
209unsafe impl ObjectClassSubclassExt for crate::Class<Object> {}
210
211unsafe impl<T: ObjectImpl> IsSubclassable<T> for Object {
212 fn class_init(class: &mut crate::Class<Self>) {
213 let klass = class.as_mut();
214 klass.set_property = Some(set_property::<T>);
215 klass.get_property = Some(property::<T>);
216 klass.constructed = Some(constructed::<T>);
217 klass.notify = Some(notify::<T>);
218 klass.dispatch_properties_changed = Some(dispatch_properties_changed::<T>);
219 klass.dispose = Some(dispose::<T>);
220
221 let pspecs = <T as ObjectImpl>::properties();
222 if !pspecs.is_empty() {
223 unsafe {
224 let mut pspecs_ptrs = Vec::with_capacity(pspecs.len() + 1);
225
226 pspecs_ptrs.push(ptr::null_mut());
227
228 for pspec in pspecs {
229 pspecs_ptrs.push(pspec.to_glib_none().0);
230 }
231
232 gobject_ffi::g_object_class_install_properties(
233 klass,
234 pspecs_ptrs.len() as u32,
235 pspecs_ptrs.as_mut_ptr(),
236 );
237 }
238 }
239
240 let type_ = T::type_();
241 let signals = <T as ObjectImpl>::signals();
242 for signal in signals {
243 signal.register(type_);
244 }
245 }
246
247 #[inline]
248 fn instance_init(_instance: &mut super::InitializingObject<T>) {}
249}
250
251mod sealed {
252 pub trait Sealed {}
253 impl<T: super::ObjectImplExt> Sealed for T {}
254}
255
256pub trait ObjectImplExt: sealed::Sealed + ObjectSubclass {
257 // rustdoc-stripper-ignore-next
258 /// Chain up to the parent class' implementation of `glib::Object::constructed()`.
259 #[inline]
260 fn parent_constructed(&self) {
261 unsafe {
262 let data = Self::type_data();
263 let parent_class = data.as_ref().parent_class() as *mut gobject_ffi::GObjectClass;
264
265 if let Some(ref func) = (*parent_class).constructed {
266 func(self.obj().unsafe_cast_ref::<Object>().to_glib_none().0);
267 }
268 }
269 }
270
271 // rustdoc-stripper-ignore-next
272 /// Chain up to the parent class' implementation of `glib::Object::notify()`.
273 #[inline]
274 fn parent_notify(&self, pspec: &ParamSpec) {
275 unsafe {
276 let data = Self::type_data();
277 let parent_class = data.as_ref().parent_class() as *mut gobject_ffi::GObjectClass;
278
279 if let Some(ref func) = (*parent_class).notify {
280 func(
281 self.obj().unsafe_cast_ref::<Object>().to_glib_none().0,
282 pspec.to_glib_none().0,
283 );
284 }
285 }
286 }
287
288 // rustdoc-stripper-ignore-next
289 /// Chain up to the parent class' implementation of `glib::Object::dispatch_properties_changed()`.
290 #[inline]
291 fn parent_dispatch_properties_changed(&self, pspecs: &[ParamSpec]) {
292 unsafe {
293 let data = Self::type_data();
294 let parent_class = data.as_ref().parent_class() as *mut gobject_ffi::GObjectClass;
295
296 if let Some(ref func) = (*parent_class).dispatch_properties_changed {
297 func(
298 self.obj().unsafe_cast_ref::<Object>().to_glib_none().0,
299 pspecs.len() as _,
300 pspecs.as_ptr() as *mut _,
301 );
302 }
303 }
304 }
305
306 // rustdoc-stripper-ignore-next
307 /// Chain up to parent class signal handler.
308 fn signal_chain_from_overridden(
309 &self,
310 token: &super::SignalClassHandlerToken,
311 values: &[Value],
312 ) -> Option<Value> {
313 unsafe {
314 super::types::signal_chain_from_overridden(self.obj().as_ptr() as *mut _, token, values)
315 }
316 }
317}
318
319impl<T: ObjectImpl> ObjectImplExt for T {}
320
321#[cfg(test)]
322mod test {
323 use std::cell::RefCell;
324
325 use super::*;
326 // We rename the current crate as glib, since the macros in glib-macros
327 // generate the glib namespace through the crate_ident_new utility,
328 // and that returns `glib` (and not `crate`) when called inside the glib crate
329 use crate as glib;
330
331 mod imp {
332 use super::*;
333
334 // A dummy `Object` to test setting an `Object` property and returning an `Object` in signals
335 #[derive(Default)]
336 pub struct ChildObject;
337
338 #[glib::object_subclass]
339 impl ObjectSubclass for ChildObject {
340 const NAME: &'static str = "ChildObject";
341 type Type = super::ChildObject;
342 }
343
344 impl ObjectImpl for ChildObject {}
345
346 #[derive(Default)]
347 pub struct SimpleObject {
348 name: RefCell<Option<String>>,
349 construct_name: RefCell<Option<String>>,
350 constructed: RefCell<bool>,
351 }
352
353 #[glib::object_subclass]
354 impl ObjectSubclass for SimpleObject {
355 const NAME: &'static str = "SimpleObject";
356 type Type = super::SimpleObject;
357 type Interfaces = (super::Dummy,);
358 }
359
360 impl ObjectImpl for SimpleObject {
361 fn properties() -> &'static [ParamSpec] {
362 use once_cell::sync::Lazy;
363 static PROPERTIES: Lazy<Vec<ParamSpec>> = Lazy::new(|| {
364 vec![
365 crate::ParamSpecString::builder("name").build(),
366 crate::ParamSpecString::builder("construct-name")
367 .construct_only()
368 .build(),
369 crate::ParamSpecBoolean::builder("constructed")
370 .read_only()
371 .build(),
372 crate::ParamSpecObject::builder::<super::ChildObject>("child").build(),
373 ]
374 });
375
376 PROPERTIES.as_ref()
377 }
378
379 fn signals() -> &'static [super::Signal] {
380 use once_cell::sync::Lazy;
381 static SIGNALS: Lazy<Vec<super::Signal>> = Lazy::new(|| {
382 vec![
383 super::Signal::builder("name-changed")
384 .param_types([String::static_type()])
385 .build(),
386 super::Signal::builder("change-name")
387 .param_types([String::static_type()])
388 .return_type::<String>()
389 .action()
390 .class_handler(|_, args| {
391 let obj = args[0]
392 .get::<super::SimpleObject>()
393 .expect("Failed to get Object from args[0]");
394 let new_name = args[1]
395 .get::<String>()
396 .expect("Failed to get Object from args[1]");
397 let imp = obj.imp();
398
399 let old_name = imp.name.replace(Some(new_name));
400
401 obj.emit_by_name::<()>("name-changed", &[&*imp.name.borrow()]);
402
403 Some(old_name.to_value())
404 })
405 .build(),
406 super::Signal::builder("create-string")
407 .return_type::<String>()
408 .build(),
409 super::Signal::builder("create-child-object")
410 .return_type::<super::ChildObject>()
411 .build(),
412 ]
413 });
414
415 SIGNALS.as_ref()
416 }
417
418 fn set_property(&self, _id: usize, value: &Value, pspec: &crate::ParamSpec) {
419 match pspec.name() {
420 "name" => {
421 let name = value
422 .get()
423 .expect("type conformity checked by 'Object::set_property'");
424 self.name.replace(name);
425 self.obj()
426 .emit_by_name::<()>("name-changed", &[&*self.name.borrow()]);
427 }
428 "construct-name" => {
429 let name = value
430 .get()
431 .expect("type conformity checked by 'Object::set_property'");
432 self.construct_name.replace(name);
433 }
434 "child" => {
435 // not stored, only used to test `set_property` with `Objects`
436 }
437 _ => unimplemented!(),
438 }
439 }
440
441 fn property(&self, _id: usize, pspec: &crate::ParamSpec) -> Value {
442 match pspec.name() {
443 "name" => self.name.borrow().to_value(),
444 "construct-name" => self.construct_name.borrow().to_value(),
445 "constructed" => self.constructed.borrow().to_value(),
446 _ => unimplemented!(),
447 }
448 }
449
450 fn constructed(&self) {
451 self.parent_constructed();
452
453 debug_assert_eq!(self as *const _, self.obj().imp() as *const _);
454
455 *self.constructed.borrow_mut() = true;
456 }
457 }
458
459 #[derive(Clone, Copy)]
460 #[repr(C)]
461 pub struct DummyInterface {
462 parent: gobject_ffi::GTypeInterface,
463 }
464
465 #[glib::object_interface]
466 unsafe impl ObjectInterface for DummyInterface {
467 const NAME: &'static str = "Dummy";
468 }
469 }
470
471 wrapper! {
472 pub struct ChildObject(ObjectSubclass<imp::ChildObject>);
473 }
474
475 wrapper! {
476 pub struct SimpleObject(ObjectSubclass<imp::SimpleObject>);
477 }
478
479 wrapper! {
480 pub struct Dummy(ObjectInterface<imp::DummyInterface>);
481 }
482
483 unsafe impl<T: ObjectSubclass> IsImplementable<T> for Dummy {}
484
485 #[test]
486 fn test_create() {
487 let type_ = SimpleObject::static_type();
488 let obj = Object::with_type(type_);
489
490 assert!(obj.type_().is_a(Dummy::static_type()));
491
492 // Assert that the object representation is equivalent to the underlying C GObject pointer
493 assert_eq!(
494 mem::size_of::<SimpleObject>(),
495 mem::size_of::<ffi::gpointer>()
496 );
497 assert_eq!(obj.as_ptr() as ffi::gpointer, unsafe {
498 *(&obj as *const _ as *const ffi::gpointer)
499 });
500
501 assert!(obj.property::<bool>("constructed"));
502
503 let weak = obj.downgrade();
504 drop(obj);
505 assert!(weak.upgrade().is_none());
506 }
507
508 #[test]
509 fn test_properties() {
510 let type_ = SimpleObject::static_type();
511 let obj = Object::with_type(type_);
512
513 assert!(obj.type_().is_a(Dummy::static_type()));
514
515 let properties = obj.list_properties();
516 assert_eq!(properties.len(), 4);
517 assert_eq!(properties[0].name(), "name");
518 assert_eq!(properties[1].name(), "construct-name");
519 assert_eq!(properties[2].name(), "constructed");
520 assert_eq!(properties[3].name(), "child");
521 }
522
523 #[test]
524 fn test_create_child_object() {
525 let obj: ChildObject = Object::new();
526
527 assert_eq!(&obj, obj.imp().obj().as_ref());
528 }
529
530 #[test]
531 fn test_builder() {
532 let obj = Object::builder::<SimpleObject>()
533 .property("construct-name", "meh")
534 .property("name", "initial")
535 .build();
536
537 assert_eq!(
538 obj.property::<String>("construct-name"),
539 String::from("meh")
540 );
541
542 assert_eq!(obj.property::<String>("name"), String::from("initial"));
543 }
544
545 #[test]
546 fn test_set_property() {
547 let obj = Object::builder::<SimpleObject>()
548 .property("construct-name", "meh")
549 .property("name", "initial")
550 .build();
551
552 assert_eq!(
553 obj.property::<String>("construct-name"),
554 String::from("meh")
555 );
556
557 assert_eq!(
558 obj.property::<String>("construct-name"),
559 String::from("meh")
560 );
561
562 assert_eq!(obj.property::<String>("name"), String::from("initial"));
563 obj.set_property("name", "test");
564 assert_eq!(obj.property::<String>("name"), String::from("test"));
565
566 let child = Object::with_type(ChildObject::static_type());
567 obj.set_property("child", &child);
568 }
569
570 #[test]
571 #[should_panic = "property 'construct-name' of type 'SimpleObject' is not writable"]
572 fn test_set_property_non_writable() {
573 let obj = Object::builder::<SimpleObject>()
574 .property("construct-name", "meh")
575 .property("name", "initial")
576 .build();
577
578 obj.set_property("construct-name", "test");
579 }
580
581 #[test]
582 #[should_panic = "property 'test' of type 'SimpleObject' not found"]
583 fn test_set_property_not_found() {
584 let obj = Object::builder::<SimpleObject>()
585 .property("construct-name", "meh")
586 .property("name", "initial")
587 .build();
588
589 obj.set_property("test", true);
590 }
591
592 #[test]
593 #[should_panic = "property 'constructed' of type 'SimpleObject' is not writable"]
594 fn test_set_property_not_writable() {
595 let obj = Object::builder::<SimpleObject>()
596 .property("construct-name", "meh")
597 .property("name", "initial")
598 .build();
599
600 obj.set_property("constructed", false);
601 }
602
603 #[test]
604 #[should_panic = "property 'name' of type 'SimpleObject' can't be set from the given type (expected: 'gchararray', got: 'gboolean')"]
605 fn test_set_property_wrong_type() {
606 let obj = Object::builder::<SimpleObject>()
607 .property("construct-name", "meh")
608 .property("name", "initial")
609 .build();
610
611 obj.set_property("name", false);
612 }
613
614 #[test]
615 #[should_panic = "property 'child' of type 'SimpleObject' can't be set from the given type (expected: 'ChildObject', got: 'SimpleObject')"]
616 fn test_set_property_wrong_type_2() {
617 let obj = Object::builder::<SimpleObject>()
618 .property("construct-name", "meh")
619 .property("name", "initial")
620 .build();
621
622 let other_obj = Object::with_type(SimpleObject::static_type());
623
624 obj.set_property("child", &other_obj);
625 }
626
627 #[test]
628 #[should_panic = "Can't set construct property 'construct-name' for type 'SimpleObject' twice"]
629 fn test_construct_property_set_twice() {
630 let _obj = Object::builder::<SimpleObject>()
631 .property("construct-name", "meh")
632 .property("construct-name", "meh2")
633 .build();
634 }
635
636 #[test]
637 fn test_signals() {
638 use std::sync::{
639 atomic::{AtomicBool, Ordering},
640 Arc,
641 };
642
643 let obj = Object::builder::<SimpleObject>()
644 .property("name", "old-name")
645 .build();
646
647 let name_changed_triggered = Arc::new(AtomicBool::new(false));
648 let name_changed_clone = name_changed_triggered.clone();
649 obj.connect("name-changed", false, move |args| {
650 let _obj = args[0].get::<Object>().expect("Failed to get args[0]");
651 let name = args[1].get::<&str>().expect("Failed to get args[1]");
652
653 assert_eq!(name, "new-name");
654 name_changed_clone.store(true, Ordering::Relaxed);
655
656 None
657 });
658
659 assert_eq!(obj.property::<String>("name"), String::from("old-name"));
660 assert!(!name_changed_triggered.load(Ordering::Relaxed));
661
662 assert_eq!(
663 obj.emit_by_name::<String>("change-name", &[&"new-name"]),
664 "old-name"
665 );
666 assert!(name_changed_triggered.load(Ordering::Relaxed));
667 }
668
669 #[test]
670 fn test_signal_return_expected_type() {
671 let obj = Object::with_type(SimpleObject::static_type());
672
673 obj.connect("create-string", false, move |_args| {
674 Some("return value".to_value())
675 });
676
677 let signal_id = imp::SimpleObject::signals()[2].signal_id();
678
679 let value = obj.emit::<String>(signal_id, &[]);
680 assert_eq!(value, "return value");
681 }
682
683 #[test]
684 fn test_callback_validity() {
685 use std::sync::{
686 atomic::{AtomicBool, Ordering},
687 Arc,
688 };
689
690 let obj = Object::builder::<SimpleObject>()
691 .property("name", "old-name")
692 .build();
693
694 let name_changed_triggered = Arc::new(AtomicBool::new(false));
695 let name_changed_clone = name_changed_triggered.clone();
696
697 obj.connect_notify(Some("name"), move |_, _| {
698 name_changed_clone.store(true, Ordering::Relaxed);
699 });
700 obj.notify("name");
701 assert!(name_changed_triggered.load(Ordering::Relaxed));
702 }
703
704 // Note: can't test type mismatch in signals since panics accross FFI boundaries
705 // are UB. See https://github.com/gtk-rs/glib/issues/518
706
707 #[test]
708 fn test_signal_return_expected_object_type() {
709 let obj = Object::with_type(SimpleObject::static_type());
710
711 obj.connect("create-child-object", false, move |_args| {
712 Some(Object::with_type(ChildObject::static_type()).to_value())
713 });
714 let value: glib::Object = obj.emit_by_name("create-child-object", &[]);
715 assert!(value.type_().is_a(ChildObject::static_type()));
716 }
717}
718