diff --git a/glib/src/subclass/interface.rs b/glib/src/subclass/interface.rs index b87bfaebc1f1..b8c9bba52e7b 100644 --- a/glib/src/subclass/interface.rs +++ b/glib/src/subclass/interface.rs @@ -1,27 +1,27 @@ // Take a look at the license at the top of the repository in the LICENSE file. -use std::{marker, mem}; +use std::{marker, mem, ptr}; use super::{InitializingType, Signal}; -use crate::{prelude::*, translate::*, Object, ParamSpec, Type}; +use crate::{prelude::*, translate::*, Object, ParamSpec, Type, TypeFlags, TypeInfo, TypeModule}; // rustdoc-stripper-ignore-next /// Trait for a type list of prerequisite object types. pub trait PrerequisiteList { // rustdoc-stripper-ignore-next /// Returns the list of types for this list. - fn types() -> Vec; + fn types() -> Vec; } impl PrerequisiteList for () { - fn types() -> Vec { + fn types() -> Vec { vec![] } } impl PrerequisiteList for (T,) { - fn types() -> Vec { - vec![T::static_type().into_glib()] + fn types() -> Vec { + vec![T::static_type()] } } @@ -47,8 +47,8 @@ macro_rules! prerequisite_list_trait( macro_rules! prerequisite_list_trait_impl( ($($name:ident),+) => ( impl<$($name: crate::ObjectType),+> PrerequisiteList for ( $($name),+ ) { - fn types() -> Vec { - vec![$($name::static_type().into_glib()),+] + fn types() -> Vec { + vec![$($name::static_type()),+] } } ); @@ -204,7 +204,7 @@ pub fn register_interface() -> Type { let prerequisites = T::Prerequisites::types(); for prerequisite in prerequisites { - gobject_ffi::g_type_interface_add_prerequisite(type_, prerequisite); + gobject_ffi::g_type_interface_add_prerequisite(type_, prerequisite.into_glib()); } let type_ = Type::from_glib(type_); @@ -215,3 +215,58 @@ pub fn register_interface() -> Type { type_ } } + +/// Register a `glib::Type` ID for `T` as a module interface. +/// +/// A module interface must be explicitly registered when a module is loaded (see [`TypeModuleImpl::load`]). +/// Therefore, unlike for non module interfaces a module interface can be registered several times. +/// +/// [`TypeModuleImpl::load`]: ../type_module/trait.TypeModuleImpl.html#method.load +pub fn register_module_interface(type_module: &TypeModule) -> Type { + unsafe { + use std::ffi::CString; + + let type_name = CString::new(T::NAME).unwrap(); + + let already_registered = + gobject_ffi::g_type_from_name(type_name.as_ptr()) != gobject_ffi::G_TYPE_INVALID; + + let type_info = TypeInfo::unsafe_from(gobject_ffi::GTypeInfo { + class_size: mem::size_of::() as u16, + base_init: None, + base_finalize: None, + class_init: Some(interface_init::), + class_finalize: None, + class_data: ptr::null(), + instance_size: 0, + n_preallocs: 0, + instance_init: None, + value_table: ptr::null(), + }); + + // register the interface within the `type_module` + let type_ = type_module.register_type( + Type::INTERFACE, + type_name.to_str().unwrap(), + &type_info, + TypeFlags::ABSTRACT, + ); + + let prerequisites = T::Prerequisites::types(); + for prerequisite in prerequisites { + // adding prerequisite interface can be done only once + if !already_registered { + gobject_ffi::g_type_interface_add_prerequisite( + type_.into_glib(), + prerequisite.into_glib(), + ); + } + } + + assert!(type_.is_valid()); + + T::type_init(&mut InitializingType::(type_, marker::PhantomData)); + + type_ + } +} diff --git a/glib/src/subclass/mod.rs b/glib/src/subclass/mod.rs index 8fd16ad7dbc5..9a6a393ffbe8 100644 --- a/glib/src/subclass/mod.rs +++ b/glib/src/subclass/mod.rs @@ -241,6 +241,10 @@ pub mod signal; mod object_impl_ref; pub use object_impl_ref::{ObjectImplRef, ObjectImplWeakRef}; +pub mod type_module; + +pub mod type_plugin; + pub mod prelude { // rustdoc-stripper-ignore-next //! Prelude that re-exports all important traits from this crate. @@ -249,6 +253,8 @@ pub mod prelude { interface::{ObjectInterface, ObjectInterfaceExt, ObjectInterfaceType}, object::{DerivedObjectProperties, ObjectClassSubclassExt, ObjectImpl, ObjectImplExt}, shared::{RefCounted, SharedType}, + type_module::{TypeModuleImpl, TypeModuleImplExt, TypeModuleObserver}, + type_plugin::{TypePluginImpl, TypePluginImplExt}, types::{ ClassStruct, InstanceStruct, InstanceStructExt, IsImplementable, IsSubclassable, IsSubclassableExt, ObjectSubclass, ObjectSubclassExt, ObjectSubclassIsExt, @@ -259,9 +265,9 @@ pub mod prelude { pub use self::{ boxed::register_boxed_type, - interface::register_interface, + interface::{register_interface, register_module_interface}, signal::{ Signal, SignalClassHandlerToken, SignalId, SignalInvocationHint, SignalQuery, SignalType, }, - types::{register_type, InitializingObject, InitializingType, TypeData}, + types::{register_module_type, register_type, InitializingObject, InitializingType, TypeData}, }; diff --git a/glib/src/subclass/type_module.rs b/glib/src/subclass/type_module.rs new file mode 100644 index 000000000000..1abc652f6b6d --- /dev/null +++ b/glib/src/subclass/type_module.rs @@ -0,0 +1,145 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use crate::{subclass::prelude::*, translate::*, Cast, IsA, TypeModule}; + +pub trait TypeModuleObserver> { + fn on_load(type_module: &T) -> bool; + fn on_unload(type_module: &T); +} + +pub trait TypeModuleImpl: ObjectImpl + TypeModuleImplExt { + // rustdoc-stripper-ignore-next + /// Loads the module, registers one or more types using + /// [`register_module_type`] and registers one or more interfaces using + /// [`register_module_interface`] (see [`TypeModule`]). + /// + /// [`register_module_type`]: ../types/fn.register_module_type.html + /// [`register_module_interface`]: ../interface/fn.register_module_interface.html + /// [`TypeModule`]: ../../gobject/auto/type_module/struct.TypeModule.html + fn load(&self) -> bool; + // rustdoc-stripper-ignore-next + /// Unloads the module (see [`TypeModuleExt::unuse`]). + /// + /// [`TypeModuleExt::unuse`]: ../../gobject/auto/type_module/trait.TypeModuleExt.html#method.unuse + fn unload(&self); +} + +pub trait TypeModuleImplExt: ObjectSubclass { + fn parent_load(&self) -> bool; + fn parent_unload(&self); +} + +impl TypeModuleImplExt for T { + fn parent_load(&self) -> bool { + unsafe { + let data = T::type_data(); + let parent_class = data.as_ref().parent_class() as *const gobject_ffi::GTypeModuleClass; + + let f = (*parent_class) + .load + .expect("No parent class implementation for \"load\""); + + from_glib(f(self + .obj() + .unsafe_cast_ref::() + .to_glib_none() + .0)) + } + } + + fn parent_unload(&self) { + unsafe { + let data = T::type_data(); + let parent_class = data.as_ref().parent_class() as *const gobject_ffi::GTypeModuleClass; + + let f = (*parent_class) + .unload + .expect("No parent class implementation for \"unload\""); + + f(self.obj().unsafe_cast_ref::().to_glib_none().0); + } + } +} + +unsafe impl IsSubclassable for TypeModule { + fn class_init(class: &mut crate::Class) { + Self::parent_class_init::(class); + + let klass = class.as_mut(); + klass.load = Some(load::); + klass.unload = Some(unload::); + } +} + +unsafe extern "C" fn load( + type_module: *mut gobject_ffi::GTypeModule, +) -> ffi::gboolean { + let instance = &*(type_module as *mut T::Instance); + let imp = instance.imp(); + + let res = imp.load(); + + if res { + unsafe { + gobject_ffi::g_object_ref(type_module as _); + } + } + + res.into_glib() +} + +unsafe extern "C" fn unload(type_module: *mut gobject_ffi::GTypeModule) { + let instance = &*(type_module as *mut T::Instance); + let imp = instance.imp(); + + imp.unload(); + + unsafe { + gobject_ffi::g_object_unref(type_module as _); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::prelude::*; + + mod imp { + use super::*; + + use crate as glib; + + #[derive(Default)] + pub struct SimpleTypeModule; + + #[glib::object_subclass] + impl ObjectSubclass for SimpleTypeModule { + const NAME: &'static str = "SimpleTypeModule"; + type Type = super::SimpleTypeModule; + type ParentType = TypeModule; + } + + impl ObjectImpl for SimpleTypeModule {} + + impl TypeModuleImpl for SimpleTypeModule { + fn load(&self) -> bool { + true + } + + fn unload(&self) {} + } + } + + crate::wrapper! { + pub struct SimpleTypeModule(ObjectSubclass) + @extends crate::TypeModule; + } + + #[test] + fn test_simple_type_module() { + let simple_type_module = crate::Object::new::(); + // simulate the glib type system to load the module + assert!(simple_type_module.use_()); + simple_type_module.unuse(); + } +} diff --git a/glib/src/subclass/type_plugin.rs b/glib/src/subclass/type_plugin.rs new file mode 100644 index 000000000000..b887f10088aa --- /dev/null +++ b/glib/src/subclass/type_plugin.rs @@ -0,0 +1,186 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use crate::translate::IntoGlib; +use crate::translate::{FromGlib, ToGlibPtr, ToGlibPtrMut}; +use crate::{ + subclass::prelude::*, Cast, Interface, InterfaceInfo, Type, TypeInfo, TypePlugin, + TypeValueTable, +}; + +pub trait TypePluginImpl: ObjectImpl + TypePluginImplExt { + fn use_plugin(&self) { + self.parent_use_plugin(); + } + + fn unuse_plugin(&self) { + self.parent_unuse_plugin(); + } + + fn complete_type_info( + &self, + type_: Type, + info: &mut TypeInfo, + value_table: &mut TypeValueTable, + ) { + self.parent_complete_type_info(type_, info, value_table); + } + + fn complete_interface_info( + &self, + instance_type: Type, + interface_type: Type, + info: &mut InterfaceInfo, + ) { + self.parent_complete_interface_info(instance_type, interface_type, info); + } +} + +pub trait TypePluginImplExt: ObjectSubclass { + fn parent_use_plugin(&self); + fn parent_unuse_plugin(&self); + fn parent_complete_type_info( + &self, + type_: Type, + info: &mut TypeInfo, + value_table: &mut TypeValueTable, + ); + fn parent_complete_interface_info( + &self, + instance_type: Type, + interface_type: Type, + info: &mut InterfaceInfo, + ); +} + +impl TypePluginImplExt for T { + fn parent_use_plugin(&self) { + unsafe { + let type_data = Self::type_data(); + let parent_iface = type_data.as_ref().parent_interface::() + as *const gobject_ffi::GTypePluginClass; + + let f = (*parent_iface) + .use_plugin + .expect("no parent \"use_plugin\" implementation"); + + f(self.obj().unsafe_cast_ref::().to_glib_none().0) + } + } + + fn parent_unuse_plugin(&self) { + unsafe { + let type_data = Self::type_data(); + let parent_iface = type_data.as_ref().parent_interface::() + as *const gobject_ffi::GTypePluginClass; + + let f = (*parent_iface) + .unuse_plugin + .expect("no parent \"unuse_plugin\" implementation"); + + f(self.obj().unsafe_cast_ref::().to_glib_none().0) + } + } + + fn parent_complete_type_info( + &self, + type_: Type, + info: &mut TypeInfo, + value_table: &mut TypeValueTable, + ) { + unsafe { + let type_data = Self::type_data(); + let parent_iface = type_data.as_ref().parent_interface::() + as *const gobject_ffi::GTypePluginClass; + + let f = (*parent_iface) + .complete_type_info + .expect("no parent \"complete_type_info\" implementation"); + + f( + self.obj().unsafe_cast_ref::().to_glib_none().0, + type_.into_glib(), + info.to_glib_none_mut().0, + value_table.to_glib_none_mut().0, + ) + } + } + + fn parent_complete_interface_info( + &self, + instance_type: Type, + interface_type: Type, + info: &mut InterfaceInfo, + ) { + unsafe { + let type_data = Self::type_data(); + let parent_iface = type_data.as_ref().parent_interface::() + as *const gobject_ffi::GTypePluginClass; + + let f = (*parent_iface) + .complete_interface_info + .expect("no parent \"complete_interface_info\" implementation"); + + f( + self.obj().unsafe_cast_ref::().to_glib_none().0, + instance_type.into_glib(), + interface_type.into_glib(), + info.to_glib_none_mut().0, + ) + } + } +} + +unsafe impl IsImplementable for TypePlugin { + fn interface_init(iface: &mut Interface) { + let iface = iface.as_mut(); + + iface.use_plugin = Some(use_plugin::); + iface.unuse_plugin = Some(unuse_plugin::); + iface.complete_type_info = Some(complete_type_info::); + iface.complete_interface_info = Some(complete_interface_info::); + } +} + +unsafe extern "C" fn use_plugin(type_plugin: *mut gobject_ffi::GTypePlugin) { + let instance = &*(type_plugin as *mut T::Instance); + let imp = instance.imp(); + + imp.use_plugin(); +} + +unsafe extern "C" fn unuse_plugin(type_plugin: *mut gobject_ffi::GTypePlugin) { + let instance = &*(type_plugin as *mut T::Instance); + let imp = instance.imp(); + + imp.unuse_plugin(); +} + +unsafe extern "C" fn complete_type_info( + type_plugin: *mut gobject_ffi::GTypePlugin, + gtype: ffi::GType, + info_ptr: *mut gobject_ffi::GTypeInfo, + value_table_ptr: *mut gobject_ffi::GTypeValueTable, +) { + let instance = &*(type_plugin as *mut T::Instance); + let imp = instance.imp(); + let type_ = Type::from_glib(gtype); + let info = TypeInfo::from_glib_ptr_borrow_mut(info_ptr); + let value_table = TypeValueTable::from_glib_ptr_borrow_mut(value_table_ptr); + + imp.complete_type_info(type_, info, value_table); +} + +unsafe extern "C" fn complete_interface_info( + type_plugin: *mut gobject_ffi::GTypePlugin, + instance_gtype: ffi::GType, + interface_gtype: ffi::GType, + info_ptr: *mut gobject_ffi::GInterfaceInfo, +) { + let instance = &*(type_plugin as *mut T::Instance); + let imp = instance.imp(); + let instance_type = Type::from_glib(instance_gtype); + let interface_type = Type::from_glib(interface_gtype); + let info = InterfaceInfo::from_glib_ptr_borrow_mut(info_ptr); + + imp.complete_interface_info(instance_type, interface_type, info); +} diff --git a/glib/src/subclass/types.rs b/glib/src/subclass/types.rs index 7e1cdd566e88..9018ba69e267 100644 --- a/glib/src/subclass/types.rs +++ b/glib/src/subclass/types.rs @@ -10,7 +10,7 @@ use crate::{ object::{IsClass, IsInterface, ObjectSubclassIs, ParentClassIs}, prelude::*, translate::*, - Closure, Object, Type, Value, + Closure, InterfaceInfo, Object, Type, TypeFlags, TypeInfo, TypeModule, TypePlugin, Value, }; // rustdoc-stripper-ignore-next @@ -328,7 +328,7 @@ unsafe extern "C" fn interface_init>( pub trait InterfaceList { // rustdoc-stripper-ignore-next /// Returns the list of types and corresponding interface infos for this list. - fn iface_infos() -> Vec<(ffi::GType, gobject_ffi::GInterfaceInfo)>; + fn iface_infos() -> Vec<(Type, InterfaceInfo)>; // rustdoc-stripper-ignore-next /// Runs `instance_init` on each of the `IsImplementable` items. @@ -336,7 +336,7 @@ pub trait InterfaceList { } impl InterfaceList for () { - fn iface_infos() -> Vec<(ffi::GType, gobject_ffi::GInterfaceInfo)> { + fn iface_infos() -> Vec<(Type, InterfaceInfo)> { vec![] } @@ -348,15 +348,14 @@ impl> InterfaceList for (A,) where ::GlibClassType: Copy, { - fn iface_infos() -> Vec<(ffi::GType, gobject_ffi::GInterfaceInfo)> { - vec![( - A::static_type().into_glib(), - gobject_ffi::GInterfaceInfo { + fn iface_infos() -> Vec<(Type, InterfaceInfo)> { + vec![(A::static_type(), unsafe { + InterfaceInfo::unsafe_from(gobject_ffi::GInterfaceInfo { interface_init: Some(interface_init::), interface_finalize: None, interface_data: ptr::null_mut(), - }, - )] + }) + })] } #[inline] @@ -390,16 +389,16 @@ macro_rules! interface_list_trait_impl( where $(<$name as ObjectType>::GlibClassType: Copy),+ { - fn iface_infos() -> Vec<(ffi::GType, gobject_ffi::GInterfaceInfo)> { + fn iface_infos() -> Vec<(Type, InterfaceInfo)> { vec![ $( ( - $name::static_type().into_glib(), - gobject_ffi::GInterfaceInfo { + $name::static_type(), + unsafe { InterfaceInfo::unsafe_from(gobject_ffi::GInterfaceInfo { interface_init: Some(interface_init::), interface_finalize: None, interface_data: ptr::null_mut(), - }, + })}, ) ),+ ] @@ -1040,7 +1039,105 @@ pub fn register_type() -> Type { let iface_types = T::Interfaces::iface_infos(); for (iface_type, iface_info) in iface_types { - gobject_ffi::g_type_add_interface_static(type_.into_glib(), iface_type, &iface_info); + gobject_ffi::g_type_add_interface_static( + type_.into_glib(), + iface_type.into_glib(), + iface_info.to_glib_none().0, + ); + } + + T::type_init(&mut InitializingType::(type_, marker::PhantomData)); + + type_ + } +} + +// rustdoc-stripper-ignore-next +/// Register a `glib::Type` ID for `T` as a module type. +/// +/// A module type must be explicitly registered when a module is loaded (see [`TypeModuleImpl::load`]). +/// Therefore, unlike for non module types a module type can be registered several times. +/// +/// [`TypeModuleImpl::load`]: ../type_module/trait.TypeModuleImpl.html#method.load +pub fn register_module_type(type_module: &TypeModule) -> Type { + // GLib aligns the type private data to two gsizes, so we can't safely store any type there that + // requires a bigger alignment. + assert!( + mem::align_of::() <= 2 * mem::size_of::(), + "Alignment {} of type not supported, bigger than {}", + mem::align_of::(), + 2 * mem::size_of::(), + ); + + unsafe { + use std::ffi::CString; + + let type_name = CString::new(T::NAME).unwrap(); + + let already_registered = + gobject_ffi::g_type_from_name(type_name.as_ptr()) != gobject_ffi::G_TYPE_INVALID; + + let type_info = TypeInfo::unsafe_from(gobject_ffi::GTypeInfo { + class_size: mem::size_of::() as u16, + base_init: None, + base_finalize: None, + class_init: Some(class_init::), + class_finalize: None, + class_data: ptr::null(), + instance_size: mem::size_of::() as u16, + n_preallocs: 0, + instance_init: Some(instance_init::), + value_table: ptr::null(), + }); + + // register the type within the `type_module` + let type_ = type_module.register_type( + ::static_type(), + type_name.to_str().unwrap(), + &type_info, + if T::ABSTRACT { + TypeFlags::ABSTRACT + } else { + TypeFlags::NONE + }, + ); + assert!(type_.is_valid()); + + let mut data = T::type_data(); + data.as_mut().type_ = type_; + + let private_offset = mem::size_of::>(); + data.as_mut().private_offset = private_offset as isize; + + // Get the offset from PrivateStruct to the imp field in it. This has to go through + // some hoops because Rust doesn't have an offsetof operator yet. + data.as_mut().private_imp_offset = { + // Must not be a dangling pointer so let's create some uninitialized memory + let priv_ = mem::MaybeUninit::>::uninit(); + let ptr = priv_.as_ptr(); + let imp_ptr = ptr::addr_of!((*ptr).imp); + (imp_ptr as isize) - (ptr as isize) + }; + + let module_as_plugin = type_module.upcast_ref::().to_glib_none().0; + let iface_types = T::Interfaces::iface_infos(); + for (iface_type, iface_info) in iface_types { + match gobject_ffi::g_type_get_plugin(iface_type.into_glib()) { + // if interface type's plugin is null or is different to the `type_module`, + // then interface can only be added as if the type was static + iface_plugin if iface_plugin != module_as_plugin => { + // but adding interface to a static type can be done only once + if !already_registered { + gobject_ffi::g_type_add_interface_static( + type_.into_glib(), + iface_type.into_glib(), + iface_info.to_glib_none().0, + ); + } + } + // else interface can be added and registered to live in the `type_module` + _ => type_module.add_interface(type_, iface_type, &iface_info), + } } T::type_init(&mut InitializingType::(type_, marker::PhantomData));