1use 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#[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
534unsafe 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}