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