use super::prelude::*;
use super::types;
use glib_sys;
use gobject_sys;
use std::borrow::Borrow;
use std::fmt;
use std::mem;
use std::ptr;
use translate::*;
use {Object, ObjectClass, ObjectType, SignalFlags, Type, Value};
#[macro_export]
macro_rules! glib_object_impl {
() => {
fn get_type_data(&self) -> ::std::ptr::NonNull<$crate::subclass::TypeData> {
Self::type_data()
}
};
}
pub trait ObjectImpl: ObjectImplExt + 'static {
fn get_type_data(&self) -> ptr::NonNull<types::TypeData>;
fn set_property(&self, _obj: &Object, _id: usize, _value: &Value) {
unimplemented!()
}
fn get_property(&self, _obj: &Object, _id: usize) -> Result<Value, ()> {
unimplemented!()
}
fn constructed(&self, obj: &Object) {
self.parent_constructed(obj);
}
}
unsafe extern "C" fn get_property<T: ObjectSubclass>(
obj: *mut gobject_sys::GObject,
id: u32,
value: *mut gobject_sys::GValue,
_pspec: *mut gobject_sys::GParamSpec,
) {
let instance = &*(obj as *mut T::Instance);
let imp = instance.get_impl();
match imp.get_property(&from_glib_borrow(obj), (id - 1) as usize) {
Ok(v) => {
gobject_sys::g_value_unset(value);
ptr::write(value, ptr::read(v.to_glib_none().0));
mem::forget(v);
}
Err(()) => eprintln!("Failed to get property"),
}
}
unsafe extern "C" fn set_property<T: ObjectSubclass>(
obj: *mut gobject_sys::GObject,
id: u32,
value: *mut gobject_sys::GValue,
_pspec: *mut gobject_sys::GParamSpec,
) {
let instance = &*(obj as *mut T::Instance);
let imp = instance.get_impl();
imp.set_property(
&from_glib_borrow(obj),
(id - 1) as usize,
&*(value as *mut Value),
);
}
unsafe extern "C" fn constructed<T: ObjectSubclass>(obj: *mut gobject_sys::GObject) {
let instance = &*(obj as *mut T::Instance);
let imp = instance.get_impl();
imp.constructed(&from_glib_borrow(obj));
}
#[derive(Clone)]
pub struct Property<'a>(pub &'a str, pub fn(&str) -> ::ParamSpec);
impl<'a> fmt::Debug for Property<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
f.debug_tuple("Property").field(&self.0).finish()
}
}
pub unsafe trait ObjectClassSubclassExt: Sized + 'static {
fn install_properties<'a, T: Borrow<Property<'a>>>(&mut self, properties: &[T]) {
if properties.is_empty() {
return;
}
let mut pspecs = Vec::with_capacity(properties.len());
for property in properties {
let property = property.borrow();
let pspec = (property.1)(property.0);
pspecs.push(pspec);
}
unsafe {
let mut pspecs_ptrs = Vec::with_capacity(properties.len());
pspecs_ptrs.push(ptr::null_mut());
for pspec in &pspecs {
pspecs_ptrs.push(pspec.to_glib_none().0);
}
gobject_sys::g_object_class_install_properties(
self as *mut _ as *mut gobject_sys::GObjectClass,
pspecs_ptrs.len() as u32,
pspecs_ptrs.as_mut_ptr(),
);
}
}
fn add_signal(&mut self, name: &str, flags: SignalFlags, arg_types: &[Type], ret_type: Type) {
unsafe {
super::types::add_signal(
*(self as *mut _ as *mut glib_sys::GType),
name,
flags,
arg_types,
ret_type,
);
}
}
fn add_signal_with_class_handler<F>(
&mut self,
name: &str,
flags: SignalFlags,
arg_types: &[Type],
ret_type: Type,
class_handler: F,
) where
F: Fn(&super::SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
{
unsafe {
super::types::add_signal_with_class_handler(
*(self as *mut _ as *mut glib_sys::GType),
name,
flags,
arg_types,
ret_type,
class_handler,
);
}
}
fn add_signal_with_accumulator<F>(
&mut self,
name: &str,
flags: SignalFlags,
arg_types: &[Type],
ret_type: Type,
accumulator: F,
) where
F: Fn(&super::SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static,
{
unsafe {
super::types::add_signal_with_accumulator(
*(self as *mut _ as *mut glib_sys::GType),
name,
flags,
arg_types,
ret_type,
accumulator,
);
}
}
fn add_signal_with_class_handler_and_accumulator<F, G>(
&mut self,
name: &str,
flags: SignalFlags,
arg_types: &[Type],
ret_type: Type,
class_handler: F,
accumulator: G,
) where
F: Fn(&super::SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
G: Fn(&super::SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static,
{
unsafe {
super::types::add_signal_with_class_handler_and_accumulator(
*(self as *mut _ as *mut glib_sys::GType),
name,
flags,
arg_types,
ret_type,
class_handler,
accumulator,
);
}
}
fn override_signal_class_handler<F>(&mut self, name: &str, class_handler: F)
where
F: Fn(&super::SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
{
unsafe {
super::types::signal_override_class_handler(
name,
*(self as *mut _ as *mut glib_sys::GType),
class_handler,
);
}
}
}
unsafe impl ObjectClassSubclassExt for ObjectClass {}
unsafe impl<T: ObjectSubclass> IsSubclassable<T> for ObjectClass {
fn override_vfuncs(&mut self) {
unsafe {
let klass = &mut *(self as *mut Self as *mut gobject_sys::GObjectClass);
klass.set_property = Some(set_property::<T>);
klass.get_property = Some(get_property::<T>);
klass.constructed = Some(constructed::<T>);
}
}
}
pub trait ObjectImplExt {
fn parent_constructed(&self, obj: &Object);
fn signal_chain_from_overridden(
&self,
token: &super::SignalClassHandlerToken,
values: &[Value],
) -> Option<Value>;
}
impl<T: ObjectImpl + ObjectSubclass> ObjectImplExt for T {
fn parent_constructed(&self, obj: &Object) {
unsafe {
let data = self.get_type_data();
let parent_class = data.as_ref().get_parent_class() as *mut gobject_sys::GObjectClass;
if let Some(ref func) = (*parent_class).constructed {
func(obj.to_glib_none().0);
}
}
}
fn signal_chain_from_overridden(
&self,
token: &super::SignalClassHandlerToken,
values: &[Value],
) -> Option<Value> {
unsafe {
super::types::signal_chain_from_overridden(
self.get_instance().as_ptr() as *mut _,
token,
values,
)
}
}
}
#[cfg(test)]
mod test {
use super::super::super::object::ObjectExt;
use super::super::super::subclass;
use super::super::super::value::{ToValue, Value};
use super::*;
use prelude::*;
use std::{cell::RefCell, error::Error};
pub struct ChildObject;
impl ObjectSubclass for ChildObject {
const NAME: &'static str = "ChildObject";
type ParentType = Object;
type Instance = subclass::simple::InstanceStruct<Self>;
type Class = subclass::simple::ClassStruct<Self>;
glib_object_subclass!();
fn new() -> Self {
ChildObject
}
}
impl ObjectImpl for ChildObject {
glib_object_impl!();
}
impl StaticType for ChildObject {
fn static_type() -> Type {
ChildObject::get_type()
}
}
static PROPERTIES: [Property; 3] = [
Property("name", |name| {
::ParamSpec::string(
name,
"Name",
"Name of this object",
None,
::ParamFlags::READWRITE,
)
}),
Property("constructed", |name| {
::ParamSpec::boolean(
name,
"Constructed",
"True if the constructed() virtual method was called",
false,
::ParamFlags::READABLE,
)
}),
Property("child", |name| {
::ParamSpec::object(
name,
"Child",
"Child object",
ChildObject::static_type(),
::ParamFlags::READWRITE,
)
}),
];
pub struct SimpleObject {
name: RefCell<Option<String>>,
constructed: RefCell<bool>,
}
impl ObjectSubclass for SimpleObject {
const NAME: &'static str = "SimpleObject";
type ParentType = Object;
type Instance = subclass::simple::InstanceStruct<Self>;
type Class = subclass::simple::ClassStruct<Self>;
glib_object_subclass!();
fn type_init(type_: &mut subclass::InitializingType<Self>) {
type_.add_interface::<DummyInterface>();
}
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
klass.install_properties(&PROPERTIES);
klass.add_signal(
"name-changed",
SignalFlags::RUN_LAST,
&[String::static_type()],
::Type::Unit,
);
klass.add_signal_with_class_handler(
"change-name",
SignalFlags::RUN_LAST | SignalFlags::ACTION,
&[String::static_type()],
String::static_type(),
|_, args| {
let obj = args[0]
.get::<Object>()
.expect("Failed to get args[0]")
.expect("Failed to get Object from args[0]");
let new_name = args[1]
.get::<String>()
.expect("Failed to get args[1]")
.expect("Failed to get Object from args[1]");
let imp = Self::from_instance(&obj);
let old_name = imp.name.borrow_mut().take();
*imp.name.borrow_mut() = Some(new_name);
obj.emit("name-changed", &[&*imp.name.borrow()])
.expect("Failed to borrow name");
Some(old_name.to_value())
},
);
klass.add_signal(
"create-string",
SignalFlags::RUN_LAST,
&[],
String::static_type(),
);
klass.add_signal(
"create-child-object",
SignalFlags::RUN_LAST,
&[],
ChildObject::static_type(),
);
}
fn new() -> Self {
Self {
name: RefCell::new(None),
constructed: RefCell::new(false),
}
}
}
impl ObjectImpl for SimpleObject {
glib_object_impl!();
fn set_property(&self, obj: &Object, id: usize, value: &Value) {
let prop = &PROPERTIES[id];
match *prop {
Property("name", ..) => {
let name = value
.get()
.expect("type conformity checked by 'Object::set_property'");
self.name.replace(name);
obj.emit("name-changed", &[&*self.name.borrow()])
.expect("Failed to borrow name");
}
Property("child", ..) => {
}
_ => unimplemented!(),
}
}
fn get_property(&self, _obj: &Object, id: usize) -> Result<Value, ()> {
let prop = &PROPERTIES[id];
match *prop {
Property("name", ..) => Ok(self.name.borrow().to_value()),
Property("constructed", ..) => Ok(self.constructed.borrow().to_value()),
_ => unimplemented!(),
}
}
fn constructed(&self, obj: &Object) {
self.parent_constructed(obj);
assert_eq!(obj, &self.get_instance());
assert_eq!(self as *const _, Self::from_instance(obj) as *const _);
*self.constructed.borrow_mut() = true;
}
}
#[repr(C)]
pub struct DummyInterface {
parent: gobject_sys::GTypeInterface,
}
impl ObjectInterface for DummyInterface {
const NAME: &'static str = "DummyInterface";
glib_object_interface!();
fn type_init(type_: &mut subclass::InitializingType<Self>) {
type_.add_prerequisite::<Object>();
}
}
impl StaticType for DummyInterface {
fn static_type() -> Type {
DummyInterface::get_type()
}
}
unsafe impl<T: ObjectSubclass> IsImplementable<T> for DummyInterface {
unsafe extern "C" fn interface_init(
_iface: glib_sys::gpointer,
_iface_data: glib_sys::gpointer,
) {
}
}
#[test]
fn test_create() {
let type_ = SimpleObject::get_type();
let obj = Object::new(type_, &[]).expect("Object::new failed");
assert!(obj.get_type().is_a(&DummyInterface::static_type()));
assert_eq!(
obj.get_property("constructed")
.expect("Failed to get 'constructed' property")
.get_some::<bool>()
.expect("Failed to get bool from 'constructed' property"),
true
);
let weak = obj.downgrade();
drop(obj);
assert!(weak.upgrade().is_none());
}
#[test]
fn test_create_child_object() {
let type_ = ChildObject::get_type();
let obj = Object::new(type_, &[]).expect("Object::new failed");
let imp = ChildObject::from_instance(&obj);
assert_eq!(imp as *const _ as *const (), obj.as_ptr() as *const _);
}
#[test]
fn test_set_properties() {
let obj = Object::new(SimpleObject::get_type(), &[]).expect("Object::new failed");
assert!(obj
.get_property("name")
.expect("Failed to get 'name' property")
.get::<&str>()
.expect("Failed to get str from 'name' property")
.is_none());
assert!(obj.set_property("name", &"test").is_ok());
assert_eq!(
obj.get_property("name")
.expect("Failed to get 'name' property")
.get::<&str>()
.expect("Failed to get str from 'name' property"),
Some("test")
);
assert_eq!(
obj.set_property("test", &true)
.err()
.expect("set_property failed")
.description(),
"property not found",
);
assert_eq!(
obj.set_property("constructed", &false)
.err()
.expect("Failed to set 'constructed' property")
.description(),
"property is not writable",
);
assert_eq!(
obj.set_property("name", &false)
.err()
.expect("Failed to set 'name' property")
.description(),
"property can't be set from the given type (expected: gchararray, got: gboolean)",
);
let other_obj = Object::new(SimpleObject::get_type(), &[]).expect("Object::new failed");
assert_eq!(
obj.set_property("child", &other_obj)
.err()
.expect("Failed to set 'child' property")
.description(),
"property can't be set from the given object type (expected: ChildObject, got: SimpleObject)",
);
let child = Object::new(ChildObject::get_type(), &[]).expect("Object::new failed");
assert!(obj.set_property("child", &child).is_ok());
}
#[test]
fn test_signals() {
use std::sync::{Arc, Mutex};
let type_ = SimpleObject::get_type();
let obj = Object::new(type_, &[("name", &"old-name")]).expect("Object::new failed");
let name_changed_triggered = Arc::new(Mutex::new(false));
let name_changed_clone = name_changed_triggered.clone();
obj.connect("name-changed", false, move |args| {
let _obj = args[0]
.get::<Object>()
.expect("Failed to get args[0]")
.expect("Failed to get str from args[0]");
let name = args[1]
.get::<&str>()
.expect("Failed to get args[1]")
.expect("Failed to get str from args[1]");
assert_eq!(name, "new-name");
*name_changed_clone.lock().expect("Failed to lock") = true;
None
})
.expect("Failed to connect on 'name-changed'");
assert_eq!(
obj.get_property("name")
.expect("Failed to get 'name' property")
.get::<&str>()
.expect("Failed to get str from 'name' property"),
Some("old-name")
);
assert!(!*name_changed_triggered.lock().expect("Failed to lock"));
let old_name = obj
.emit("change-name", &[&"new-name"])
.expect("Failed to emit")
.expect("Failed to get value from emit")
.get::<String>()
.expect("Failed to get str from emit");
assert_eq!(old_name, Some("old-name".to_string()));
assert!(*name_changed_triggered.lock().expect("Failed to lock"));
}
#[test]
fn test_signal_return_expected_type() {
let obj = Object::new(SimpleObject::get_type(), &[]).expect("Object::new failed");
obj.connect("create-string", false, move |_args| {
Some("return value".to_value())
})
.expect("Failed to connect on 'create-string'");
let value = obj
.emit("create-string", &[])
.expect("Failed to emit")
.expect("Failed to get value from emit");
assert_eq!(value.get::<String>(), Ok(Some("return value".to_string())));
}
#[test]
fn test_signal_return_expected_object_type() {
let obj = Object::new(SimpleObject::get_type(), &[]).expect("Object::new failed");
obj.connect("create-child-object", false, move |_args| {
Some(
Object::new(ChildObject::get_type(), &[])
.expect("Object::new failed")
.to_value(),
)
})
.expect("Failed to connect on 'create-child-object'");
let value = obj
.emit("create-child-object", &[])
.expect("Failed to emit")
.expect("Failed to get value from emit");
assert!(value.type_().is_a(&ChildObject::static_type()));
}
}