Skip to content
This repository was archived by the owner on Jun 8, 2021. It is now read-only.

Commit 14d3bb3

Browse files
authored
Merge pull request #438 from sdroege/interfaces
Add support for defining new interfaces and other minor things
2 parents a0712e7 + 08f04dc commit 14d3bb3

File tree

6 files changed

+467
-60
lines changed

6 files changed

+467
-60
lines changed

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ pub use object::{
104104
Object,
105105
ObjectExt,
106106
ObjectClass,
107+
InitiallyUnowned,
108+
InitiallyUnownedClass,
107109
WeakRef,
108110
SendWeakRef,
109111
};

src/object.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,45 @@ pub unsafe trait IsClassFor: Sized + 'static {
125125
Some(&mut *klass)
126126
}
127127
}
128+
129+
/// Gets the class struct corresponding to `type_`.
130+
///
131+
/// This will return `None` if `type_` is not a subclass of `Self`.
132+
fn from_type(type_: Type) -> Option<ClassRef<Self>> {
133+
if !type_.is_a(&Self::Instance::static_type()) {
134+
return None;
135+
}
136+
137+
unsafe {
138+
let ptr = gobject_ffi::g_type_class_ref(type_.to_glib());
139+
if ptr.is_null() {
140+
None
141+
} else {
142+
Some(ClassRef(ptr::NonNull::new_unchecked(ptr as *mut Self)))
143+
}
144+
}
145+
}
146+
}
147+
148+
#[derive(Debug)]
149+
pub struct ClassRef<T: IsClassFor>(ptr::NonNull<T>);
150+
151+
impl<T: IsClassFor> ops::Deref for ClassRef<T> {
152+
type Target = T;
153+
154+
fn deref(&self) -> &T {
155+
unsafe {
156+
self.0.as_ref()
157+
}
158+
}
159+
}
160+
161+
impl<T: IsClassFor> Drop for ClassRef<T> {
162+
fn drop(&mut self) {
163+
unsafe {
164+
gobject_ffi::g_type_class_unref(self.0.as_ptr() as *mut _);
165+
}
166+
}
128167
}
129168

130169
/// Upcasting and downcasting support.
@@ -1264,6 +1303,14 @@ impl ObjectClass {
12641303
}
12651304
}
12661305

1306+
glib_wrapper! {
1307+
pub struct InitiallyUnowned(Object<gobject_ffi::GInitiallyUnowned, gobject_ffi::GInitiallyUnownedClass, InitiallyUnownedClass>);
1308+
1309+
match fn {
1310+
get_type => || gobject_ffi::g_initially_unowned_get_type(),
1311+
}
1312+
}
1313+
12671314
pub struct WeakRef<T: ObjectType>(Box<gobject_ffi::GWeakRef>, PhantomData<*const T>);
12681315

