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

Commit fe523ef

Browse files
committed
Add support for defining new interfaces
1 parent d856438 commit fe523ef

File tree

3 files changed

+251
-3
lines changed

3 files changed

+251
-3
lines changed

src/subclass/interface.rs

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
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 {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+
/// Install properties on the interface.
105+
///
106+
/// All implementors of the interface must provide these properties.
107+
fn install_properties(&mut self, properties: &[Property]) {
108+
if properties.is_empty() {
109+
return;
110+
}
111+
112+
for property in properties {
113+
let pspec = (property.1)(property.0);
114+
unsafe {
115+
gobject_ffi::g_object_interface_install_property(
116+
self as *mut Self as *mut _,
117+
pspec.to_glib_none().0,
118+
);
119+
}
120+
}
121+
}
122+
123+
/// Add a new signal to the interface.
124+
///
125+
/// This can be emitted later by `glib::Object::emit` and external code
126+
/// can connect to the signal to get notified about emissions.
127+
fn add_signal(&mut self, name: &str, arg_types: &[Type], ret_type: Type) {
128+
unsafe {
129+
super::types::add_signal(
130+
*(self as *mut _ as *mut ffi::GType),
131+
name,
132+
arg_types,
133+
ret_type,
134+
);
135+
}
136+
}
137+
138+
/// Add a new signal with accumulator 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+
///
143+
/// The accumulator function is used for accumulating the return values of
144+
/// multiple signal handlers. The new value is passed as second argument and
145+
/// should be combined with the old value in the first argument. If no further
146+
/// signal handlers should be called, `false` should be returned.
147+
fn add_signal_with_accumulator<F>(
148+
&mut self,
149+
name: &str,
150+
arg_types: &[Type],
151+
ret_type: Type,
152+
accumulator: F,
153+
) where
154+
F: Fn(&mut Value, &Value) -> bool + Send + Sync + 'static,
155+
{
156+
unsafe {
157+
super::types::add_signal_with_accumulator(
158+
*(self as *mut _ as *mut ffi::GType),
159+
name,
160+
arg_types,
161+
ret_type,
162+
accumulator,
163+
);
164+
}
165+
}
166+
167+
/// Add a new action signal with accumulator to the interface.
168+
///
169+
/// Different to normal signals, action signals are supposed to be emitted
170+
/// by external code and will cause the provided handler to be called.
171+
///
172+
/// It can be thought of as a dynamic function call.
173+
fn add_action_signal<F>(&mut self, name: &str, arg_types: &[Type], ret_type: Type, handler: F)
174+
where
175+
F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static,
176+
{
177+
unsafe {
178+
super::types::add_action_signal(
179+
*(self as *mut _ as *mut ffi::GType),
180+
name,
181+
arg_types,
182+
ret_type,
183+
handler,
184+
);
185+
}
186+
}
187+
}
188+
189+
impl<T: ObjectInterface> ObjectInterfaceExt for T {}
190+
191+
unsafe extern "C" fn interface_init<T: ObjectInterface>(
192+
klass: ffi::gpointer,
193+
_klass_data: ffi::gpointer,
194+
) {
195+
let iface = &mut *(klass as *mut T);
196+
iface.interface_init();
197+
}
198+
199+
/// Register a `glib::Type` ID for `T`.
200+
///
201+
/// This must be called only once and will panic on a second call.
202+
///
203+
/// The [`glib_object_interface!`] macro will create a `get_type()` function around this, which will
204+
/// ensure that it's only ever called once.
205+
///
206+
/// [`glib_object_interface!`]: ../../macro.glib_object_interface.html
207+
pub fn register_interface<T: ObjectInterface>() -> Type {
208+
unsafe {
209+
use std::ffi::CString;
210+
211+
let type_info = gobject_ffi::GTypeInfo {
212+
class_size: mem::size_of::<T>() as u16,
213+
base_init: None,
214+
base_finalize: None,
215+
class_init: Some(interface_init::<T>),
216+
class_finalize: None,
217+
class_data: ptr::null_mut(),
218+
instance_size: 0,
219+
n_preallocs: 0,
220+
instance_init: None,
221+
value_table: ptr::null(),
222+
};
223+
224+
let type_name = CString::new(T::NAME).unwrap();
225+
assert_eq!(
226+
gobject_ffi::g_type_from_name(type_name.as_ptr()),
227+
gobject_ffi::G_TYPE_INVALID
228+
);
229+
230+
let type_ = from_glib(gobject_ffi::g_type_register_static(
231+
Type::BaseInterface.to_glib(),
232+
type_name.as_ptr(),
233+
&type_info,
234+
0,
235+
));
236+
237+
T::type_init(&mut InitializingType::<T>(type_, marker::PhantomData));
238+
239+
type_
240+
}
241+
}

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};

src/subclass/types.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use super::object::ObjectImpl;
2222
/// This allows running additional type-setup functions, e.g. for implementing
2323
/// interfaces on the type.
2424
#[derive(Debug, PartialEq, Eq)]
25-
pub struct InitializingType<T: ObjectSubclass>(Type, marker::PhantomData<T>);
25+
pub struct InitializingType<T>(pub(crate) Type, pub(crate) marker::PhantomData<T>);
2626

2727
impl<T: ObjectSubclass> InitializingType<T> {
2828
/// Adds an interface implementation for `I` to the type.
@@ -42,7 +42,7 @@ impl<T: ObjectSubclass> InitializingType<T> {
4242
}
4343
}
4444

45-
impl<T: ObjectSubclass> ToGlib for InitializingType<T> {
45+
impl<T> ToGlib for InitializingType<T> {
4646
type GlibType = ffi::GType;
4747

4848
fn to_glib(&self) -> ffi::GType {

0 commit comments

Comments
 (0)