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