gobject_subclass/
object.rs

1// Copyright (C) 2017,2018 Sebastian Dröge <sebastian@centricular.com>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use std::any::TypeId;
10use std::collections::BTreeMap;
11use std::ffi::CString;
12use std::mem;
13use std::ptr;
14use std::sync::Mutex;
15
16use glib_ffi;
17use gobject_ffi;
18
19use glib;
20use glib::translate::*;
21
22use anyimpl::*;
23pub use properties::*;
24
25pub trait ObjectImpl<T: ObjectBase>: AnyImpl + 'static {
26    fn set_property(&self, _obj: &glib::Object, _id: u32, _value: &glib::Value) {
27        unimplemented!()
28    }
29
30    fn get_property(&self, _obj: &glib::Object, _id: u32) -> Result<glib::Value, ()> {
31        unimplemented!()
32    }
33
34    fn constructed(&self, obj: &T) {
35        obj.parent_constructed()
36    }
37}
38
39// warning: constraints is defined as a repetition to minimize code duplication.
40// multiple items will generate invalide code.
41#[macro_export]
42macro_rules! box_object_impl(
43    ($name:ident $(, $constraint:ident)*) => {
44        impl<T: ObjectBase> ObjectImpl<T> for Box<$name<T>>
45        $(
46            where T::InstanceStructType: $constraint
47        )*
48        {
49            fn set_property(&self, obj: &glib::Object, id: u32, value: &glib::Value) {
50                let imp: &$name<T> = self.as_ref();
51                imp.set_property(obj, id, value);
52            }
53
54            fn get_property(&self, obj: &glib::Object, id: u32) -> Result<glib::Value, ()> {
55                let imp: &$name<T> = self.as_ref();
56                imp.get_property(obj, id)
57            }
58
59            fn constructed(&self, obj: &T) {
60                let imp: &$name<T> = self.as_ref();
61                imp.constructed(obj)
62            }
63        }
64    };
65);
66
67box_object_impl!(ObjectImpl);
68
69pub trait ImplTypeStatic<T: ObjectType>: Send + Sync + 'static {
70    fn get_name(&self) -> &str;
71    fn new(&self, &T) -> T::ImplType;
72    fn class_init(&self, &mut ClassStruct<T>);
73    fn type_init(&self, _: &TypeInitToken, _type_: glib::Type) {}
74}
75
76pub struct ClassInitToken(());
77pub struct TypeInitToken(());
78
79pub trait ObjectType: FromGlibPtrBorrow<*mut <Self as ObjectType>::InstanceStructType>
80where
81    Self: glib::IsA<glib::Object> + Sized + 'static,
82    Self::InstanceStructType: Instance<Self>,
83{
84    const NAME: &'static str;
85    type InstanceStructType: Instance<Self> + 'static;
86    type ParentType: glib::IsA<glib::Object>;
87    type ImplType: ObjectImpl<Self>;
88
89    fn class_init(token: &ClassInitToken, klass: &mut ClassStruct<Self>);
90
91    unsafe fn get_instance(&self) -> *mut Self::InstanceStructType;
92
93    fn get_impl(&self) -> &Self::ImplType {
94        unsafe { (*self.get_instance()).get_impl() }
95    }
96
97    unsafe fn get_class(&self) -> *const ClassStruct<Self> {
98        (*self.get_instance()).get_class()
99    }
100}
101
102#[macro_export]
103macro_rules! object_type_fns(
104    () => {
105        unsafe fn get_instance(&self) -> *mut Self::InstanceStructType {
106            self.to_glib_none().0
107        }
108    }
109);
110
111pub unsafe trait Instance<T: ObjectType> {
112    fn parent(&self) -> &<T::ParentType as glib::wrapper::Wrapper>::GlibType;
113
114    fn get_impl(&self) -> &<T as ObjectType>::ImplType;
115
116    unsafe fn set_impl(&mut self, imp: ptr::NonNull<T::ImplType>);
117
118    unsafe fn get_class(&self) -> *const ClassStruct<T>;
119}
120
121#[repr(C)]
122pub struct InstanceStruct<T: ObjectType> {
123    _parent: <T::ParentType as glib::wrapper::Wrapper>::GlibType,
124    _imp: ptr::NonNull<T::ImplType>,
125}
126
127unsafe impl<T: ObjectType> Instance<T> for InstanceStruct<T> {
128    fn parent(&self) -> &<T::ParentType as glib::wrapper::Wrapper>::GlibType {
129        &self._parent
130    }
131
132    fn get_impl(&self) -> &T::ImplType {
133        unsafe { self._imp.as_ref() }
134    }
135
136    unsafe fn set_impl(&mut self, imp: ptr::NonNull<T::ImplType>) {
137        self._imp = imp;
138    }
139
140    unsafe fn get_class(&self) -> *const ClassStruct<T> {
141        *(self as *const _ as *const *const ClassStruct<T>)
142    }
143}
144
145#[repr(C)]
146pub struct ClassStruct<T: ObjectType> {
147    pub parent: <T::ParentType as glib::wrapper::Wrapper>::GlibClassType,
148    pub imp_static: ptr::NonNull<Box<ImplTypeStatic<T>>>,
149    pub parent_class: ptr::NonNull<<T::ParentType as glib::wrapper::Wrapper>::GlibClassType>,
150    pub interfaces_static: *const Vec<(glib_ffi::GType, glib_ffi::gpointer)>,
151}
152
153impl<T: ObjectType> ClassStruct<T> {
154    pub fn get_parent_class(
155        &self,
156    ) -> *const <T::ParentType as glib::wrapper::Wrapper>::GlibClassType {
157        self.parent_class.as_ptr()
158    }
159
160    pub fn get_interface_static(&self, type_: glib_ffi::GType) -> glib_ffi::gpointer {
161        unsafe {
162            if self.interfaces_static.is_null() {
163                return ptr::null_mut();
164            }
165
166            for &(t, p) in &(*self.interfaces_static) {
167                if t == type_ {
168                    return p;
169                }
170            }
171
172            ptr::null_mut()
173        }
174    }
175}
176
177pub unsafe trait ObjectClassExt<T: ObjectBase>
178where
179    T::ImplType: ObjectImpl<T>,
180{
181    fn override_vfuncs(&mut self, _: &ClassInitToken) {
182        unsafe {
183            let klass = &mut *(self as *const Self as *mut gobject_ffi::GObjectClass);
184            klass.constructed = Some(object_constructed::<T>);
185        }
186    }
187
188    fn install_properties(&mut self, properties: &[Property]) {
189        if properties.is_empty() {
190            return;
191        }
192
193        let mut pspecs = Vec::with_capacity(properties.len());
194
195        pspecs.push(ptr::null_mut());
196
197        for property in properties {
198            pspecs.push(property.into());
199        }
200
201        unsafe {
202            gobject_ffi::g_object_class_install_properties(
203                self as *mut _ as *mut gobject_ffi::GObjectClass,
204                pspecs.len() as u32,
205                pspecs.as_mut_ptr(),
206            );
207        }
208    }
209
210    fn add_signal(&mut self, name: &str, arg_types: &[glib::Type], ret_type: glib::Type) {
211        let arg_types = arg_types.iter().map(|t| t.to_glib()).collect::<Vec<_>>();
212        unsafe {
213            gobject_ffi::g_signal_newv(
214                name.to_glib_none().0,
215                *(self as *mut _ as *mut glib_ffi::GType),
216                gobject_ffi::G_SIGNAL_RUN_LAST,
217                ptr::null_mut(),
218                None,
219                ptr::null_mut(),
220                None,
221                ret_type.to_glib(),
222                arg_types.len() as u32,
223                arg_types.as_ptr() as *mut _,
224            );
225        }
226    }
227
228    fn add_signal_with_accumulator<F>(
229        &mut self,
230        name: &str,
231        arg_types: &[glib::Type],
232        ret_type: glib::Type,
233        accumulator: F,
234    ) where
235        F: Fn(&mut glib::Value, &glib::Value) -> bool + Send + Sync + 'static,
236    {
237        let arg_types = arg_types.iter().map(|t| t.to_glib()).collect::<Vec<_>>();
238
239        let accumulator: Box<
240            Box<Fn(&mut glib::Value, &glib::Value) -> bool + Send + Sync + 'static>,
241        > = Box::new(Box::new(accumulator));
242
243        unsafe extern "C" fn accumulator_trampoline(
244            _ihint: *mut gobject_ffi::GSignalInvocationHint,
245            return_accu: *mut gobject_ffi::GValue,
246            handler_return: *const gobject_ffi::GValue,
247            data: glib_ffi::gpointer,
248        ) -> glib_ffi::gboolean {
249            let accumulator: &&(Fn(&mut glib::Value, &glib::Value) -> bool
250                                    + Send
251                                    + Sync
252                                    + 'static) = mem::transmute(data);
253            accumulator(
254                &mut *(return_accu as *mut glib::Value),
255                &*(handler_return as *const glib::Value),
256            )
257            .to_glib()
258        }
259
260        unsafe {
261            gobject_ffi::g_signal_newv(
262                name.to_glib_none().0,
263                *(self as *mut _ as *mut glib_ffi::GType),
264                gobject_ffi::G_SIGNAL_RUN_LAST,
265                ptr::null_mut(),
266                Some(accumulator_trampoline),
267                Box::into_raw(accumulator) as glib_ffi::gpointer,
268                None,
269                ret_type.to_glib(),
270                arg_types.len() as u32,
271                arg_types.as_ptr() as *mut _,
272            );
273        }
274    }
275
276    fn add_action_signal<F>(
277        &mut self,
278        name: &str,
279        arg_types: &[glib::Type],
280        ret_type: glib::Type,
281        handler: F,
282    ) where
283        F: Fn(&[glib::Value]) -> Option<glib::Value> + Send + Sync + 'static,
284    {
285        let arg_types = arg_types.iter().map(|t| t.to_glib()).collect::<Vec<_>>();
286        let handler = glib::Closure::new(handler);
287        unsafe {
288            gobject_ffi::g_signal_newv(
289                name.to_glib_none().0,
290                *(self as *mut _ as *mut glib_ffi::GType),
291                gobject_ffi::G_SIGNAL_RUN_LAST | gobject_ffi::G_SIGNAL_ACTION,
292                handler.to_glib_none().0,
293                None,
294                ptr::null_mut(),
295                None,
296                ret_type.to_glib(),
297                arg_types.len() as u32,
298                arg_types.as_ptr() as *mut _,
299            );
300        }
301    }
302}
303
304unsafe extern "C" fn class_init<T: ObjectType>(
305    klass: glib_ffi::gpointer,
306    _klass_data: glib_ffi::gpointer,
307) {
308    {
309        let gobject_klass = &mut *(klass as *mut gobject_ffi::GObjectClass);
310
311        gobject_klass.finalize = Some(finalize::<T>);
312    }
313
314    {
315        let klass = &mut *(klass as *mut ClassStruct<T>);
316        let parent_class =
317            gobject_ffi::g_type_class_peek_parent(klass as *mut _ as glib_ffi::gpointer)
318                as *mut <T::ParentType as glib::wrapper::Wrapper>::GlibClassType;
319        assert!(!parent_class.is_null());
320        klass.parent_class = ptr::NonNull::new_unchecked(parent_class);
321        T::class_init(&ClassInitToken(()), klass);
322    }
323}
324
325unsafe extern "C" fn finalize<T: ObjectType>(obj: *mut gobject_ffi::GObject) {
326    let instance = &mut *(obj as *mut T::InstanceStructType);
327
328    drop(Box::from_raw(
329        instance.get_impl() as *const _ as *mut T::ImplType
330    ));
331    instance.set_impl(ptr::NonNull::dangling());
332
333    let klass = *(obj as *const glib_ffi::gpointer);
334    let parent_klass = gobject_ffi::g_type_class_peek_parent(klass);
335    let parent_klass =
336        &*(gobject_ffi::g_type_class_peek_parent(parent_klass) as *const gobject_ffi::GObjectClass);
337    parent_klass.finalize.map(|f| f(obj));
338}
339
340static mut TYPES: *mut Mutex<BTreeMap<TypeId, glib::Type>> = 0 as *mut _;
341
342pub unsafe fn get_type<T: ObjectType>() -> glib_ffi::GType {
343    use std::sync::{Once, ONCE_INIT};
344
345    static ONCE: Once = ONCE_INIT;
346
347    ONCE.call_once(|| {
348        TYPES = Box::into_raw(Box::new(Mutex::new(BTreeMap::new())));
349    });
350
351    let mut types = (*TYPES).lock().unwrap();
352    types
353        .entry(TypeId::of::<T>())
354        .or_insert_with(|| {
355            let type_info = gobject_ffi::GTypeInfo {
356                class_size: mem::size_of::<ClassStruct<T>>() as u16,
357                base_init: None,
358                base_finalize: None,
359                class_init: Some(class_init::<T>),
360                class_finalize: None,
361                class_data: ptr::null_mut(),
362                instance_size: mem::size_of::<T::InstanceStructType>() as u16,
363                n_preallocs: 0,
364                instance_init: None,
365                value_table: ptr::null(),
366            };
367
368            let type_name = {
369                let mut idx = 0;
370
371                loop {
372                    let type_name = CString::new(format!("{}-{}", T::NAME, idx)).unwrap();
373                    if gobject_ffi::g_type_from_name(type_name.as_ptr())
374                        == gobject_ffi::G_TYPE_INVALID
375                    {
376                        break type_name;
377                    }
378                    idx += 1;
379                }
380            };
381
382            from_glib(gobject_ffi::g_type_register_static(
383                <T::ParentType as glib::StaticType>::static_type().to_glib(),
384                type_name.as_ptr(),
385                &type_info,
386                gobject_ffi::G_TYPE_FLAG_ABSTRACT,
387            ))
388        })
389        .to_glib()
390}
391
392unsafe extern "C" fn sub_class_init<T: ObjectType>(
393    klass: glib_ffi::gpointer,
394    klass_data: glib_ffi::gpointer,
395) {
396    {
397        let gobject_klass = &mut *(klass as *mut gobject_ffi::GObjectClass);
398
399        gobject_klass.set_property = Some(sub_set_property::<T>);
400        gobject_klass.get_property = Some(sub_get_property::<T>);
401    }
402    {
403        assert!(!klass_data.is_null());
404        let klass = &mut *(klass as *mut ClassStruct<T>);
405        let imp_static = klass_data as *mut Box<ImplTypeStatic<T>>;
406        klass.imp_static = ptr::NonNull::new_unchecked(imp_static);
407        klass.interfaces_static = Box::into_raw(Box::new(Vec::new()));
408
409        (*imp_static).class_init(klass);
410    }
411}
412
413unsafe extern "C" fn sub_get_property<T: ObjectType>(
414    obj: *mut gobject_ffi::GObject,
415    id: u32,
416    value: *mut gobject_ffi::GValue,
417    _pspec: *mut gobject_ffi::GParamSpec,
418) {
419    floating_reference_guard!(obj);
420    let instance = &*(obj as *mut T::InstanceStructType);
421    let imp = instance.get_impl();
422
423    match imp.get_property(&from_glib_borrow(obj), id - 1) {
424        Ok(v) => {
425            gobject_ffi::g_value_unset(value);
426            ptr::write(value, ptr::read(v.to_glib_none().0));
427            mem::forget(v);
428        }
429        Err(()) => eprintln!("Failed to get property"),
430    }
431}
432
433unsafe extern "C" fn sub_set_property<T: ObjectType>(
434    obj: *mut gobject_ffi::GObject,
435    id: u32,
436    value: *mut gobject_ffi::GValue,
437    _pspec: *mut gobject_ffi::GParamSpec,
438) {
439    floating_reference_guard!(obj);
440    let instance = &*(obj as *mut T::InstanceStructType);
441    let imp = instance.get_impl();
442    imp.set_property(
443        &from_glib_borrow(obj),
444        id - 1,
445        &*(value as *mut glib::Value),
446    );
447}
448
449unsafe extern "C" fn sub_init<T: ObjectType>(
450    obj: *mut gobject_ffi::GTypeInstance,
451    _klass: glib_ffi::gpointer,
452) {
453    floating_reference_guard!(obj);
454    let instance = &mut *(obj as *mut T::InstanceStructType);
455    let klass = &**(obj as *const *const ClassStruct<T>);
456    let rs_instance: T = from_glib_borrow(obj as *mut T::InstanceStructType);
457
458    let imp = klass.imp_static.as_ref().new(&rs_instance);
459    instance.set_impl(ptr::NonNull::new_unchecked(Box::into_raw(Box::new(imp))));
460}
461
462unsafe extern "C" fn object_constructed<T: ObjectBase>(ptr: *mut gobject_ffi::GObject)
463where
464    T::ImplType: ObjectImpl<T>,
465    T::InstanceStructType: Instance<T>,
466{
467    floating_reference_guard!(ptr);
468    let obj = &*(ptr as *mut T::InstanceStructType);
469    let wrap: T = from_glib_borrow(ptr as *mut T::InstanceStructType);
470    let imp = obj.get_impl();
471    imp.constructed(&wrap);
472}
473
474pub fn register_type<T: ObjectType, I: ImplTypeStatic<T>>(imp: I) -> glib::Type {
475    unsafe {
476        let parent_type = get_type::<T>();
477        let type_name = format!("{}-{}", T::NAME, imp.get_name());
478
479        let imp: Box<ImplTypeStatic<T>> = Box::new(imp);
480        let imp_ptr = Box::into_raw(Box::new(imp));
481
482        let type_info = gobject_ffi::GTypeInfo {
483            class_size: mem::size_of::<ClassStruct<T>>() as u16,
484            base_init: None,
485            base_finalize: None,
486            class_init: Some(sub_class_init::<T>),
487            class_finalize: None,
488            class_data: imp_ptr as glib_ffi::gpointer,
489            instance_size: mem::size_of::<T::InstanceStructType>() as u16,
490            n_preallocs: 0,
491            instance_init: Some(sub_init::<T>),
492            value_table: ptr::null(),
493        };
494
495        let type_ = from_glib(gobject_ffi::g_type_register_static(
496            parent_type,
497            type_name.to_glib_none().0,
498            &type_info,
499            0,
500        ));
501
502        (*imp_ptr).type_init(&TypeInitToken(()), type_);
503
504        type_
505    }
506}
507
508any_impl!(ObjectBase, ObjectImpl);
509
510pub unsafe trait ObjectBase: glib::IsA<glib::Object> + ObjectType {
511    fn parent_constructed(&self) {
512        unsafe {
513            let klass = self.get_class();
514            let parent_klass = (*klass).get_parent_class() as *const gobject_ffi::GObjectClass;
515            (*parent_klass)
516                .constructed
517                .map(|f| f(self.to_glib_none().0));;
518        }
519    }
520}
521
522glib_wrapper! {
523    pub struct Object(Object<InstanceStruct<Object>>);
524
525    match fn {
526        get_type => || get_type::<Object>(),
527    }
528}
529
530unsafe impl<T: glib::IsA<glib::Object> + ObjectType> ObjectBase for T {}
531
532pub type ObjectClass = ClassStruct<Object>;
533
534// FIXME: Boilerplate
535unsafe impl ObjectClassExt<Object> for ObjectClass {}
536
537impl ObjectType for Object {
538    const NAME: &'static str = "RsObject";
539    type ParentType = glib::Object;
540    type ImplType = Box<ObjectImpl<Self>>;
541    type InstanceStructType = InstanceStruct<Self>;
542
543    fn class_init(token: &ClassInitToken, klass: &mut ObjectClass) {
544        klass.override_vfuncs(token);
545    }
546
547    object_type_fns!();
548}