use ffi;
use gobject_ffi;
use translate::*;
use value::*;
use std::ops;
pub trait BoxedType: Clone + Sized + 'static {
const NAME: &'static str;
fn get_type() -> ::Type;
}
pub fn register_boxed_type<T: BoxedType>() -> ::Type {
unsafe {
use std::ffi::CString;
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
);
from_glib(gobject_ffi::g_boxed_type_register_static(
type_name.as_ptr(),
Some(boxed_copy::<T>),
Some(boxed_free::<T>),
))
}
}
unsafe extern "C" fn boxed_copy<T: BoxedType>(v: ffi::gpointer) -> ffi::gpointer {
let v = &*(v as *mut T);
let copy = Box::new(v.clone());
Box::into_raw(copy) as ffi::gpointer
}
unsafe extern "C" fn boxed_free<T: BoxedType>(v: ffi::gpointer) {
let v = v as *mut T;
let _ = Box::from_raw(v);
}
#[macro_export]
macro_rules! glib_boxed_type {
() => {
fn get_type() -> $crate::Type {
static mut TYPE_: $crate::Type = $crate::Type::Invalid;
static ONCE: ::std::sync::Once = ::std::sync::Once::new();
ONCE.call_once(|| {
let type_ = $crate::subclass::register_boxed_type::<Self>();
unsafe {
TYPE_ = type_;
}
});
unsafe { TYPE_ }
}
};
}
#[macro_export]
macro_rules! glib_boxed_derive_traits {
($name:ident) => {
impl $crate::StaticType for $name {
fn static_type() -> $crate::Type {
<$name as $crate::subclass::boxed::BoxedType>::get_type()
}
}
impl $crate::value::SetValue for $name {
unsafe fn set_value(value: &mut $crate::value::Value, this: &Self) {
let ptr: *mut $name = Box::into_raw(Box::new(this.clone()));
$crate::gobject_ffi::g_value_take_boxed(
$crate::translate::ToGlibPtrMut::to_glib_none_mut(value).0,
ptr as *mut _,
);
}
}
impl $crate::value::SetValueOptional for $name {
unsafe fn set_value_optional(value: &mut $crate::value::Value, this: Option<&Self>) {
let this = this.expect("None not allowed");
let ptr: *mut $name = Box::into_raw(Box::new(this.clone()));
$crate::gobject_ffi::g_value_take_boxed(
$crate::translate::ToGlibPtrMut::to_glib_none_mut(value).0,
ptr as *mut _,
);
}
}
impl<'a> $crate::value::FromValueOptional<'a> for &'a $name {
unsafe fn from_value_optional(value: &'a $crate::value::Value) -> Option<Self> {
let ptr = $crate::gobject_ffi::g_value_get_boxed(
$crate::translate::ToGlibPtr::to_glib_none(value).0,
);
assert!(!ptr.is_null());
Some(&*(ptr as *mut $name))
}
}
impl<'a> $crate::value::FromValue<'a> for &'a $name {
unsafe fn from_value(value: &'a $crate::value::Value) -> Self {
let ptr = $crate::gobject_ffi::g_value_get_boxed(
$crate::translate::ToGlibPtr::to_glib_none(value).0,
);
assert!(!ptr.is_null());
&*(ptr as *mut $name)
}
}
};
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Boxed<T: BoxedType>(pub T);
impl<T: BoxedType> ops::Deref for Boxed<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
impl<T: BoxedType> ops::DerefMut for Boxed<T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.0
}
}
impl<T: BoxedType> ::StaticType for Boxed<T> {
fn static_type() -> ::Type {
T::get_type()
}
}
impl<T: BoxedType> SetValue for Boxed<T> {
unsafe fn set_value(value: &mut Value, this: &Self) {
let ptr: *mut Boxed<T> = Box::into_raw(Box::new(this.clone()));
gobject_ffi::g_value_take_boxed(value.to_glib_none_mut().0, ptr as *mut _);
}
}
impl<T: BoxedType> SetValueOptional for Boxed<T> {
unsafe fn set_value_optional(value: &mut Value, this: Option<&Self>) {
let this = this.expect("None not allowed");
let ptr: *mut Boxed<T> = Box::into_raw(Box::new(this.clone()));
gobject_ffi::g_value_take_boxed(value.to_glib_none_mut().0, ptr as *mut _);
}
}
impl<'a, T: BoxedType> FromValueOptional<'a> for &'a Boxed<T> {
unsafe fn from_value_optional(value: &'a Value) -> Option<Self> {
let ptr = gobject_ffi::g_value_get_boxed(value.to_glib_none().0);
assert!(!ptr.is_null());
Some(&*(ptr as *mut Boxed<T>))
}
}
impl<'a, T: BoxedType> FromValue<'a> for &'a Boxed<T> {
unsafe fn from_value(value: &'a Value) -> Self {
let ptr = gobject_ffi::g_value_get_boxed(value.to_glib_none().0);
assert!(!ptr.is_null());
&*(ptr as *mut Boxed<T>)
}
}
#[cfg(test)]
mod test {
use super::*;
#[derive(Clone, Debug, PartialEq, Eq)]
struct MyBoxed(String);
impl BoxedType for MyBoxed {
const NAME: &'static str = "MyBoxed";
glib_boxed_type!();
}
glib_boxed_derive_traits!(MyBoxed);
#[test]
fn test_register() {
assert_ne!(::Type::Invalid, MyBoxed::get_type());
}
#[test]
fn test_value_boxed() {
assert_ne!(::Type::Invalid, MyBoxed::get_type());
let b = Boxed(MyBoxed(String::from("abc")));
let v = b.to_value();
let b2 = v.get::<&Boxed<MyBoxed>>().unwrap();
assert_eq!(&b, b2);
}
#[test]
fn test_value() {
assert_ne!(::Type::Invalid, MyBoxed::get_type());
let b = MyBoxed(String::from("abc"));
let v = b.to_value();
let b2 = v.get::<&MyBoxed>().unwrap();
assert_eq!(&b, b2);
}
}