12691316
impl<T: ObjectType> WeakRef<T> {

src/subclass/interface.rs

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
// Copyright 2019, The Gtk-rs Project Developers.
2+
// See the COPYRIGHT file at the top-level directory of this distribution.
3+
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>
4+
5+
use ffi;
6+
use gobject_ffi;
7+
8+
use std::marker;
9+
use std::mem;
10+
use std::ptr;
11+
12+
use super::{InitializingType, Property};
13+
use translate::*;
14+
use {IsA, Object, ObjectExt, StaticType, Type, Value};
15+
16+
impl<T: ObjectInterface> InitializingType<T> {
17+
/// Adds an interface prerequisite for `I` to the type.
18+
///
19+
/// All implementors of the interface must be a subclass of `I` or implement the interface `I`.
20+
pub fn add_prerequisite<I: StaticType>(&mut self) {
21+
unsafe {
22+
gobject_ffi::g_type_interface_add_prerequisite(
23+
self.0.to_glib(),
24+
I::static_type().to_glib(),
25+
)
26+
}
27+
}
28+
}
29+
30+
/// Macro for boilerplate of [`ObjectInterface`] implementations.
31+
///
32+
/// [`ObjectInterface`]: subclass/types/trait.ObjectInterface.html
33+
#[macro_export]
34+
macro_rules! glib_object_interface {
35+
() => {
36+
fn get_type() -> $crate::Type {
37+
static ONCE: ::std::sync::Once = ::std::sync::Once::new();
38+
static mut TYPE: $crate::Type = $crate::Type::Invalid;
39+
40+
ONCE.call_once(|| {
41+
let type_ = $crate::subclass::register_interface::<Self>();
42+
unsafe {
43+
TYPE = type_;
44+
}
45+
});
46+
47+
unsafe {
48+
assert_ne!(TYPE, $crate::Type::Invalid);
49+
50+
TYPE
51+
}
52+
}
53+
};
54+
}
55+
56+
/// The central trait for defining a `GObject` interface.
57+
///
58+
/// Links together the type name and the interface struct for type registration and allows to hook
59+
/// into various steps of the type registration and initialization.
60+
///
61+
/// This must only be implemented on `#[repr(C)]` structs and have `gobject_ffi::GTypeInterface` as
62+
/// the first field.
63+
///
64+
/// See [`register_interface`] for registering an implementation of this trait
65+
/// with the type system.
66+
///
67+
/// [`register_interface`]: fn.register_interface.html
68+
pub trait ObjectInterface: Sized + 'static {
69+
/// `GObject` type name.
70+
///
71+
/// This must be unique in the whole process.
72+
const NAME: &'static str;
73+
74+
/// Returns the `glib::Type` ID of the interface.
75+
///
76+
/// This will register the type with the type system on the first call and is usually generated
77+
/// by the [`glib_object_interface!`] macro.
78+
///
79+
/// [`glib_object_interface!`]: ../../macro.glib_object_interface.html
80+
fn get_type() -> Type;
81+
82+
/// Additional type initialization.
83+
///
84+
/// This is called right after the type was registered and allows
85+
/// interfaces to do additional type-specific initialization, e.g.
86+
/// for adding prerequisites.
87+
///
88+
/// Optional
89+
fn type_init(_type_: &mut InitializingType<Self>) {}
90+
91+
/// Interface initialization.
92+
///
93+
/// This is called after `type_init` and before the first implementor
94+
/// of the interface is created. Interfaces can use this to do interface-
95+
/// specific initialization, e.g. for installing properties or signals
96+
/// on the interface, and for setting default implementations of interface
97+
/// functions.
98+
///
99+
/// Optional
100+
fn interface_init(&mut self) {}
101+
}
102+
103+
pub trait ObjectInterfaceExt: ObjectInterface {
104+
/// Get interface from an instance.
105+
///
106+
/// This will panic if `obj` does not implement the interface.
107+
fn from_instance<T: IsA<Object>>(obj: &T) -> &Self {
108+
assert!(obj.as_ref().get_type().is_a(&Self::get_type()));
109+
110+
unsafe {
111+
let klass = (*(obj.as_ptr() as *const gobject_ffi::GTypeInstance)).g_class;
112+
let interface =
113+
gobject_ffi::g_type_interface_peek(klass as *mut _, Self::get_type().to_glib());
114+
assert!(!interface.is_null());
115+
&*(interface as *const Self)
116+
}
117+
}
118+
119+
/// Install properties on the interface.
120+
///
121+
/// All implementors of the interface must provide these properties.
122+
fn install_properties(&mut self, properties: &[Property]) {
123+
if properties.is_empty() {
124+
return;
125+
}
126+
127+
for property in properties {
128+
let pspec = (property.1)(property.0);
129+
unsafe {
130+
gobject_ffi::g_object_interface_install_property(
131+
self as *mut Self as *mut _,
132+
pspec.to_glib_none().0,
133+
);
134+
}
135+
}
136+
}
137+
138+
/// Add a new signal to the interface.
139+
///
140+
/// This can be emitted later by `glib::Object::emit` and external code
141+
/// can connect to the signal to get notified about emissions.
142+
fn add_signal(&mut self, name: &str, arg_types: &[Type], ret_type: Type) {
143+
unsafe {
144+
super::types::add_signal(
145+
*(self as *mut _ as *mut ffi::GType),
146+
name,
147+
arg_types,
148+
ret_type,
149+
);
150+
}
151+
}
152+
153+
/// Add a new signal with accumulator to the interface.
154+
///
155+
/// This can be emitted later by `glib::Object::emit` and external code
156+
/// can connect to the signal to get notified about emissions.
157+
///
158+
/// The accumulator function is used for accumulating the return values of
159+
/// multiple signal handlers. The new value is passed as second argument and
160+
/// should be combined with the old value in the first argument. If no further
161+
/// signal handlers should be called, `false` should be returned.
162+
fn add_signal_with_accumulator<F>(
163+
&mut self,
164+
name: &str,
165+
arg_types: &[Type],
166+
ret_type: Type,
167+
accumulator: F,
168+
) where
169+
F: Fn(&mut Value, &Value) -> bool + Send + Sync + 'static,
170+
{
171+
unsafe {
172+
super::types::add_signal_with_accumulator(
173+
*(self as *mut _ as *mut ffi::GType),
174+
name,
175+
arg_types,
176+
ret_type,
177+
accumulator,
178+
);
179+
}
180+
}
181+
182+
/// Add a new action signal with accumulator to the interface.
183+
///
184+
/// Different to normal signals, action signals are supposed to be emitted
185+
/// by external code and will cause the provided handler to be called.
186+
///
187+
/// It can be thought of as a dynamic function call.
188+
fn add_action_signal<F>(&mut self, name: &str, arg_types: &[Type], ret_type: Type, handler: F)
189+
where
190+
F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static,
191+
{
192+
unsafe {
193+
super::types::add_action_signal(
194+
*(self as *mut _ as *mut ffi::GType),
195+
name,
196+
arg_types,
197+
ret_type,
198+
handler,
199+
);
200+
}
201+
}
202+
}
203+
204+
impl<T: ObjectInterface> ObjectInterfaceExt for T {}
205+
206+
unsafe extern "C" fn interface_init<T: ObjectInterface>(
207+
klass: ffi::gpointer,
208+
_klass_data: ffi::gpointer,
209+
) {
210+
let iface = &mut *(klass as *mut T);
211+
iface.interface_init();
212+
}
213+
214+
/// Register a `glib::Type` ID for `T`.
215+
///
216+
/// This must be called only once and will panic on a second call.
217+
///
218+
/// The [`glib_object_interface!`] macro will create a `get_type()` function around this, which will
219+
/// ensure that it's only ever called once.
220+
///
221+
/// [`glib_object_interface!`]: ../../macro.glib_object_interface.html
222+
pub fn register_interface<T: ObjectInterface>() -> Type {
223+
unsafe {
224+
use std::ffi::CString;
225+
226+
let type_info = gobject_ffi::GTypeInfo {
227+
class_size: mem::size_of::<T>() as u16,
228+
base_init: None,
229+
base_finalize: None,
230+
class_init: Some(interface_init::<T>),
231+
class_finalize: None,
232+
class_data: ptr::null_mut(),
233+
instance_size: 0,
234+
n_preallocs: 0,
235+
instance_init: None,
236+
value_table: ptr::null(),
237+
};
238+
239+
let type_name = CString::new(T::NAME).unwrap();
240+
assert_eq!(
241+
gobject_ffi::g_type_from_name(type_name.as_ptr()),
242+
gobject_ffi::G_TYPE_INVALID
243+
);
244+
245+
let type_ = from_glib(gobject_ffi::g_type_register_static(
246+
Type::BaseInterface.to_glib(),
247+
type_name.as_ptr(),
248+
&type_info,
249+
0,
250+
));
251+
252+
T::type_init(&mut InitializingType::<T>(type_, marker::PhantomData));
253+
254+
type_
255+
}
256+
}

src/subclass/mod.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,9 @@ pub mod simple;
173173
#[macro_use]
174174
pub mod types;
175175

176+
#[macro_use]
177+
pub mod interface;
178+
176179
#[macro_use]
177180
pub mod object;
178181

@@ -182,10 +185,14 @@ pub mod boxed;
182185
pub mod prelude {
183186
//! Prelude that re-exports all important traits from this crate.
184187
pub use super::boxed::BoxedType;
188+
pub use super::interface::{ObjectInterface, ObjectInterfaceExt};
185189
pub use super::object::{ObjectClassSubclassExt, ObjectImpl};
186-
pub use super::types::{ClassStruct, InstanceStruct, IsImplementable, IsSubclassable, ObjectSubclass};
190+
pub use super::types::{
191+
ClassStruct, InstanceStruct, IsImplementable, IsSubclassable, ObjectSubclass,
192+
};
187193
}
188194

189195
pub use self::boxed::register_boxed_type;
196+
pub use self::interface::register_interface;
190197
pub use self::object::Property;
191198
pub use self::types::{register_type, InitializingType, TypeData};

0 commit comments

Comments
 (0)