Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support of module types #1169

Merged
merged 1 commit into from
Oct 31, 2023
Merged

add support of module types #1169

merged 1 commit into from
Oct 31, 2023

Conversation

fbrouille
Copy link
Contributor

@fbrouille fbrouille commented Aug 11, 2023

This PR aims to add support of module types and interfaces. See https://docs.gtk.org/gobject/class.TypeModule.html
The new macros module_object_subclass and module_object_interface allow to register types and interfaces that are part of a module (e.g. a shared library on linux). The macros mimic object_subclass and object_interface but generate code that support registration when the module (TypeModule) is loaded, following the behavior defined in Glib doc. However it is possible to postpone the registration at first use of the type (like for object_subclass) by explicitly setting the macro attribute lazy_registration = true.
Some examples in glib-macros/tests/test.rs, glib/src/subclass/mod.rs and glib/src/subclass/type_module.rs

@fbrouille fbrouille force-pushed the module_type branch 6 times, most recently from d08dff6 to 5266273 Compare August 12, 2023 10:59
@sdroege
Copy link
Member

sdroege commented Aug 14, 2023

This would fix #55, or would something still be missing?

@bilelmoussaoui bilelmoussaoui added the needs-backport PR needs backporting to the current stable branch label Aug 14, 2023
glib-macros/src/lib.rs Outdated Show resolved Hide resolved
}

