1 // Copyright 2019, The Gtk-rs Project Developers.
2 // See the COPYRIGHT file at the top-level directory of this distribution.
3 // Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>
4 
5 use super::{InitializingType, Property};
6 use glib_sys;
7 use gobject_sys;
8 use std::borrow::Borrow;
9 use std::marker;
10 use std::mem;
11 use std::ptr;
12 use translate::*;
13 use {IsA, Object, ObjectExt, SignalFlags, StaticType, Type, Value};
14 
15 impl<T: ObjectInterface> InitializingType<T> {
16     /// Adds an interface prerequisite for `I` to the type.
17     ///
18     /// All implementors of the interface must be a subclass of `I` or implement the interface `I`.
add_prerequisite<I: StaticType>(&mut self)19     pub fn add_prerequisite<I: StaticType>(&mut self) {
20         unsafe {
21             gobject_sys::g_type_interface_add_prerequisite(
22                 self.0.to_glib(),
23                 I::static_type().to_glib(),
24             )
25         }
26     }
27 }
28 
29 /// Macro for boilerplate of [`ObjectInterface`] implementations.
30 ///
31 /// [`ObjectInterface`]: subclass/types/trait.ObjectInterface.html
32 #[macro_export]
33 macro_rules! glib_object_interface {
34     () => {
35         fn get_type() -> $crate::Type {
36             static ONCE: ::std::sync::Once = ::std::sync::Once::new();
37             static mut TYPE: $crate::Type = $crate::Type::Invalid;
38 
39             ONCE.call_once(|| {
40                 let type_ = $crate::subclass::register_interface::<Self>();
41                 unsafe {
42                     TYPE = type_;
43                 }
44             });
45 
46             unsafe {
47                 assert_ne!(TYPE, $crate::Type::Invalid);
48 
49                 TYPE
50             }
51         }
52     };
53 }
54 
55 /// The central trait for defining a `GObject` interface.
56 ///
57 /// Links together the type name and the interface struct for type registration and allows to hook
58 /// into various steps of the type registration and initialization.
59 ///
60 /// This must only be implemented on `#[repr(C)]` structs and have `gobject_sys::GTypeInterface` as
61 /// the first field.
62 ///
63 /// See [`register_interface`] for registering an implementation of this trait
64 /// with the type system.
65 ///
66 /// [`register_interface`]: fn.register_interface.html
67 pub trait ObjectInterface: Sized + 'static {
68     /// `GObject` type name.
69     ///
70     /// This must be unique in the whole process.
71     const NAME: &'static str;
72 
73     /// Returns the `glib::Type` ID of the interface.
74     ///
75     /// This will register the type with the type system on the first call and is usually generated
76     /// by the [`glib_object_interface!`] macro.
77     ///
78     /// [`glib_object_interface!`]: ../../macro.glib_object_interface.html
get_type() -> Type79     fn get_type() -> Type;
80 
81     /// Additional type initialization.
82     ///
83     /// This is called right after the type was registered and allows
84     /// interfaces to do additional type-specific initialization, e.g.
85     /// for adding prerequisites.
86     ///
87     /// Optional
type_init(_type_: &mut InitializingType<Self>)88     fn type_init(_type_: &mut InitializingType<Self>) {}
89 
90     /// Interface initialization.
91     ///
92     /// This is called after `type_init` and before the first implementor
93     /// of the interface is created. Interfaces can use this to do interface-
94     /// specific initialization, e.g. for installing properties or signals
95     /// on the interface, and for setting default implementations of interface
96     /// functions.
97     ///
98     /// Optional
interface_init(&mut self)99     fn interface_init(&mut self) {}
100 }
101 
102 pub trait ObjectInterfaceExt: ObjectInterface {
103     /// Get interface from an instance.
104     ///
105     /// This will panic if `obj` does not implement the interface.
from_instance<T: IsA<Object>>(obj: &T) -> &Self106     fn from_instance<T: IsA<Object>>(obj: &T) -> &Self {
107         assert!(obj.as_ref().get_type().is_a(&Self::get_type()));
108 
109         unsafe {
110             let klass = (*(obj.as_ptr() as *const gobject_sys::GTypeInstance)).g_class;
111             let interface =
112                 gobject_sys::g_type_interface_peek(klass as *mut _, Self::get_type().to_glib());
113             assert!(!interface.is_null());
114             &*(interface as *const Self)
115         }
116     }
117 
118     /// Install properties on the interface.
119     ///
120     /// All implementors of the interface must provide these properties.
install_properties<'a, T: Borrow<Property<'a>>>(&mut self, properties: &[T])121     fn install_properties<'a, T: Borrow<Property<'a>>>(&mut self, properties: &[T]) {
122         if properties.is_empty() {
123             return;
124         }
125 
126         for property in properties {
127             let property = property.borrow();
128             let pspec = (property.1)(property.0);
129             unsafe {
130                 gobject_sys::g_object_interface_install_property(
131                     self as *mut Self as *mut _,
132                     pspec.to_glib_none().0,
133                 );
134             }
135         }
136     }
137 
138     /// Add a new signal to the interface.
139     ///
140     /// This can be emitted later by `glib::Object::emit` and external code
141     /// can connect to the signal to get notified about emissions.
add_signal(&mut self, name: &str, flags: SignalFlags, arg_types: &[Type], ret_type: Type)142     fn add_signal(&mut self, name: &str, flags: SignalFlags, arg_types: &[Type], ret_type: Type) {
143         unsafe {
144             super::types::add_signal(
145                 *(self as *mut _ as *mut glib_sys::GType),
146                 name,
147                 flags,
148                 arg_types,
149                 ret_type,
150             );
151         }
152     }
153 
154     /// Add a new signal with class handler to the interface.
155     ///
156     /// This can be emitted later by `glib::Object::emit` and external code
157     /// can connect to the signal to get notified about emissions.
158     ///
159     /// 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,160     fn add_signal_with_class_handler<F>(
161         &mut self,
162         name: &str,
163         flags: SignalFlags,
164         arg_types: &[Type],
165         ret_type: Type,
166         class_handler: F,
167     ) where
168         F: Fn(&super::SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
169     {
170         unsafe {
171             super::types::add_signal_with_class_handler(
172                 *(self as *mut _ as *mut glib_sys::GType),
173                 name,
174                 flags,
175                 arg_types,
176                 ret_type,
177                 class_handler,
178             );
179         }
180     }
181 
182     /// Add a new signal with accumulator to the interface.
183     ///
184     /// This can be emitted later by `glib::Object::emit` and external code
185     /// can connect to the signal to get notified about emissions.
186     ///
187     /// The accumulator function is used for accumulating the return values of
188     /// multiple signal handlers. The new value is passed as second argument and
189     /// should be combined with the old value in the first argument. If no further
190     /// 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,191     fn add_signal_with_accumulator<F>(
192         &mut self,
193         name: &str,
194         flags: SignalFlags,
195         arg_types: &[Type],
196         ret_type: Type,
197         accumulator: F,
198     ) where
199         F: Fn(&super::SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static,
200     {
201         unsafe {
202             super::types::add_signal_with_accumulator(
203                 *(self as *mut _ as *mut glib_sys::GType),
204                 name,
205                 flags,
206                 arg_types,
207                 ret_type,
208                 accumulator,
209             );
210         }
211     }
212 
213     /// Add a new signal with accumulator and class handler to the interface.
214     ///
215     /// This can be emitted later by `glib::Object::emit` and external code
216     /// can connect to the signal to get notified about emissions.
217     ///
218     /// The accumulator function is used for accumulating the return values of
219     /// multiple signal handlers. The new value is passed as second argument and
220     /// should be combined with the old value in the first argument. If no further
221     /// signal handlers should be called, `false` should be returned.
222     ///
223     /// 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,224     fn add_signal_with_class_handler_and_accumulator<F, G>(
225         &mut self,
226         name: &str,
227         flags: SignalFlags,
228         arg_types: &[Type],
229         ret_type: Type,
230         class_handler: F,
231         accumulator: G,
232     ) where
233         F: Fn(&super::SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
234         G: Fn(&super::SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static,
235     {
236         unsafe {
237             super::types::add_signal_with_class_handler_and_accumulator(
238                 *(self as *mut _ as *mut glib_sys::GType),
239                 name,
240                 flags,
241                 arg_types,
242                 ret_type,
243                 class_handler,
244                 accumulator,
245             );
246         }
247     }
248 }
249 
250 impl<T: ObjectInterface> ObjectInterfaceExt for T {}
251 
interface_init<T: ObjectInterface>( klass: glib_sys::gpointer, _klass_data: glib_sys::gpointer, )252 unsafe extern "C" fn interface_init<T: ObjectInterface>(
253     klass: glib_sys::gpointer,
254     _klass_data: glib_sys::gpointer,
255 ) {
256     let iface = &mut *(klass as *mut T);
257     iface.interface_init();
258 }
259 
260 /// Register a `glib::Type` ID for `T`.
261 ///
262 /// This must be called only once and will panic on a second call.
263 ///
264 /// The [`glib_object_interface!`] macro will create a `get_type()` function around this, which will
265 /// ensure that it's only ever called once.
266 ///
267 /// [`glib_object_interface!`]: ../../macro.glib_object_interface.html
register_interface<T: ObjectInterface>() -> Type268 pub fn register_interface<T: ObjectInterface>() -> Type {
269     unsafe {
270         use std::ffi::CString;
271 
272         let type_info = gobject_sys::GTypeInfo {
273             class_size: mem::size_of::<T>() as u16,
274             base_init: None,
275             base_finalize: None,
276             class_init: Some(interface_init::<T>),
277             class_finalize: None,
278             class_data: ptr::null_mut(),
279             instance_size: 0,
280             n_preallocs: 0,
281             instance_init: None,
282             value_table: ptr::null(),
283         };
284 
285         let type_name = CString::new(T::NAME).unwrap();
286         assert_eq!(
287             gobject_sys::g_type_from_name(type_name.as_ptr()),
288             gobject_sys::G_TYPE_INVALID
289         );
290 
291         let type_ = from_glib(gobject_sys::g_type_register_static(
292             Type::BaseInterface.to_glib(),
293             type_name.as_ptr(),
294             &type_info,
295             0,
296         ));
297 
298         T::type_init(&mut InitializingType::<T>(type_, marker::PhantomData));
299 
300         type_
301     }
302 }
303