Skip to content

Commit

Permalink
glib: Decouple ObjectInterface impl from interface class struct
Browse files Browse the repository at this point in the history
This makes ObjectInterface and ObjectSubclass more similar to each other
and allows for cleaner separation between unsafe ffi code and application
implementation.
  • Loading branch information
felinira committed May 4, 2024
1 parent 1827f28 commit d849f56
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 40 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 InterfaceClassStruct for MyStaticInterfaceClass {
type Type = MyStaticInterface;
}

pub struct MyStaticInterface;

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

type Class = 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 InterfaceClassStruct for MyModuleInterfaceClass {
type Type = MyModuleInterface;
}

pub struct 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 Class = 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 InterfaceClassStruct for MyModuleInterfaceLazyClass {
type Type = MyModuleInterfaceLazy;
}

pub struct 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 Class = 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 InterfaceClassStruct for MyPluginInterfaceClass {
type Type = MyPluginInterface;
}

pub struct 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 Class = 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 InterfaceClassStruct for MyPluginInterfaceLazyClass {
type Type = MyPluginInterfaceLazy;
}

pub struct 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 Class = 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>::Class,
@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::InterfaceClassStruct, 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 Class: InterfaceClassStruct<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::Class) {}

/// 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::Class);

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::Class 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::Class>() 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::Class>() 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, InterfaceClassStruct, 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 InterfaceClassStruct for DummyInterface {
type Type = Dummy;
}

pub struct Dummy;

#[glib::object_interface]
unsafe impl ObjectInterface for DummyInterface {
impl ObjectInterface for Dummy {
const NAME: &'static str = "Dummy";
type Class = 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
34 changes: 26 additions & 8 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
/// `gobject_ffi::GTypeInterface` as the first field.
pub unsafe trait InterfaceClassStruct: 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 All @@ -305,22 +323,22 @@ unsafe extern "C" fn interface_init<T: ObjectSubclass, A: IsImplementable<T>>(
) where
<A as ObjectType>::GlibClassType: Copy,
{
let iface = &mut *(iface as *mut crate::Interface<A>);
let klass = &mut *(iface as *mut crate::Interface<A>);

let mut data = T::type_data();
if data.as_ref().parent_ifaces.is_none() {
data.as_mut().parent_ifaces = Some(BTreeMap::default());
}
{
let copy = Box::new(*iface.as_ref());
let copy = Box::new(*klass.as_ref());
data.as_mut()
.parent_ifaces
.as_mut()
.unwrap()
.insert(A::static_type(), Box::into_raw(copy) as ffi::gpointer);
}

A::interface_init(iface);
A::interface_init(klass);
}

// rustdoc-stripper-ignore-next
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 d849f56

Please sign in to comment.