use std::{marker, mem};
use super::{types::InterfaceStruct, InitializingType, Signal};
use crate::{
ffi, gobject_ffi, prelude::*, translate::*, Object, ParamSpec, Type, TypeFlags, TypeInfo,
};
pub trait PrerequisiteList {
fn types() -> Vec<Type>;
}
impl PrerequisiteList for () {
fn types() -> Vec<Type> {
vec![]
}
}
impl<T: ObjectType> PrerequisiteList for (T,) {
fn types() -> Vec<Type> {
vec![T::static_type()]
}
}
macro_rules! prerequisite_list_trait(
($name1:ident, $name2: ident, $($name:ident),*) => (
prerequisite_list_trait!(__impl $name1, $name2; $($name),*);
);
(__impl $($name:ident),+; $name1:ident, $($name2:ident),*) => (
prerequisite_list_trait_impl!($($name),+);
prerequisite_list_trait!(__impl $($name),+ , $name1; $($name2),*);
);
(__impl $($name:ident),+; $name1:ident) => (
prerequisite_list_trait_impl!($($name),+);
prerequisite_list_trait_impl!($($name),+, $name1);
);
);
macro_rules! prerequisite_list_trait_impl(
($($name:ident),+) => (
impl<$($name: ObjectType),+> PrerequisiteList for ( $($name),+ ) {
fn types() -> Vec<Type> {
vec![$($name::static_type()),+]
}
}
);
);
prerequisite_list_trait!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);
pub unsafe trait ObjectInterfaceType {
#[doc(alias = "get_type")]
fn type_() -> Type;
}
pub trait ObjectInterface: ObjectInterfaceType + Sized + 'static {
const NAME: &'static str;
const ALLOW_NAME_CONFLICT: bool = false;
type Prerequisites: PrerequisiteList;
type Instance;
type Interface: InterfaceStruct<Type = Self>;
fn type_init(_type_: &mut InitializingType<Self>) {}
fn interface_init(_klass: &mut Self::Interface) {}
fn properties() -> &'static [ParamSpec] {
&[]
}
fn signals() -> &'static [Signal] {
&[]
}
}
pub trait ObjectInterfaceExt: ObjectInterface {
#[inline]
fn from_obj<T: IsA<Object>>(obj: &T) -> &Self {
assert!(obj.as_ref().type_().is_a(Self::type_()));
unsafe {
let klass = (*(obj.as_ptr() as *const gobject_ffi::GTypeInstance)).g_class;
let interface =
gobject_ffi::g_type_interface_peek(klass as *mut _, Self::type_().into_glib());
debug_assert!(!interface.is_null());
&*(interface as *const Self)
}
}
}
impl<T: ObjectInterface> ObjectInterfaceExt for T {}
unsafe extern "C" fn interface_init<T: ObjectInterface>(
klass: ffi::gpointer,
_klass_data: ffi::gpointer,
) {
let iface = &mut *(klass as *mut T::Interface);
let pspecs = <T as ObjectInterface>::properties();
for pspec in pspecs {
gobject_ffi::g_object_interface_install_property(
iface as *mut T::Interface as *mut _,
pspec.to_glib_none().0,
);
}
let type_ = T::type_();
let signals = <T as ObjectInterface>::signals();
for signal in signals {
signal.register(type_);
}
T::interface_init(iface);
}
pub fn register_interface<T: ObjectInterface>() -> Type {
assert_eq!(mem::size_of::<T>(), 0);
unsafe {
use std::ffi::CString;
let type_name = if T::ALLOW_NAME_CONFLICT {
let mut i = 0;
loop {
let type_name = CString::new(if i == 0 {
T::NAME.to_string()
} else {
format!("{}-{}", T::NAME, i)
})
.unwrap();
if gobject_ffi::g_type_from_name(type_name.as_ptr()) == gobject_ffi::G_TYPE_INVALID
{
break type_name;
}
i += 1;
}
} else {
let type_name = CString::new(T::NAME).unwrap();
assert_eq!(
gobject_ffi::g_type_from_name(type_name.as_ptr()),
gobject_ffi::G_TYPE_INVALID,
"Type {} has already been registered",
type_name.to_str().unwrap()
);
type_name
};
let type_ = gobject_ffi::g_type_register_static_simple(
Type::INTERFACE.into_glib(),
type_name.as_ptr(),
mem::size_of::<T::Interface>() as u32,
Some(interface_init::<T>),
0,
None,
0,
);
let prerequisites = T::Prerequisites::types();
for prerequisite in prerequisites {
gobject_ffi::g_type_interface_add_prerequisite(type_, prerequisite.into_glib());
}
let type_ = Type::from_glib(type_);
assert!(type_.is_valid());
T::type_init(&mut InitializingType::<T>(type_, marker::PhantomData));
type_
}
}
pub fn register_dynamic_interface<P: DynamicObjectRegisterExt, T: ObjectInterface>(
type_plugin: &P,
) -> Type {
assert_eq!(mem::size_of::<T>(), 0);
unsafe {
use std::ffi::CString;
let type_name = CString::new(T::NAME).unwrap();
let already_registered =
gobject_ffi::g_type_from_name(type_name.as_ptr()) != gobject_ffi::G_TYPE_INVALID;
let type_info = TypeInfo(gobject_ffi::GTypeInfo {
class_size: mem::size_of::<T::Interface>() as u16,
class_init: Some(interface_init::<T>),
..TypeInfo::default().0
});
let type_ = type_plugin.register_dynamic_type(
Type::INTERFACE,
type_name.to_str().unwrap(),
&type_info,
TypeFlags::ABSTRACT,
);
let prerequisites = T::Prerequisites::types();
for prerequisite in prerequisites {
if !already_registered {
gobject_ffi::g_type_interface_add_prerequisite(
type_.into_glib(),
prerequisite.into_glib(),
);
}
}
assert!(type_.is_valid());
T::type_init(&mut InitializingType::<T>(type_, marker::PhantomData));
type_
}
}