Skip to content

Commit

Permalink
Merge pull request #1384 from felinira/object-interface-struct
Browse files Browse the repository at this point in the history
glib: Decouple ObjectInterface impl from interface class struct
  • Loading branch information
sdroege authored May 8, 2024
2 parents 0899c1e + 60ffeec commit 839fc12
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 37 deletions.
56 changes: 46 additions & 10 deletions glib-macros/tests/object_subclass_dynamic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,21 @@ mod static_ {
// impl for an object interface to register as a static type.
#[derive(Clone, Copy)]
#[repr(C)]
pub struct MyStaticInterface {
pub struct MyStaticInterfaceClass {
parent: glib::gobject_ffi::GTypeInterface,
}

unsafe impl InterfaceStruct for MyStaticInterfaceClass {
type Type = MyStaticInterface;
}

pub enum MyStaticInterface {}

#[glib::object_interface]
unsafe impl ObjectInterface for MyStaticInterface {
impl ObjectInterface for MyStaticInterface {
const NAME: &'static str = "MyStaticInterface";

type Interface = MyStaticInterfaceClass;
}

pub trait MyStaticInterfaceImpl: ObjectImpl + ObjectSubclass {}
Expand Down Expand Up @@ -69,15 +77,22 @@ mod module {
// impl for a object interface to register as a dynamic type and that extends `MyStaticInterface`.
#[derive(Clone, Copy)]
#[repr(C)]
pub struct MyModuleInterface {
pub struct MyModuleInterfaceClass {
parent: glib::gobject_ffi::GTypeInterface,
}

unsafe impl InterfaceStruct for MyModuleInterfaceClass {
type Type = MyModuleInterface;
}

pub enum MyModuleInterface {}

#[glib::object_interface]
#[object_interface_dynamic]
unsafe impl ObjectInterface for MyModuleInterface {
impl ObjectInterface for MyModuleInterface {
const NAME: &'static str = "MyModuleInterface";
type Prerequisites = (MyStaticInterface,);
type Interface = MyModuleInterfaceClass;
}

pub trait MyModuleInterfaceImpl: ObjectImpl + ObjectSubclass {}
Expand Down Expand Up @@ -106,15 +121,22 @@ mod module {
// impl for an object interface to lazy register as a dynamic type and that extends `MyStaticInterface`.
#[derive(Clone, Copy)]
#[repr(C)]
pub struct MyModuleInterfaceLazy {
pub struct MyModuleInterfaceLazyClass {
parent: glib::gobject_ffi::GTypeInterface,
}

unsafe impl InterfaceStruct for MyModuleInterfaceLazyClass {
type Type = MyModuleInterfaceLazy;
}

pub enum MyModuleInterfaceLazy {}

#[glib::object_interface]
#[object_interface_dynamic(lazy_registration = true)]
unsafe impl ObjectInterface for MyModuleInterfaceLazy {
impl ObjectInterface for MyModuleInterfaceLazy {
const NAME: &'static str = "MyModuleInterfaceLazy";
type Prerequisites = (MyStaticInterface,);
type Interface = MyModuleInterfaceLazyClass;
}

pub trait MyModuleInterfaceLazyImpl: ObjectImpl + ObjectSubclass {}
Expand Down Expand Up @@ -295,15 +317,22 @@ pub mod plugin {
// impl for a object interface to register as a dynamic type and that extends `MyStaticInterface`.
#[derive(Clone, Copy)]
#[repr(C)]
pub struct MyPluginInterface {
pub struct MyPluginInterfaceClass {
parent: glib::gobject_ffi::GTypeInterface,
}

unsafe impl InterfaceStruct for MyPluginInterfaceClass {
type Type = MyPluginInterface;
}

pub enum MyPluginInterface {}

#[glib::object_interface]
#[object_interface_dynamic(plugin_type = super::MyPlugin)]
unsafe impl ObjectInterface for MyPluginInterface {
impl ObjectInterface for MyPluginInterface {
const NAME: &'static str = "MyPluginInterface";
type Prerequisites = (MyStaticInterface,);
type Interface = MyPluginInterfaceClass;
}

pub trait MyPluginInterfaceImpl: ObjectImpl + ObjectSubclass {}
Expand Down Expand Up @@ -332,15 +361,22 @@ pub mod plugin {
// impl for an object interface to lazy register as a dynamic type and that extends `MyStaticInterface`.
#[derive(Clone, Copy)]
#[repr(C)]
pub struct MyPluginInterfaceLazy {
pub struct MyPluginInterfaceLazyClass {
parent: glib::gobject_ffi::GTypeInterface,
}

unsafe impl InterfaceStruct for MyPluginInterfaceLazyClass {
type Type = MyPluginInterfaceLazy;
}

pub enum MyPluginInterfaceLazy {}

#[glib::object_interface]
#[object_interface_dynamic(plugin_type = super::MyPlugin, lazy_registration = true)]
unsafe impl ObjectInterface for MyPluginInterfaceLazy {
impl ObjectInterface for MyPluginInterfaceLazy {
const NAME: &'static str = "MyPluginInterfaceLazy";
type Prerequisites = (MyStaticInterface,);
type Interface = MyPluginInterfaceLazyClass;
}

pub trait MyPluginInterfaceLazyImpl: ObjectImpl + ObjectSubclass {}
Expand Down
9 changes: 9 additions & 0 deletions glib/src/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1317,6 +1317,15 @@ macro_rules! glib_object_wrapper {
);
};

(@object_interface [$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $iface:ty,
@type_ $get_type_expr:expr, @requires [$($requires:tt)*]) => {
$crate::glib_object_wrapper!(
@interface [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $iface, std::os::raw::c_void,
@ffi_class <$iface as $crate::subclass::interface::ObjectInterface>::Interface,
@type_ $get_type_expr, @requires [$($requires)*]
);
};

(@interface [$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $impl_type:ty, $ffi_name:ty, @ffi_class $ffi_class_name:ty,
@type_ $get_type_expr:expr, @requires [$($requires:tt)*]) => {
$crate::glib_object_wrapper!(
Expand Down
32 changes: 17 additions & 15 deletions glib/src/subclass/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use std::{marker, mem};

use super::{InitializingType, Signal};
use super::{types::InterfaceStruct, InitializingType, Signal};
use crate::{prelude::*, translate::*, Object, ParamSpec, Type, TypeFlags, TypeInfo};

// rustdoc-stripper-ignore-next
Expand Down Expand Up @@ -69,17 +69,15 @@ pub unsafe trait ObjectInterfaceType {

/// The central trait for defining a `GObject` interface.
///
/// Links together the type name, and the interface struct for type registration and allows hooking
/// into various steps of the type registration and initialization.
///
/// This must only be implemented on `#[repr(C)]` structs and have `gobject_ffi::GTypeInterface` as
/// the first field.
/// Links together the type name, the empty instance and class structs for type
/// registration and allows hooking into various steps of the type registration
/// and initialization.
///
/// See [`register_interface`] for registering an implementation of this trait
/// with the type system.
///
/// [`register_interface`]: fn.register_interface.html
pub unsafe trait ObjectInterface: ObjectInterfaceType + Sized + 'static {
pub trait ObjectInterface: ObjectInterfaceType + Sized + 'static {
/// `GObject` type name.
///
/// This must be unique in the whole process.
Expand All @@ -91,6 +89,10 @@ pub unsafe trait ObjectInterface: ObjectInterfaceType + Sized + 'static {
/// in case of interfaces.
type Prerequisites: PrerequisiteList;

// rustdoc-stripper-ignore-next
/// The C class struct.
type Interface: InterfaceStruct<Type = Self>;

/// Additional type initialization.
///
/// This is called right after the type was registered and allows
Expand All @@ -107,7 +109,7 @@ pub unsafe trait ObjectInterface: ObjectInterfaceType + Sized + 'static {
/// and for setting default implementations of interface functions.
///
/// Optional
fn interface_init(&mut self) {}
fn interface_init(_klass: &mut Self::Interface) {}

/// Properties installed for this interface.
///
Expand Down Expand Up @@ -155,12 +157,12 @@ unsafe extern "C" fn interface_init<T: ObjectInterface>(
klass: ffi::gpointer,
_klass_data: ffi::gpointer,
) {
let iface = &mut *(klass as *mut T);
let iface = &mut *(klass as *mut T::Interface);

let pspecs = <T as ObjectInterface>::properties();
for pspec in pspecs {
gobject_ffi::g_object_interface_install_property(
iface as *mut T as *mut _,
iface as *mut T::Interface as *mut _,
pspec.to_glib_none().0,
);
}
Expand All @@ -171,10 +173,10 @@ unsafe extern "C" fn interface_init<T: ObjectInterface>(
signal.register(type_);
}

iface.interface_init();
T::interface_init(iface);
}

/// Register a `glib::Type` ID for `T`.
/// Register a `glib::Type` ID for `T::Class`.
///
/// This must be called only once and will panic on a second call.
///
Expand All @@ -195,7 +197,7 @@ pub fn register_interface<T: ObjectInterface>() -> Type {
let type_ = gobject_ffi::g_type_register_static_simple(
Type::INTERFACE.into_glib(),
type_name.as_ptr(),
mem::size_of::<T>() as u32,
mem::size_of::<T::Interface>() as u32,
Some(interface_init::<T>),
0,
None,
Expand All @@ -216,7 +218,7 @@ pub fn register_interface<T: ObjectInterface>() -> Type {
}
}

/// Registers a `glib::Type` ID for `T` as a dynamic type.
/// Registers a `glib::Type` ID for `T::Class` as a dynamic type.
///
/// An object interface must be explicitly registered as a dynamic type when
/// the system loads the implementation by calling [`TypePluginImpl::use_`] or
Expand All @@ -243,7 +245,7 @@ pub fn register_dynamic_interface<P: DynamicObjectRegisterExt, T: ObjectInterfac
gobject_ffi::g_type_from_name(type_name.as_ptr()) != gobject_ffi::G_TYPE_INVALID;

let type_info = TypeInfo(gobject_ffi::GTypeInfo {
class_size: mem::size_of::<T>() as u16,
class_size: mem::size_of::<T::Interface>() as u16,
class_init: Some(interface_init::<T>),
..TypeInfo::default().0
});
Expand Down
6 changes: 3 additions & 3 deletions glib/src/subclass/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -450,9 +450,9 @@ pub mod prelude {
type_module::{TypeModuleImpl, TypeModuleImplExt},
type_plugin::{TypePluginImpl, TypePluginImplExt, TypePluginRegisterImpl},
types::{
ClassStruct, InstanceStruct, InstanceStructExt, IsImplementable, IsSubclassable,
IsSubclassableExt, ObjectSubclass, ObjectSubclassExt, ObjectSubclassIsExt,
ObjectSubclassType,
ClassStruct, InstanceStruct, InstanceStructExt, InterfaceStruct, IsImplementable,
IsSubclassable, IsSubclassableExt, ObjectSubclass, ObjectSubclassExt,
ObjectSubclassIsExt, ObjectSubclassType,
},
};
}
Expand Down
11 changes: 9 additions & 2 deletions glib/src/subclass/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -460,9 +460,16 @@ mod test {
parent: gobject_ffi::GTypeInterface,
}

unsafe impl InterfaceStruct for DummyInterface {
type Type = Dummy;
}

pub enum Dummy {}

#[glib::object_interface]
unsafe impl ObjectInterface for DummyInterface {
impl ObjectInterface for Dummy {
const NAME: &'static str = "Dummy";
type Interface = DummyInterface;
}
}

Expand All @@ -475,7 +482,7 @@ mod test {
}

wrapper! {
pub struct Dummy(ObjectInterface<imp::DummyInterface>);
pub struct Dummy(ObjectInterface<imp::Dummy>);
}

unsafe impl<T: ObjectSubclass> IsImplementable<T> for Dummy {}
Expand Down
28 changes: 23 additions & 5 deletions glib/src/subclass/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

use std::{any::Any, collections::BTreeMap, marker, mem, ptr};

use super::SignalId;
use super::{interface::ObjectInterface, SignalId};
use crate::{
object::{IsClass, IsInterface, ObjectSubclassIs, ParentClassIs},
prelude::*,
Expand Down Expand Up @@ -280,11 +280,29 @@ impl<U: IsClass + ParentClassIs> IsSubclassableExt for U {
}

// rustdoc-stripper-ignore-next
/// Trait for implementable interfaces.
pub unsafe trait IsImplementable<T: ObjectSubclass>: IsInterface
/// Trait implemented by structs that implement a `GTypeInterface` C class struct.
///
/// This must only be implemented on `#[repr(C)]` structs and have an interface
/// that inherits from `gobject_ffi::GTypeInterface` as the first field.
pub unsafe trait InterfaceStruct: Sized + 'static
where
<Self as ObjectType>::GlibClassType: Copy,
Self: Copy,
{
// rustdoc-stripper-ignore-next
/// Corresponding object interface type for this class struct.
type Type: ObjectInterface;

// rustdoc-stripper-ignore-next
/// Set up default implementations for interface vfuncs.
///
/// This is automatically called during type initialization.
#[inline]
fn interface_init(&mut self) {}
}

// rustdoc-stripper-ignore-next
/// Trait for implementable interfaces.
pub unsafe trait IsImplementable<T: ObjectSubclass>: IsInterface {
// rustdoc-stripper-ignore-next
/// Override the virtual methods of this interface for the given subclass and do other
/// interface initialization.
Expand Down Expand Up @@ -631,7 +649,7 @@ pub trait ObjectSubclass: ObjectSubclassType + Sized + 'static {
// rustdoc-stripper-ignore-next
/// The C class struct.
///
/// See [`basic::ClassStruct`] for an basic instance struct that should be
/// See [`basic::ClassStruct`] for an basic class struct that should be
/// used in most cases.
///
/// [`basic::ClassStruct`]: ../basic/struct.ClassStruct.html
Expand Down
3 changes: 1 addition & 2 deletions glib/src/wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -436,8 +436,7 @@ macro_rules! wrapper {
$visibility:vis struct $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)? (ObjectInterface<$iface_name:ty>) $(@requires $($requires:path),+)?;
) => {
$crate::glib_object_wrapper!(
@interface [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, *mut std::os::raw::c_void, std::os::raw::c_void,
@ffi_class $iface_name,
@object_interface [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $iface_name,
@type_ $crate::translate::IntoGlib::into_glib(<$iface_name as $crate::subclass::interface::ObjectInterfaceType>::type_()),
@requires [$( $($requires),+ )?]
);
Expand Down

0 comments on commit 839fc12

Please sign in to comment.