use super::object::ObjectImpl;
use glib_sys;
use gobject_sys;
use object::{ObjectExt, ObjectType};
use std::fmt;
use std::marker;
use std::mem;
use std::ptr;
use translate::*;
use {Closure, IsA, IsClassFor, SignalFlags, StaticType, Type, Value};
#[derive(Debug, PartialEq, Eq)]
pub struct InitializingType<T>(pub(crate) Type, pub(crate) marker::PhantomData<T>);
impl<T: ObjectSubclass> InitializingType<T> {
pub fn add_interface<I: IsImplementable<T>>(&mut self) {
unsafe {
let iface_info = gobject_sys::GInterfaceInfo {
interface_init: Some(I::interface_init),
interface_finalize: None,
interface_data: ptr::null_mut(),
};
gobject_sys::g_type_add_interface_static(
self.0.to_glib(),
I::static_type().to_glib(),
&iface_info,
);
}
}
}
impl<T> ToGlib for InitializingType<T> {
type GlibType = glib_sys::GType;
fn to_glib(&self) -> glib_sys::GType {
self.0.to_glib()
}
}
pub unsafe trait InstanceStruct: Sized + 'static {
type Type: ObjectSubclass;
fn get_impl(&self) -> &Self::Type {
unsafe {
let data = Self::Type::type_data();
let private_offset = data.as_ref().private_offset;
let ptr: *const u8 = self as *const _ as *const u8;
let priv_ptr = ptr.offset(private_offset);
let imp = priv_ptr as *const Self::Type;
&*imp
}
}
fn get_class(&self) -> &<Self::Type as ObjectSubclass>::Class {
unsafe { &**(self as *const _ as *const *const <Self::Type as ObjectSubclass>::Class) }
}
}
pub unsafe trait ClassStruct: Sized + 'static {
type Type: ObjectSubclass;
fn override_vfuncs(&mut self)
where
<<Self::Type as ObjectSubclass>::ParentType as ObjectType>::RustClassType:
IsSubclassable<Self::Type>,
{
unsafe {
let base = &mut *(self as *mut _
as *mut <<Self::Type as ObjectSubclass>::ParentType as ObjectType>::RustClassType);
base.override_vfuncs();
}
}
}
pub unsafe trait IsSubclassable<T: ObjectSubclass>: IsClassFor {
fn override_vfuncs(&mut self);
}
pub unsafe trait IsImplementable<T: ObjectSubclass>: StaticType {
unsafe extern "C" fn interface_init(iface: glib_sys::gpointer, _iface_data: glib_sys::gpointer);
}
pub struct TypeData {
#[doc(hidden)]
pub type_: Type,
#[doc(hidden)]
pub parent_class: glib_sys::gpointer,
#[doc(hidden)]
pub interface_data: *const Vec<(glib_sys::GType, glib_sys::gpointer)>,
#[doc(hidden)]
pub private_offset: isize,
}
unsafe impl Send for TypeData {}
unsafe impl Sync for TypeData {}
impl TypeData {
pub fn get_type(&self) -> Type {
self.type_
}
pub fn get_parent_class(&self) -> glib_sys::gpointer {
self.parent_class
}
pub fn get_interface_data(&self, type_: glib_sys::GType) -> glib_sys::gpointer {
unsafe {
if self.interface_data.is_null() {
return ptr::null_mut();
}
for &(t, p) in &(*self.interface_data) {
if t == type_ {
return p;
}
}
ptr::null_mut()
}
}
pub fn get_private_offset(&self) -> isize {
self.private_offset
}
}
#[macro_export]
macro_rules! glib_object_subclass {
() => {
fn type_data() -> ::std::ptr::NonNull<$crate::subclass::TypeData> {
static mut DATA: $crate::subclass::TypeData = $crate::subclass::TypeData {
type_: $crate::Type::Invalid,
parent_class: ::std::ptr::null_mut(),
interface_data: ::std::ptr::null_mut(),
private_offset: 0,
};
unsafe { ::std::ptr::NonNull::new_unchecked(&mut DATA) }
}
fn get_type() -> $crate::Type {
static ONCE: ::std::sync::Once = ::std::sync::Once::new();
ONCE.call_once(|| {
$crate::subclass::register_type::<Self>();
});
unsafe {
let data = Self::type_data();
let type_ = data.as_ref().get_type();
assert_ne!(type_, $crate::Type::Invalid);
type_
}
}
};
}
pub trait ObjectSubclass: ObjectImpl + Sized + 'static {
const NAME: &'static str;
const ABSTRACT: bool = false;
type ParentType: ObjectType
+ FromGlibPtrFull<*mut <Self::ParentType as ObjectType>::GlibType>
+ FromGlibPtrBorrow<*mut <Self::ParentType as ObjectType>::GlibType>
+ FromGlibPtrNone<*mut <Self::ParentType as ObjectType>::GlibType>;
type Instance: InstanceStruct<Type = Self>;
type Class: ClassStruct<Type = Self>;
fn type_data() -> ptr::NonNull<TypeData>;
fn get_type() -> Type;
fn get_instance(&self) -> Self::ParentType {
unsafe {
let data = Self::type_data();
let type_ = data.as_ref().get_type();
assert_ne!(type_, Type::Invalid);
let offset = -data.as_ref().private_offset;
assert_ne!(offset, 0);
let ptr = self as *const Self as *const u8;
let ptr = ptr.offset(offset);
let ptr = ptr as *mut u8 as *mut <Self::ParentType as ObjectType>::GlibType;
gobject_sys::g_object_ref(ptr as *mut gobject_sys::GObject);
from_glib_full(ptr)
}
}
fn from_instance<T: IsA<Self::ParentType>>(obj: &T) -> &Self {
unsafe {
let data = Self::type_data();
let type_ = data.as_ref().get_type();
assert_ne!(type_, Type::Invalid);
assert!(obj.get_type().is_a(&type_));
let ptr = obj.as_ptr() as *const Self::Instance;
(*ptr).get_impl()
}
}
fn type_init(_type_: &mut InitializingType<Self>) {}
fn class_init(_klass: &mut Self::Class) {}
fn new() -> Self {
unimplemented!();
}
fn new_with_class(_klass: &Self::Class) -> Self {
Self::new()
}
}
unsafe extern "C" fn class_init<T: ObjectSubclass>(
klass: glib_sys::gpointer,
_klass_data: glib_sys::gpointer,
) where
<<T as ObjectSubclass>::ParentType as ObjectType>::RustClassType: IsSubclassable<T>,
{
let mut data = T::type_data();
if mem::size_of::<T>() != 0 {
let mut private_offset = data.as_ref().private_offset as i32;
gobject_sys::g_type_class_adjust_private_offset(klass, &mut private_offset);
(*data.as_mut()).private_offset = private_offset as isize;
}
{
let gobject_klass = &mut *(klass as *mut gobject_sys::GObjectClass);
gobject_klass.finalize = Some(finalize::<T>);
}
{
let klass = &mut *(klass as *mut T::Class);
let parent_class =
gobject_sys::g_type_class_peek_parent(klass as *mut _ as glib_sys::gpointer)
as *mut <T::ParentType as ObjectType>::GlibClassType;
assert!(!parent_class.is_null());
(*data.as_mut()).parent_class = parent_class as glib_sys::gpointer;
klass.override_vfuncs();
T::class_init(klass);
}
}
unsafe extern "C" fn instance_init<T: ObjectSubclass>(
obj: *mut gobject_sys::GTypeInstance,
klass: glib_sys::gpointer,
) {
let mut data = T::type_data();
let private_offset = (*data.as_mut()).private_offset;
let ptr: *mut u8 = obj as *mut _ as *mut u8;
let priv_ptr = ptr.offset(private_offset);
let imp_storage = priv_ptr as *mut T;
let klass = &*(klass as *const T::Class);
let imp = T::new_with_class(klass);
ptr::write(imp_storage, imp);
}
unsafe extern "C" fn finalize<T: ObjectSubclass>(obj: *mut gobject_sys::GObject) {
let mut data = T::type_data();
let private_offset = (*data.as_mut()).private_offset;
let ptr: *mut u8 = obj as *mut _ as *mut u8;
let priv_ptr = ptr.offset(private_offset);
let imp_storage = priv_ptr as *mut T;
ptr::drop_in_place(imp_storage);
let parent_class = &*(data.as_ref().get_parent_class() as *const gobject_sys::GObjectClass);
if let Some(ref func) = parent_class.finalize {
func(obj);
}
}
pub fn register_type<T: ObjectSubclass>() -> Type
where
<<T as ObjectSubclass>::ParentType as ObjectType>::RustClassType: IsSubclassable<T>,
{
if mem::align_of::<T>() > 2 * mem::size_of::<usize>() {
panic!(
"Alignment {} of type not supported, bigger than {}",
mem::align_of::<T>(),
2 * mem::size_of::<usize>(),
);
}
unsafe {
use std::ffi::CString;
let type_info = gobject_sys::GTypeInfo {
class_size: mem::size_of::<T::Class>() as u16,
base_init: None,
base_finalize: None,
class_init: Some(class_init::<T>),
class_finalize: None,
class_data: ptr::null_mut(),
instance_size: mem::size_of::<T::Instance>() as u16,
n_preallocs: 0,
instance_init: Some(instance_init::<T>),
value_table: ptr::null(),
};
let type_name = CString::new(T::NAME).unwrap();
if gobject_sys::g_type_from_name(type_name.as_ptr()) != gobject_sys::G_TYPE_INVALID {
panic!(
"Type {} has already been registered",
type_name.to_str().unwrap()
);
}
let type_ = from_glib(gobject_sys::g_type_register_static(
<T::ParentType as StaticType>::static_type().to_glib(),
type_name.as_ptr(),
&type_info,
if T::ABSTRACT {
gobject_sys::G_TYPE_FLAG_ABSTRACT
} else {
0
},
));
let mut data = T::type_data();
(*data.as_mut()).type_ = type_;
let private_offset = if mem::size_of::<T>() == 0 {
0
} else {
gobject_sys::g_type_add_instance_private(type_.to_glib(), mem::size_of::<T>())
};
(*data.as_mut()).private_offset = private_offset as isize;
T::type_init(&mut InitializingType::<T>(type_, marker::PhantomData));
type_
}
}
pub(crate) unsafe fn add_signal(
type_: glib_sys::GType,
name: &str,
flags: SignalFlags,
arg_types: &[Type],
ret_type: Type,
) {
let arg_types = arg_types.iter().map(ToGlib::to_glib).collect::<Vec<_>>();
gobject_sys::g_signal_newv(
name.to_glib_none().0,
type_,
flags.to_glib(),
ptr::null_mut(),
None,
ptr::null_mut(),
None,
ret_type.to_glib(),
arg_types.len() as u32,
arg_types.as_ptr() as *mut _,
);
}
#[repr(C)]
pub struct SignalInvocationHint(gobject_sys::GSignalInvocationHint);
impl SignalInvocationHint {
pub fn detail(&self) -> ::Quark {
from_glib(self.0.detail)
}
pub fn run_type(&self) -> SignalFlags {
from_glib(self.0.run_type)
}
}
impl fmt::Debug for SignalInvocationHint {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
f.debug_struct("SignalInvocationHint")
.field("detail", &self.detail())
.field("run_type", &self.run_type())
.finish()
}
}
pub(crate) unsafe fn add_signal_with_accumulator<F>(
type_: glib_sys::GType,
name: &str,
flags: SignalFlags,
arg_types: &[Type],
ret_type: Type,
accumulator: F,
) where
F: Fn(&SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static,
{
let arg_types = arg_types.iter().map(ToGlib::to_glib).collect::<Vec<_>>();
let accumulator: Box<F> = Box::new(accumulator);
unsafe extern "C" fn accumulator_trampoline<
F: Fn(&SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static,
>(
ihint: *mut gobject_sys::GSignalInvocationHint,
return_accu: *mut gobject_sys::GValue,
handler_return: *const gobject_sys::GValue,
data: glib_sys::gpointer,
) -> glib_sys::gboolean {
let accumulator: &F = &*(data as *const &F);
accumulator(
&SignalInvocationHint(*ihint),
&mut *(return_accu as *mut Value),
&*(handler_return as *const Value),
)
.to_glib()
}
gobject_sys::g_signal_newv(
name.to_glib_none().0,
type_,
flags.to_glib(),
ptr::null_mut(),
Some(accumulator_trampoline::<F>),
Box::into_raw(accumulator) as glib_sys::gpointer,
None,
ret_type.to_glib(),
arg_types.len() as u32,
arg_types.as_ptr() as *mut _,
);
}
pub struct SignalClassHandlerToken(*mut gobject_sys::GTypeInstance);
impl fmt::Debug for SignalClassHandlerToken {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
f.debug_tuple("SignalClassHandlerToken")
.field(&unsafe { ::Object::from_glib_borrow(self.0 as *mut gobject_sys::GObject) })
.finish()
}
}
pub(crate) unsafe fn add_signal_with_class_handler<F>(
type_: glib_sys::GType,
name: &str,
flags: SignalFlags,
arg_types: &[Type],
ret_type: Type,
class_handler: F,
) where
F: Fn(&SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
{
let arg_types = arg_types.iter().map(ToGlib::to_glib).collect::<Vec<_>>();
let class_handler = Closure::new(move |values| {
let instance = gobject_sys::g_value_get_object(values[0].to_glib_none().0);
class_handler(&SignalClassHandlerToken(instance as *mut _), values)
});
gobject_sys::g_signal_newv(
name.to_glib_none().0,
type_,
flags.to_glib(),
class_handler.to_glib_none().0,
None,
ptr::null_mut(),
None,
ret_type.to_glib(),
arg_types.len() as u32,
arg_types.as_ptr() as *mut _,
);
}
pub(crate) unsafe fn add_signal_with_class_handler_and_accumulator<F, G>(
type_: glib_sys::GType,
name: &str,
flags: SignalFlags,
arg_types: &[Type],
ret_type: Type,
class_handler: F,
accumulator: G,
) where
F: Fn(&SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
G: Fn(&SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static,
{
let arg_types = arg_types.iter().map(ToGlib::to_glib).collect::<Vec<_>>();
let class_handler = Closure::new(move |values| {
let instance = gobject_sys::g_value_get_object(values[0].to_glib_none().0);
class_handler(&SignalClassHandlerToken(instance as *mut _), values)
});
let accumulator: Box<G> = Box::new(accumulator);
unsafe extern "C" fn accumulator_trampoline<
G: Fn(&SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static,
>(
ihint: *mut gobject_sys::GSignalInvocationHint,
return_accu: *mut gobject_sys::GValue,
handler_return: *const gobject_sys::GValue,
data: glib_sys::gpointer,
) -> glib_sys::gboolean {
let accumulator: &G = &*(data as *const &G);
accumulator(
&SignalInvocationHint(*ihint),
&mut *(return_accu as *mut Value),
&*(handler_return as *const Value),
)
.to_glib()
}
gobject_sys::g_signal_newv(
name.to_glib_none().0,
type_,
flags.to_glib(),
class_handler.to_glib_none().0,
Some(accumulator_trampoline::<G>),
Box::into_raw(accumulator) as glib_sys::gpointer,
None,
ret_type.to_glib(),
arg_types.len() as u32,
arg_types.as_ptr() as *mut _,
);
}
pub(crate) unsafe fn signal_override_class_handler<F>(
name: &str,
type_: glib_sys::GType,
class_handler: F,
) where
F: Fn(&super::SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
{
let class_handler = Closure::new(move |values| {
let instance = gobject_sys::g_value_get_object(values[0].to_glib_none().0);
class_handler(&SignalClassHandlerToken(instance as *mut _), values)
});
let mut signal_id = 0;
let found: bool = from_glib(gobject_sys::g_signal_parse_name(
name.to_glib_none().0,
type_,
&mut signal_id,
ptr::null_mut(),
false.to_glib(),
));
if !found {
panic!("Signal '{}' not found", name);
}
gobject_sys::g_signal_override_class_closure(signal_id, type_, class_handler.to_glib_none().0);
}
pub(crate) unsafe fn signal_chain_from_overridden(
instance: *mut gobject_sys::GTypeInstance,
token: &SignalClassHandlerToken,
values: &[Value],
) -> Option<Value> {
assert_eq!(instance, token.0);
let mut result = Value::uninitialized();
gobject_sys::g_signal_chain_from_overridden(
values.as_ptr() as *mut Value as *mut gobject_sys::GValue,
result.to_glib_none_mut().0,
);
if result.type_() != Type::Unit && result.type_() != Type::Invalid {
Some(result)
} else {
None
}
}