1 // Copyright 2017-2018, 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 //! Module that contains all types needed for creating a direct subclass of `GObject`
6 //! or implementing virtual methods of it.
7 
8 use super::prelude::*;
9 use super::types;
10 use glib_sys;
11 use gobject_sys;
12 use std::borrow::Borrow;
13 use std::fmt;
14 use std::mem;
15 use std::ptr;
16 use translate::*;
17 use {Object, ObjectClass, ObjectType, SignalFlags, Type, Value};
18 
19 #[macro_export]
20 /// Macro for boilerplate of [`ObjectImpl`] implementations.
21 ///
22 /// [`ObjectImpl`]: subclass/object/trait.ObjectImpl.html
23 macro_rules! glib_object_impl {
24     () => {
25         fn get_type_data(&self) -> ::std::ptr::NonNull<$crate::subclass::TypeData> {
26             Self::type_data()
27         }
28     };
29 }
30 
31 /// Trait for implementors of `glib::Object` subclasses.
32 ///
33 /// This allows overriding the virtual methods of `glib::Object`.
34 pub trait ObjectImpl: ObjectImplExt + 'static {
35     /// Storage for the type-specific data used during registration.
36     ///
37     /// This is usually generated by the [`glib_object_impl!`] macro.
38     ///
39     /// [`glib_object_impl!`]: ../../macro.glib_object_impl.html
get_type_data(&self) -> ptr::NonNull<types::TypeData>40     fn get_type_data(&self) -> ptr::NonNull<types::TypeData>;
41 
42     /// Property setter.
43     ///
44     /// This is called whenever the property of this specific subclass with the
45     /// given index is set. The new value is passed as `glib::Value`.
set_property(&self, _obj: &Object, _id: usize, _value: &Value)46     fn set_property(&self, _obj: &Object, _id: usize, _value: &Value) {
47         unimplemented!()
48     }
49 
50     /// Property getter.
51     ///
52     /// This is called whenever the property value of the specific subclass with the
53     /// given index should be returned.
get_property(&self, _obj: &Object, _id: usize) -> Result<Value, ()>54     fn get_property(&self, _obj: &Object, _id: usize) -> Result<Value, ()> {
55         unimplemented!()
56     }
57 
58     /// Constructed.
59     ///
60     /// This is called once construction of the instance is finished.
61     ///
62     /// Should chain up to the parent class' implementation.
constructed(&self, obj: &Object)63     fn constructed(&self, obj: &Object) {
64         self.parent_constructed(obj);
65     }
66 }
67 
get_property<T: ObjectSubclass>( obj: *mut gobject_sys::GObject, id: u32, value: *mut gobject_sys::GValue, _pspec: *mut gobject_sys::GParamSpec, )68 unsafe extern "C" fn get_property<T: ObjectSubclass>(
69     obj: *mut gobject_sys::GObject,
70     id: u32,
71     value: *mut gobject_sys::GValue,
72     _pspec: *mut gobject_sys::GParamSpec,
73 ) {
74     let instance = &*(obj as *mut T::Instance);
75     let imp = instance.get_impl();
76 
77     match imp.get_property(&from_glib_borrow(obj), (id - 1) as usize) {
78         Ok(v) => {
79             // We first unset the value we get passed in, in case it contained
80             // any previous data. Then we directly overwrite it with our new
81             // value, and pass ownership of the contained data to the C GValue
82             // by forgetting it on the Rust side.
83             //
84             // Without this, by using the GValue API, we would have to create
85             // a copy of the value when setting it on the destination just to
86             // immediately free the original value afterwards.
87             gobject_sys::g_value_unset(value);
88             ptr::write(value, ptr::read(v.to_glib_none().0));
89             mem::forget(v);
90         }
91         Err(()) => eprintln!("Failed to get property"),
92     }
93 }
94 
set_property<T: ObjectSubclass>( obj: *mut gobject_sys::GObject, id: u32, value: *mut gobject_sys::GValue, _pspec: *mut gobject_sys::GParamSpec, )95 unsafe extern "C" fn set_property<T: ObjectSubclass>(
96     obj: *mut gobject_sys::GObject,
97     id: u32,
98     value: *mut gobject_sys::GValue,
99     _pspec: *mut gobject_sys::GParamSpec,
100 ) {
101     let instance = &*(obj as *mut T::Instance);
102     let imp = instance.get_impl();
103     imp.set_property(
104         &from_glib_borrow(obj),
105         (id - 1) as usize,
106         &*(value as *mut Value),
107     );
108 }
109 
constructed<T: ObjectSubclass>(obj: *mut gobject_sys::GObject)110 unsafe extern "C" fn constructed<T: ObjectSubclass>(obj: *mut gobject_sys::GObject) {
111     let instance = &*(obj as *mut T::Instance);
112     let imp = instance.get_impl();
113 
114     imp.constructed(&from_glib_borrow(obj));
115 }
116 
117 /// Definition of a property.
118 #[derive(Clone)]
119 pub struct Property<'a>(pub &'a str, pub fn(&str) -> ::ParamSpec);
120 
121 impl<'a> fmt::Debug for Property<'a> {
fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>122     fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
123         f.debug_tuple("Property").field(&self.0).finish()
124     }
125 }
126 
127 /// Extension trait for `glib::Object`'s class struct.
128 ///
129 /// This contains various class methods and allows subclasses to override the virtual methods.
130 pub unsafe trait ObjectClassSubclassExt: Sized + 'static {
131     /// Install properties on the subclass.
132     ///
133     /// The index in the properties array is going to be the index passed to the
134     /// property setters and getters.
install_properties<'a, T: Borrow<Property<'a>>>(&mut self, properties: &[T])135     fn install_properties<'a, T: Borrow<Property<'a>>>(&mut self, properties: &[T]) {
136         if properties.is_empty() {
137             return;
138         }
139 
140         let mut pspecs = Vec::with_capacity(properties.len());
141 
142         for property in properties {
143             let property = property.borrow();
144             let pspec = (property.1)(property.0);
145             pspecs.push(pspec);
146         }
147 
148         unsafe {
149             let mut pspecs_ptrs = Vec::with_capacity(properties.len());
150 
151             pspecs_ptrs.push(ptr::null_mut());
152 
153             for pspec in &pspecs {
154                 pspecs_ptrs.push(pspec.to_glib_none().0);
155             }
156 
157             gobject_sys::g_object_class_install_properties(
158                 self as *mut _ as *mut gobject_sys::GObjectClass,
159                 pspecs_ptrs.len() as u32,
160                 pspecs_ptrs.as_mut_ptr(),
161             );
162         }
163     }
164 
165     /// Add a new signal to the subclass.
166     ///
167     /// This can be emitted later by `glib::Object::emit` and external code
168     /// can connect to the signal to get notified about emissions.
add_signal(&mut self, name: &str, flags: SignalFlags, arg_types: &[Type], ret_type: Type)169     fn add_signal(&mut self, name: &str, flags: SignalFlags, arg_types: &[Type], ret_type: Type) {
170         unsafe {
171             super::types::add_signal(
172                 *(self as *mut _ as *mut glib_sys::GType),
173                 name,
174                 flags,
175                 arg_types,
176                 ret_type,
177             );
178         }
179     }
180 
181     /// Add a new signal with class handler to the subclass.
182     ///
183     /// This can be emitted later by `glib::Object::emit` and external code
184     /// can connect to the signal to get notified about emissions.
185     ///
186     /// The class handler will be called during the signal emission at the corresponding stage.
add_signal_with_class_handler<F>( &mut self, name: &str, flags: SignalFlags, arg_types: &[Type], ret_type: Type, class_handler: F, ) where F: Fn(&super::SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,187     fn add_signal_with_class_handler<F>(
188         &mut self,
189         name: &str,
190         flags: SignalFlags,
191         arg_types: &[Type],
192         ret_type: Type,
193         class_handler: F,
194     ) where
195         F: Fn(&super::SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
196     {
197         unsafe {
198             super::types::add_signal_with_class_handler(
199                 *(self as *mut _ as *mut glib_sys::GType),
200                 name,
201                 flags,
202                 arg_types,
203                 ret_type,
204                 class_handler,
205             );
206         }
207     }
208 
209     /// Add a new signal with accumulator to the subclass.
210     ///
211     /// This can be emitted later by `glib::Object::emit` and external code
212     /// can connect to the signal to get notified about emissions.
213     ///
214     /// The accumulator function is used for accumulating the return values of
215     /// multiple signal handlers. The new value is passed as second argument and
216     /// should be combined with the old value in the first argument. If no further
217     /// signal handlers should be called, `false` should be returned.
add_signal_with_accumulator<F>( &mut self, name: &str, flags: SignalFlags, arg_types: &[Type], ret_type: Type, accumulator: F, ) where F: Fn(&super::SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static,218     fn add_signal_with_accumulator<F>(
219         &mut self,
220         name: &str,
221         flags: SignalFlags,
222         arg_types: &[Type],
223         ret_type: Type,
224         accumulator: F,
225     ) where
226         F: Fn(&super::SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static,
227     {
228         unsafe {
229             super::types::add_signal_with_accumulator(
230                 *(self as *mut _ as *mut glib_sys::GType),
231                 name,
232                 flags,
233                 arg_types,
234                 ret_type,
235                 accumulator,
236             );
237         }
238     }
239 
240     /// Add a new signal with accumulator and class handler to the subclass.
241     ///
242     /// This can be emitted later by `glib::Object::emit` and external code
243     /// can connect to the signal to get notified about emissions.
244     ///
245     /// The accumulator function is used for accumulating the return values of
246     /// multiple signal handlers. The new value is passed as second argument and
247     /// should be combined with the old value in the first argument. If no further
248     /// signal handlers should be called, `false` should be returned.
249     ///
250     /// The class handler will be called during the signal emission at the corresponding stage.
add_signal_with_class_handler_and_accumulator<F, G>( &mut self, name: &str, flags: SignalFlags, arg_types: &[Type], ret_type: Type, class_handler: F, accumulator: G, ) where F: Fn(&super::SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static, G: Fn(&super::SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static,251     fn add_signal_with_class_handler_and_accumulator<F, G>(
252         &mut self,
253         name: &str,
254         flags: SignalFlags,
255         arg_types: &[Type],
256         ret_type: Type,
257         class_handler: F,
258         accumulator: G,
259     ) where
260         F: Fn(&super::SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
261         G: Fn(&super::SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static,
262     {
263         unsafe {
264             super::types::add_signal_with_class_handler_and_accumulator(
265                 *(self as *mut _ as *mut glib_sys::GType),
266                 name,
267                 flags,
268                 arg_types,
269                 ret_type,
270                 class_handler,
271                 accumulator,
272             );
273         }
274     }
275 
override_signal_class_handler<F>(&mut self, name: &str, class_handler: F) where F: Fn(&super::SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,276     fn override_signal_class_handler<F>(&mut self, name: &str, class_handler: F)
277     where
278         F: Fn(&super::SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
279     {
280         unsafe {
281             super::types::signal_override_class_handler(
282                 name,
283                 *(self as *mut _ as *mut glib_sys::GType),
284                 class_handler,
285             );
286         }
287     }
288 }
289 
290 unsafe impl ObjectClassSubclassExt for ObjectClass {}
291 
292 unsafe impl<T: ObjectSubclass> IsSubclassable<T> for ObjectClass {
override_vfuncs(&mut self)293     fn override_vfuncs(&mut self) {
294         unsafe {
295             let klass = &mut *(self as *mut Self as *mut gobject_sys::GObjectClass);
296             klass.set_property = Some(set_property::<T>);
297             klass.get_property = Some(get_property::<T>);
298             klass.constructed = Some(constructed::<T>);
299         }
300     }
301 }
302 
303 pub trait ObjectImplExt {
304     /// Chain up to the parent class' implementation of `glib::Object::constructed()`.
parent_constructed(&self, obj: &Object)305     fn parent_constructed(&self, obj: &Object);
306 
signal_chain_from_overridden( &self, token: &super::SignalClassHandlerToken, values: &[Value], ) -> Option<Value>307     fn signal_chain_from_overridden(
308         &self,
309         token: &super::SignalClassHandlerToken,
310         values: &[Value],
311     ) -> Option<Value>;
312 }
313 
314 impl<T: ObjectImpl + ObjectSubclass> ObjectImplExt for T {
parent_constructed(&self, obj: &Object)315     fn parent_constructed(&self, obj: &Object) {
316         unsafe {
317             let data = self.get_type_data();
318             let parent_class = data.as_ref().get_parent_class() as *mut gobject_sys::GObjectClass;
319 
320             if let Some(ref func) = (*parent_class).constructed {
321                 func(obj.to_glib_none().0);
322             }
323         }
324     }
325 
signal_chain_from_overridden( &self, token: &super::SignalClassHandlerToken, values: &[Value], ) -> Option<Value>326     fn signal_chain_from_overridden(
327         &self,
328         token: &super::SignalClassHandlerToken,
329         values: &[Value],
330     ) -> Option<Value> {
331         unsafe {
332             super::types::signal_chain_from_overridden(
333                 self.get_instance().as_ptr() as *mut _,
334                 token,
335                 values,
336             )
337         }
338     }
339 }
340 
341 #[cfg(test)]
342 mod test {
343     use super::super::super::object::ObjectExt;
344     use super::super::super::subclass;
345     use super::super::super::value::{ToValue, Value};
346     use super::*;
347     use prelude::*;
348 
349     use std::{cell::RefCell, error::Error};
350 
351     // A dummy `Object` to test setting an `Object` property and returning an `Object` in signals
352     pub struct ChildObject;
353     impl ObjectSubclass for ChildObject {
354         const NAME: &'static str = "ChildObject";
355         type ParentType = Object;
356         type Instance = subclass::simple::InstanceStruct<Self>;
357         type Class = subclass::simple::ClassStruct<Self>;
358 
359         glib_object_subclass!();
360 
new() -> Self361         fn new() -> Self {
362             ChildObject
363         }
364     }
365     impl ObjectImpl for ChildObject {
366         glib_object_impl!();
367     }
368     impl StaticType for ChildObject {
static_type() -> Type369         fn static_type() -> Type {
370             ChildObject::get_type()
371         }
372     }
373 
374     static PROPERTIES: [Property; 3] = [
375         Property("name", |name| {
376             ::ParamSpec::string(
377                 name,
378                 "Name",
379                 "Name of this object",
380                 None,
381                 ::ParamFlags::READWRITE,
382             )
383         }),
384         Property("constructed", |name| {
385             ::ParamSpec::boolean(
386                 name,
387                 "Constructed",
388                 "True if the constructed() virtual method was called",
389                 false,
390                 ::ParamFlags::READABLE,
391             )
392         }),
393         Property("child", |name| {
394             ::ParamSpec::object(
395                 name,
396                 "Child",
397                 "Child object",
398                 ChildObject::static_type(),
399                 ::ParamFlags::READWRITE,
400             )
401         }),
402     ];
403 
404     pub struct SimpleObject {
405         name: RefCell<Option<String>>,
406         constructed: RefCell<bool>,
407     }
408 
409     impl ObjectSubclass for SimpleObject {
410         const NAME: &'static str = "SimpleObject";
411         type ParentType = Object;
412         type Instance = subclass::simple::InstanceStruct<Self>;
413         type Class = subclass::simple::ClassStruct<Self>;
414 
415         glib_object_subclass!();
416 
type_init(type_: &mut subclass::InitializingType<Self>)417         fn type_init(type_: &mut subclass::InitializingType<Self>) {
418             type_.add_interface::<DummyInterface>();
419         }
420 
class_init(klass: &mut subclass::simple::ClassStruct<Self>)421         fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
422             klass.install_properties(&PROPERTIES);
423 
424             klass.add_signal(
425                 "name-changed",
426                 SignalFlags::RUN_LAST,
427                 &[String::static_type()],
428                 ::Type::Unit,
429             );
430 
431             klass.add_signal_with_class_handler(
432                 "change-name",
433                 SignalFlags::RUN_LAST | SignalFlags::ACTION,
434                 &[String::static_type()],
435                 String::static_type(),
436                 |_, args| {
437                     let obj = args[0]
438                         .get::<Object>()
439                         .expect("Failed to get args[0]")
440                         .expect("Failed to get Object from args[0]");
441                     let new_name = args[1]
442                         .get::<String>()
443                         .expect("Failed to get args[1]")
444                         .expect("Failed to get Object from args[1]");
445                     let imp = Self::from_instance(&obj);
446 
447                     let old_name = imp.name.borrow_mut().take();
448                     *imp.name.borrow_mut() = Some(new_name);
449 
450                     obj.emit("name-changed", &[&*imp.name.borrow()])
451                         .expect("Failed to borrow name");
452 
453                     Some(old_name.to_value())
454                 },
455             );
456 
457             klass.add_signal(
458                 "create-string",
459                 SignalFlags::RUN_LAST,
460                 &[],
461                 String::static_type(),
462             );
463 
464             klass.add_signal(
465                 "create-child-object",
466                 SignalFlags::RUN_LAST,
467                 &[],
468                 ChildObject::static_type(),
469             );
470         }
471 
new() -> Self472         fn new() -> Self {
473             Self {
474                 name: RefCell::new(None),
475                 constructed: RefCell::new(false),
476             }
477         }
478     }
479 
480     impl ObjectImpl for SimpleObject {
481         glib_object_impl!();
482 
set_property(&self, obj: &Object, id: usize, value: &Value)483         fn set_property(&self, obj: &Object, id: usize, value: &Value) {
484             let prop = &PROPERTIES[id];
485 
486             match *prop {
487                 Property("name", ..) => {
488                     let name = value
489                         .get()
490                         .expect("type conformity checked by 'Object::set_property'");
491                     self.name.replace(name);
492                     obj.emit("name-changed", &[&*self.name.borrow()])
493                         .expect("Failed to borrow name");
494                 }
495                 Property("child", ..) => {
496                     // not stored, only used to test `set_property` with `Objects`
497                 }
498                 _ => unimplemented!(),
499             }
500         }
501 
get_property(&self, _obj: &Object, id: usize) -> Result<Value, ()>502         fn get_property(&self, _obj: &Object, id: usize) -> Result<Value, ()> {
503             let prop = &PROPERTIES[id];
504 
505             match *prop {
506                 Property("name", ..) => Ok(self.name.borrow().to_value()),
507                 Property("constructed", ..) => Ok(self.constructed.borrow().to_value()),
508                 _ => unimplemented!(),
509             }
510         }
511 
constructed(&self, obj: &Object)512         fn constructed(&self, obj: &Object) {
513             self.parent_constructed(obj);
514 
515             assert_eq!(obj, &self.get_instance());
516             assert_eq!(self as *const _, Self::from_instance(obj) as *const _);
517 
518             *self.constructed.borrow_mut() = true;
519         }
520     }
521 
522     #[repr(C)]
523     pub struct DummyInterface {
524         parent: gobject_sys::GTypeInterface,
525     }
526 
527     impl ObjectInterface for DummyInterface {
528         const NAME: &'static str = "DummyInterface";
529 
530         glib_object_interface!();
531 
type_init(type_: &mut subclass::InitializingType<Self>)532         fn type_init(type_: &mut subclass::InitializingType<Self>) {
533             type_.add_prerequisite::<Object>();
534         }
535     }
536 
537     // Usually this would be implemented on a Rust wrapper type defined
538     // with glib_wrapper!() but for the test the following is susyscient
539     impl StaticType for DummyInterface {
static_type() -> Type540         fn static_type() -> Type {
541             DummyInterface::get_type()
542         }
543     }
544 
545     // Usually this would be implemented on a Rust wrapper type defined
546     // with glib_wrapper!() but for the test the following is susyscient
547     unsafe impl<T: ObjectSubclass> IsImplementable<T> for DummyInterface {
interface_init( _iface: glib_sys::gpointer, _iface_data: glib_sys::gpointer, )548         unsafe extern "C" fn interface_init(
549             _iface: glib_sys::gpointer,
550             _iface_data: glib_sys::gpointer,
551         ) {
552         }
553     }
554 
555     #[test]
test_create()556     fn test_create() {
557         let type_ = SimpleObject::get_type();
558         let obj = Object::new(type_, &[]).expect("Object::new failed");
559 
560         assert!(obj.get_type().is_a(&DummyInterface::static_type()));
561 
562         assert_eq!(
563             obj.get_property("constructed")
564                 .expect("Failed to get 'constructed' property")
565                 .get_some::<bool>()
566                 .expect("Failed to get bool from 'constructed' property"),
567             true
568         );
569 
570         let weak = obj.downgrade();
571         drop(obj);
572         assert!(weak.upgrade().is_none());
573     }
574 
575     #[test]
test_create_child_object()576     fn test_create_child_object() {
577         let type_ = ChildObject::get_type();
578         let obj = Object::new(type_, &[]).expect("Object::new failed");
579 
580         // ChildObject is a zero-sized type and we map that to the same pointer as the object
581         // itself. No private/impl data is allocated for zero-sized types.
582         let imp = ChildObject::from_instance(&obj);
583         assert_eq!(imp as *const _ as *const (), obj.as_ptr() as *const _);
584     }
585 
586     #[test]
test_set_properties()587     fn test_set_properties() {
588         let obj = Object::new(SimpleObject::get_type(), &[]).expect("Object::new failed");
589 
590         assert!(obj
591             .get_property("name")
592             .expect("Failed to get 'name' property")
593             .get::<&str>()
594             .expect("Failed to get str from 'name' property")
595             .is_none());
596         assert!(obj.set_property("name", &"test").is_ok());
597         assert_eq!(
598             obj.get_property("name")
599                 .expect("Failed to get 'name' property")
600                 .get::<&str>()
601                 .expect("Failed to get str from 'name' property"),
602             Some("test")
603         );
604 
605         assert_eq!(
606             obj.set_property("test", &true)
607                 .err()
608                 .expect("set_property failed")
609                 .description(),
610             "property not found",
611         );
612 
613         assert_eq!(
614             obj.set_property("constructed", &false)
615                 .err()
616                 .expect("Failed to set 'constructed' property")
617                 .description(),
618             "property is not writable",
619         );
620 
621         assert_eq!(
622             obj.set_property("name", &false)
623                 .err()
624                 .expect("Failed to set 'name' property")
625                 .description(),
626             "property can't be set from the given type (expected: gchararray, got: gboolean)",
627         );
628 
629         let other_obj = Object::new(SimpleObject::get_type(), &[]).expect("Object::new failed");
630         assert_eq!(
631             obj.set_property("child", &other_obj)
632                 .err()
633                 .expect("Failed to set 'child' property")
634                 .description(),
635             "property can't be set from the given object type (expected: ChildObject, got: SimpleObject)",
636         );
637 
638         let child = Object::new(ChildObject::get_type(), &[]).expect("Object::new failed");
639         assert!(obj.set_property("child", &child).is_ok());
640     }
641 
642     #[test]
test_signals()643     fn test_signals() {
644         use std::sync::{Arc, Mutex};
645 
646         let type_ = SimpleObject::get_type();
647         let obj = Object::new(type_, &[("name", &"old-name")]).expect("Object::new failed");
648 
649         let name_changed_triggered = Arc::new(Mutex::new(false));
650         let name_changed_clone = name_changed_triggered.clone();
651         obj.connect("name-changed", false, move |args| {
652             let _obj = args[0]
653                 .get::<Object>()
654                 .expect("Failed to get args[0]")
655                 .expect("Failed to get str from args[0]");
656             let name = args[1]
657                 .get::<&str>()
658                 .expect("Failed to get args[1]")
659                 .expect("Failed to get str from args[1]");
660 
661             assert_eq!(name, "new-name");
662             *name_changed_clone.lock().expect("Failed to lock") = true;
663 
664             None
665         })
666         .expect("Failed to connect on 'name-changed'");
667 
668         assert_eq!(
669             obj.get_property("name")
670                 .expect("Failed to get 'name' property")
671                 .get::<&str>()
672                 .expect("Failed to get str from 'name' property"),
673             Some("old-name")
674         );
675         assert!(!*name_changed_triggered.lock().expect("Failed to lock"));
676 
677         let old_name = obj
678             .emit("change-name", &[&"new-name"])
679             .expect("Failed to emit")
680             .expect("Failed to get value from emit")
681             .get::<String>()
682             .expect("Failed to get str from emit");
683         assert_eq!(old_name, Some("old-name".to_string()));
684         assert!(*name_changed_triggered.lock().expect("Failed to lock"));
685     }
686 
687     #[test]
test_signal_return_expected_type()688     fn test_signal_return_expected_type() {
689         let obj = Object::new(SimpleObject::get_type(), &[]).expect("Object::new failed");
690 
691         obj.connect("create-string", false, move |_args| {
692             Some("return value".to_value())
693         })
694         .expect("Failed to connect on 'create-string'");
695 
696         let value = obj
697             .emit("create-string", &[])
698             .expect("Failed to emit")
699             .expect("Failed to get value from emit");
700         assert_eq!(value.get::<String>(), Ok(Some("return value".to_string())));
701     }
702 
703     // Note: can't test type mismatch in signals since panics accross FFI boundaries
704     // are UB. See https://github.com/gtk-rs/glib/issues/518
705 
706     #[test]
test_signal_return_expected_object_type()707     fn test_signal_return_expected_object_type() {
708         let obj = Object::new(SimpleObject::get_type(), &[]).expect("Object::new failed");
709 
710         obj.connect("create-child-object", false, move |_args| {
711             Some(
712                 Object::new(ChildObject::get_type(), &[])
713                     .expect("Object::new failed")
714                     .to_value(),
715             )
716         })
717         .expect("Failed to connect on 'create-child-object'");
718 
719         let value = obj
720             .emit("create-child-object", &[])
721             .expect("Failed to emit")
722             .expect("Failed to get value from emit");
723         assert!(value.type_().is_a(&ChildObject::static_type()));
724     }
725 }
726