From 6839fc497d491e8df5b4c9cfdf3b4a28fba9f02a Mon Sep 17 00:00:00 2001 From: fbrouille Date: Sat, 28 Oct 2023 18:59:40 +0200 Subject: [PATCH] add support of object subclasses and interfaces registered as dynamic types Signed-off-by: fbrouille --- glib-macros/src/lib.rs | 140 ++++ glib-macros/src/object_interface_attribute.rs | 220 ++++++- glib-macros/src/object_subclass_attribute.rs | 192 +++++- glib-macros/tests/dynamic_objects.rs | 606 ++++++++++++++++++ glib/Gir_GObject.toml | 6 + glib/src/gobject/auto/flags.rs | 35 + glib/src/gobject/auto/mod.rs | 1 + glib/src/gobject/dynamic_object.rs | 75 +++ glib/src/gobject/interface_info.rs | 38 ++ glib/src/gobject/mod.rs | 24 + glib/src/gobject/type_info.rs | 43 ++ glib/src/gobject/type_module.rs | 88 +++ glib/src/gobject/type_plugin.rs | 73 +++ glib/src/gobject/type_value_table.rs | 43 ++ glib/src/lib.rs | 17 +- glib/src/prelude.rs | 6 +- glib/src/subclass/interface.rs | 75 ++- glib/src/subclass/mod.rs | 209 +++++- glib/src/subclass/type_module.rs | 164 +++++ glib/src/subclass/type_plugin.rs | 301 +++++++++ glib/src/subclass/types.rs | 132 +++- glib/src/types.rs | 44 +- 22 files changed, 2481 insertions(+), 51 deletions(-) create mode 100644 glib-macros/tests/dynamic_objects.rs create mode 100644 glib/src/gobject/dynamic_object.rs create mode 100644 glib/src/gobject/interface_info.rs create mode 100644 glib/src/gobject/type_info.rs create mode 100644 glib/src/gobject/type_module.rs create mode 100644 glib/src/gobject/type_plugin.rs create mode 100644 glib/src/gobject/type_value_table.rs create mode 100644 glib/src/subclass/type_module.rs create mode 100644 glib/src/subclass/type_plugin.rs diff --git a/glib-macros/src/lib.rs b/glib-macros/src/lib.rs index 2719695292fe..e0e9224ca52a 100644 --- a/glib-macros/src/lib.rs +++ b/glib-macros/src/lib.rs @@ -645,6 +645,75 @@ pub fn object_subclass(_attr: TokenStream, item: TokenStream) -> TokenStream { } } +/// Macro for boilerplate of [`ObjectSubclass`] implementations that are +/// registered as dynamic types. +/// +/// An object subclass must be explicitly registered as a dynamic type when the +/// system loads its implementation (see [`TypePlugin`] and [`TypeModule`]. +/// Therefore, whereas an object subclass can be registered only once as a +/// static type, it can be registered several times as a dynamic type. +/// +/// An object subclass registered as a dynamic type is never unregistered. The +/// system calls [`TypePluginExt::unuse`] to unload its implementation. If the +/// [`TypePlugin`] subclass is a [`TypeModule`], the object subclass registered +/// as a dynamic type is marked as unloaded and must be registered again when +/// the module is reloaded. +/// +/// This macro provides two behaviors when registering an object subclass as a +/// dynamic type: +/// +/// By default an object subclass is registered as a dynamic type when the +/// system loads its implementation (e.g. when the module is loaded): +/// ```ignore +/// #[glib::dynamic_object_subclass] +/// impl ObjectSubclass for MyType { ... } +/// ``` +/// +/// Optionally setting the macro attribute `lazy_registration` to `true` +/// postpones registration on the first use (when `type_()` is called for the +/// first time), similarly to the [`macro@object_subclass`] +/// macro: +/// ```ignore +/// #[glib::dynamic_object_subclass(lazy_registration = true)] +/// impl ObjectSubclass for MyType { ... } +/// ``` +/// +/// By default an object subclass is considered to be registered as a dynamic +/// type within a [`TypeModule`] subclass. Optionally setting the macro +/// attribute `plugin_type` allows to register an object subclass as a dynamic +/// type within a given [`TypePlugin`] subclass: +/// ```ignore +/// #[glib::dynamic_object_subclass(plugin_type = MyPlugin)] +/// impl ObjectSubclass for MyType { ... } +/// ``` +/// +/// [`ObjectSubclass`]: ../glib/subclass/types/trait.ObjectSubclass.html +/// [`TypePlugin`]: ../glib/gobject/type_plugin/struct.TypePlugin.html +/// [`TypeModule`]: ../glib/gobject/type_module/struct.TypeModule.html +/// [`TypePluginExt::unuse`]: ../glib/gobject/type_plugin/trait.TypePluginExt.html#method.unuse +#[proc_macro_attribute] +#[proc_macro_error] +pub fn dynamic_object_subclass(attr: TokenStream, item: TokenStream) -> TokenStream { + use proc_macro_error::abort_call_site; + let attrs = match syn::parse::Parser::parse( + syn::punctuated::Punctuated::::parse_terminated, + attr, + ) { + Ok(attrs) + if attrs + .iter() + .all(|attr| matches!(attr, syn::Expr::Assign(_))) => + { + attrs + } + _ => abort_call_site!(object_subclass_attribute::WRONG_EXPRESSION_MSG), + }; + match syn::parse::(item) { + Ok(input) => object_subclass_attribute::impl_dynamic_object_subclass(&attrs, &input).into(), + Err(_) => abort_call_site!(object_subclass_attribute::WRONG_PLACE_MSG), + } +} + /// Macro for boilerplate of [`ObjectInterface`] implementations. /// /// This adds implementations for the `get_type()` method, which should probably never be defined @@ -670,6 +739,77 @@ pub fn object_interface(_attr: TokenStream, item: TokenStream) -> TokenStream { } } +/// Macro for boilerplate of [`ObjectInterface`] implementations that are +/// registered as dynamic types. +/// +/// An object interface must be explicitly registeredas a dynamic type when the +/// system loads its implementation (see [`TypePlugin`] and [`TypeModule`]. +/// Therefore, whereas an object interface can be registered only once as a +/// static type, it can be registered several times as a dynamic type. +/// +/// An object interface registered as a dynamic type is never unregistered. The +/// system calls [`TypePluginExt::unuse`] to unload its implementation. If the +/// [`TypePlugin`] subclass is a [`TypeModule`], the object interface +/// registered as a dynamic type is marked as unloaded and must be registered +/// again when the module is reloaded. +/// +/// This macro provides two behaviors when registering an object interface as a +/// dynamic type: +/// +/// By default an object interface is registered as a dynamic type when the +/// system loads its implementation (e.g. when the module is loaded): +/// ```ignore +/// #[glib::dynamic_object_interface] +/// unsafe impl ObjectInterface for MyInterface { ... } +/// ``` +/// +/// Optionally setting the macro attribute `lazy_registration` to `true` +/// postpones registration on the first use (when `type_()` is called for the +/// first time), similarly to the [`macro@object_subclass`] +/// [`macro@object_interface`] macro. +/// ```ignore +/// #[glib::dynamic_object_interface(lazy_registration = true)] +/// unsafe impl ObjectInterface for MyInterface { ... } +/// ``` +/// +/// By default an object interface is considered to be registered as a dynamic +/// type within a [`TypeModule`] subclass. Optionally setting the macro +/// attribute `plugin_type` allows to register an object interface as a dynamic +/// type within a given [`TypePlugin`] subclass: +/// ```ignore +/// #[glib::dynamic_object_interface(plugin_type = MyPlugin)] +/// unsafe impl ObjectInterface for MyInterface { ... } +/// ``` +/// +/// [`ObjectInterface`]: ../glib/subclass/interface/trait.ObjectInterface.html +/// [`TypePlugin`]: ../glib/gobject/type_plugin/struct.TypePlugin.html +/// [`TypeModule`]: ../glib/gobject/type_module/struct.TypeModule.html +/// [`TypePluginExt::unuse`]: ../glib/gobject/type_plugin/trait.TypePluginExt.html#method.unuse +#[proc_macro_attribute] +#[proc_macro_error] +pub fn dynamic_object_interface(attr: TokenStream, item: TokenStream) -> TokenStream { + use proc_macro_error::abort_call_site; + let attrs = match syn::parse::Parser::parse( + syn::punctuated::Punctuated::::parse_terminated, + attr, + ) { + Ok(attrs) + if attrs + .iter() + .all(|attr| matches!(attr, syn::Expr::Assign(_))) => + { + attrs + } + _ => abort_call_site!(object_interface_attribute::WRONG_EXPRESSION_MSG), + }; + match syn::parse::(item) { + Ok(input) => { + object_interface_attribute::impl_dynamic_object_interface(&attrs, &input).into() + } + Err(_) => abort_call_site!(object_interface_attribute::WRONG_PLACE_MSG), + } +} + /// Macro for deriving implementations of [`glib::clone::Downgrade`] and /// [`glib::clone::Upgrade`] traits and a weak type. /// diff --git a/glib-macros/src/object_interface_attribute.rs b/glib-macros/src/object_interface_attribute.rs index abec274d4097..321e6624d125 100644 --- a/glib-macros/src/object_interface_attribute.rs +++ b/glib-macros/src/object_interface_attribute.rs @@ -2,12 +2,214 @@ use proc_macro2::TokenStream; use proc_macro_error::abort_call_site; -use quote::quote; +use quote::{quote, ToTokens}; + +pub const WRONG_EXPRESSION_MSG: &str = + "This macro's attributes should be a sequence of assign expressions punctuated by comma"; + +pub const UNSUPPORTED_EXPRESSION_MSG: &str = + "This macro's supported attributes are: `plugin_type = , lazy_registration = true|false`"; pub const WRONG_PLACE_MSG: &str = "This macro should be used on `impl` block for `glib::ObjectInterface` trait"; pub fn impl_object_interface(input: &syn::ItemImpl) -> TokenStream { + let crate_ident = crate::utils::crate_ident_new(); + let syn::ItemImpl { self_ty, .. } = &input; + + // registers the interface on first use (lazy registration). + let register_interface = quote! { + impl #self_ty { + /// Registers the interface only once. + #[inline] + fn register_interface() -> #crate_ident::Type { + static ONCE: ::std::sync::Once = ::std::sync::Once::new(); + static mut TYPE: #crate_ident::Type = #crate_ident::Type::INVALID; + + ONCE.call_once(|| unsafe { + TYPE = #crate_ident::subclass::register_interface::(); + }); + + unsafe { + TYPE + } + } + } + }; + + impl_object_interface_(register_interface, input) +} + +pub fn impl_dynamic_object_interface( + attrs: &syn::punctuated::Punctuated, + input: &syn::ItemImpl, +) -> TokenStream { + let crate_ident = crate::utils::crate_ident_new(); + let syn::ItemImpl { self_ty, .. } = &input; + + let mut plugin_type_opt: Option = None; + let mut lazy_registration_opt: Option = None; + + for attr in attrs { + match attr { + // attribute must be one of supported assign expressions. + syn::Expr::Assign(syn::ExprAssign { left, right, .. }) => { + match (*left.to_owned(), *right.to_owned()) { + // `plugin_type = ` + ( + syn::Expr::Path(syn::ExprPath { path: path1, .. }), + syn::Expr::Path(syn::ExprPath { path: path2, .. }), + ) if path1.is_ident(&"plugin_type") => plugin_type_opt = Some(path2), + // `lazy_registration = true|false` + ( + syn::Expr::Path(syn::ExprPath { path, .. }), + syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Bool(syn::LitBool { value, .. }), + .. + }), + ) if path.is_ident(&"lazy_registration") => lazy_registration_opt = Some(value), + _ => abort_call_site!(UNSUPPORTED_EXPRESSION_MSG), + }; + } + _ => abort_call_site!(WRONG_EXPRESSION_MSG), + }; + } + + let (plugin_ty, lazy_registration) = match (plugin_type_opt, lazy_registration_opt) { + (Some(type_plugin), lazy_registration_opt) => ( + type_plugin.into_token_stream(), + lazy_registration_opt.unwrap_or_default(), + ), + (None, lazy_registration_opt) => ( + quote!(#crate_ident::TypeModule), + lazy_registration_opt.unwrap_or_default(), + ), + }; + + // The following implementations follows the lifecycle of plugins and of dynamic types (see [`TypePluginExt::unuse`]). + // An object interface can be reregistered as a dynamic type (see [`TypePluginExt::register_type`]). + let register_interface = if lazy_registration { + // registers the object interface as a dynamic type on the first use (lazy registration). + // a weak reference on the plugin is stored and will be used later on the first use of the object interface. + // this implementation relies on a static storage of a weak reference on the plugin and of the glib type to know if the object interface has been registered. + quote! { + impl #self_ty { + /// Returns a mutable reference to the registration status: a tuple of the weak reference on the plugin and of the glib type. + /// This is safe because the mutable reference guarantees that no other threads are concurrently accessing the data. + #[inline] + fn get_registration_status_ref_mut() -> &'static mut Option<(<#plugin_ty as #crate_ident::clone::Downgrade>::Weak, #crate_ident::Type)> { + static mut REGISTRATION_STATUS: ::std::sync::Mutex::Weak, #crate_ident::Type)>> = ::std::sync::Mutex::new(None); + unsafe { REGISTRATION_STATUS.get_mut().unwrap() } + } + + /// Registers the object interface as a dynamic type within the plugin only once. + /// Plugin must have been used at least once. + /// Do nothing if plugin has never been used or if the object interface is already registered as a dynamic type. + #[inline] + fn register_interface() -> #crate_ident::Type { + let registration_status_ref_mut = Self::get_registration_status_ref_mut(); + match registration_status_ref_mut { + // plugin has never been used, so the object interface cannot be registered as a dynamic type. + None => #crate_ident::Type::INVALID, + // plugin has been used and the object interface has not been registered yet, so registers it as a dynamic type. + Some((type_plugin, type_)) if !type_.is_valid() => { + *type_ = #crate_ident::subclass::register_dynamic_interface::<#plugin_ty, Self>(&(type_plugin.upgrade().unwrap())); + *type_ + }, + // plugin has been used and the object interface has already been registered as a dynamic type. + Some((_, type_)) => *type_ + } + } + + /// Depending on the plugin lifecycle state and on the registration status of the object interface: + /// If plugin is used (and has loaded the implementation) for the first time, postpones the registration and stores a weak reference on the plugin. + /// If plugin is reused (and has reloaded the implementation) and the object interface has been already registered as a dynamic type, reregisters it. + /// An object interface can be reregistered several times as a dynamic type. + /// If plugin is reused (and has reloaded the implementation) and the object interface has not been registered yet as a dynamic type, do nothing. + #[inline] + pub fn on_implementation_load(type_plugin: &#plugin_ty) -> bool { + let registration_status_ref_mut = Self::get_registration_status_ref_mut(); + match registration_status_ref_mut { + // plugin has never been used (this is the first time), so postpones registration of the object interface as a dynamic type on the first use. + None => { + *registration_status_ref_mut = Some((#crate_ident::clone::Downgrade::downgrade(type_plugin), #crate_ident::Type::INVALID)); + true + }, + // plugin has been used at least one time and the object interface has been registered as a dynamic type at least one time, so re-registers it. + Some((_, type_)) if type_.is_valid() => { + *type_ = #crate_ident::subclass::register_dynamic_interface::<#plugin_ty, Self>(type_plugin); + type_.is_valid() + }, + // plugin has been used at least one time but the object interface has not been registered yet as a dynamic type, so keeps postponed registration. + Some(_) => { + true + } + } + } + + /// Depending on the plugin lifecycle state and on the registration status of the object interface: + /// If plugin has been used (or reused) but the object interface has not been registered yet as a dynamic type, cancels the postponed registration by deleting the weak reference on the plugin. + /// Else do nothing. + #[inline] + pub fn on_implementation_unload(type_plugin_: &#plugin_ty) -> bool { + let registration_status_ref_mut = Self::get_registration_status_ref_mut(); + match registration_status_ref_mut { + // plugin has never been used, so unload implementation is unexpected. + None => false, + // plugin has been used at least one time and the object interface has been registered as a dynamic type at least one time. + Some((_, type_)) if type_.is_valid() => true, + // plugin has been used at least one time but the object interface has not been registered yet as a dynamic type, so cancels the postponed registration. + Some(_) => { + *registration_status_ref_mut = None; + true + } + } + } + } + } + } else { + // registers immediately the object interface as a dynamic type. + quote! { + impl #self_ty { + /// Returns a mutable reference to the glib type. + /// This is safe because the mutable reference guarantees that no other threads are concurrently accessing the atomic data. + #[inline] + fn get_type_mut() -> &'static mut #crate_ident::ffi::GType { + static mut TYPE: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::AtomicUsize::new(#crate_ident::gobject_ffi::G_TYPE_INVALID); + unsafe { TYPE.get_mut() } + } + + /// Do nothing as the object interface has been registered on implementation load. + #[inline] + fn register_interface() -> #crate_ident::Type { + unsafe { <#crate_ident::Type as #crate_ident::translate::FromGlib<#crate_ident::ffi::GType>>::from_glib(*Self::get_type_mut()) } + } + + /// Registers the object interface as a dynamic type within the plugin. + /// The object interface can be registered several times as a dynamic type. + #[inline] + pub fn on_implementation_load(type_plugin: &#plugin_ty) -> bool { + let type_mut = Self::get_type_mut(); + *type_mut = #crate_ident::translate::IntoGlib::into_glib(#crate_ident::subclass::register_dynamic_interface::<#plugin_ty, Self>(type_plugin)); + *type_mut != #crate_ident::gobject_ffi::G_TYPE_INVALID + } + + /// Do nothing as object interfaces registered as dynamic types are never unregistered. + #[inline] + pub fn on_implementation_unload(type_plugin_: &#plugin_ty) -> bool { + true + } + } + } + }; + + impl_object_interface_(register_interface, input) +} + +pub fn impl_object_interface_( + register_interface: TokenStream, + input: &syn::ItemImpl, +) -> TokenStream { let mut has_prerequisites = false; for item in &input.items { if let syn::ImplItem::Type(type_) = item { @@ -53,20 +255,10 @@ pub fn impl_object_interface(input: &syn::ItemImpl) -> TokenStream { unsafe impl #crate_ident::subclass::interface::ObjectInterfaceType for #self_ty { #[inline] fn type_() -> #crate_ident::Type { - static ONCE: ::std::sync::Once = ::std::sync::Once::new(); - static mut TYPE: #crate_ident::Type = #crate_ident::Type::INVALID; - - ONCE.call_once(|| { - let type_ = #crate_ident::subclass::register_interface::(); - unsafe { - TYPE = type_; - } - }); - - unsafe { - TYPE - } + Self::register_interface() } } + + #register_interface } } diff --git a/glib-macros/src/object_subclass_attribute.rs b/glib-macros/src/object_subclass_attribute.rs index dab232f212a8..d9ad6bf7ad19 100644 --- a/glib-macros/src/object_subclass_attribute.rs +++ b/glib-macros/src/object_subclass_attribute.rs @@ -2,12 +2,194 @@ use proc_macro2::TokenStream; use proc_macro_error::abort_call_site; -use quote::quote; +use quote::{quote, ToTokens}; + +pub const WRONG_EXPRESSION_MSG: &str = + "This macro's attributes should be a sequence of assign expressions punctuated by comma"; + +pub const UNSUPPORTED_EXPRESSION_MSG: &str = + "This macro's supported attributes are: `plugin_type = , lazy_registration = true|false`"; pub const WRONG_PLACE_MSG: &str = "This macro should be used on `impl` block for `glib::ObjectSubclass` trait"; pub fn impl_object_subclass(input: &syn::ItemImpl) -> TokenStream { + let crate_ident = crate::utils::crate_ident_new(); + let syn::ItemImpl { self_ty, .. } = &input; + + // registers the type on first use (lazy registration). + let register_type = quote! { + impl #self_ty { + /// Registers the type only once. + #[inline] + fn register_type() { + static ONCE: ::std::sync::Once = ::std::sync::Once::new(); + + ONCE.call_once(|| { + #crate_ident::subclass::register_type::(); + }) + } + } + }; + + impl_object_subclass_(register_type, input) +} + +pub fn impl_dynamic_object_subclass( + attrs: &syn::punctuated::Punctuated, + input: &syn::ItemImpl, +) -> TokenStream { + let crate_ident = crate::utils::crate_ident_new(); + let syn::ItemImpl { self_ty, .. } = &input; + + let mut plugin_type_opt: Option = None; + let mut lazy_registration_opt: Option = None; + + for attr in attrs { + match attr { + // attribute must be one of supported assign expressions. + syn::Expr::Assign(syn::ExprAssign { left, right, .. }) => { + match (*left.to_owned(), *right.to_owned()) { + // `plugin_type = ` + ( + syn::Expr::Path(syn::ExprPath { path: path1, .. }), + syn::Expr::Path(syn::ExprPath { path: path2, .. }), + ) if path1.is_ident(&"plugin_type") => plugin_type_opt = Some(path2), + // `lazy_registration = true|false` + ( + syn::Expr::Path(syn::ExprPath { path, .. }), + syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Bool(syn::LitBool { value, .. }), + .. + }), + ) if path.is_ident(&"lazy_registration") => lazy_registration_opt = Some(value), + _ => abort_call_site!(UNSUPPORTED_EXPRESSION_MSG), + }; + } + _ => abort_call_site!(WRONG_EXPRESSION_MSG), + }; + } + + let (plugin_ty, lazy_registration) = match (plugin_type_opt, lazy_registration_opt) { + (Some(type_plugin), lazy_registration_opt) => ( + type_plugin.into_token_stream(), + lazy_registration_opt.unwrap_or_default(), + ), + (None, lazy_registration_opt) => ( + quote!(#crate_ident::TypeModule), + lazy_registration_opt.unwrap_or_default(), + ), + }; + + // The following implementations follows the lifecycle of plugins and of dynamic types (see [`TypePluginExt::unuse`]). + // An object subclass can be reregistered as a dynamic type (see [`TypePluginExt::register_type`]). + let register_type = if lazy_registration { + // registers the object subclass as a dynamic type on the first use (lazy registration). + // a weak reference on the plugin is stored and will be used later on the first use of the object subclass. + // this implementation relies on a static storage of a weak reference on the plugin and of the glib type to know if the object subclass has been registered. + quote! { + impl #self_ty { + /// Returns a mutable reference to the registration status: a tuple of the weak reference on the plugin and of the glib type. + /// This is safe because the mutable reference guarantees that no other threads are concurrently accessing the data. + #[inline] + fn get_registration_status_ref_mut() -> &'static mut Option<(<#plugin_ty as #crate_ident::clone::Downgrade>::Weak, #crate_ident::Type)> { + static mut REGISTRATION_STATUS: ::std::sync::Mutex::Weak, #crate_ident::Type)>> = ::std::sync::Mutex::new(None); + unsafe { REGISTRATION_STATUS.get_mut().unwrap() } + } + + /// Registers the object subclass as a dynamic type within the plugin only once. + /// Plugin must have been used at least once. + /// Do nothing if plugin has never been used or if the object subclass is already registered as a dynamic type. + #[inline] + fn register_type() { + let registration_status_ref_mut = Self::get_registration_status_ref_mut(); + match registration_status_ref_mut { + // plugin has never been used, so the object subclass cannot be registered as a dynamic type. + None => (), + // plugin has been used and the object subclass has not been registered yet, so registers it as a dynamic type. + Some((type_plugin, type_)) if !type_.is_valid() => { + *type_ = #crate_ident::subclass::register_dynamic_type::<#plugin_ty, Self>(&(type_plugin.upgrade().unwrap())); + }, + // plugin has been used and the object subclass has already been registered as a dynamic type. + Some(_) => () + } + } + + /// Depending on the plugin lifecycle state and on the registration status of the object subclass: + /// If plugin is used (and has loaded the implementation) for the first time, postpones the registration and stores a weak reference on the plugin. + /// If plugin is reused (and has reloaded the implementation) and the object subclass has been already registered as a dynamic type, reregisters it. + /// An object subclass can be reregistered several times as a dynamic type. + /// If plugin is reused (and has reloaded the implementation) and the object subclass has not been registered yet as a dynamic type, do nothing. + #[inline] + pub fn on_implementation_load(type_plugin: &#plugin_ty) -> bool { + let registration_status_ref_mut = Self::get_registration_status_ref_mut(); + match registration_status_ref_mut { + // plugin has never been used (this is the first time), so postpones registration of the object subclass as a dynamic type on the first use. + None => { + *registration_status_ref_mut = Some((#crate_ident::clone::Downgrade::downgrade(type_plugin), #crate_ident::Type::INVALID)); + true + }, + // plugin has been used at least one time and the object subclass has been registered as a dynamic type at least one time, so re-registers it. + Some((_, type_)) if type_.is_valid() => { + *type_ = #crate_ident::subclass::register_dynamic_type::<#plugin_ty, Self>(type_plugin); + type_.is_valid() + }, + // plugin has been used at least one time but the object subclass has not been registered yet as a dynamic type, so keeps postponed registration. + Some(_) => { + true + } + } + } + + /// Depending on the plugin lifecycle state and on the registration status of the object subclass: + /// If plugin has been used (or reused) but the object subclass has not been registered yet as a dynamic type, cancels the postponed registration by deleting the weak reference on the plugin. + /// Else do nothing. + #[inline] + pub fn on_implementation_unload(type_plugin_: &#plugin_ty) -> bool { + let registration_status_ref_mut = Self::get_registration_status_ref_mut(); + match registration_status_ref_mut { + // plugin has never been used, so unload implementation is unexpected. + None => false, + // plugin has been used at least one time and the object subclass has been registered as a dynamic type at least one time. + Some((_, type_)) if type_.is_valid() => true, + // plugin has been used at least one time but the object subclass has not been registered yet as a dynamic type, so cancels the postponed registration. + Some(_) => { + *registration_status_ref_mut = None; + true + } + } + } + } + } + } else { + // registers immediately the object subclass as a dynamic type. + quote! { + impl #self_ty { + /// Do nothing as the object subclass has been registered on implementation load. + #[inline] + fn register_type() { } + + /// Registers the object subclass as a dynamic type within the plugin. + /// The object subclass can be registered several times as a dynamic type. + #[inline] + pub fn on_implementation_load(type_plugin: &#plugin_ty) -> bool { + let type_ = #crate_ident::subclass::register_dynamic_type::<#plugin_ty, Self>(type_plugin); + type_ != #crate_ident::Type::INVALID + } + + /// Do nothing as object subclasses registered as dynamic types are never unregistered. + #[inline] + pub fn on_implementation_unload(type_plugin_: &#plugin_ty) -> bool { + true + } + } + } + }; + + impl_object_subclass_(register_type, input) +} + +fn impl_object_subclass_(register_type: TokenStream, input: &syn::ItemImpl) -> TokenStream { let mut has_new = false; let mut has_parent_type = false; let mut has_interfaces = false; @@ -101,11 +283,7 @@ pub fn impl_object_subclass(input: &syn::ItemImpl) -> TokenStream { #[inline] fn type_() -> #crate_ident::Type { - static ONCE: ::std::sync::Once = ::std::sync::Once::new(); - - ONCE.call_once(|| { - #crate_ident::subclass::register_type::(); - }); + Self::register_type(); unsafe { let data = Self::type_data(); @@ -116,6 +294,8 @@ pub fn impl_object_subclass(input: &syn::ItemImpl) -> TokenStream { } } + #register_type + #[doc(hidden)] impl #crate_ident::subclass::types::FromObject for #self_ty { type FromObjectType = ::Type; diff --git a/glib-macros/tests/dynamic_objects.rs b/glib-macros/tests/dynamic_objects.rs new file mode 100644 index 000000000000..cbc432240225 --- /dev/null +++ b/glib-macros/tests/dynamic_objects.rs @@ -0,0 +1,606 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use glib::{subclass::prelude::*, Cast}; + +mod static_ { + use super::*; + + pub mod imp { + use super::*; + + // impl for an object interface to register as a static type. + #[derive(Clone, Copy)] + #[repr(C)] + pub struct MyStaticInterface { + parent: glib::gobject_ffi::GTypeInterface, + } + + #[glib::object_interface] + unsafe impl ObjectInterface for MyStaticInterface { + const NAME: &'static str = "MyStaticInterface"; + } + + pub trait MyStaticInterfaceImpl: ObjectImpl + ObjectSubclass {} + + // impl for an object subclass to register as a static type and that implements `MyStaticInterface`. + #[derive(Default)] + pub struct MyStaticType; + + #[glib::object_subclass] + impl ObjectSubclass for MyStaticType { + const NAME: &'static str = "MyStaticType"; + type Type = super::MyStaticType; + type Interfaces = (super::MyStaticInterface,); + } + + impl ObjectImpl for MyStaticType {} + + impl MyStaticInterfaceImpl for MyStaticType {} + + pub trait MyStaticTypeImpl: ObjectImpl + ObjectSubclass {} + } + + // an object interface to register as a static type. + glib::wrapper! { + pub struct MyStaticInterface(ObjectInterface); + } + + unsafe impl IsImplementable for MyStaticInterface {} + + // an object subclass to register as a static type and that implements `MyStaticInterface`. + glib::wrapper! { + pub struct MyStaticType(ObjectSubclass) @implements MyStaticInterface; + } + + unsafe impl IsSubclassable for MyStaticType {} +} + +use static_::{ + imp::{MyStaticInterfaceImpl, MyStaticTypeImpl}, + *, +}; + +mod module { + use super::*; + + mod imp { + use super::*; + + // impl for a object interface to register as a dynamic type and that extends `MyStaticInterface`. + #[derive(Clone, Copy)] + #[repr(C)] + pub struct MyModuleInterface { + parent: glib::gobject_ffi::GTypeInterface, + } + + #[glib::dynamic_object_interface] + unsafe impl ObjectInterface for MyModuleInterface { + const NAME: &'static str = "MyModuleInterface"; + type Prerequisites = (MyStaticInterface,); + } + + pub trait MyModuleInterfaceImpl: ObjectImpl + ObjectSubclass {} + + // impl for an object subclass to register as a dynamic type and that extends `MyStaticType` and that implements `MyStaticInterface` and `MyModuleInterface`. + #[derive(Default)] + pub struct MyModuleType; + + #[glib::dynamic_object_subclass] + impl ObjectSubclass for MyModuleType { + const NAME: &'static str = "MyModuleType"; + type Type = super::MyModuleType; + type ParentType = MyStaticType; + type Interfaces = (MyStaticInterface, super::MyModuleInterface); + } + + impl ObjectImpl for MyModuleType {} + + impl MyStaticTypeImpl for MyModuleType {} + + impl MyStaticInterfaceImpl for MyModuleType {} + + impl MyModuleInterfaceImpl for MyModuleType {} + + // impl for an object interface to lazy register as a dynamic type and that extends `MyStaticInterface`. + #[derive(Clone, Copy)] + #[repr(C)] + pub struct MyModuleInterfaceLazy { + parent: glib::gobject_ffi::GTypeInterface, + } + + #[glib::dynamic_object_interface(lazy_registration = true)] + unsafe impl ObjectInterface for MyModuleInterfaceLazy { + const NAME: &'static str = "MyModuleInterfaceLazy"; + type Prerequisites = (MyStaticInterface,); + } + + pub trait MyModuleInterfaceLazyImpl: ObjectImpl + ObjectSubclass {} + + // impl for an object subclass to lazy register as a dynamic type and that extends `MyStaticType` and that implements `MyStaticInterface` and `MyModuleInterfaceLazy`. + #[derive(Default)] + pub struct MyModuleTypeLazy; + + #[glib::dynamic_object_subclass(lazy_registration = true)] + impl ObjectSubclass for MyModuleTypeLazy { + const NAME: &'static str = "MyModuleTypeLazy"; + type Type = super::MyModuleTypeLazy; + type ParentType = MyStaticType; + type Interfaces = (MyStaticInterface, super::MyModuleInterfaceLazy); + } + + impl ObjectImpl for MyModuleTypeLazy {} + + impl MyStaticTypeImpl for MyModuleTypeLazy {} + + impl MyStaticInterfaceImpl for MyModuleTypeLazy {} + + impl MyModuleInterfaceLazyImpl for MyModuleTypeLazy {} + + // impl for a type module (must extend `glib::TypeModule` and must implement `glib::TypePlugin`). + #[derive(Default)] + pub struct MyModule; + + #[glib::object_subclass] + impl ObjectSubclass for MyModule { + const NAME: &'static str = "MyModule"; + type Type = super::MyModule; + type ParentType = glib::TypeModule; + type Interfaces = (glib::TypePlugin,); + } + + impl ObjectImpl for MyModule {} + + impl TypePluginImpl for MyModule {} + + impl TypeModuleImpl for MyModule { + fn load(&self) -> bool { + // registers object subclasses and interfaces as dynamic types. + let my_module = self.obj(); + let type_module: &glib::TypeModule = my_module.upcast_ref(); + MyModuleInterface::on_implementation_load(type_module) + && MyModuleType::on_implementation_load(type_module) + && MyModuleInterfaceLazy::on_implementation_load(type_module) + && MyModuleTypeLazy::on_implementation_load(type_module) + } + + fn unload(&self) { + // marks object subclasses and interfaces as unregistered. + let my_module = self.obj(); + let type_module: &glib::TypeModule = my_module.upcast_ref(); + MyModuleTypeLazy::on_implementation_unload(type_module); + MyModuleInterfaceLazy::on_implementation_unload(type_module); + MyModuleType::on_implementation_unload(type_module); + MyModuleInterface::on_implementation_unload(type_module); + } + } + } + + // an object interface to register as a dynamic type and that extends `MyStaticInterface`. + glib::wrapper! { + pub struct MyModuleInterface(ObjectInterface) @requires MyStaticInterface; + } + + unsafe impl IsImplementable for MyModuleInterface {} + + // an object subclass to register as a dynamic type and that extends `MyStaticType` and that implements `MyStaticInterface` and `MyModuleInterface`. + glib::wrapper! { + pub struct MyModuleType(ObjectSubclass) @extends MyStaticType, @implements MyStaticInterface, MyModuleInterface; + } + + // an object interface to lazy register as a dynamic type and that extends `MyStaticInterface`. + glib::wrapper! { + pub struct MyModuleInterfaceLazy(ObjectInterface) @requires MyStaticInterface; + } + + unsafe impl IsImplementable for MyModuleInterfaceLazy {} + + // an object subclass to lazy register as a dynamic type and that extends `MyStaticType` that implements `MyStaticInterface` and `MyModuleInterfaceLazy`. + glib::wrapper! { + pub struct MyModuleTypeLazy(ObjectSubclass) @extends MyStaticType, @implements MyStaticInterface, MyModuleInterfaceLazy; + } + + // a module (must extend `glib::TypeModule` and must implement `glib::TypePlugin`). + glib::wrapper! { + pub struct MyModule(ObjectSubclass) + @extends glib::TypeModule, @implements glib::TypePlugin; + } + + #[test] + fn dynamic_types() { + use glib::prelude::TypeModuleExt; + + // checks types of object subclasses and of object interfaces to register as dynamic types are invalid (module is not loaded yet). + assert!(!imp::MyModuleInterface::type_().is_valid()); + assert!(!imp::MyModuleType::type_().is_valid()); + assert!(!imp::MyModuleInterfaceLazy::type_().is_valid()); + assert!(!imp::MyModuleTypeLazy::type_().is_valid()); + + // simulates the glib type system to load/unload the module. + let module = glib::Object::new::(); + TypeModuleExt::use_(&module); + TypeModuleExt::unuse(&module); + + // checks types of object subclasses and of object interfaces registered as dynamic types are valid (module is unloaded). + assert!(imp::MyModuleInterface::type_().is_valid()); + assert!(imp::MyModuleType::type_().is_valid()); + // checks types of object subclasses and of object interfaces that are lazy registered as dynamic types are still invalid (module is unloaded). + assert!(!imp::MyModuleInterfaceLazy::type_().is_valid()); + assert!(!imp::MyModuleTypeLazy::type_().is_valid()); + + // simulates the glib type system to load the module. + TypeModuleExt::use_(&module); + + // checks types of object subclasses and of object interfaces registered as dynamic types are valid (module is loaded). + let iface_type = imp::MyModuleInterface::type_(); + assert!(iface_type.is_valid()); + let obj_type = imp::MyModuleType::type_(); + assert!(obj_type.is_valid()); + let iface_lazy_type = imp::MyModuleInterfaceLazy::type_(); + assert!(iface_lazy_type.is_valid()); + let obj_lazy_type = imp::MyModuleTypeLazy::type_(); + assert!(obj_lazy_type.is_valid()); + + // checks plugin of object subclasses and of object interfaces is `MyModule`. + assert_eq!( + iface_type.plugin().as_ref(), + Some(module.upcast_ref::()) + ); + assert_eq!( + obj_type.plugin().as_ref(), + Some(module.upcast_ref::()) + ); + assert_eq!( + iface_lazy_type.plugin().as_ref(), + Some(module.upcast_ref::()) + ); + assert_eq!( + obj_lazy_type.plugin().as_ref(), + Some(module.upcast_ref::()) + ); + + // simulates the glib type system to unload the module. + TypeModuleExt::unuse(&module); + + // checks types of object subclasses and of object interfaces registered as dynamic types are still valid (should have been marked as unloaded by the glib type system but this cannot be checked). + assert!(imp::MyModuleInterface::type_().is_valid()); + assert!(imp::MyModuleType::type_().is_valid()); + assert!(imp::MyModuleInterfaceLazy::type_().is_valid()); + assert!(imp::MyModuleTypeLazy::type_().is_valid()); + + // simulates the glib type system to reload the module. + TypeModuleExt::use_(&module); + + // checks types of object subclasses and of object interfaces registered as dynamic types are still valid (should have been marked as unloaded by the glib type system but this cannot be checked). + assert!(imp::MyModuleInterface::type_().is_valid()); + assert!(imp::MyModuleType::type_().is_valid()); + assert!(imp::MyModuleInterfaceLazy::type_().is_valid()); + assert!(imp::MyModuleTypeLazy::type_().is_valid()); + + // simulates the glib type system to unload the module. + TypeModuleExt::unuse(&module); + } +} + +pub mod plugin { + use super::*; + + pub mod imp { + use super::*; + use std::cell::Cell; + + // impl for a object interface to register as a dynamic type and that extends `MyStaticInterface`. + #[derive(Clone, Copy)] + #[repr(C)] + pub struct MyPluginInterface { + parent: glib::gobject_ffi::GTypeInterface, + } + + #[glib::dynamic_object_interface(plugin_type = super::MyPlugin)] + unsafe impl ObjectInterface for MyPluginInterface { + const NAME: &'static str = "MyPluginInterface"; + type Prerequisites = (MyStaticInterface,); + } + + pub trait MyPluginInterfaceImpl: ObjectImpl + ObjectSubclass {} + + // impl for an object subclass to register as a dynamic type and that extends `MyStaticType` and that implements `MyStaticInterface` and `MyPluginInterface`. + #[derive(Default)] + pub struct MyPluginType; + + #[glib::dynamic_object_subclass(plugin_type = super::MyPlugin)] + impl ObjectSubclass for MyPluginType { + const NAME: &'static str = "MyPluginType"; + type Type = super::MyPluginType; + type ParentType = MyStaticType; + type Interfaces = (MyStaticInterface, super::MyPluginInterface); + } + + impl ObjectImpl for MyPluginType {} + + impl MyStaticTypeImpl for MyPluginType {} + + impl MyStaticInterfaceImpl for MyPluginType {} + + impl MyPluginInterfaceImpl for MyPluginType {} + + // impl for an object interface to lazy register as a dynamic type and that extends `MyStaticInterface`. + #[derive(Clone, Copy)] + #[repr(C)] + pub struct MyPluginInterfaceLazy { + parent: glib::gobject_ffi::GTypeInterface, + } + + #[glib::dynamic_object_interface(plugin_type = super::MyPlugin, lazy_registration = true)] + unsafe impl ObjectInterface for MyPluginInterfaceLazy { + const NAME: &'static str = "MyPluginInterfaceLazy"; + type Prerequisites = (MyStaticInterface,); + } + + pub trait MyPluginInterfaceLazyImpl: ObjectImpl + ObjectSubclass {} + + // impl for an object subclass to lazy register as a dynamic type and that extends `MyStaticType` and that implements `MyStaticInterface` and `MyPluginInterfaceLazy`. + #[derive(Default)] + pub struct MyPluginTypeLazy; + + #[glib::dynamic_object_subclass(plugin_type = super::MyPlugin, lazy_registration = true)] + impl ObjectSubclass for MyPluginTypeLazy { + const NAME: &'static str = "MyPluginTypeLazy"; + type Type = super::MyPluginTypeLazy; + type ParentType = MyStaticType; + type Interfaces = (MyStaticInterface, super::MyPluginInterfaceLazy); + } + + impl ObjectImpl for MyPluginTypeLazy {} + + impl MyStaticTypeImpl for MyPluginTypeLazy {} + + impl MyStaticInterfaceImpl for MyPluginTypeLazy {} + + impl MyPluginInterfaceLazyImpl for MyPluginTypeLazy {} + + // impl for a type plugin (must implement `glib::TypePlugin`). + #[derive(Default)] + pub struct MyPlugin { + my_interface_type_info: Cell>, + my_interface_iface_info: Cell>, + my_type_type_info: Cell>, + my_interface_lazy_type_info: Cell>, + my_interface_lazy_iface_info: Cell>, + my_type_lazy_type_info: Cell>, + } + + #[glib::object_subclass] + impl ObjectSubclass for MyPlugin { + const NAME: &'static str = "MyPlugin"; + type Type = super::MyPlugin; + type Interfaces = (glib::TypePlugin,); + } + + impl ObjectImpl for MyPlugin {} + + impl TypePluginImpl for MyPlugin { + fn use_plugin(&self) { + // registers object subclasses and interfaces as dynamic types. + let my_plugin = self.obj(); + MyPluginInterface::on_implementation_load(my_plugin.as_ref()); + MyPluginType::on_implementation_load(my_plugin.as_ref()); + MyPluginInterfaceLazy::on_implementation_load(my_plugin.as_ref()); + MyPluginTypeLazy::on_implementation_load(my_plugin.as_ref()); + } + + fn unuse_plugin(&self) { + // marks object subclasses and interfaces as unregistered. + let my_plugin = self.obj(); + MyPluginTypeLazy::on_implementation_unload(my_plugin.as_ref()); + MyPluginInterfaceLazy::on_implementation_unload(my_plugin.as_ref()); + MyPluginType::on_implementation_unload(my_plugin.as_ref()); + MyPluginInterface::on_implementation_unload(my_plugin.as_ref()); + } + + fn complete_type_info( + &self, + type_: glib::Type, + ) -> (glib::TypeInfo, glib::TypeValueTable) { + let info = match type_ { + type_ if type_ == MyPluginType::type_() => self.my_type_type_info.get(), + type_ if type_ == MyPluginInterface::type_() => { + self.my_interface_type_info.get() + } + type_ if type_ == MyPluginTypeLazy::type_() => { + self.my_type_lazy_type_info.get() + } + type_ if type_ == MyPluginInterfaceLazy::type_() => { + self.my_interface_lazy_type_info.get() + } + _ => panic!("unexpected"), + }; + match info { + Some(info) => (info, glib::TypeValueTable::default()), + _ => panic!("unexpected"), + } + } + + fn complete_interface_info( + &self, + _instance_type: glib::Type, + interface_type: glib::Type, + ) -> glib::InterfaceInfo { + let info = match interface_type { + type_ if type_ == MyPluginInterface::type_() => { + self.my_interface_iface_info.get() + } + type_ if type_ == MyPluginInterfaceLazy::type_() => { + self.my_interface_lazy_iface_info.get() + } + _ => panic!("unexpected"), + }; + match info { + Some(info) => info, + _ => panic!("unexpected"), + } + } + } + + impl TypePluginRegisterImpl for MyPlugin { + fn add_dynamic_interface( + &self, + instance_type: glib::Type, + interface_type: glib::Type, + interface_info: &glib::InterfaceInfo, + ) { + if !instance_type.is_a(interface_type) { + instance_type.add_interface_dynamic( + interface_type, + self.obj().upcast_ref::(), + ); + } + match interface_type { + type_ if type_ == imp::MyPluginInterface::type_() => { + self.my_interface_iface_info.set(Some(*interface_info)) + } + type_ if type_ == imp::MyPluginInterfaceLazy::type_() => { + self.my_interface_lazy_iface_info.set(Some(*interface_info)) + } + _ => panic!("unexpected"), + }; + } + + fn register_dynamic_type( + &self, + parent_type: glib::Type, + type_name: &str, + type_info: &glib::TypeInfo, + flags: glib::TypeFlags, + ) -> glib::Type { + let type_ = glib::Type::from_name(type_name).unwrap_or_else(|| { + glib::Type::register_dynamic( + parent_type, + type_name, + self.obj().upcast_ref::(), + flags, + ) + }); + if type_.is_valid() { + match type_name { + imp::MyPluginType::NAME => self.my_type_type_info.set(Some(*type_info)), + imp::MyPluginInterface::NAME => { + self.my_interface_type_info.set(Some(*type_info)) + } + imp::MyPluginTypeLazy::NAME => { + self.my_type_lazy_type_info.set(Some(*type_info)) + } + imp::MyPluginInterfaceLazy::NAME => { + self.my_interface_lazy_type_info.set(Some(*type_info)) + } + _ => panic!("unexpected"), + }; + } + type_ + } + } + } + + // an object interface to register as a dynamic type and that extends `MyStaticInterface`. + glib::wrapper! { + pub struct MyPluginInterface(ObjectInterface) @requires MyStaticInterface; + } + + unsafe impl IsImplementable for MyPluginInterface {} + + // an object subclass to register as a dynamic type and that extends `MyStaticType` and that implements `MyStaticInterface` and `MyPluginInterface`. + glib::wrapper! { + pub struct MyPluginType(ObjectSubclass) @implements MyPluginInterface; + } + + // an object interface to lazy register as a dynamic type and that extends `MyStaticInterface`. + glib::wrapper! { + pub struct MyPluginInterfaceLazy(ObjectInterface) @requires MyStaticInterface; + } + + unsafe impl IsImplementable for MyPluginInterfaceLazy {} + + // an object subclass to lazy register as a dynamic type and that extends `MyStaticType` that implements `MyStaticInterface` and `MyPluginInterfaceLazy`. + glib::wrapper! { + pub struct MyPluginTypeLazy(ObjectSubclass) @implements MyPluginInterfaceLazy; + } + + // a plugin (must implement `glib::TypePlugin`). + glib::wrapper! { + pub struct MyPlugin(ObjectSubclass) @implements glib::TypePlugin; + } + + #[test] + fn dynamic_types() { + use glib::prelude::TypePluginExt; + + // checks types of object subclasses and of object interfaces to register as dynamic types are invalid (plugin is not used yet). + assert!(!imp::MyPluginInterface::type_().is_valid()); + assert!(!imp::MyPluginType::type_().is_valid()); + assert!(!imp::MyPluginInterfaceLazy::type_().is_valid()); + assert!(!imp::MyPluginTypeLazy::type_().is_valid()); + + // simulates the glib type system to use/unuse the plugin. + let plugin = glib::Object::new::(); + TypePluginExt::use_(&plugin); + TypePluginExt::unuse(&plugin); + + // checks types of object subclasses and of object interfaces registered as dynamic types are valid (plugin is unused). + assert!(imp::MyPluginInterface::type_().is_valid()); + assert!(imp::MyPluginType::type_().is_valid()); + // check types of object subclasses and of object interfaces that are lazy registered as dynamic types are still invalid (plugin is unused) + assert!(!imp::MyPluginInterfaceLazy::type_().is_valid()); + assert!(!imp::MyPluginTypeLazy::type_().is_valid()); + + // simulates the glib type system to use the plugin. + TypePluginExt::use_(&plugin); + + // checks types of object subclasses and of object interfaces registered as dynamic types are valid (plugin is used). + let iface_type = imp::MyPluginInterface::type_(); + assert!(iface_type.is_valid()); + let obj_type = imp::MyPluginType::type_(); + assert!(obj_type.is_valid()); + let iface_lazy_type = imp::MyPluginInterfaceLazy::type_(); + assert!(iface_lazy_type.is_valid()); + let obj_lazy_type = imp::MyPluginTypeLazy::type_(); + assert!(obj_lazy_type.is_valid()); + + // checks plugin of object subclasses and of object interfaces is `MyPlugin`. + assert_eq!( + iface_type.plugin().as_ref(), + Some(plugin.upcast_ref::()) + ); + assert_eq!( + obj_type.plugin().as_ref(), + Some(plugin.upcast_ref::()) + ); + assert_eq!( + iface_lazy_type.plugin().as_ref(), + Some(plugin.upcast_ref::()) + ); + assert_eq!( + obj_lazy_type.plugin().as_ref(), + Some(plugin.upcast_ref::()) + ); + + // simulates the glib type system to unuse the plugin. + TypePluginExt::unuse(&plugin); + + // checks types of object subclasses and of object interfaces registered as dynamic types are still valid. + assert!(imp::MyPluginInterface::type_().is_valid()); + assert!(imp::MyPluginType::type_().is_valid()); + assert!(imp::MyPluginInterfaceLazy::type_().is_valid()); + assert!(imp::MyPluginTypeLazy::type_().is_valid()); + + // simulates the glib type system to reuse the plugin. + TypePluginExt::use_(&plugin); + + // checks types of object subclasses and of object interfaces registered as dynamic types are still valid. + assert!(imp::MyPluginInterface::type_().is_valid()); + assert!(imp::MyPluginType::type_().is_valid()); + assert!(imp::MyPluginInterfaceLazy::type_().is_valid()); + assert!(imp::MyPluginTypeLazy::type_().is_valid()); + + // simulates the glib type system to unuse the plugin. + TypePluginExt::unuse(&plugin); + } +} diff --git a/glib/Gir_GObject.toml b/glib/Gir_GObject.toml index 8b6845669d74..7de9527f73a4 100644 --- a/glib/Gir_GObject.toml +++ b/glib/Gir_GObject.toml @@ -14,6 +14,7 @@ girs_directories = ["../gir-files"] generate = [ "GObject.BindingFlags", "GObject.SignalFlags", + "GObject.TypeFlags", ] ignore = [ @@ -23,6 +24,7 @@ manual = [ "GLib.Quark", "GObject.Object", "GObject.Value", + "GObject.TypeValueTable", "GObject.ParamFlags", "GObject.ParamSpec", "GObject.ParamSpecChar", @@ -48,6 +50,10 @@ manual = [ "GObject.ParamSpecOverride", "GObject.ParamSpecGType", "GObject.ParamSpecVariant", + "GObject.InterfaceInfo", + "GObject.TypeInfo", + "GObject.TypePlugin", + "GObject.TypeModule", ] [[object]] diff --git a/glib/src/gobject/auto/flags.rs b/glib/src/gobject/auto/flags.rs index b2f870c463e0..d0e726795fc6 100644 --- a/glib/src/gobject/auto/flags.rs +++ b/glib/src/gobject/auto/flags.rs @@ -137,3 +137,38 @@ impl FromGlib for SignalFlags { Self::from_bits_truncate(value) } } + +bitflags! { + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] + #[doc(alias = "GTypeFlags")] + pub struct TypeFlags: u32 { + #[doc(alias = "G_TYPE_FLAG_NONE")] + const NONE = gobject_ffi::G_TYPE_FLAG_NONE as _; + #[doc(alias = "G_TYPE_FLAG_ABSTRACT")] + const ABSTRACT = gobject_ffi::G_TYPE_FLAG_ABSTRACT as _; + #[doc(alias = "G_TYPE_FLAG_VALUE_ABSTRACT")] + const VALUE_ABSTRACT = gobject_ffi::G_TYPE_FLAG_VALUE_ABSTRACT as _; + #[doc(alias = "G_TYPE_FLAG_FINAL")] + const FINAL = gobject_ffi::G_TYPE_FLAG_FINAL as _; + #[doc(alias = "G_TYPE_FLAG_DEPRECATED")] + const DEPRECATED = gobject_ffi::G_TYPE_FLAG_DEPRECATED as _; + } +} + +#[doc(hidden)] +impl IntoGlib for TypeFlags { + type GlibType = gobject_ffi::GTypeFlags; + + #[inline] + fn into_glib(self) -> gobject_ffi::GTypeFlags { + self.bits() + } +} + +#[doc(hidden)] +impl FromGlib for TypeFlags { + #[inline] + unsafe fn from_glib(value: gobject_ffi::GTypeFlags) -> Self { + Self::from_bits_truncate(value) + } +} diff --git a/glib/src/gobject/auto/mod.rs b/glib/src/gobject/auto/mod.rs index dbd122a8a0b5..504417e5be1f 100644 --- a/glib/src/gobject/auto/mod.rs +++ b/glib/src/gobject/auto/mod.rs @@ -22,3 +22,4 @@ pub use self::signal_group::SignalGroup; mod flags; pub use self::flags::BindingFlags; pub use self::flags::SignalFlags; +pub use self::flags::TypeFlags; diff --git a/glib/src/gobject/dynamic_object.rs b/glib/src/gobject/dynamic_object.rs new file mode 100644 index 000000000000..e8f3cc4f06c6 --- /dev/null +++ b/glib/src/gobject/dynamic_object.rs @@ -0,0 +1,75 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use crate::{ + prelude::*, subclass::prelude::*, InterfaceInfo, IsA, TypeFlags, TypeInfo, TypeModule, + TypePlugin, +}; + +mod sealed { + pub trait Sealed {} + impl> Sealed for T {} +} + +pub trait DynamicObjectRegisterExt: AsRef + sealed::Sealed + 'static { + fn add_dynamic_interface( + &self, + instance_type: crate::types::Type, + interface_type: crate::types::Type, + interface_info: &InterfaceInfo, + ); + + fn register_dynamic_type( + &self, + parent_type: crate::types::Type, + type_name: &str, + type_info: &TypeInfo, + flags: TypeFlags, + ) -> crate::types::Type; +} + +impl + ObjectSubclassIsExt> DynamicObjectRegisterExt for O +where + O::Subclass: TypePluginRegisterImpl, +{ + fn add_dynamic_interface( + &self, + instance_type: crate::types::Type, + interface_type: crate::types::Type, + interface_info: &InterfaceInfo, + ) { + self.imp() + .add_dynamic_interface(instance_type, interface_type, interface_info); + } + + fn register_dynamic_type( + &self, + parent_type: crate::types::Type, + type_name: &str, + type_info: &TypeInfo, + flags: TypeFlags, + ) -> crate::types::Type { + self.imp() + .register_dynamic_type(parent_type, type_name, type_info, flags) + } +} + +impl DynamicObjectRegisterExt for TypeModule { + fn add_dynamic_interface( + &self, + instance_type: crate::types::Type, + interface_type: crate::types::Type, + interface_info: &InterfaceInfo, + ) { + ::add_interface(self, instance_type, interface_type, interface_info); + } + + fn register_dynamic_type( + &self, + parent_type: crate::types::Type, + type_name: &str, + type_info: &TypeInfo, + flags: TypeFlags, + ) -> crate::types::Type { + ::register_type(self, parent_type, type_name, type_info, flags) + } +} diff --git a/glib/src/gobject/interface_info.rs b/glib/src/gobject/interface_info.rs new file mode 100644 index 000000000000..14649e35e967 --- /dev/null +++ b/glib/src/gobject/interface_info.rs @@ -0,0 +1,38 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +#[derive(Debug, Copy, Clone)] +#[doc(alias = "GInterfaceInfo")] +#[repr(transparent)] +pub struct InterfaceInfo(pub(crate) gobject_ffi::GInterfaceInfo); + +impl InterfaceInfo { + // rustdoc-stripper-ignore-next + /// Returns a `GInterfaceInfo` pointer. + #[doc(hidden)] + #[inline] + pub fn as_ptr(&self) -> *mut gobject_ffi::GInterfaceInfo { + &self.0 as *const gobject_ffi::GInterfaceInfo as *mut _ + } + + // rustdoc-stripper-ignore-next + /// Borrows the underlying C value mutably. + #[doc(hidden)] + #[inline] + pub unsafe fn from_glib_ptr_borrow_mut<'a>( + ptr: *mut gobject_ffi::GInterfaceInfo, + ) -> &'a mut Self { + &mut *(ptr as *mut Self) + } +} + +impl Default for InterfaceInfo { + // rustdoc-stripper-ignore-next + /// Creates a new InterfaceInfo with default value. + fn default() -> Self { + Self(gobject_ffi::GInterfaceInfo { + interface_init: None, + interface_finalize: None, + interface_data: ::std::ptr::null_mut(), + }) + } +} diff --git a/glib/src/gobject/mod.rs b/glib/src/gobject/mod.rs index 274451c1b671..d1c03b4c097d 100644 --- a/glib/src/gobject/mod.rs +++ b/glib/src/gobject/mod.rs @@ -20,3 +20,27 @@ pub use binding_group::BindingGroupBuilder; pub use self::{auto::*, flags::*}; //pub use self::auto::functions::*; + +mod interface_info; +pub use interface_info::InterfaceInfo; + +mod type_info; +pub use type_info::TypeInfo; + +mod type_value_table; +pub use type_value_table::TypeValueTable; + +mod type_module; +pub use self::type_module::TypeModule; + +mod type_plugin; +pub use self::type_plugin::TypePlugin; + +mod dynamic_object; + +#[doc(hidden)] +pub mod traits { + pub use super::dynamic_object::DynamicObjectRegisterExt; + pub use super::type_module::TypeModuleExt; + pub use super::type_plugin::TypePluginExt; +} diff --git a/glib/src/gobject/type_info.rs b/glib/src/gobject/type_info.rs new file mode 100644 index 000000000000..0f81ffe15598 --- /dev/null +++ b/glib/src/gobject/type_info.rs @@ -0,0 +1,43 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +#[derive(Debug, Copy, Clone)] +#[doc(alias = "GTypeInfo")] +#[repr(transparent)] +pub struct TypeInfo(pub(crate) gobject_ffi::GTypeInfo); + +impl TypeInfo { + // rustdoc-stripper-ignore-next + /// Returns a `GTypeInfo` pointer. + #[doc(hidden)] + #[inline] + pub fn as_ptr(&self) -> *mut gobject_ffi::GTypeInfo { + &self.0 as *const gobject_ffi::GTypeInfo as *mut _ + } + + // rustdoc-stripper-ignore-next + /// Borrows the underlying C value mutably. + #[doc(hidden)] + #[inline] + pub unsafe fn from_glib_ptr_borrow_mut<'a>(ptr: *mut gobject_ffi::GTypeInfo) -> &'a mut Self { + &mut *(ptr as *mut Self) + } +} + +impl Default for TypeInfo { + // rustdoc-stripper-ignore-next + /// Creates a new TypeInfo with default value. + fn default() -> Self { + Self(gobject_ffi::GTypeInfo { + class_size: 0u16, + base_init: None, + base_finalize: None, + class_init: None, + class_finalize: None, + class_data: ::std::ptr::null(), + instance_size: 0, + n_preallocs: 0, + instance_init: None, + value_table: ::std::ptr::null(), + }) + } +} diff --git a/glib/src/gobject/type_module.rs b/glib/src/gobject/type_module.rs new file mode 100644 index 000000000000..6181d33137bb --- /dev/null +++ b/glib/src/gobject/type_module.rs @@ -0,0 +1,88 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use crate::{prelude::*, translate::*, InterfaceInfo, TypeFlags, TypeInfo, TypePlugin}; + +crate::wrapper! { + #[doc(alias = "GTypeModule")] + pub struct TypeModule(Object) @implements TypePlugin; + + match fn { + type_ => || gobject_ffi::g_type_module_get_type(), + } +} + +impl TypeModule { + pub const NONE: Option<&'static TypeModule> = None; +} + +mod sealed { + pub trait Sealed {} + impl> Sealed for T {} +} + +pub trait TypeModuleExt: IsA + sealed::Sealed + 'static { + #[doc(alias = "g_type_module_add_interface")] + fn add_interface( + &self, + instance_type: crate::types::Type, + interface_type: crate::types::Type, + interface_info: &InterfaceInfo, + ) { + unsafe { + gobject_ffi::g_type_module_add_interface( + self.as_ref().to_glib_none().0, + instance_type.into_glib(), + interface_type.into_glib(), + interface_info.as_ptr(), + ); + } + } + + #[doc(alias = "g_type_module_register_type")] + fn register_type( + &self, + parent_type: crate::types::Type, + type_name: &str, + type_info: &TypeInfo, + flags: TypeFlags, + ) -> crate::types::Type { + unsafe { + from_glib(gobject_ffi::g_type_module_register_type( + self.as_ref().to_glib_none().0, + parent_type.into_glib(), + type_name.to_glib_none().0, + type_info.as_ptr(), + flags.into_glib(), + )) + } + } + + #[doc(alias = "g_type_module_set_name")] + fn set_name(&self, name: &str) { + unsafe { + gobject_ffi::g_type_module_set_name( + self.as_ref().to_glib_none().0, + name.to_glib_none().0, + ); + } + } + + #[doc(alias = "g_type_module_unuse")] + fn unuse(&self) { + unsafe { + gobject_ffi::g_type_module_unuse(self.as_ref().to_glib_none().0); + } + } + + #[doc(alias = "g_type_module_use")] + #[doc(alias = "use")] + fn use_(&self) -> bool { + unsafe { + from_glib(gobject_ffi::g_type_module_use( + self.as_ref().to_glib_none().0, + )) + } + } +} + +impl> TypeModuleExt for O {} diff --git a/glib/src/gobject/type_plugin.rs b/glib/src/gobject/type_plugin.rs new file mode 100644 index 000000000000..c49a3eeaff58 --- /dev/null +++ b/glib/src/gobject/type_plugin.rs @@ -0,0 +1,73 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use crate::{prelude::*, translate::*, InterfaceInfo, TypeInfo, TypeValueTable}; + +crate::wrapper! { + #[doc(alias = "GTypePlugin")] + pub struct TypePlugin(Interface); + + match fn { + type_ => || gobject_ffi::g_type_plugin_get_type(), + } +} + +impl TypePlugin { + pub const NONE: Option<&'static TypePlugin> = None; +} + +mod sealed { + pub trait Sealed {} + impl> Sealed for T {} +} + +pub trait TypePluginExt: IsA + sealed::Sealed + 'static { + #[doc(alias = "g_type_plugin_complete_interface_info")] + fn complete_interface_info( + &self, + instance_type: crate::types::Type, + interface_type: crate::types::Type, + ) -> InterfaceInfo { + let info = InterfaceInfo::default(); + unsafe { + gobject_ffi::g_type_plugin_complete_interface_info( + self.as_ref().to_glib_none().0, + instance_type.into_glib(), + interface_type.into_glib(), + info.as_ptr(), + ); + } + info + } + + #[doc(alias = "g_type_plugin_complete_type_info")] + fn complete_type_info(&self, g_type: crate::types::Type) -> (TypeInfo, TypeValueTable) { + let info = TypeInfo::default(); + let value_table = TypeValueTable::default(); + unsafe { + gobject_ffi::g_type_plugin_complete_type_info( + self.as_ref().to_glib_none().0, + g_type.into_glib(), + info.as_ptr(), + value_table.as_ptr(), + ); + } + (info, value_table) + } + + #[doc(alias = "g_type_plugin_unuse")] + fn unuse(&self) { + unsafe { + gobject_ffi::g_type_plugin_unuse(self.as_ref().to_glib_none().0); + } + } + + #[doc(alias = "g_type_plugin_use")] + #[doc(alias = "use")] + fn use_(&self) { + unsafe { + gobject_ffi::g_type_plugin_use(self.as_ref().to_glib_none().0); + } + } +} + +impl> TypePluginExt for O {} diff --git a/glib/src/gobject/type_value_table.rs b/glib/src/gobject/type_value_table.rs new file mode 100644 index 000000000000..68797da7eeeb --- /dev/null +++ b/glib/src/gobject/type_value_table.rs @@ -0,0 +1,43 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +#[derive(Debug, Copy, Clone)] +#[doc(alias = "GTypeValueTable")] +#[repr(transparent)] +pub struct TypeValueTable(pub(crate) gobject_ffi::GTypeValueTable); + +impl TypeValueTable { + // rustdoc-stripper-ignore-next + /// Returns a `GTypeValueTable` pointer. + #[doc(hidden)] + #[inline] + pub fn as_ptr(&self) -> *mut gobject_ffi::GTypeValueTable { + &self.0 as *const gobject_ffi::GTypeValueTable as *mut _ + } + + // rustdoc-stripper-ignore-next + /// Borrows the underlying C value mutably. + #[doc(hidden)] + #[inline] + pub unsafe fn from_glib_ptr_borrow_mut<'a>( + ptr: *mut gobject_ffi::GTypeValueTable, + ) -> &'a mut Self { + &mut *(ptr as *mut Self) + } +} + +impl Default for TypeValueTable { + // rustdoc-stripper-ignore-next + /// Creates a new TypeValueTable with default value. + fn default() -> Self { + Self(gobject_ffi::GTypeValueTable { + value_init: None, + value_free: None, + value_copy: None, + value_peek_pointer: None, + collect_format: ::std::ptr::null(), + collect_value: None, + lcopy_format: ::std::ptr::null(), + lcopy_value: None, + }) + } +} diff --git a/glib/src/lib.rs b/glib/src/lib.rs index 4d5dbc017e1e..9fb4f3993464 100644 --- a/glib/src/lib.rs +++ b/glib/src/lib.rs @@ -9,8 +9,9 @@ pub use ffi; #[doc(hidden)] pub use glib_macros::cstr_bytes; pub use glib_macros::{ - clone, closure, closure_local, derived_properties, flags, object_interface, object_subclass, - Boxed, Downgrade, Enum, ErrorDomain, Properties, SharedBoxed, ValueDelegate, Variant, + clone, closure, closure_local, derived_properties, dynamic_object_interface, + dynamic_object_subclass, flags, object_interface, object_subclass, Boxed, Downgrade, Enum, + ErrorDomain, Properties, SharedBoxed, ValueDelegate, Variant, }; pub use gobject_ffi; pub use once_cell; @@ -115,7 +116,17 @@ pub use self::auto::*; #[allow(non_upper_case_globals)] mod auto; -pub use self::gobject::*; +#[cfg(feature = "v2_74")] +#[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))] +pub use self::gobject::SignalGroup; +pub use self::gobject::{ + Binding, BindingFlags, InterfaceInfo, ParamFlags, SignalFlags, TypeFlags, TypeInfo, TypeModule, + TypePlugin, TypeValueTable, +}; +#[cfg(feature = "v2_72")] +#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))] +pub use self::gobject::{BindingGroup, BindingGroupBuilder}; + mod gobject; mod byte_array; diff --git a/glib/src/prelude.rs b/glib/src/prelude.rs index 3ca66e4627bc..c719431361a0 100644 --- a/glib/src/prelude.rs +++ b/glib/src/prelude.rs @@ -4,6 +4,8 @@ //! Traits and essential types intended for blanket imports. pub use crate::{ - param_spec::ParamSpecBuilderExt, Cast, CastNone, IsA, ObjectClassExt, ObjectExt, ObjectType, - ParamSpecType, StaticType, StaticTypeExt, StaticVariantType, ToSendValue, ToValue, ToVariant, + gobject::traits::{DynamicObjectRegisterExt, TypeModuleExt, TypePluginExt}, + param_spec::ParamSpecBuilderExt, + Cast, CastNone, IsA, ObjectClassExt, ObjectExt, ObjectType, ParamSpecType, StaticType, + StaticTypeExt, StaticVariantType, ToSendValue, ToValue, ToVariant, }; diff --git a/glib/src/subclass/interface.rs b/glib/src/subclass/interface.rs index b87bfaebc1f1..e6e4991b4905 100644 --- a/glib/src/subclass/interface.rs +++ b/glib/src/subclass/interface.rs @@ -3,25 +3,25 @@ use std::{marker, mem}; use super::{InitializingType, Signal}; -use crate::{prelude::*, translate::*, Object, ParamSpec, Type}; +use crate::{prelude::*, translate::*, Object, ParamSpec, Type, TypeFlags, TypeInfo}; // 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,62 @@ pub fn register_interface() -> Type { type_ } } + +/// Registers a `glib::Type` ID for `T` 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 +/// more specifically [`TypeModuleImpl::load`]. Therefore, unlike for object +/// interfaces registered as static types, object interfaces registered as +/// dynamic types can be registered several times. +/// +/// The [`dynamic_object_interface!`] macro will create `register_interface()` +/// and `on_implementation_load()` functions around this, which will ensure +/// that the function is called when necessary. +/// +/// [`dynamic_object_interface!`]: ../../../glib_macros/attr.dynamic_object_interface.html +/// [`TypePluginImpl::use_`]: ../type_plugin/trait.TypePluginImpl.html#method.use_ +/// [`TypeModuleImpl::load`]: ../type_module/trait.TypeModuleImpl.html#method.load +pub fn register_dynamic_interface( + type_plugin: &P, +) -> 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(gobject_ffi::GTypeInfo { + class_size: mem::size_of::() as u16, + class_init: Some(interface_init::), + ..TypeInfo::default().0 + }); + + // registers the interface within the `type_plugin` + let type_ = type_plugin.register_dynamic_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..271a4a98c81e 100644 --- a/glib/src/subclass/mod.rs +++ b/glib/src/subclass/mod.rs @@ -197,7 +197,204 @@ //! } //! ``` //! -//! # Example for registering a boxed type for a Rust struct +//! # Example for registering a `glib::Object` subclass within a module +//! +//! The following code implements a subclass of `glib::Object` and registers it as +//! a dynamic type. +//! +//! ```rust +//! use glib::prelude::*; +//! use glib::subclass::prelude::*; +//! +//! pub mod imp { +//! use super::*; +//! +//! // SimpleModuleObject is a dynamic type. +//! #[derive(Default)] +//! pub struct SimpleModuleObject; +//! +//! #[glib::dynamic_object_subclass] +//! impl ObjectSubclass for SimpleModuleObject { +//! const NAME: &'static str = "SimpleModuleObject"; +//! type Type = super::SimpleModuleObject; +//! } +//! +//! impl ObjectImpl for SimpleModuleObject {} +//! +//! // SimpleTypeModule is the type module within the object subclass is registered as a dynamic type. +//! #[derive(Default)] +//! pub struct SimpleTypeModule; +//! +//! #[glib::object_subclass] +//! impl ObjectSubclass for SimpleTypeModule { +//! const NAME: &'static str = "SimpleTypeModule"; +//! type Type = super::SimpleTypeModule; +//! type ParentType = glib::TypeModule; +//! type Interfaces = (glib::TypePlugin,); +//! } +//! +//! impl ObjectImpl for SimpleTypeModule {} +//! +//! impl TypeModuleImpl for SimpleTypeModule { +//! /// Loads the module and registers the object subclass as a dynamic type. +//! fn load(&self) -> bool { +//! SimpleModuleObject::on_implementation_load(self.obj().upcast_ref::()) +//! } +//! +//! /// Unloads the module. +//! fn unload(&self) { +//! SimpleModuleObject::on_implementation_unload(self.obj().upcast_ref::()); +//! } +//! } +//! +//! impl TypePluginImpl for SimpleTypeModule {} +//! } +//! +//! // Optionally, defines a wrapper type to make SimpleModuleObject more ergonomic to use from Rust. +//! glib::wrapper! { +//! pub struct SimpleModuleObject(ObjectSubclass); +//! } +//! +//! // Optionally, defines a wrapper type to make SimpleTypeModule more ergonomic to use from Rust. +//! glib::wrapper! { +//! pub struct SimpleTypeModule(ObjectSubclass) +//! @extends glib::TypeModule, @implements glib::TypePlugin; +//! } +//! +//! impl SimpleTypeModule { +//! // Creates an object instance of the new type. +//! pub fn new() -> Self { +//! glib::Object::new() +//! } +//! } +//! +//! pub fn main() { +//! let simple_type_module = SimpleTypeModule::new(); +//! // at this step, SimpleTypeModule has not been loaded therefore +//! // SimpleModuleObject must not be registered yet. +//! let simple_module_object_type = imp::SimpleModuleObject::type_(); +//! assert!(!simple_module_object_type.is_valid()); +//! +//! // simulates the glib type system to load the module. +//! TypeModuleExt::use_(&simple_type_module); +//! +//! // at this step, SimpleModuleObject must have been registered. +//! let simple_module_object_type = imp::SimpleModuleObject::type_(); +//! assert!(simple_module_object_type.is_valid()); +//! } +//! ``` +//! +//! # Example for registering a `glib::Object` subclass within a plugin +//! +//! The following code implements a subclass of `glib::Object` and registers it as +//! a dynamic type. +//! +//! ```rust +//! use glib::prelude::*; +//! use glib::subclass::prelude::*; +//! +//! pub mod imp { +//! use super::*; +//! +//! // SimplePluginObject is a dynamic type. +//! #[derive(Default)] +//! pub struct SimplePluginObject; +//! +//! #[glib::dynamic_object_subclass(plugin_type = super::SimpleTypePlugin)] +//! impl ObjectSubclass for SimplePluginObject { +//! const NAME: &'static str = "SimplePluginObject"; +//! type Type = super::SimplePluginObject; +//! } +//! +//! impl ObjectImpl for SimplePluginObject {} +//! +//! // SimpleTypePlugin is the type plugin within the object subclass is registered as a dynamic type. +//! #[derive(Default)] +//! pub struct SimpleTypePlugin { +//! type_info: std::cell::Cell> +//! } +//! +//! #[glib::object_subclass] +//! impl ObjectSubclass for SimpleTypePlugin { +//! const NAME: &'static str = "SimpleTypePlugin"; +//! type Type = super::SimpleTypePlugin; +//! type Interfaces = (glib::TypePlugin,); +//! } +//! +//! impl ObjectImpl for SimpleTypePlugin {} +//! +//! impl TypePluginImpl for SimpleTypePlugin { +//! /// Uses the plugin and registers the object subclass as a dynamic type. +//! fn use_plugin(&self) { +//! SimplePluginObject::on_implementation_load(self.obj().as_ref()); +//! } +//! +//! /// Unuses the plugin. +//! fn unuse_plugin(&self) { +//! SimplePluginObject::on_implementation_unload(self.obj().as_ref()); +//! } +//! +//! /// Returns type information about the object subclass registered as a dynamic type. +//! fn complete_type_info(&self, _type_: glib::Type) -> (glib::TypeInfo, glib::TypeValueTable) { +//! assert!(self.type_info.get().is_some()); +//! // returns type info. +//! (self.type_info.get().unwrap(), glib::TypeValueTable::default()) +//! } +//! } +//! +//! impl TypePluginRegisterImpl for SimpleTypePlugin { +//! fn add_dynamic_interface(&self, _: glib::Type, _: glib::Type, _: &glib::InterfaceInfo) { +//! unimplemented!() +//! } +//! +//! fn register_dynamic_type(&self, parent_type: glib::Type, type_name: &str, type_info: &glib::TypeInfo, flags: glib::TypeFlags) -> glib::Type { +//! let type_ = glib::Type::from_name(type_name).unwrap_or_else(|| { +//! glib::Type::register_dynamic(parent_type, type_name, self.obj().upcast_ref::(), flags) +//! }); +//! if type_.is_valid() { +//! // saves type info. +//! self.type_info.set(Some(*type_info)); +//! } +//! type_ +//! } +//! } +//! } +//! +//! // Optionally, defines a wrapper type to make SimplePluginObject more ergonomic to use from Rust. +//! glib::wrapper! { +//! pub struct SimplePluginObject(ObjectSubclass); +//! } +//! +//! // Optionally, defines a wrapper type to make SimpleTypePlugin more ergonomic to use from Rust. +//! glib::wrapper! { +//! pub struct SimpleTypePlugin(ObjectSubclass) +//! @implements glib::TypePlugin; +//! } +//! +//! impl SimpleTypePlugin { +//! // Creates an object instance of the new type. +//! pub fn new() -> Self { +//! glib::Object::new() +//! } +//! } +//! +//! pub fn main() { +//! let simple_type_plugin = SimpleTypePlugin::new(); +//! // at this step, SimpleTypePlugin has not been used therefore +//! // SimplePluginObject must not be registered yet. +//! let simple_plugin_object_type = imp::SimplePluginObject::type_(); +//! assert!(!simple_plugin_object_type.is_valid()); +//! +//! // simulates the glib type system to use the plugin. +//! TypePluginExt::use_(&simple_type_plugin); +//! +//! // at this step, SimplePluginObject must have been registered. +//! let simple_plugin_object_type = imp::SimplePluginObject::type_(); +//! assert!(simple_plugin_object_type.is_valid()); +//! } +//! ``` +//! +//!//! # Example for registering a boxed type for a Rust struct //! //! The following code boxed type for a tuple struct around `String` and uses it in combination //! with `glib::Value`. @@ -241,6 +438,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 +450,8 @@ pub mod prelude { interface::{ObjectInterface, ObjectInterfaceExt, ObjectInterfaceType}, object::{DerivedObjectProperties, ObjectClassSubclassExt, ObjectImpl, ObjectImplExt}, shared::{RefCounted, SharedType}, + type_module::{TypeModuleImpl, TypeModuleImplExt}, + type_plugin::{TypePluginImpl, TypePluginImplExt, TypePluginRegisterImpl}, types::{ ClassStruct, InstanceStruct, InstanceStructExt, IsImplementable, IsSubclassable, IsSubclassableExt, ObjectSubclass, ObjectSubclassExt, ObjectSubclassIsExt, @@ -259,9 +462,9 @@ pub mod prelude { pub use self::{ boxed::register_boxed_type, - interface::register_interface, + interface::{register_dynamic_interface, register_interface}, signal::{ Signal, SignalClassHandlerToken, SignalId, SignalInvocationHint, SignalQuery, SignalType, }, - types::{register_type, InitializingObject, InitializingType, TypeData}, + types::{register_dynamic_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..bef50c6ad7a4 --- /dev/null +++ b/glib/src/subclass/type_module.rs @@ -0,0 +1,164 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use crate::{subclass::prelude::*, translate::*, Cast, TypeModule}; + +pub trait TypeModuleImpl: ObjectImpl + TypeModuleImplExt { + // rustdoc-stripper-ignore-next + /// Loads the module, registers one or more object subclasses using + /// [`register_dynamic_type`] and registers one or more object interfaces + /// using [`register_dynamic_interface`] (see [`TypeModule`]). + /// + /// [`register_dynamic_type`]: ../types/fn.register_dynamic_type.html + /// [`register_dynamic_interface`]: ../interface/fn.register_dynamic_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(); + // GLib type system expects a module to never be disposed if types has been + // successfully loaded. + // The following code prevents the Rust wrapper (`glib::TypeModule` subclass) + // to dispose the module when dropped by ensuring the reference count is > 1. + // Nothing is done if loading types has failed, allowing application to drop + // and dispose the invalid module. + if res && (*(type_module as *const gobject_ffi::GObject)).ref_count == 1 { + 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(); +} + +#[cfg(test)] +mod tests { + use crate::{self as glib, prelude::TypeModuleExt}; + + use super::*; + + mod imp { + use super::*; + + #[derive(Default)] + pub struct SimpleModule; + + #[crate::object_subclass] + impl ObjectSubclass for SimpleModule { + const NAME: &'static str = "SimpleModule"; + type Type = super::SimpleModule; + type ParentType = TypeModule; + type Interfaces = (crate::TypePlugin,); + } + + impl ObjectImpl for SimpleModule {} + + impl TypePluginImpl for SimpleModule {} + + impl TypeModuleImpl for SimpleModule { + fn load(&self) -> bool { + // register types on implementation load + SimpleModuleType::on_implementation_load(self.obj().upcast_ref::()) + } + + fn unload(&self) { + // unregister types on implementation unload + SimpleModuleType::on_implementation_unload(self.obj().upcast_ref::()); + } + } + + #[derive(Default)] + pub struct SimpleModuleType; + + #[crate::dynamic_object_subclass] + impl ObjectSubclass for SimpleModuleType { + const NAME: &'static str = "SimpleModuleType"; + type Type = super::SimpleModuleType; + } + + impl ObjectImpl for SimpleModuleType {} + } + + crate::wrapper! { + pub struct SimpleModule(ObjectSubclass) + @extends TypeModule, @implements crate::TypePlugin; + } + + crate::wrapper! { + pub struct SimpleModuleType(ObjectSubclass); + } + + #[test] + fn test_module() { + assert!(!imp::SimpleModuleType::type_().is_valid()); + let simple_module = glib::Object::new::(); + // simulates the glib type system to load the module. + assert!(simple_module.use_()); + assert!(imp::SimpleModuleType::type_().is_valid()); + simple_module.unuse(); + } +} diff --git a/glib/src/subclass/type_plugin.rs b/glib/src/subclass/type_plugin.rs new file mode 100644 index 000000000000..cda3581bb97e --- /dev/null +++ b/glib/src/subclass/type_plugin.rs @@ -0,0 +1,301 @@ +// 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}; +use crate::TypeFlags; +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) -> (TypeInfo, TypeValueTable) { + self.parent_complete_type_info(type_) + } + + fn complete_interface_info(&self, instance_type: Type, interface_type: Type) -> InterfaceInfo { + self.parent_complete_interface_info(instance_type, interface_type) + } +} + +pub trait TypePluginImplExt: ObjectSubclass { + fn parent_use_plugin(&self); + fn parent_unuse_plugin(&self); + fn parent_complete_type_info(&self, type_: Type) -> (TypeInfo, TypeValueTable); + fn parent_complete_interface_info( + &self, + instance_type: Type, + interface_type: Type, + ) -> 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) -> (TypeInfo, 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"); + + let info = TypeInfo::default(); + let value_table = TypeValueTable::default(); + f( + self.obj().unsafe_cast_ref::().to_glib_none().0, + type_.into_glib(), + info.as_ptr(), + value_table.as_ptr(), + ); + + (info, value_table) + } + } + + fn parent_complete_interface_info( + &self, + instance_type: Type, + interface_type: Type, + ) -> InterfaceInfo { + let info = InterfaceInfo::default(); + 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.as_ptr(), + ) + } + info + } +} + +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, +) { + assert!(!info_ptr.is_null()); + assert!(!value_table_ptr.is_null()); + 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); + + let (info_, value_table_) = imp.complete_type_info(type_); + + *info = info_; + *value_table = 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, +) { + assert!(!info_ptr.is_null()); + 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); + + let info_ = imp.complete_interface_info(instance_type, interface_type); + *info = info_; +} + +pub trait TypePluginRegisterImpl: ObjectImpl + TypePluginImpl { + fn add_dynamic_interface( + &self, + _instance_type: Type, + _interface_type: Type, + _interface_info: &InterfaceInfo, + ); + + fn register_dynamic_type( + &self, + _parent_type: Type, + _type_name: &str, + _type_info: &TypeInfo, + _flags: TypeFlags, + ) -> Type; +} + +#[cfg(test)] +mod tests { + use crate::{self as glib, prelude::TypePluginExt}; + + use super::*; + + mod imp { + use super::*; + + #[derive(Default)] + pub struct SimplePlugin { + type_info: std::cell::Cell>, + } + + #[crate::object_subclass] + impl ObjectSubclass for SimplePlugin { + const NAME: &'static str = "SimplePlugin"; + type Type = super::SimplePlugin; + type Interfaces = (TypePlugin,); + } + + impl ObjectImpl for SimplePlugin {} + + impl TypePluginImpl for SimplePlugin { + fn use_plugin(&self) { + // registers types on implementation load + SimplePluginType::on_implementation_load(self.obj().as_ref()); + } + + fn unuse_plugin(&self) { + // unregisters types on implementation unload + SimplePluginType::on_implementation_unload(self.obj().as_ref()); + } + + fn complete_type_info(&self, _type_: Type) -> (TypeInfo, TypeValueTable) { + assert!(self.type_info.get().is_some()); + // returns type info + (self.type_info.get().unwrap(), TypeValueTable::default()) + } + } + + impl TypePluginRegisterImpl for SimplePlugin { + fn add_dynamic_interface( + &self, + _instance_type: Type, + _interface_type: Type, + _interface_info: &InterfaceInfo, + ) { + unimplemented!() + } + + fn register_dynamic_type( + &self, + parent_type: Type, + type_name: &str, + type_info: &TypeInfo, + flags: TypeFlags, + ) -> Type { + let type_ = Type::from_name(type_name).unwrap_or_else(|| { + Type::register_dynamic( + parent_type, + type_name, + self.obj().upcast_ref::(), + flags, + ) + }); + if type_.is_valid() { + // save type info + self.type_info.set(Some(*type_info)); + } + type_ + } + } + + #[derive(Default)] + pub struct SimplePluginType; + + #[crate::dynamic_object_subclass(plugin_type = super::SimplePlugin)] + impl ObjectSubclass for SimplePluginType { + const NAME: &'static str = "SimplePluginType"; + type Type = super::SimplePluginType; + } + + impl ObjectImpl for SimplePluginType {} + } + + crate::wrapper! { + pub struct SimplePlugin(ObjectSubclass) + @implements TypePlugin; + } + + crate::wrapper! { + pub struct SimplePluginType(ObjectSubclass); + } + + #[test] + fn test_plugin() { + assert!(!imp::SimplePluginType::type_().is_valid()); + let simple_plugin = crate::Object::new::(); + // simulates the glib type system to use the plugin. + TypePluginExt::use_(&simple_plugin); + assert!(imp::SimplePluginType::type_().is_valid()); + TypePluginExt::unuse(&simple_plugin); + } +} diff --git a/glib/src/subclass/types.rs b/glib/src/subclass/types.rs index 7e1cdd566e88..cbc68771f32b 100644 --- a/glib/src/subclass/types.rs +++ b/glib/src/subclass/types.rs @@ -7,10 +7,11 @@ use std::{any::Any, collections::BTreeMap, marker, mem, ptr}; use super::SignalId; use crate::{ + gobject::traits::DynamicObjectRegisterExt, object::{IsClass, IsInterface, ObjectSubclassIs, ParentClassIs}, prelude::*, translate::*, - Closure, Object, Type, Value, + Closure, InterfaceInfo, Object, Type, TypeFlags, TypeInfo, Value, }; // rustdoc-stripper-ignore-next @@ -328,7 +329,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 +337,7 @@ pub trait InterfaceList { } impl InterfaceList for () { - fn iface_infos() -> Vec<(ffi::GType, gobject_ffi::GInterfaceInfo)> { + fn iface_infos() -> Vec<(Type, InterfaceInfo)> { vec![] } @@ -348,14 +349,13 @@ impl> InterfaceList for (A,) where ::GlibClassType: Copy, { - fn iface_infos() -> Vec<(ffi::GType, gobject_ffi::GInterfaceInfo)> { + fn iface_infos() -> Vec<(Type, InterfaceInfo)> { vec![( - A::static_type().into_glib(), - gobject_ffi::GInterfaceInfo { + A::static_type(), + InterfaceInfo(gobject_ffi::GInterfaceInfo { interface_init: Some(interface_init::), - interface_finalize: None, - interface_data: ptr::null_mut(), - }, + ..InterfaceInfo::default().0 + }), )] } @@ -390,16 +390,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(), + InterfaceInfo(gobject_ffi::GInterfaceInfo { interface_init: Some(interface_init::), interface_finalize: None, interface_data: ptr::null_mut(), - }, + }), ) ),+ ] @@ -1040,7 +1040,111 @@ 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.as_ptr(), + ); + } + + T::type_init(&mut InitializingType::(type_, marker::PhantomData)); + + type_ + } +} + +// rustdoc-stripper-ignore-next +/// Registers a `glib::Type` ID for `T` as a dynamic type. +/// +/// An object subclass must be explicitly registered as a ynamic type when the +/// system loads the implementation by calling [`TypePluginImpl::use_`] or more +/// specifically [`TypeModuleImpl::load`]. Therefore, unlike for object +/// subclasses registered as static types, object subclasses registered as +/// dynamic types can be registered several times. +/// +/// The [`dynamic_object_subclass!`] macro will create `register_type()` and +/// `on_implementation_load()` functions around this, which will ensure that +/// the function is called when necessary. +/// +/// [`dynamic_object_subclass!`]: ../../../glib_macros/attr.dynamic_object_subclass.html +/// [`TypePluginImpl::use_`]: ../type_plugin/trait.TypePluginImpl.html#method.use_ +/// [`TypeModuleImpl::load`]: ../type_module/trait.TypeModuleImpl.html#method.load +pub fn register_dynamic_type( + type_plugin: &P, +) -> 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(gobject_ffi::GTypeInfo { + class_size: mem::size_of::() as u16, + class_init: Some(class_init::), + instance_size: mem::size_of::() as u16, + instance_init: Some(instance_init::), + ..TypeInfo::default().0 + }); + + // registers the type within the `type_plugin` + let type_ = type_plugin.register_dynamic_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; + + // gets 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 plugin_ptr = type_plugin.as_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_plugin`, + // then interface can only be added as if the type was static + iface_plugin if iface_plugin != plugin_ptr => { + // 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.as_ptr(), + ); + } + } + // else interface can be added and registered to live in the `type_plugin` + _ => type_plugin.add_dynamic_interface(type_, iface_type, &iface_info), + } } T::type_init(&mut InitializingType::(type_, marker::PhantomData)); diff --git a/glib/src/types.rs b/glib/src/types.rs index 4cfa291e8b3a..f332ed123e8b 100644 --- a/glib/src/types.rs +++ b/glib/src/types.rs @@ -12,7 +12,7 @@ use std::{ ptr, }; -use crate::{translate::*, IntoGStr, Slice}; +use crate::{translate::*, IntoGStr, ObjectType, Slice, TypeFlags, TypePlugin}; // rustdoc-stripper-ignore-next /// A GLib or GLib-based library type @@ -223,6 +223,48 @@ impl Type { } } + #[doc(alias = "g_type_get_plugin")] + pub fn plugin(self) -> Option { + unsafe { + let plugin_ptr = gobject_ffi::g_type_get_plugin(self.into_glib()); + if plugin_ptr.is_null() { + None + } else { + Some(TypePlugin::from_glib_none(plugin_ptr)) + } + } + } + + #[doc(alias = "g_type_register_dynamic")] + pub fn register_dynamic( + parent_type: Self, + name: impl IntoGStr, + plugin: &TypePlugin, + flags: TypeFlags, + ) -> Self { + unsafe { + name.run_with_gstr(|name| { + Self::from_glib(gobject_ffi::g_type_register_dynamic( + parent_type.into_glib(), + name.as_ptr(), + plugin.as_ptr(), + flags.into_glib(), + )) + }) + } + } + + #[doc(alias = "g_type_add_interface_dynamic")] + pub fn add_interface_dynamic(self, interface_type: Self, plugin: &TypePlugin) { + unsafe { + gobject_ffi::g_type_add_interface_dynamic( + self.into_glib(), + interface_type.into_glib(), + plugin.as_ptr(), + ); + } + } + // rustdoc-stripper-ignore-next /// Checks that the type is not [`INVALID`](Self::INVALID) #[inline]