Skip to content

Commit

Permalink
add glib::subclass::{register_module_type,register_module_interface}
Browse files Browse the repository at this point in the history
Signed-off-by: fbrouille <[email protected]>
  • Loading branch information
fbrouille committed Aug 12, 2023
1 parent 711507e commit 37aa425
Show file tree
Hide file tree
Showing 5 changed files with 502 additions and 25 deletions.
73 changes: 64 additions & 9 deletions glib/src/subclass/interface.rs
Original file line number Diff line number Diff line change
@@ -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<ffi::GType>;
fn types() -> Vec<Type>;
}

impl PrerequisiteList for () {
fn types() -> Vec<ffi::GType> {
fn types() -> Vec<Type> {
vec![]
}
}

impl<T: crate::ObjectType> PrerequisiteList for (T,) {
fn types() -> Vec<ffi::GType> {
vec![T::static_type().into_glib()]
fn types() -> Vec<Type> {
vec![T::static_type()]
}
}

Expand All @@ -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<ffi::GType> {
vec![$($name::static_type().into_glib()),+]
fn types() -> Vec<Type> {
vec![$($name::static_type()),+]
}
}
);
Expand Down Expand Up @@ -204,7 +204,7 @@ pub fn register_interface<T: ObjectInterface>() -> 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_);
Expand All @@ -215,3 +215,58 @@ pub fn register_interface<T: ObjectInterface>() -> 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<T: ObjectInterface>(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::<T>() as u16,
base_init: None,
base_finalize: None,
class_init: Some(interface_init::<T>),
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::<T>(type_, marker::PhantomData));

type_
}
}
10 changes: 8 additions & 2 deletions glib/src/subclass/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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,
Expand All @@ -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},
};
133 changes: 133 additions & 0 deletions glib/src/subclass/type_module.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// 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<T: IsA<TypeModule>> {
fn on_load(type_module: &T) -> bool;
fn on_unload(type_module: &T);
}

pub trait TypeModuleImpl: ObjectImpl + TypeModuleImplExt {
fn load(&self) -> bool;
fn unload(&self);
}

pub trait TypeModuleImplExt: ObjectSubclass {
fn parent_load(&self) -> bool;
fn parent_unload(&self);
}

impl<T: TypeModuleImpl> 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::<TypeModule>()
.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::<TypeModule>().to_glib_none().0);
}
}
}

unsafe impl<T: TypeModuleImpl> IsSubclassable<T> for TypeModule {
fn class_init(class: &mut crate::Class<Self>) {
Self::parent_class_init::<T>(class);

let klass = class.as_mut();
klass.load = Some(load::<T>);
klass.unload = Some(unload::<T>);
}
}

unsafe extern "C" fn load<T: TypeModuleImpl>(
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<T: TypeModuleImpl>(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<imp::SimpleTypeModule>)
@extends crate::TypeModule;
}

#[test]
fn test_simple_type_module() {
let simple_type_module = crate::Object::new::<SimpleTypeModule>();
// simulate the glib type system to load the module
assert!(simple_type_module.use_());
simple_type_module.unuse();
}
}
Loading

0 comments on commit 37aa425

Please sign in to comment.