glib/subclass/interface.rs
1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{marker, mem};
4
5use super::{InitializingType, Signal, types::InterfaceStruct};
6use crate::{
7 Object, ParamSpec, Type, TypeFlags, TypeInfo, ffi, gobject_ffi, prelude::*, translate::*,
8};
9
10// rustdoc-stripper-ignore-next
11/// Trait for a type list of prerequisite object types.
12pub trait PrerequisiteList {
13 // rustdoc-stripper-ignore-next
14 /// Returns the list of types for this list.
15 fn types() -> Vec<Type>;
16}
17
18impl PrerequisiteList for () {
19 fn types() -> Vec<Type> {
20 vec![]
21 }
22}
23
24impl<T: ObjectType> PrerequisiteList for (T,) {
25 fn types() -> Vec<Type> {
26 vec![T::static_type()]
27 }
28}
29
30// Generates all the PrerequisiteList impls for prerequisite_lists of arbitrary sizes based on a list of type
31// parameters like A B C. It would generate the impl then for (A, B) and (A, B, C).
32macro_rules! prerequisite_list_trait(
33 ($name1:ident, $name2: ident, $($name:ident),*) => (
34 prerequisite_list_trait!(__impl $name1, $name2; $($name),*);
35 );
36 (__impl $($name:ident),+; $name1:ident, $($name2:ident),*) => (
37 prerequisite_list_trait_impl!($($name),+);
38 prerequisite_list_trait!(__impl $($name),+ , $name1; $($name2),*);
39 );
40 (__impl $($name:ident),+; $name1:ident) => (
41 prerequisite_list_trait_impl!($($name),+);
42 prerequisite_list_trait_impl!($($name),+, $name1);
43 );
44);
45
46// Generates the impl block for PrerequisiteList on prerequisite_lists or arbitrary sizes based on its
47// arguments. Takes a list of type parameters as parameters, e.g. A B C
48// and then implements the trait on (A, B, C).
49macro_rules! prerequisite_list_trait_impl(
50 ($($name:ident),+) => (
51 impl<$($name: ObjectType),+> PrerequisiteList for ( $($name),+ ) {
52 fn types() -> Vec<Type> {
53 vec![$($name::static_type()),+]
54 }
55 }
56 );
57);
58
59prerequisite_list_trait!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);
60
61/// Type methods required for an [`ObjectInterface`] implementation.
62///
63/// This is usually generated by the [`#[object_interface]`](crate::object_interface) attribute macro.
64pub unsafe trait ObjectInterfaceType {
65 /// Returns the `glib::Type` ID of the interface.
66 ///
67 /// This will register the type with the type system on the first call.
68 #[doc(alias = "get_type")]
69 fn type_() -> Type;
70}
71
72/// The central trait for defining a `GObject` interface.
73///
74/// Links together the type name, the empty instance and class structs for type
75/// registration and allows hooking into various steps of the type registration
76/// and initialization.
77///
78/// See [`register_interface`] for registering an implementation of this trait
79/// with the type system.
80///
81/// [`register_interface`]: fn.register_interface.html
82pub trait ObjectInterface: ObjectInterfaceType + Sized + 'static {
83 /// `GObject` type name.
84 ///
85 /// This must be unique in the whole process.
86 const NAME: &'static str;
87
88 // rustdoc-stripper-ignore-next
89 /// Allow name conflicts for this class.
90 ///
91 /// By default, trying to register a type with a name that was registered before will panic. If
92 /// this is set to `true` then a new name will be selected by appending a counter.
93 ///
94 /// This is useful for defining new types in Rust library crates that might be linked multiple
95 /// times in the same process.
96 ///
97 /// A consequence of setting this to `true` is that it's not guaranteed that
98 /// `glib::Type::from_name(Self::NAME).unwrap() == Self::type_()`.
99 ///
100 /// Note that this is not allowed for dynamic types. If a dynamic type is registered and a type
101 /// with that name exists already, it is assumed that they're the same.
102 ///
103 /// Optional.
104 const ALLOW_NAME_CONFLICT: bool = false;
105
106 /// Prerequisites for this interface.
107 ///
108 /// Any implementer of the interface must be a subclass of the prerequisites or implement them
109 /// in case of interfaces.
110 type Prerequisites: PrerequisiteList;
111
112 // rustdoc-stripper-ignore-next
113 /// The C instance struct. This is usually either `std::ffi::c_void` or a newtype wrapper
114 /// around it.
115 ///
116 /// Optional
117 type Instance;
118
119 // rustdoc-stripper-ignore-next
120 /// The C class struct.
121 type Interface: InterfaceStruct<Type = Self>;
122
123 /// Additional type initialization.
124 ///
125 /// This is called right after the type was registered and allows
126 /// interfaces to do additional type-specific initialization.
127 ///
128 /// Optional
129 fn type_init(_type_: &mut InitializingType<Self>) {}
130
131 /// Interface initialization.
132 ///
133 /// This is called after `type_init` and before the first implementor
134 /// of the interface is created. Interfaces can use this to do interface-
135 /// specific initialization, e.g. for installing signals on the interface,
136 /// and for setting default implementations of interface functions.
137 ///
138 /// Optional
139 fn interface_init(_klass: &mut Self::Interface) {}
140
141 /// Properties installed for this interface.
142 ///
143 /// All implementors of the interface must provide these properties.
144 fn properties() -> &'static [ParamSpec] {
145 &[]
146 }
147
148 /// Signals installed for this interface.
149 fn signals() -> &'static [Signal] {
150 &[]
151 }
152}
153
154pub trait ObjectInterfaceExt: ObjectInterface {
155 /// Get interface from an instance.
156 ///
157 /// This will panic if `obj` does not implement the interface.
158 #[inline]
159 fn from_obj<T: IsA<Object>>(obj: &T) -> &Self {
160 assert!(obj.as_ref().type_().is_a(Self::type_()));
161
162 unsafe {
163 let klass = (*(obj.as_ptr() as *const gobject_ffi::GTypeInstance)).g_class;
164 let interface =
165 gobject_ffi::g_type_interface_peek(klass as *mut _, Self::type_().into_glib());
166 debug_assert!(!interface.is_null());
167 &*(interface as *const Self)
168 }
169 }
170}
171
172impl<T: ObjectInterface> ObjectInterfaceExt for T {}
173
174unsafe extern "C" fn interface_init<T: ObjectInterface>(
175 klass: ffi::gpointer,
176 _klass_data: ffi::gpointer,
177) {
178 unsafe {
179 let iface = &mut *(klass as *mut T::Interface);
180
181 let pspecs = <T as ObjectInterface>::properties();
182 for pspec in pspecs {
183 gobject_ffi::g_object_interface_install_property(
184 iface as *mut T::Interface as *mut _,
185 pspec.to_glib_none().0,
186 );
187 }
188
189 let type_ = T::type_();
190 let signals = <T as ObjectInterface>::signals();
191 for signal in signals {
192 signal.register(type_);
193 }
194
195 T::interface_init(iface);
196 }
197}
198
199/// Register a `glib::Type` ID for `T::Class`.
200///
201/// This must be called only once and will panic on a second call.
202///
203/// The [`object_interface!`] macro will create a `type_()` function around this, which will
204/// ensure that it's only ever called once.
205///
206/// [`object_interface!`]: ../../macro.object_interface.html
207pub fn register_interface<T: ObjectInterface>() -> Type {
208 assert_eq!(mem::size_of::<T>(), 0);
209
210 unsafe {
211 use std::ffi::CString;
212
213 let type_name = if T::ALLOW_NAME_CONFLICT {
214 let mut i = 0;
215 loop {
216 let type_name = CString::new(if i == 0 {
217 T::NAME.to_string()
218 } else {
219 format!("{}-{}", T::NAME, i)
220 })
221 .unwrap();
222 if gobject_ffi::g_type_from_name(type_name.as_ptr()) == gobject_ffi::G_TYPE_INVALID
223 {
224 break type_name;
225 }
226 i += 1;
227 }
228 } else {
229 let type_name = CString::new(T::NAME).unwrap();
230 assert_eq!(
231 gobject_ffi::g_type_from_name(type_name.as_ptr()),
232 gobject_ffi::G_TYPE_INVALID,
233 "Type {} has already been registered",
234 type_name.to_str().unwrap()
235 );
236
237 type_name
238 };
239
240 let type_ = gobject_ffi::g_type_register_static_simple(
241 Type::INTERFACE.into_glib(),
242 type_name.as_ptr(),
243 mem::size_of::<T::Interface>() as u32,
244 Some(interface_init::<T>),
245 0,
246 None,
247 0,
248 );
249
250 let prerequisites = T::Prerequisites::types();
251 for prerequisite in prerequisites {
252 gobject_ffi::g_type_interface_add_prerequisite(type_, prerequisite.into_glib());
253 }
254
255 let type_ = Type::from_glib(type_);
256 assert!(type_.is_valid());
257
258 T::type_init(&mut InitializingType::<T>(type_, marker::PhantomData));
259
260 type_
261 }
262}
263
264/// Registers a `glib::Type` ID for `T::Class` as a dynamic type.
265///
266/// An object interface must be explicitly registered as a dynamic type when
267/// the system loads the implementation by calling [`TypePluginImpl::use_`] or
268/// more specifically [`TypeModuleImpl::load`]. Therefore, unlike for object
269/// interfaces registered as static types, object interfaces registered as
270/// dynamic types can be registered several times.
271///
272/// The [`object_interface_dynamic!`] macro helper attribute will create
273/// `register_interface()` and `on_implementation_load()` functions around this,
274/// which will ensure that the function is called when necessary.
275///
276/// [`object_interface_dynamic!`]: ../../../glib_macros/attr.object_interface.html
277/// [`TypePluginImpl::use_`]: ../type_plugin/trait.TypePluginImpl.html#method.use_
278/// [`TypeModuleImpl::load`]: ../type_module/trait.TypeModuleImpl.html#method.load
279pub fn register_dynamic_interface<P: DynamicObjectRegisterExt, T: ObjectInterface>(
280 type_plugin: &P,
281) -> Type {
282 assert_eq!(mem::size_of::<T>(), 0);
283
284 unsafe {
285 use std::ffi::CString;
286
287 let type_name = CString::new(T::NAME).unwrap();
288
289 let already_registered =
290 gobject_ffi::g_type_from_name(type_name.as_ptr()) != gobject_ffi::G_TYPE_INVALID;
291
292 let type_info = TypeInfo(gobject_ffi::GTypeInfo {
293 class_size: mem::size_of::<T::Interface>() as u16,
294 class_init: Some(interface_init::<T>),
295 ..TypeInfo::default().0
296 });
297
298 // registers the interface within the `type_plugin`
299 let type_ = type_plugin.register_dynamic_type(
300 Type::INTERFACE,
301 type_name.to_str().unwrap(),
302 &type_info,
303 TypeFlags::ABSTRACT,
304 );
305
306 let prerequisites = T::Prerequisites::types();
307 for prerequisite in prerequisites {
308 // adding prerequisite interface can be done only once
309 if !already_registered {
310 gobject_ffi::g_type_interface_add_prerequisite(
311 type_.into_glib(),
312 prerequisite.into_glib(),
313 );
314 }
315 }
316
317 assert!(type_.is_valid());
318
319 T::type_init(&mut InitializingType::<T>(type_, marker::PhantomData));
320
321 type_
322 }
323}