#[doc(alias = "g_type_module_register_enum")]
fn register_enum(&self, name: &str, const_static_values: &EnumValue) -> crate::types::Type {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The add() / register() API here, would it make more sense to hide that behind a macro? E.g. the glib::Enum macro with some additional configuration?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes you are right. Honestly I did not do it because I don't need it in my project. I will try to add it...


pub trait TypePluginExt: IsA<TypePlugin> + sealed::Sealed + 'static {
#[doc(alias = "g_type_plugin_complete_interface_info")]
fn complete_interface_info(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And also this maybe as part of a macro instead of proper public API?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR focuses more on TypeModule than on TypePlugin. I added TypePlugin and TypePluginExt only because GTypeModule implements GTypePlugin in Glib and I generated the code from GIR files.
I tried to respect the behavior of the TypeModule (according to Glib documentation) in the new macros and i suppose TypePlugin has a different behavior (e.g. it does not define load and unload virtual functions) so I don't really now how to deal with it... And I don't need that since GTypeModule already implements it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I mean is that this PR adds quite a bit of public API that should probably never be called by user code but only by the macro, so maybe it can be moved directly into the macro instead and call the FFI API there.

TypePlugin / TypeModule` and their ext traits with the (useful) public API should still exists of course.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm currently working on a new implementation of the macros to support TypePlugin as well. I have to finalize it (fix comments and add tests) but I hope I will push a new commit next week. However I won't have so much time for the 2 next weeks, so I will do my best. Stay tuned...

Copy link
Contributor Author

@fbrouille fbrouille Oct 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here it is (see 22ab846).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have changed implementation of macros in order to use static dispatch and avoid dynamic dispatch (see 0fe707d)

wrapper! {
#[derive(Debug)]
#[doc(alias = "GInterfaceInfo")]
pub struct InterfaceInfo(BoxedInline<gobject_ffi::GInterfaceInfo>);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this has no GType assigned and also otherwise no memory management functions, maybe this should be implemented fully manually? There's no point in ever cloning these, for example, is there? So it could just be a & reference returned from the functions

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(same for the other two types here)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have some examples to help me understand how to do it ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'd define a

#[repr(transparent)]
pub struct InterfaceInfo(gobject_ffi::GInterfaceInfo);

and then can do things like

let ptr: *const gobject_ffi::GInterfaceInfo;
let info: &InterfaceInfo = &*(ptr as *const InterfaceInfo);

As there's no memory management associated with these structs in C, inventing that on the bindings side is going to be problematic.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New commit c484822 to change the implementation.
As TypePlugin and TypeModule are generated by gir tool, they require InterfaceInfo, TypeInfo and TypeValueTable to implement function from_glib_ptr_borrow_mut and trait ToGlibPtr.
I don't know how to tell gir to not use these functions.
Therefore I decided to implement the missing functions and missing trait (ToGlibPtr) and also implement other complementary traits (IntoGlib, FromGlib, ToGlibPtrMut, FromGlibPtrNone) which imo is more coherent.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can't have ToGlibPtr::to_glib_full() for these and also not FromGlibPtrNone and IntoGlib and FromGlib. Those implementations conceptually make no sense.

For the time being you will have to implement the functions with these types manually to get the correct behaviour (borrowing).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very basic infrastructure so it's quite expected that gir won't do the right thing here and manual bindings are needed in many places.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have removed implementation of traits and I have made manual bindings so now code of TypePluginand TypeModule is manual and not generated anymore. See commit d631c13

TypeValueTable,
};

pub trait TypePluginImpl: ObjectImpl + TypePluginImplExt {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a minimal test for this too?

Copy link
Contributor Author

@fbrouille fbrouille Aug 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i will try, but again I added TypePlugin types only because I wanted to respect how GTypeModule is implemented in Glib (by implementing the GTypePugin interface)

Copy link
Contributor Author

@fbrouille fbrouille Oct 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done (see 22ab846).

Copy link
Member

@sdroege sdroege left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally looks good to me but I don't really know the dynamic type API of GObject :)

Copy link
Member

@sdroege sdroege left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems mostly fine to me now. If this is the best API design for this will have to be figured out by someone actually using it, but it works for now apparently :)

#[derive(Debug)]
#[doc(alias = "GTypeInfo")]
#[repr(transparent)]
pub struct TypeInfo(pub gobject_ffi::GTypeInfo);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub struct TypeInfo(pub gobject_ffi::GTypeInfo);
pub struct TypeInfo(pub(crate) gobject_ffi::GTypeInfo);

Maybe, plus an as_ptr() function that returns the underlying FFI pointer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done (see f1eac51). Also added from_glib_ptr_borrow_mut.

fn parent_complete_type_info(
&self,
type_: Type,
info: &mut TypeInfo,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this have to be mutable? There's nothing we (can) change in it, is there?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, according to Glib documentation GObject.TypePlugin.complete_type_info implementations of the interface must fill info.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would they fill that currently?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

complete_type_info is usually called by the Glib type system. A rust implementation of TypePlugin could be something like:

pub struct MyPlugin {
    my_type_type_info: Cell<glib::TypeInfo>,
    ...
}

impl TypePluginImpl for MyPlugin {
    ....
    fn complete_type_info(&self, type_: glib::Type, info: &mut glib::TypeInfo, value_table: &mut glib::TypeValueTable) {
         match self.my_type_type_info.get() {
             Some(my_type_info) => *info = my_type_info,
             _ => panic!("unexpected"),
        }
    }
    ...
}

impl MyPlugin {
    fn register_dynamic_type(&self, parent_type: glib::Type, type_name: &str, type_info: &glib::TypeInfo, flags: glib::TypeFlags) -> glib::Type {
        let type_ = unsafe {
            glib::Type::from_glib(glib::gobject_ffi::g_type_register_dynamic(parent_type.into_glib(), type_name.as_ptr(),  self.obj().upcast_ref::<glib::TypePlugin>().as_ptr(), flags.into_glib()))
        };
        if type_.is_valid() {
            self.my_type_type_info.set(Some(*type_info));
        }
        type_
    }
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that sounds like this vfunc should be returning (glib::TypeInfo, glib::TypeValueTable) then

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My question was more how an implementation in Rust would even create or modify a TypeInfo right now. But I see, that makes sense for future extensibility then.

Is the TypeInfo passed in by GObject here already correctly initialized, or is the implementer supposed to return a new one? That would basically be what decides if the mutable references or the return tuple is the right choice.

Copy link
Contributor Author

@fbrouille fbrouille Oct 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I finally changed it to return a tuple (see 22ab846).

&self,
g_type: crate::types::Type,
info: &mut TypeInfo,
value_table: &mut TypeValueTable,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are these two mutable? (also in the other places)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for value_table.
Also same for info within implementations of GObject.TypePlugin.complete_interface_info.

Copy link
Contributor Author

@fbrouille fbrouille Oct 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I finally changed it to return a tuple (see 22ab846).

#[derive(Debug)]
#[doc(alias = "GTypeValueTable")]
#[repr(transparent)]
pub struct TypeValueTable(pub gobject_ffi::GTypeValueTable);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub struct TypeValueTable(pub gobject_ffi::GTypeValueTable);
pub struct TypeValueTable(pub(crate) gobject_ffi::GTypeValueTable);

maybe

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done (see f1eac51). Also added as_ptrand from_glib_ptr_borrow_mut.

@@ -3,4 +3,24 @@
#[derive(Debug)]
#[doc(alias = "GInterfaceInfo")]
#[repr(transparent)]
pub struct InterfaceInfo(pub gobject_ffi::GInterfaceInfo);
pub struct InterfaceInfo(pub(crate) gobject_ffi::GInterfaceInfo);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub struct InterfaceInfo(pub(crate) gobject_ffi::GInterfaceInfo);
pub struct InterfaceInfo(gobject_ffi::GInterfaceInfo);

as you would be from_glib_ptr_borrow_mut to construct them anyway, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, some code needs to create instance of InterfaceInfo and similar types: see in glib/src/subclass/types.rs
Or maybe I can add a new method ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems fine to me like this, but a constructor pub(crate) would also work and reduce the code a bit

Comment on lines 115 to 119
impl fmt::Display for TypeModule {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("TypeModule")
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove all those Display implementations, we have dropped them in the main branch

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Member

@bilelmoussaoui bilelmoussaoui left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good to me otherwise, thanks!

bilelmoussaoui
bilelmoussaoui previously approved these changes Oct 18, 2023
@fbrouille
Copy link
Contributor Author

This PR does not support dynamic registration of glib::Enum and glib::Flags within a module or a plugin.
Let me know if I have to modify this PR (needs some work) or if you prefer me to create a new one.

@fbrouille
Copy link
Contributor Author

fix some bugs

@sdroege
Copy link
Member

sdroege commented Oct 27, 2023

Let me know if I have to modify this PR (needs some work) or if you prefer me to create a new one.

Seems fine in a separate PR.

Comment on lines 272 to 273
let type_name = CString::new(type_name).unwrap();
let mut g_type = gobject_ffi::g_type_from_name(type_name.as_ptr());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let type_name = CString::new(type_name).unwrap();
let mut g_type = gobject_ffi::g_type_from_name(type_name.as_ptr());
let mut g_type = gobject_ffi::g_type_from_name(type_name.to_glib_none().0);

or

Suggested change
let type_name = CString::new(type_name).unwrap();
let mut g_type = gobject_ffi::g_type_from_name(type_name.as_ptr());
let mut g_type = type_name.run_with_gstr(|type_name| gobject_ffi::g_type_from_name(type_name.as_ptr()));

also elsewhere

//! }
//!
//! fn register_dynamic_type(&self, parent_type: glib::Type, type_name: &str, type_info: &glib::TypeInfo, flags: glib::TypeFlags) -> glib::Type {
//! let type_ = unsafe {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if there wouldn't be some more convenient (even if unsafe) API that wouldn't require user code to do FFI directly.

g_type_from_name() for example has safe wrappers, g_type_register_dynamic() should be possible to wrap in something a bit more convenient at least (taking non-FFI types, taking care of the C string conversion, ...)

Copy link
Contributor Author

@fbrouille fbrouille Oct 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes indeed. I have added Type::get_plugin(), Type::register_dynamic() and Type::add_interface_dynamic() wrappers and code is now concise and clearer.

@sdroege
Copy link
Member

sdroege commented Oct 27, 2023

Once this is ready, please also squash it all into fewer commits that are each one functional and useful change (i.e. in your case I guess a single commit)

@fbrouille
Copy link
Contributor Author

About enums and flags, I have removed the piece of code that was introduced early with this PR (essentially due to the generation of code). It will be part of a separate PR.
All commits have been squashed and rebased.

Copy link
Member

@sdroege sdroege left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One last thing, otherwise this is IMHO ready. @bilelmoussaoui ?

@@ -223,6 +223,48 @@ impl Type {
}
}

#[doc(alias = "g_type_get_plugin")]
pub fn get_plugin(self) -> Option<TypePlugin> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub fn get_plugin(self) -> Option<TypePlugin> {
pub fn plugin(self) -> Option<TypePlugin> {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Member

@bilelmoussaoui bilelmoussaoui left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

otherwise lgtm, thanks

@@ -3,25 +3,28 @@
use std::{marker, mem};

use super::{InitializingType, Signal};
use crate::{prelude::*, translate::*, Object, ParamSpec, Type};
use crate::{
gobject::traits::DynamicObjectRegisterExt, prelude::*, translate::*, Object, ParamSpec, Type,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
gobject::traits::DynamicObjectRegisterExt, prelude::*, translate::*, Object, ParamSpec, Type,
prelude::*, translate::*, Object, ParamSpec, Type,

glib's prelude should include gobject's one

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@sdroege sdroege merged commit 2cb10ec into gtk-rs:master Oct 31, 2023
48 checks passed
@sdroege sdroege mentioned this pull request Oct 31, 2023
@fbrouille fbrouille deleted the module_type branch October 31, 2023 16:58
@bilelmoussaoui bilelmoussaoui removed the needs-backport PR needs backporting to the current stable branch label Feb 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants