Backport official GLib iterator safety fix for Linux desktop

This commit is contained in:
karl committed 2026-10-13 10:35:50 +02:00
1 parent 43c43ebdc9
commit 8ef874c609
133 files changed
+55342 -2

No files matched your search

@@ -0,0 +1,72 @@
// Take a look at the license at the top of the repository in the LICENSE file.
// rustdoc-stripper-ignore-next
//! This module contains basic instance and class structs to be used for
//! `GObject` subclasses that don't require any additional data in these
//! structs and don't provide any new virtual methods.
use std::{fmt, ops};
use super::prelude::*;
use crate::prelude::*;
// rustdoc-stripper-ignore-next
/// A basic instance struct that does not store any additional data.
#[repr(C)]
pub struct InstanceStruct<T: ObjectSubclass> {
parent: <T::ParentType as ObjectType>::GlibType,
}
impl<T: ObjectSubclass> fmt::Debug for InstanceStruct<T>
where
<T::ParentType as ObjectType>::GlibType: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("InstanceStruct")
.field("parent", &self.parent)
.finish()
}
}
unsafe impl<T: ObjectSubclass> super::types::InstanceStruct for InstanceStruct<T> {
type Type = T;
}
// rustdoc-stripper-ignore-next
/// A basic class struct that does not store any additional data
/// or virtual methods.
#[repr(C)]
pub struct ClassStruct<T: ObjectSubclass> {
parent_class: <T::ParentType as ObjectType>::GlibClassType,
}
impl<T: ObjectSubclass> fmt::Debug for ClassStruct<T>
where
<T::ParentType as ObjectType>::GlibClassType: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("InstanceStruct")
.field("parent_class", &self.parent_class)
.finish()
}
}
unsafe impl<T: ObjectSubclass> super::types::ClassStruct for ClassStruct<T> {
type Type = T;
}
impl<T: ObjectSubclass> ops::Deref for ClassStruct<T> {
type Target = crate::Class<<T as ObjectSubclass>::Type>;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { &*(self as *const _ as *const Self::Target) }
}
}
impl<T: ObjectSubclass> ops::DerefMut for ClassStruct<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *(self as *mut _ as *mut Self::Target) }
}
}
+109
View File
@@ -0,0 +1,109 @@
// Take a look at the license at the top of the repository in the LICENSE file.
// rustdoc-stripper-ignore-next
//! Module for registering boxed types for Rust types.
use crate::{prelude::*, translate::*};
// rustdoc-stripper-ignore-next
/// Trait for defining boxed types.
///
/// Links together the type name with the type itself.
///
/// See [`register_boxed_type`] for registering an implementation of this trait
/// with the type system.
///
/// [`register_boxed_type`]: fn.register_boxed_type.html
pub trait BoxedType: StaticType + Clone + Sized + 'static {
// rustdoc-stripper-ignore-next
/// Boxed type name.
///
/// This must be unique in the whole process.
const NAME: &'static str;
}
// rustdoc-stripper-ignore-next
/// Register a boxed `glib::Type` ID for `T`.
///
/// This must be called only once and will panic on a second call.
///
/// See [`Boxed!`] for defining a function that ensures that
/// this is only called once and returns the type id.
///
/// [`Boxed!`]: ../../derive.Boxed.html
pub fn register_boxed_type<T: BoxedType>() -> crate::Type {
unsafe extern "C" fn boxed_copy<T: BoxedType>(v: ffi::gpointer) -> ffi::gpointer {
let v = &*(v as *mut T);
let copy = Box::new(v.clone());
Box::into_raw(copy) as ffi::gpointer
}
unsafe extern "C" fn boxed_free<T: BoxedType>(v: ffi::gpointer) {
let v = v as *mut T;
let _ = Box::from_raw(v);
}
unsafe {
use std::ffi::CString;
let type_name = CString::new(T::NAME).unwrap();
assert_eq!(
gobject_ffi::g_type_from_name(type_name.as_ptr()),
gobject_ffi::G_TYPE_INVALID,
"Type {} has already been registered",
type_name.to_str().unwrap()
);
let type_ = crate::Type::from_glib(gobject_ffi::g_boxed_type_register_static(
type_name.as_ptr(),
Some(boxed_copy::<T>),
Some(boxed_free::<T>),
));
assert!(type_.is_valid());
type_
}
}
#[cfg(test)]
mod test {
// We rename the current crate as glib, since the macros in glib-macros
// generate the glib namespace through the crate_ident_new utility,
// and that returns `glib` (and not `crate`) when called inside the glib crate
use crate as glib;
use crate::prelude::*;
use crate::translate::{FromGlibPtrBorrow, FromGlibPtrFull, IntoGlibPtr};
#[derive(Clone, Debug, PartialEq, Eq, glib::Boxed)]
#[boxed_type(name = "MyBoxed")]
struct MyBoxed(String);
#[test]
fn test_register() {
assert!(MyBoxed::static_type().is_valid());
}
#[test]
fn test_value() {
assert!(MyBoxed::static_type().is_valid());
let b = MyBoxed(String::from("abc"));
let v = b.to_value();
let b2 = v.get::<&MyBoxed>().unwrap();
assert_eq!(&b, b2);
}
#[test]
fn test_from_glib_borrow() {
assert!(MyBoxed::static_type().is_valid());
let b = MyBoxed(String::from("abc"));
let raw_ptr = unsafe { MyBoxed::into_glib_ptr(b) };
// test that the from_glib_borrow does not take ownership of the raw_ptr
let _ = unsafe { MyBoxed::from_glib_borrow(raw_ptr) };
let new_b = unsafe { MyBoxed::from_glib_full(raw_ptr) };
assert_eq!(new_b.0, "abc".to_string());
}
}
@@ -0,0 +1,217 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{marker, mem};
use super::{InitializingType, Signal};
use crate::{prelude::*, translate::*, Object, ParamSpec, Type};
// rustdoc-stripper-ignore-next
/// Trait for a type list of prerequisite object types.
pub trait PrerequisiteList {
// rustdoc-stripper-ignore-next
/// Returns the list of types for this list.
fn types() -> Vec<ffi::GType>;
}
impl PrerequisiteList for () {
fn types() -> Vec<ffi::GType> {
vec![]
}
}
impl<T: crate::ObjectType> PrerequisiteList for (T,) {
fn types() -> Vec<ffi::GType> {
vec![T::static_type().into_glib()]
}
}
// Generates all the PrerequisiteList impls for prerequisite_lists of arbitrary sizes based on a list of type
// parameters like A B C. It would generate the impl then for (A, B) and (A, B, C).
macro_rules! prerequisite_list_trait(
($name1:ident, $name2: ident, $($name:ident),*) => (
prerequisite_list_trait!(__impl $name1, $name2; $($name),*);
);
(__impl $($name:ident),+; $name1:ident, $($name2:ident),*) => (
prerequisite_list_trait_impl!($($name),+);
prerequisite_list_trait!(__impl $($name),+ , $name1; $($name2),*);
);
(__impl $($name:ident),+; $name1:ident) => (
prerequisite_list_trait_impl!($($name),+);
prerequisite_list_trait_impl!($($name),+, $name1);
);
);
// Generates the impl block for PrerequisiteList on prerequisite_lists or arbitrary sizes based on its
// arguments. Takes a list of type parameters as parameters, e.g. A B C
// and then implements the trait on (A, B, C).
macro_rules! prerequisite_list_trait_impl(
($($name:ident),+) => (
impl<$($name: crate::ObjectType),+> PrerequisiteList for ( $($name),+ ) {
fn types() -> Vec<ffi::GType> {
vec![$($name::static_type().into_glib()),+]
}
}
);
);
prerequisite_list_trait!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);
/// Type methods required for an [`ObjectInterface`] implementation.
///
/// This is usually generated by the [`#[object_interface]`](crate::object_interface) attribute macro.
pub unsafe trait ObjectInterfaceType {
/// Returns the `glib::Type` ID of the interface.
///
/// This will register the type with the type system on the first call.
#[doc(alias = "get_type")]
fn type_() -> Type;
}
/// The central trait for defining a `GObject` interface.
///
/// Links together the type name, and the interface struct for type registration and allows hooking
/// into various steps of the type registration and initialization.
///
/// This must only be implemented on `#[repr(C)]` structs and have `gobject_ffi::GTypeInterface` as
/// the first field.
///
/// See [`register_interface`] for registering an implementation of this trait
/// with the type system.
///
/// [`register_interface`]: fn.register_interface.html
pub unsafe trait ObjectInterface: ObjectInterfaceType + Sized + 'static {
/// `GObject` type name.
///
/// This must be unique in the whole process.
const NAME: &'static str;
/// Prerequisites for this interface.
///
/// Any implementer of the interface must be a subclass of the prerequisites or implement them
/// in case of interfaces.
type Prerequisites: PrerequisiteList;
/// Additional type initialization.
///
/// This is called right after the type was registered and allows
/// interfaces to do additional type-specific initialization.
///
/// Optional
fn type_init(_type_: &mut InitializingType<Self>) {}
/// Interface initialization.
///
/// This is called after `type_init` and before the first implementor
/// of the interface is created. Interfaces can use this to do interface-
/// specific initialization, e.g. for installing signals on the interface,
/// and for setting default implementations of interface functions.
///
/// Optional
fn interface_init(&mut self) {}
/// Properties installed for this interface.
///
/// All implementors of the interface must provide these properties.
fn properties() -> &'static [ParamSpec] {
&[]
}
/// Signals installed for this interface.
fn signals() -> &'static [Signal] {
&[]
}
}
pub trait ObjectInterfaceExt: ObjectInterface {
/// Get interface from an instance.
///
/// This will panic if `obj` does not implement the interface.
#[inline]
#[deprecated = "Use from_obj() instead"]
fn from_instance<T: IsA<Object>>(obj: &T) -> &Self {
Self::from_obj(obj)
}
/// Get interface from an instance.
///
/// This will panic if `obj` does not implement the interface.
#[inline]
fn from_obj<T: IsA<Object>>(obj: &T) -> &Self {
assert!(obj.as_ref().type_().is_a(Self::type_()));
unsafe {
let klass = (*(obj.as_ptr() as *const gobject_ffi::GTypeInstance)).g_class;
let interface =
gobject_ffi::g_type_interface_peek(klass as *mut _, Self::type_().into_glib());
debug_assert!(!interface.is_null());
&*(interface as *const Self)
}
}
}
impl<T: ObjectInterface> ObjectInterfaceExt for T {}
unsafe extern "C" fn interface_init<T: ObjectInterface>(
klass: ffi::gpointer,
_klass_data: ffi::gpointer,
) {
let iface = &mut *(klass as *mut T);
let pspecs = <T as ObjectInterface>::properties();
for pspec in pspecs {
gobject_ffi::g_object_interface_install_property(
iface as *mut T as *mut _,
pspec.to_glib_none().0,
);
}
let type_ = T::type_();
let signals = <T as ObjectInterface>::signals();
for signal in signals {
signal.register(type_);
}
iface.interface_init();
}
/// Register a `glib::Type` ID for `T`.
///
/// This must be called only once and will panic on a second call.
///
/// The [`object_interface!`] macro will create a `type_()` function around this, which will
/// ensure that it's only ever called once.
///
/// [`object_interface!`]: ../../macro.object_interface.html
pub fn register_interface<T: ObjectInterface>() -> Type {
unsafe {
use std::ffi::CString;
let type_name = CString::new(T::NAME).unwrap();
assert_eq!(
gobject_ffi::g_type_from_name(type_name.as_ptr()),
gobject_ffi::G_TYPE_INVALID
);
let type_ = gobject_ffi::g_type_register_static_simple(
Type::INTERFACE.into_glib(),
type_name.as_ptr(),
mem::size_of::<T>() as u32,
Some(interface_init::<T>),
0,
None,
0,
);
let prerequisites = T::Prerequisites::types();
for prerequisite in prerequisites {
gobject_ffi::g_type_interface_add_prerequisite(type_, prerequisite);
}
let type_ = Type::from_glib(type_);
assert!(type_.is_valid());
T::type_init(&mut InitializingType::<T>(type_, marker::PhantomData));
type_
}
}
+267
View File
@@ -0,0 +1,267 @@
// Take a look at the license at the top of the repository in the LICENSE file.
#![allow(clippy::needless_doctest_main)]
// rustdoc-stripper-ignore-next
//! Module containing infrastructure for subclassing `GObject`s and registering boxed types.
//!
//! # Example for registering a `glib::Object` subclass
//!
//! The following code implements a subclass of `glib::Object` with a
//! string-typed "name" property.
//!
//! ```rust
//! use glib::prelude::*;
//! use glib::subclass;
//! use glib::subclass::prelude::*;
//! use glib::{Variant, VariantType};
//!
//! use std::cell::{Cell, RefCell};
//!
//! #[derive(Debug, Eq, PartialEq, Clone, Copy, glib::Enum)]
//! #[repr(u32)]
//! // type_name: GType name of the enum (mandatory)
//! #[enum_type(name = "SimpleObjectAnimal")]
//! enum Animal {
//! Goat = 0,
//! #[enum_value(name = "The Dog")]
//! Dog = 1,
//! // name: the name of the GEnumValue (optional), default to the enum name in CamelCase
//! // nick: the nick of the GEnumValue (optional), default to the enum name in kebab-case
//! #[enum_value(name = "The Cat", nick = "chat")]
//! Cat = 2,
//! }
//!
//! impl Default for Animal {
//! fn default() -> Self {
//! Animal::Goat
//! }
//! }
//!
//! #[glib::flags(name = "MyFlags")]
//! enum MyFlags {
//! #[flags_value(name = "Flag A", nick = "nick-a")]
//! A = 0b00000001,
//! #[flags_value(name = "Flag B")]
//! B = 0b00000010,
//! #[flags_value(skip)]
//! AB = Self::A.bits() | Self::B.bits(),
//! C = 0b00000100,
//! }
//!
//! impl Default for MyFlags {
//! fn default() -> Self {
//! MyFlags::A
//! }
//! }
//!
//! mod imp {
//! use super::*;
//!
//! // This is the struct containing all state carried with
//! // the new type. Generally this has to make use of
//! // interior mutability.
//! // If it implements the `Default` trait, then `Self::default()`
//! // will be called every time a new instance is created.
//! #[derive(Default)]
//! pub struct SimpleObject {
//! name: RefCell<Option<String>>,
//! animal: Cell<Animal>,
//! flags: Cell<MyFlags>,
//! variant: RefCell<Option<Variant>>,
//! }
//!
//! // ObjectSubclass is the trait that defines the new type and
//! // contains all information needed by the GObject type system,
//! // including the new type's name, parent type, etc.
//! // If you do not want to implement `Default`, you can provide
//! // a `new()` method.
//! #[glib::object_subclass]
//! impl ObjectSubclass for SimpleObject {
//! // This type name must be unique per process.
//! const NAME: &'static str = "SimpleObject";
//!
//! type Type = super::SimpleObject;
//!
//! // The parent type this one is inheriting from.
//! // Optional, if not specified it defaults to `glib::Object`
//! type ParentType = glib::Object;
//!
//! // Interfaces this type implements.
//! // Optional, if not specified it defaults to `()`
//! type Interfaces = ();
//! }
//!
//! // Trait that is used to override virtual methods of glib::Object.
//! impl ObjectImpl for SimpleObject {
//! // Called once in the very beginning to list all properties of this class.
//! fn properties() -> &'static [glib::ParamSpec] {
//! use once_cell::sync::Lazy;
//! static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
//! vec![
//! glib::ParamSpecString::builder("name")
//! .build(),
//! glib::ParamSpecEnum::builder::<Animal>("animal")
//! .build(),
//! glib::ParamSpecFlags::builder::<MyFlags>("flags")
//! .build(),
//! glib::ParamSpecVariant::builder("variant", glib::VariantTy::ANY)
//! .build(),
//! ]
//! });
//!
//! PROPERTIES.as_ref()
//! }
//!
//! // Called whenever a property is set on this instance. The id
//! // is the same as the index of the property in the PROPERTIES array.
//! fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
//! match pspec.name() {
//! "name" => {
//! let name = value
//! .get()
//! .expect("type conformity checked by `Object::set_property`");
//! self.name.replace(name);
//! },
//! "animal" => {
//! let animal = value
//! .get()
//! .expect("type conformity checked by `Object::set_property`");
//! self.animal.replace(animal);
//! },
//! "flags" => {
//! let flags = value
//! .get()
//! .expect("type conformity checked by `Object::set_property`");
//! self.flags.replace(flags);
//! },
//! "variant" => {
//! let variant = value
//! .get()
//! .expect("type conformity checked by `Object::set_property`");
//! self.variant.replace(variant);
//! },
//! _ => unimplemented!(),
//! }
//! }
//!
//! // Called whenever a property is retrieved from this instance. The id
//! // is the same as the index of the property in the PROPERTIES array.
//! fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
//! match pspec.name() {
//! "name" => self.name.borrow().to_value(),
//! "animal" => self.animal.get().to_value(),
//! "flags" => self.flags.get().to_value(),
//! "variant" => self.variant.borrow().to_value(),
//! _ => unimplemented!(),
//! }
//! }
//!
//! // Called right after construction of the instance.
//! fn constructed(&self) {
//! // Chain up to the parent type's implementation of this virtual
//! // method.
//! self.parent_constructed();
//!
//! // And here we could do our own initialization.
//! }
//! }
//! }
//!
//! // Optionally, define a wrapper type to make it more ergonomic to use from Rust
//! glib::wrapper! {
//! pub struct SimpleObject(ObjectSubclass<imp::SimpleObject>);
//! }
//!
//! impl SimpleObject {
//! // Create an object instance of the new type.
//! pub fn new() -> Self {
//! glib::Object::new()
//! }
//! }
//!
//! pub fn main() {
//! let obj = SimpleObject::new();
//!
//! // Get the name property and change its value.
//! assert_eq!(obj.property::<Option<String>>("name"), None);
//! obj.set_property("name", "test");
//! assert_eq!(&obj.property::<String>("name"), "test");
//!
//! assert_eq!(obj.property::<Animal>("animal"), Animal::Goat);
//! obj.set_property("animal", Animal::Cat);
//! assert_eq!(obj.property::<Animal>("animal"), Animal::Cat);
//!
//! assert_eq!(obj.property::<MyFlags>("flags"), MyFlags::A);
//! obj.set_property("flags", MyFlags::B);
//! assert_eq!(obj.property::<MyFlags>("flags"), MyFlags::B);
//! }
//! ```
//!
//! # 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`.
//!
//! ```rust
//! use glib::prelude::*;
//! use glib::subclass;
//! use glib::subclass::prelude::*;
//!
//! #[derive(Clone, Debug, PartialEq, Eq, glib::Boxed)]
//! #[boxed_type(name = "MyBoxed")]
//! struct MyBoxed(String);
//!
//! pub fn main() {
//! assert!(MyBoxed::static_type().is_valid());
//!
//! let b = MyBoxed(String::from("abc"));
//! let v = b.to_value();
//! let b2 = v.get::<&MyBoxed>().unwrap();
//! assert_eq!(&b, b2);
//! }
//! ```
pub mod basic;
#[macro_use]
pub mod types;
#[macro_use]
pub mod interface;
#[macro_use]
pub mod object;
#[macro_use]
pub mod boxed;
pub mod shared;
pub mod signal;
mod object_impl_ref;
pub use object_impl_ref::{ObjectImplRef, ObjectImplWeakRef};
pub mod prelude {
// rustdoc-stripper-ignore-next
//! Prelude that re-exports all important traits from this crate.
pub use super::{
boxed::BoxedType,
interface::{ObjectInterface, ObjectInterfaceExt, ObjectInterfaceType},
object::{DerivedObjectProperties, ObjectClassSubclassExt, ObjectImpl, ObjectImplExt},
shared::{RefCounted, SharedType},
types::{
ClassStruct, InstanceStruct, InstanceStructExt, IsImplementable, IsSubclassable,
IsSubclassableExt, ObjectSubclass, ObjectSubclassExt, ObjectSubclassIsExt,
ObjectSubclassType,
},
};
}
pub use self::{
boxed::register_boxed_type,
interface::register_interface,
signal::{
Signal, SignalClassHandlerToken, SignalId, SignalInvocationHint, SignalQuery, SignalType,
},
types::{register_type, InitializingObject, InitializingType, TypeData},
};
@@ -0,0 +1,717 @@
// Take a look at the license at the top of the repository in the LICENSE file.
// rustdoc-stripper-ignore-next
//! Module that contains all types needed for creating a direct subclass of `GObject`
//! or implementing virtual methods of it.
use std::{mem, ptr};
use super::{prelude::*, Signal};
use crate::{prelude::*, translate::*, Cast, Object, ParamSpec, Slice, Value};
// rustdoc-stripper-ignore-next
/// Trait for implementors of `glib::Object` subclasses.
///
/// This allows overriding the virtual methods of `glib::Object`. Except for
/// `finalize` as implementing `Drop` would allow the same behavior.
pub trait ObjectImpl: ObjectSubclass + ObjectImplExt {
// rustdoc-stripper-ignore-next
/// Properties installed for this type.
fn properties() -> &'static [ParamSpec] {
&[]
}
// rustdoc-stripper-ignore-next
/// Signals installed for this type.
fn signals() -> &'static [Signal] {
&[]
}
// rustdoc-stripper-ignore-next
/// Property setter.
///
/// This is called whenever the property of this specific subclass with the
/// given index is set. The new value is passed as `glib::Value`.
///
/// `value` is guaranteed to be of the correct type for the given property.
fn set_property(&self, _id: usize, _value: &Value, _pspec: &ParamSpec) {
unimplemented!()
}
// rustdoc-stripper-ignore-next
/// Property getter.
///
/// This is called whenever the property value of the specific subclass with the
/// given index should be returned.
///
/// The returned `Value` must be of the correct type for the given property.
#[doc(alias = "get_property")]
fn property(&self, _id: usize, _pspec: &ParamSpec) -> Value {
unimplemented!()
}
// rustdoc-stripper-ignore-next
/// Constructed.
///
/// This is called once construction of the instance is finished.
///
/// Should chain up to the parent class' implementation.
fn constructed(&self) {
self.parent_constructed();
}
// rustdoc-stripper-ignore-next
/// Disposes of the object.
///
/// When `dispose()` ends, the object should not hold any reference to any other member object.
/// The object is also expected to be able to answer client method invocations (with possibly an
/// error code but no memory violation) until it is dropped. `dispose()` can be executed more
/// than once.
fn dispose(&self) {}
// rustdoc-stripper-ignore-next
/// Function to be called when property change is notified for with
/// `self.notify("property")`.
fn notify(&self, pspec: &ParamSpec) {
self.parent_notify(pspec)
}
fn dispatch_properties_changed(&self, pspecs: &[ParamSpec]) {
self.parent_dispatch_properties_changed(pspecs)
}
}
#[doc(alias = "get_property")]
unsafe extern "C" fn property<T: ObjectImpl>(
obj: *mut gobject_ffi::GObject,
id: u32,
value: *mut gobject_ffi::GValue,
pspec: *mut gobject_ffi::GParamSpec,
) {
let instance = &*(obj as *mut T::Instance);
let imp = instance.imp();
let v = imp.property(id as usize, &from_glib_borrow(pspec));
// We first unset the value we get passed in, in case it contained
// any previous data. Then we directly overwrite it with our new
// value, and pass ownership of the contained data to the C GValue
// by forgetting it on the Rust side.
//
// Without this, by using the GValue API, we would have to create
// a copy of the value when setting it on the destination just to
// immediately free the original value afterwards.
gobject_ffi::g_value_unset(value);
let v = mem::ManuallyDrop::new(v);
ptr::write(value, ptr::read(v.to_glib_none().0));
}
unsafe extern "C" fn set_property<T: ObjectImpl>(
obj: *mut gobject_ffi::GObject,
id: u32,
value: *mut gobject_ffi::GValue,
pspec: *mut gobject_ffi::GParamSpec,
) {
let instance = &*(obj as *mut T::Instance);
let imp = instance.imp();
imp.set_property(
id as usize,
&*(value as *mut Value),
&from_glib_borrow(pspec),
);
}
unsafe extern "C" fn constructed<T: ObjectImpl>(obj: *mut gobject_ffi::GObject) {
let instance = &*(obj as *mut T::Instance);
let imp = instance.imp();
imp.constructed();
}
unsafe extern "C" fn notify<T: ObjectImpl>(
obj: *mut gobject_ffi::GObject,
pspec: *mut gobject_ffi::GParamSpec,
) {
let instance = &*(obj as *mut T::Instance);
let imp = instance.imp();
imp.notify(&from_glib_borrow(pspec));
}
unsafe extern "C" fn dispatch_properties_changed<T: ObjectImpl>(
obj: *mut gobject_ffi::GObject,
n_pspecs: u32,
pspecs: *mut *mut gobject_ffi::GParamSpec,
) {
let instance = &*(obj as *mut T::Instance);
let imp = instance.imp();
imp.dispatch_properties_changed(Slice::from_glib_borrow_num(pspecs, n_pspecs as _));
}
unsafe extern "C" fn dispose<T: ObjectImpl>(obj: *mut gobject_ffi::GObject) {
let instance = &*(obj as *mut T::Instance);
let imp = instance.imp();
imp.dispose();
// Chain up to the parent's dispose.
let data = T::type_data();
let parent_class = data.as_ref().parent_class() as *mut gobject_ffi::GObjectClass;
if let Some(ref func) = (*parent_class).dispose {
func(obj);
}
}
// rustdoc-stripper-ignore-next
/// Trait containing only the property related functions of `ObjectImpl`.
/// Implemented by the `Props` macro.
/// When implementing `ObjectImpl` you may want to delegate the function calls to this trait.
pub trait DerivedObjectProperties: ObjectSubclass {
// rustdoc-stripper-ignore-next
/// Properties installed for this type.
fn derived_properties() -> &'static [ParamSpec] {
&[]
}
// rustdoc-stripper-ignore-next
/// Similar to [`ObjectImpl`](trait.ObjectImpl.html) but auto-generated by the [`Properties`] macro
/// to allow handling more complex use-cases.
fn derived_set_property(&self, _id: usize, _value: &Value, _pspec: &ParamSpec) {
unimplemented!()
}
// rustdoc-stripper-ignore-next
/// Similar to [`ObjectImpl`](trait.ObjectImpl.html) but auto-generated by the [`Properties`] macro
/// to allow handling more complex use-cases.
fn derived_property(&self, _id: usize, _pspec: &ParamSpec) -> Value {
unimplemented!()
}
}
// rustdoc-stripper-ignore-next
/// Extension trait for `glib::Object`'s class struct.
///
/// This contains various class methods and allows subclasses to override signal class handlers.
pub unsafe trait ObjectClassSubclassExt: Sized + 'static {
fn override_signal_class_handler<F>(&mut self, name: &str, class_handler: F)
where
F: Fn(&super::SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
{
unsafe {
super::types::signal_override_class_handler(
name,
*(self as *mut _ as *mut ffi::GType),
class_handler,
);
}
}
}
unsafe impl ObjectClassSubclassExt for crate::Class<Object> {}
unsafe impl<T: ObjectImpl> IsSubclassable<T> for Object {
fn class_init(class: &mut crate::Class<Self>) {
let klass = class.as_mut();
klass.set_property = Some(set_property::<T>);
klass.get_property = Some(property::<T>);
klass.constructed = Some(constructed::<T>);
klass.notify = Some(notify::<T>);
klass.dispatch_properties_changed = Some(dispatch_properties_changed::<T>);
klass.dispose = Some(dispose::<T>);
let pspecs = <T as ObjectImpl>::properties();
if !pspecs.is_empty() {
unsafe {
let mut pspecs_ptrs = Vec::with_capacity(pspecs.len() + 1);
pspecs_ptrs.push(ptr::null_mut());
for pspec in pspecs {
pspecs_ptrs.push(pspec.to_glib_none().0);
}
gobject_ffi::g_object_class_install_properties(
klass,
pspecs_ptrs.len() as u32,
pspecs_ptrs.as_mut_ptr(),
);
}
}
let type_ = T::type_();
let signals = <T as ObjectImpl>::signals();
for signal in signals {
signal.register(type_);
}
}
#[inline]
fn instance_init(_instance: &mut super::InitializingObject<T>) {}
}
mod sealed {
pub trait Sealed {}
impl<T: super::ObjectImplExt> Sealed for T {}
}
pub trait ObjectImplExt: sealed::Sealed + ObjectSubclass {
// rustdoc-stripper-ignore-next
/// Chain up to the parent class' implementation of `glib::Object::constructed()`.
#[inline]
fn parent_constructed(&self) {
unsafe {
let data = Self::type_data();
let parent_class = data.as_ref().parent_class() as *mut gobject_ffi::GObjectClass;
if let Some(ref func) = (*parent_class).constructed {
func(self.obj().unsafe_cast_ref::<Object>().to_glib_none().0);
}
}
}
// rustdoc-stripper-ignore-next
/// Chain up to the parent class' implementation of `glib::Object::notify()`.
#[inline]
fn parent_notify(&self, pspec: &ParamSpec) {
unsafe {
let data = Self::type_data();
let parent_class = data.as_ref().parent_class() as *mut gobject_ffi::GObjectClass;
if let Some(ref func) = (*parent_class).notify {
func(
self.obj().unsafe_cast_ref::<Object>().to_glib_none().0,
pspec.to_glib_none().0,
);
}
}
}
// rustdoc-stripper-ignore-next
/// Chain up to the parent class' implementation of `glib::Object::dispatch_properties_changed()`.
#[inline]
fn parent_dispatch_properties_changed(&self, pspecs: &[ParamSpec]) {
unsafe {
let data = Self::type_data();
let parent_class = data.as_ref().parent_class() as *mut gobject_ffi::GObjectClass;
if let Some(ref func) = (*parent_class).dispatch_properties_changed {
func(
self.obj().unsafe_cast_ref::<Object>().to_glib_none().0,
pspecs.len() as _,
pspecs.as_ptr() as *mut _,
);
}
}
}
// rustdoc-stripper-ignore-next
/// Chain up to parent class signal handler.
fn signal_chain_from_overridden(
&self,
token: &super::SignalClassHandlerToken,
values: &[Value],
) -> Option<Value> {
unsafe {
super::types::signal_chain_from_overridden(self.obj().as_ptr() as *mut _, token, values)
}
}
}
impl<T: ObjectImpl> ObjectImplExt for T {}
#[cfg(test)]
mod test {
use std::cell::RefCell;
use super::*;
// We rename the current crate as glib, since the macros in glib-macros
// generate the glib namespace through the crate_ident_new utility,
// and that returns `glib` (and not `crate`) when called inside the glib crate
use crate as glib;
mod imp {
use super::*;
// A dummy `Object` to test setting an `Object` property and returning an `Object` in signals
#[derive(Default)]
pub struct ChildObject;
#[glib::object_subclass]
impl ObjectSubclass for ChildObject {
const NAME: &'static str = "ChildObject";
type Type = super::ChildObject;
}
impl ObjectImpl for ChildObject {}
#[derive(Default)]
pub struct SimpleObject {
name: RefCell<Option<String>>,
construct_name: RefCell<Option<String>>,
constructed: RefCell<bool>,
}
#[glib::object_subclass]
impl ObjectSubclass for SimpleObject {
const NAME: &'static str = "SimpleObject";
type Type = super::SimpleObject;
type Interfaces = (super::Dummy,);
}
impl ObjectImpl for SimpleObject {
fn properties() -> &'static [ParamSpec] {
use once_cell::sync::Lazy;
static PROPERTIES: Lazy<Vec<ParamSpec>> = Lazy::new(|| {
vec![
crate::ParamSpecString::builder("name").build(),
crate::ParamSpecString::builder("construct-name")
.construct_only()
.build(),
crate::ParamSpecBoolean::builder("constructed")
.read_only()
.build(),
crate::ParamSpecObject::builder::<super::ChildObject>("child").build(),
]
});
PROPERTIES.as_ref()
}
fn signals() -> &'static [super::Signal] {
use once_cell::sync::Lazy;
static SIGNALS: Lazy<Vec<super::Signal>> = Lazy::new(|| {
vec![
super::Signal::builder("name-changed")
.param_types([String::static_type()])
.build(),
super::Signal::builder("change-name")
.param_types([String::static_type()])
.return_type::<String>()
.action()
.class_handler(|_, args| {
let obj = args[0]
.get::<super::SimpleObject>()
.expect("Failed to get Object from args[0]");
let new_name = args[1]
.get::<String>()
.expect("Failed to get Object from args[1]");
let imp = obj.imp();
let old_name = imp.name.replace(Some(new_name));
obj.emit_by_name::<()>("name-changed", &[&*imp.name.borrow()]);
Some(old_name.to_value())
})
.build(),
super::Signal::builder("create-string")
.return_type::<String>()
.build(),
super::Signal::builder("create-child-object")
.return_type::<super::ChildObject>()
.build(),
]
});
SIGNALS.as_ref()
}
fn set_property(&self, _id: usize, value: &Value, pspec: &crate::ParamSpec) {
match pspec.name() {
"name" => {
let name = value
.get()
.expect("type conformity checked by 'Object::set_property'");
self.name.replace(name);
self.obj()
.emit_by_name::<()>("name-changed", &[&*self.name.borrow()]);
}
"construct-name" => {
let name = value
.get()
.expect("type conformity checked by 'Object::set_property'");
self.construct_name.replace(name);
}
"child" => {
// not stored, only used to test `set_property` with `Objects`
}
_ => unimplemented!(),
}
}
fn property(&self, _id: usize, pspec: &crate::ParamSpec) -> Value {
match pspec.name() {
"name" => self.name.borrow().to_value(),
"construct-name" => self.construct_name.borrow().to_value(),
"constructed" => self.constructed.borrow().to_value(),
_ => unimplemented!(),
}
}
fn constructed(&self) {
self.parent_constructed();
debug_assert_eq!(self as *const _, self.obj().imp() as *const _);
*self.constructed.borrow_mut() = true;
}
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct DummyInterface {
parent: gobject_ffi::GTypeInterface,
}
#[glib::object_interface]
unsafe impl ObjectInterface for DummyInterface {
const NAME: &'static str = "Dummy";
}
}
wrapper! {
pub struct ChildObject(ObjectSubclass<imp::ChildObject>);
}
wrapper! {
pub struct SimpleObject(ObjectSubclass<imp::SimpleObject>);
}
wrapper! {
pub struct Dummy(ObjectInterface<imp::DummyInterface>);
}
unsafe impl<T: ObjectSubclass> IsImplementable<T> for Dummy {}
#[test]
fn test_create() {
let type_ = SimpleObject::static_type();
let obj = Object::with_type(type_);
assert!(obj.type_().is_a(Dummy::static_type()));
// Assert that the object representation is equivalent to the underlying C GObject pointer
assert_eq!(
mem::size_of::<SimpleObject>(),
mem::size_of::<ffi::gpointer>()
);
assert_eq!(obj.as_ptr() as ffi::gpointer, unsafe {
*(&obj as *const _ as *const ffi::gpointer)
});
assert!(obj.property::<bool>("constructed"));
let weak = obj.downgrade();
drop(obj);
assert!(weak.upgrade().is_none());
}
#[test]
fn test_properties() {
let type_ = SimpleObject::static_type();
let obj = Object::with_type(type_);
assert!(obj.type_().is_a(Dummy::static_type()));
let properties = obj.list_properties();
assert_eq!(properties.len(), 4);
assert_eq!(properties[0].name(), "name");
assert_eq!(properties[1].name(), "construct-name");
assert_eq!(properties[2].name(), "constructed");
assert_eq!(properties[3].name(), "child");
}
#[test]
fn test_create_child_object() {
let obj: ChildObject = Object::new();
assert_eq!(&obj, obj.imp().obj().as_ref());
}
#[test]
fn test_builder() {
let obj = Object::builder::<SimpleObject>()
.property("construct-name", "meh")
.property("name", "initial")
.build();
assert_eq!(
obj.property::<String>("construct-name"),
String::from("meh")
);
assert_eq!(obj.property::<String>("name"), String::from("initial"));
}
#[test]
fn test_set_property() {
let obj = Object::builder::<SimpleObject>()
.property("construct-name", "meh")
.property("name", "initial")
.build();
assert_eq!(
obj.property::<String>("construct-name"),
String::from("meh")
);
assert_eq!(
obj.property::<String>("construct-name"),
String::from("meh")
);
assert_eq!(obj.property::<String>("name"), String::from("initial"));
obj.set_property("name", "test");
assert_eq!(obj.property::<String>("name"), String::from("test"));
let child = Object::with_type(ChildObject::static_type());
obj.set_property("child", &child);
}
#[test]
#[should_panic = "property 'construct-name' of type 'SimpleObject' is not writable"]
fn test_set_property_non_writable() {
let obj = Object::builder::<SimpleObject>()
.property("construct-name", "meh")
.property("name", "initial")
.build();
obj.set_property("construct-name", "test");
}
#[test]
#[should_panic = "property 'test' of type 'SimpleObject' not found"]
fn test_set_property_not_found() {
let obj = Object::builder::<SimpleObject>()
.property("construct-name", "meh")
.property("name", "initial")
.build();
obj.set_property("test", true);
}
#[test]
#[should_panic = "property 'constructed' of type 'SimpleObject' is not writable"]
fn test_set_property_not_writable() {
let obj = Object::builder::<SimpleObject>()
.property("construct-name", "meh")
.property("name", "initial")
.build();
obj.set_property("constructed", false);
}
#[test]
#[should_panic = "property 'name' of type 'SimpleObject' can't be set from the given type (expected: 'gchararray', got: 'gboolean')"]
fn test_set_property_wrong_type() {
let obj = Object::builder::<SimpleObject>()
.property("construct-name", "meh")
.property("name", "initial")
.build();
obj.set_property("name", false);
}
#[test]
#[should_panic = "property 'child' of type 'SimpleObject' can't be set from the given type (expected: 'ChildObject', got: 'SimpleObject')"]
fn test_set_property_wrong_type_2() {
let obj = Object::builder::<SimpleObject>()
.property("construct-name", "meh")
.property("name", "initial")
.build();
let other_obj = Object::with_type(SimpleObject::static_type());
obj.set_property("child", &other_obj);
}
#[test]
#[should_panic = "Can't set construct property 'construct-name' for type 'SimpleObject' twice"]
fn test_construct_property_set_twice() {
let _obj = Object::builder::<SimpleObject>()
.property("construct-name", "meh")
.property("construct-name", "meh2")
.build();
}
#[test]
fn test_signals() {
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
let obj = Object::builder::<SimpleObject>()
.property("name", "old-name")
.build();
let name_changed_triggered = Arc::new(AtomicBool::new(false));
let name_changed_clone = name_changed_triggered.clone();
obj.connect("name-changed", false, move |args| {
let _obj = args[0].get::<Object>().expect("Failed to get args[0]");
let name = args[1].get::<&str>().expect("Failed to get args[1]");
assert_eq!(name, "new-name");
name_changed_clone.store(true, Ordering::Relaxed);
None
});
assert_eq!(obj.property::<String>("name"), String::from("old-name"));
assert!(!name_changed_triggered.load(Ordering::Relaxed));
assert_eq!(
obj.emit_by_name::<String>("change-name", &[&"new-name"]),
"old-name"
);
assert!(name_changed_triggered.load(Ordering::Relaxed));
}
#[test]
fn test_signal_return_expected_type() {
let obj = Object::with_type(SimpleObject::static_type());
obj.connect("create-string", false, move |_args| {
Some("return value".to_value())
});
let signal_id = imp::SimpleObject::signals()[2].signal_id();
let value = obj.emit::<String>(signal_id, &[]);
assert_eq!(value, "return value");
}
#[test]
fn test_callback_validity() {
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
let obj = Object::builder::<SimpleObject>()
.property("name", "old-name")
.build();
let name_changed_triggered = Arc::new(AtomicBool::new(false));
let name_changed_clone = name_changed_triggered.clone();
obj.connect_notify(Some("name"), move |_, _| {
name_changed_clone.store(true, Ordering::Relaxed);
});
obj.notify("name");
assert!(name_changed_triggered.load(Ordering::Relaxed));
}
// Note: can't test type mismatch in signals since panics accross FFI boundaries
// are UB. See https://github.com/gtk-rs/glib/issues/518
#[test]
fn test_signal_return_expected_object_type() {
let obj = Object::with_type(SimpleObject::static_type());
obj.connect("create-child-object", false, move |_args| {
Some(Object::with_type(ChildObject::static_type()).to_value())
});
let value: glib::Object = obj.emit_by_name("create-child-object", &[]);
assert!(value.type_().is_a(ChildObject::static_type()));
}
}
@@ -0,0 +1,163 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{cmp, fmt, hash};
use super::prelude::*;
use crate::{
clone::{Downgrade, Upgrade},
prelude::*,
WeakRef,
};
// rustdoc-stripper-ignore-next
/// Reference-counted wrapper around an [`ObjectSubclass`] reference.
///
/// This can be used for passing into closures as strong or weak reference without manually going
/// from the implementation type to the instance type and back.
pub struct ObjectImplRef<T: ObjectSubclass>(T::Type);
unsafe impl<T: ObjectSubclass + Send + Sync> Send for ObjectImplRef<T> {}
unsafe impl<T: ObjectSubclass + Send + Sync> Sync for ObjectImplRef<T> {}
impl<T: ObjectSubclass> ObjectImplRef<T> {
// rustdoc-stripper-ignore-next
/// Create a new reference-counting wrapper around `imp`.
#[inline]
pub fn new(imp: &T) -> Self {
Self(imp.obj().clone())
}
// rustdoc-stripper-ignore-next
/// Downgrade to a weak reference.
///
/// This can be upgraded to a strong reference again via [`ObjectImplWeakRef::upgrade`].
#[inline]
pub fn downgrade(&self) -> ObjectImplWeakRef<T> {
ObjectImplWeakRef(self.0.downgrade())
}
}
impl<T: ObjectSubclass> Clone for ObjectImplRef<T> {
#[inline]
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T: ObjectSubclass> fmt::Debug for ObjectImplRef<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
<T::Type as fmt::Debug>::fmt(&self.0, f)
}
}
impl<T: ObjectSubclass> std::ops::Deref for ObjectImplRef<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
T::from_obj(&self.0)
}
}
impl<T: ObjectSubclass> Downgrade for ObjectImplRef<T> {
type Weak = ObjectImplWeakRef<T>;
#[inline]
fn downgrade(&self) -> Self::Weak {
self.downgrade()
}
}
impl<T: ObjectSubclass> PartialOrd for ObjectImplRef<T> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<T: ObjectSubclass, OT: crate::object::ObjectType> PartialOrd<OT> for ObjectImplRef<T>
where
T::Type: PartialOrd<OT>,
{
#[inline]
fn partial_cmp(&self, other: &OT) -> Option<cmp::Ordering> {
self.0.partial_cmp(other)
}
}
impl<T: ObjectSubclass> Ord for ObjectImplRef<T> {
#[inline]
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.0.cmp(&other.0)
}
}
impl<T: ObjectSubclass> PartialEq for ObjectImplRef<T> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<T: ObjectSubclass, OT: crate::object::ObjectType> PartialEq<OT> for ObjectImplRef<T>
where
T::Type: PartialEq<OT>,
{
#[inline]
fn eq(&self, other: &OT) -> bool {
self.0 == *other
}
}
impl<T: ObjectSubclass> Eq for ObjectImplRef<T> {}
impl<T: ObjectSubclass> hash::Hash for ObjectImplRef<T> {
#[inline]
fn hash<H>(&self, state: &mut H)
where
H: hash::Hasher,
{
self.0.hash(state)
}
}
// rustdoc-stripper-ignore-next
/// Weak reference to an [`ObjectSubclass`] reference.
pub struct ObjectImplWeakRef<T: ObjectSubclass>(WeakRef<T::Type>);
unsafe impl<T: ObjectSubclass + Send + Sync> Send for ObjectImplWeakRef<T> {}
unsafe impl<T: ObjectSubclass + Send + Sync> Sync for ObjectImplWeakRef<T> {}
impl<T: ObjectSubclass> ObjectImplWeakRef<T> {
// rustdoc-stripper-ignore-next
/// Upgrade to a strong reference, if possible.
///
/// This will return `None` if the underlying object was freed in the meantime.
#[inline]
pub fn upgrade(&self) -> Option<ObjectImplRef<T>> {
let obj = self.0.upgrade()?;
Some(ObjectImplRef(obj))
}
}
impl<T: ObjectSubclass> Clone for ObjectImplWeakRef<T> {
#[inline]
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T: ObjectSubclass> fmt::Debug for ObjectImplWeakRef<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
<crate::WeakRef<T::Type> as fmt::Debug>::fmt(&self.0, f)
}
}
impl<T: ObjectSubclass> Upgrade for ObjectImplWeakRef<T> {
type Strong = ObjectImplRef<T>;
#[inline]
fn upgrade(&self) -> Option<Self::Strong> {
self.upgrade()
}
}
@@ -0,0 +1,295 @@
// Take a look at the license at the top of the repository in the LICENSE file.
// rustdoc-stripper-ignore-next
//! Module for registering shared types for Rust types.
use crate::{prelude::*, translate::*};
pub unsafe trait RefCounted: Clone + Sized + 'static {
// rustdoc-stripper-ignore-next
/// The inner type
type InnerType;
// rustdoc-stripper-ignore-next
/// The function used to increment the inner type refcount
unsafe fn ref_(this: *const Self::InnerType) -> *const Self::InnerType;
// rustdoc-stripper-ignore-next
/// Provides access to a raw pointer to InnerType
fn as_ptr(&self) -> *const Self::InnerType;
// rustdoc-stripper-ignore-next
/// Converts the RefCounted object to a raw pointer to InnerType
unsafe fn into_raw(self) -> *const Self::InnerType;
// rustdoc-stripper-ignore-next
/// Converts a raw pointer to InnerType to a RefCounted object
unsafe fn from_raw(this: *const Self::InnerType) -> Self;
}
unsafe impl<T> RefCounted for std::sync::Arc<T>
where
T: 'static,
{
type InnerType = T;
#[inline]
unsafe fn ref_(this: *const Self::InnerType) -> *const Self::InnerType {
std::sync::Arc::increment_strong_count(this);
this
}
#[inline]
fn as_ptr(&self) -> *const Self::InnerType {
std::sync::Arc::as_ptr(self)
}
#[inline]
unsafe fn into_raw(self) -> *const Self::InnerType {
std::sync::Arc::into_raw(self)
}
#[inline]
unsafe fn from_raw(this: *const Self::InnerType) -> Self {
std::sync::Arc::from_raw(this)
}
}
unsafe impl<T> RefCounted for std::rc::Rc<T>
where
T: 'static,
{
type InnerType = T;
#[inline]
unsafe fn ref_(this: *const Self::InnerType) -> *const Self::InnerType {
use std::mem::ManuallyDrop;
let this_rc = ManuallyDrop::new(std::rc::Rc::from_raw(this));
std::rc::Rc::into_raw(ManuallyDrop::take(&mut this_rc.clone()))
}
#[inline]
fn as_ptr(&self) -> *const Self::InnerType {
std::rc::Rc::as_ptr(self)
}
#[inline]
unsafe fn into_raw(self) -> *const Self::InnerType {
std::rc::Rc::into_raw(self)
}
#[inline]
unsafe fn from_raw(this: *const Self::InnerType) -> Self {
std::rc::Rc::from_raw(this)
}
}
// rustdoc-stripper-ignore-next
/// Trait for defining shared types.
///
/// Links together the type name with the type itself.
///
/// See [`register_shared_type`] for registering an implementation of this trait
/// with the type system.
///
/// [`register_shared_type`]: fn.register_shared_type.html
pub trait SharedType: StaticType + Clone + Sized + 'static {
// rustdoc-stripper-ignore-next
/// Shared type name.
///
/// This must be unique in the whole process.
const NAME: &'static str;
// rustdoc-stripper-ignore-next
/// The inner refcounted type
type RefCountedType: RefCounted;
// rustdoc-stripper-ignore-next
/// Converts the SharedType into its inner RefCountedType
fn into_refcounted(self) -> Self::RefCountedType;
// rustdoc-stripper-ignore-next
/// Constructs a SharedType from a RefCountedType
fn from_refcounted(this: Self::RefCountedType) -> Self;
}
// rustdoc-stripper-ignore-next
/// Register a boxed `glib::Type` ID for `T`.
///
/// This must be called only once and will panic on a second call.
///
/// See [`Shared!`] for defining a function that ensures that
/// this is only called once and returns the type id.
///
/// [`Shared!`]: ../../derive.Shared.html
pub fn register_shared_type<T: SharedType>() -> crate::Type {
unsafe {
use std::ffi::CString;
unsafe extern "C" fn shared_ref<T: SharedType>(v: ffi::gpointer) -> ffi::gpointer {
T::RefCountedType::ref_(v as *const <T::RefCountedType as RefCounted>::InnerType)
as ffi::gpointer
}
unsafe extern "C" fn shared_unref<T: SharedType>(v: ffi::gpointer) {
let _ = T::RefCountedType::from_raw(
v as *const <T::RefCountedType as RefCounted>::InnerType,
);
}
let type_name = CString::new(T::NAME).unwrap();
assert_eq!(
gobject_ffi::g_type_from_name(type_name.as_ptr()),
gobject_ffi::G_TYPE_INVALID,
"Type {} has already been registered",
type_name.to_str().unwrap()
);
let type_ = crate::Type::from_glib(gobject_ffi::g_boxed_type_register_static(
type_name.as_ptr(),
Some(shared_ref::<T>),
Some(shared_unref::<T>),
));
assert!(type_.is_valid());
type_
}
}
#[cfg(test)]
mod test {
use super::*;
// We rename the current crate as glib, since the macros in glib-macros
// generate the glib namespace through the crate_ident_new utility,
// and that returns `glib` (and not `crate`) when called inside the glib crate
use crate as glib;
#[derive(Clone, Debug, PartialEq, Eq)]
struct MySharedInner {
foo: String,
}
#[derive(Clone, Debug, PartialEq, Eq, glib::SharedBoxed)]
#[shared_boxed_type(name = "MySharedArc")]
struct MySharedArc(std::sync::Arc<MySharedInner>);
#[derive(Clone, Debug, PartialEq, Eq, glib::SharedBoxed)]
#[shared_boxed_type(name = "MySharedRc")]
struct MySharedRc(std::rc::Rc<MySharedInner>);
#[test]
fn test_register() {
assert_ne!(crate::Type::INVALID, MySharedArc::static_type());
assert_ne!(crate::Type::INVALID, MySharedRc::static_type());
}
#[test]
fn test_value_arc() {
assert_ne!(crate::Type::INVALID, MySharedArc::static_type());
let b = MySharedArc::from_refcounted(std::sync::Arc::new(MySharedInner {
foo: String::from("abc"),
}));
let v = b.to_value();
let b2 = v.get::<MySharedArc>().unwrap();
assert!(std::sync::Arc::ptr_eq(&b.0, &b2.0));
}
#[test]
fn test_value_rc() {
assert_ne!(crate::Type::INVALID, MySharedRc::static_type());
let b = MySharedRc::from_refcounted(std::rc::Rc::new(MySharedInner {
foo: String::from("abc"),
}));
let v = b.to_value();
let b2 = v.get::<MySharedRc>().unwrap();
assert!(std::rc::Rc::ptr_eq(&b.0, &b2.0));
}
#[test]
fn same_ffi_pointer_arc() {
assert_ne!(crate::Type::INVALID, MySharedArc::static_type());
let b = MySharedArc::from_refcounted(std::sync::Arc::new(MySharedInner {
foo: String::from("abc"),
}));
let inner_raw_ptr = std::sync::Arc::into_raw(b.clone().0);
assert_eq!(std::sync::Arc::strong_count(&b.0), 2);
let inner_raw_ptr_clone =
unsafe { <MySharedArc as SharedType>::RefCountedType::ref_(inner_raw_ptr) };
assert_eq!(std::sync::Arc::strong_count(&b.0), 3);
assert!(std::ptr::eq(inner_raw_ptr, inner_raw_ptr_clone));
let _ = unsafe { <MySharedArc as SharedType>::RefCountedType::from_raw(inner_raw_ptr) };
let _ =
unsafe { <MySharedArc as SharedType>::RefCountedType::from_raw(inner_raw_ptr_clone) };
assert_eq!(std::sync::Arc::strong_count(&b.0), 1);
}
#[test]
fn same_ffi_pointer_rc() {
assert_ne!(crate::Type::INVALID, MySharedRc::static_type());
let b = MySharedRc::from_refcounted(std::rc::Rc::new(MySharedInner {
foo: String::from("abc"),
}));
let inner_raw_ptr = std::rc::Rc::into_raw(b.clone().0);
assert_eq!(std::rc::Rc::strong_count(&b.0), 2);
let inner_raw_ptr_clone =
unsafe { <MySharedRc as SharedType>::RefCountedType::ref_(inner_raw_ptr) };
assert_eq!(std::rc::Rc::strong_count(&b.0), 3);
assert!(std::ptr::eq(inner_raw_ptr, inner_raw_ptr_clone));
let _ = unsafe { <MySharedRc as SharedType>::RefCountedType::from_raw(inner_raw_ptr) };
let _ =
unsafe { <MySharedRc as SharedType>::RefCountedType::from_raw(inner_raw_ptr_clone) };
assert_eq!(std::rc::Rc::strong_count(&b.0), 1);
}
#[test]
fn from_glib_borrow_arc() {
assert_ne!(crate::Type::INVALID, MySharedRc::static_type());
let b = MySharedArc::from_refcounted(std::sync::Arc::new(MySharedInner {
foo: String::from("abc"),
}));
let inner_raw_ptr = std::sync::Arc::into_raw(b.clone().0);
assert_eq!(std::sync::Arc::strong_count(&b.0), 2);
unsafe {
let _ = MySharedArc::from_glib_borrow(inner_raw_ptr);
assert_eq!(std::sync::Arc::strong_count(&b.0), 2);
}
assert_eq!(std::sync::Arc::strong_count(&b.0), 2);
}
#[test]
fn from_glib_borrow_rc() {
assert_ne!(crate::Type::INVALID, MySharedRc::static_type());
let b = MySharedRc::from_refcounted(std::rc::Rc::new(MySharedInner {
foo: String::from("abc"),
}));
let inner_raw_ptr = std::rc::Rc::into_raw(b.clone().0);
assert_eq!(std::rc::Rc::strong_count(&b.0), 2);
unsafe {
let _ = MySharedRc::from_glib_borrow(inner_raw_ptr);
assert_eq!(std::rc::Rc::strong_count(&b.0), 2);
}
assert_eq!(std::rc::Rc::strong_count(&b.0), 2);
}
}
@@ -0,0 +1,715 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{fmt, num::NonZeroU32, ptr, sync::Mutex};
use crate::{
prelude::*, translate::*, utils::is_canonical_pspec_name, Closure, IntoGStr, SignalFlags, Type,
Value,
};
// rustdoc-stripper-ignore-next
/// Builder for signals.
#[allow(clippy::type_complexity)]
#[must_use = "The builder must be built to be used"]
pub struct SignalBuilder {
name: String,
flags: SignalFlags,
param_types: Vec<SignalType>,
return_type: SignalType,
class_handler: Option<
Box<dyn Fn(&SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static>,
>,
accumulator: Option<
Box<dyn Fn(&SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static>,
>,
}
// rustdoc-stripper-ignore-next
/// Signal metadata.
pub struct Signal {
name: String,
flags: SignalFlags,
param_types: Vec<SignalType>,
return_type: SignalType,
registration: Mutex<SignalRegistration>,
}
// rustdoc-stripper-ignore-next
/// Token passed to signal class handlers.
pub struct SignalClassHandlerToken(
// rustdoc-stripper-ignore-next
/// Instance for which the signal is emitted.
pub(super) *mut gobject_ffi::GTypeInstance,
// rustdoc-stripper-ignore-next
/// Return type.
pub(super) Type,
// rustdoc-stripper-ignore-next
/// Arguments value array.
pub(super) *const Value,
);
impl fmt::Debug for SignalClassHandlerToken {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
f.debug_struct("SignalClassHandlerToken")
.field("type", &unsafe {
crate::Object::from_glib_borrow(self.0 as *mut gobject_ffi::GObject)
})
.finish()
}
}
// rustdoc-stripper-ignore-next
/// Signal invocation hint passed to signal accumulators.
#[repr(transparent)]
pub struct SignalInvocationHint(gobject_ffi::GSignalInvocationHint);
impl SignalInvocationHint {
#[inline]
pub fn detail(&self) -> Option<crate::Quark> {
unsafe { try_from_glib(self.0.detail).ok() }
}
#[inline]
pub fn run_type(&self) -> SignalFlags {
unsafe { from_glib(self.0.run_type) }
}
}
impl fmt::Debug for SignalInvocationHint {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
f.debug_struct("SignalInvocationHint")
.field("detail", &self.detail())
.field("run_type", &self.run_type())
.finish()
}
}
// rustdoc-stripper-ignore-next
/// In-depth information of a specific signal
pub struct SignalQuery(gobject_ffi::GSignalQuery);
unsafe impl Send for SignalQuery {}
unsafe impl Sync for SignalQuery {}
impl SignalQuery {
// rustdoc-stripper-ignore-next
/// The name of the signal.
#[inline]
pub fn signal_name<'a>(&self) -> &'a str {
unsafe {
let ptr = self.0.signal_name;
std::ffi::CStr::from_ptr(ptr).to_str().unwrap()
}
}
// rustdoc-stripper-ignore-next
/// The ID of the signal.
#[inline]
pub fn signal_id(&self) -> SignalId {
unsafe { SignalId::from_glib(self.0.signal_id) }
}
// rustdoc-stripper-ignore-next
/// The instance type this signal can be emitted for.
#[inline]
pub fn type_(&self) -> Type {
unsafe { from_glib(self.0.itype) }
}
// rustdoc-stripper-ignore-next
/// The signal flags.
#[inline]
pub fn flags(&self) -> SignalFlags {
unsafe { from_glib(self.0.signal_flags) }
}
// rustdoc-stripper-ignore-next
/// The return type for the user callback.
#[inline]
pub fn return_type(&self) -> SignalType {
unsafe { from_glib(self.0.return_type) }
}
// rustdoc-stripper-ignore-next
/// The number of parameters the user callback takes.
#[inline]
pub fn n_params(&self) -> u32 {
self.0.n_params
}
// rustdoc-stripper-ignore-next
/// The parameters for the user callback.
#[inline]
pub fn param_types(&self) -> &[SignalType] {
if self.n_params() == 0 {
return &[];
}
unsafe {
std::slice::from_raw_parts(
self.0.param_types as *const SignalType,
self.0.n_params as usize,
)
}
}
}
impl fmt::Debug for SignalQuery {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
f.debug_struct("SignalQuery")
.field("signal_name", &self.signal_name())
.field("type", &self.type_())
.field("flags", &self.flags())
.field("return_type", &self.return_type())
.field("param_types", &self.param_types())
.finish()
}
}
// rustdoc-stripper-ignore-next
/// Signal ID.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SignalId(NonZeroU32);
impl SignalId {
// rustdoc-stripper-ignore-next
/// Create a new Signal Identifier.
///
/// # Safety
///
/// The caller has to ensure it's a valid signal identifier.
#[inline]
pub unsafe fn new(id: NonZeroU32) -> Self {
Self(id)
}
#[doc(alias = "g_signal_parse_name")]
#[inline]
pub fn parse_name(
name: &str,
type_: Type,
force_detail: bool,
) -> Option<(Self, Option<crate::Quark>)> {
let mut signal_id = std::mem::MaybeUninit::uninit();
let mut detail_quark = std::mem::MaybeUninit::uninit();
unsafe {
let found: bool = name.run_with_gstr(|name| {
from_glib(gobject_ffi::g_signal_parse_name(
name.as_ptr(),
type_.into_glib(),
signal_id.as_mut_ptr(),
detail_quark.as_mut_ptr(),
force_detail.into_glib(),
))
});
if found {
Some((
from_glib(signal_id.assume_init()),
crate::Quark::try_from_glib(detail_quark.assume_init()).ok(),
))
} else {
None
}
}
}
// rustdoc-stripper-ignore-next
/// Find a SignalId by its `name`, and the `type` it connects to.
#[doc(alias = "g_signal_lookup")]
#[inline]
pub fn lookup(name: &str, type_: Type) -> Option<Self> {
unsafe {
let signal_id = name.run_with_gstr(|name| {
gobject_ffi::g_signal_lookup(name.as_ptr(), type_.into_glib())
});
if signal_id == 0 {
None
} else {
Some(Self::new(NonZeroU32::new_unchecked(signal_id)))
}
}
}
// rustdoc-stripper-ignore-next
/// Queries more in-depth information about the current signal.
#[doc(alias = "g_signal_query")]
#[inline]
pub fn query(&self) -> SignalQuery {
unsafe {
let mut query_ptr = std::mem::MaybeUninit::uninit();
gobject_ffi::g_signal_query(self.into_glib(), query_ptr.as_mut_ptr());
let query = query_ptr.assume_init();
debug_assert_ne!(query.signal_id, 0);
SignalQuery(query)
}
}
// rustdoc-stripper-ignore-next
/// Find the signal name.
#[doc(alias = "g_signal_name")]
#[inline]
pub fn name<'a>(&self) -> &'a str {
unsafe {
let ptr = gobject_ffi::g_signal_name(self.into_glib());
std::ffi::CStr::from_ptr(ptr).to_str().unwrap()
}
}
}
#[doc(hidden)]
impl FromGlib<u32> for SignalId {
#[inline]
unsafe fn from_glib(signal_id: u32) -> Self {
debug_assert_ne!(signal_id, 0);
Self::new(NonZeroU32::new_unchecked(signal_id))
}
}
#[doc(hidden)]
impl IntoGlib for SignalId {
type GlibType = u32;
#[inline]
fn into_glib(self) -> u32 {
self.0.into()
}
}
#[derive(Copy, Clone, Hash)]
#[repr(transparent)]
pub struct SignalType(ffi::GType);
impl SignalType {
#[inline]
pub fn with_static_scope(type_: Type) -> Self {
Self(type_.into_glib() | gobject_ffi::G_TYPE_FLAG_RESERVED_ID_BIT)
}
#[inline]
pub fn static_scope(&self) -> bool {
(self.0 & gobject_ffi::G_TYPE_FLAG_RESERVED_ID_BIT) != 0
}
#[inline]
pub fn type_(&self) -> Type {
(*self).into()
}
}
impl From<Type> for SignalType {
#[inline]
fn from(type_: Type) -> Self {
Self(type_.into_glib())
}
}
impl From<SignalType> for Type {
#[inline]
fn from(type_: SignalType) -> Self {
// Remove the extra-bit used for G_SIGNAL_TYPE_STATIC_SCOPE
let type_ = type_.0 & (!gobject_ffi::G_TYPE_FLAG_RESERVED_ID_BIT);
unsafe { from_glib(type_) }
}
}
impl PartialEq<Type> for SignalType {
#[inline]
fn eq(&self, other: &Type) -> bool {
let type_: Type = (*self).into();
type_.eq(other)
}
}
impl std::fmt::Debug for SignalType {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let type_: Type = (*self).into();
f.debug_struct("SignalType")
.field("name", &type_.name())
.field("static_scope", &self.static_scope())
.finish()
}
}
impl std::fmt::Display for SignalType {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let type_: Type = (*self).into();
f.debug_struct("SignalType")
.field("name", &type_.name())
.field("static_scope", &self.static_scope())
.finish()
}
}
#[doc(hidden)]
impl FromGlib<ffi::GType> for SignalType {
#[inline]
unsafe fn from_glib(type_: ffi::GType) -> Self {
Self(type_)
}
}
#[doc(hidden)]
impl IntoGlib for SignalType {
type GlibType = ffi::GType;
#[inline]
fn into_glib(self) -> ffi::GType {
self.0
}
}
#[allow(clippy::type_complexity)]
enum SignalRegistration {
Unregistered {
class_handler: Option<
Box<
dyn Fn(&SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
>,
>,
accumulator: Option<
Box<dyn Fn(&SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static>,
>,
},
Registered {
type_: Type,
signal_id: SignalId,
},
}
impl SignalBuilder {
// rustdoc-stripper-ignore-next
/// The signal's parameters.
pub fn param_types(
mut self,
param_types: impl IntoIterator<Item = impl Into<SignalType>>,
) -> Self {
self.param_types = param_types
.into_iter()
.map(|t| t.into())
.collect::<Vec<_>>();
self
}
// rustdoc-stripper-ignore-next
/// The signal's returned value type.
pub fn return_type<T: StaticType>(mut self) -> Self {
self.return_type = T::static_type().into();
self
}
// rustdoc-stripper-ignore-next
/// The signal's returned value type.
pub fn return_type_from(mut self, type_: impl Into<SignalType>) -> Self {
self.return_type = type_.into();
self
}
// rustdoc-stripper-ignore-next
/// Run the signal class handler in the first emission stage.
pub fn run_first(mut self) -> Self {
self.flags |= SignalFlags::RUN_FIRST;
self
}
// rustdoc-stripper-ignore-next
/// Run the signal class handler in the third emission stage.
pub fn run_last(mut self) -> Self {
self.flags |= SignalFlags::RUN_LAST;
self
}
// rustdoc-stripper-ignore-next
/// Run the signal class handler in the last emission stage.
pub fn run_cleanup(mut self) -> Self {
self.flags |= SignalFlags::RUN_CLEANUP;
self
}
// rustdoc-stripper-ignore-next
/// Signals being emitted for an object while currently being in emission for this very object
/// will not be emitted recursively, but instead cause the first emission to be restarted.
pub fn no_recurse(mut self) -> Self {
self.flags |= SignalFlags::NO_RECURSE;
self
}
// rustdoc-stripper-ignore-next
/// This signal supports "::detail" appendices to the signal name upon handler connections and
/// emissions.
pub fn detailed(mut self) -> Self {
self.flags |= SignalFlags::DETAILED;
self
}
// rustdoc-stripper-ignore-next
/// Action signals are signals that may freely be emitted on alive objects from user code.
pub fn action(mut self) -> Self {
self.flags |= SignalFlags::ACTION;
self
}
// rustdoc-stripper-ignore-next
/// No emissions hooks are supported for this signal.
pub fn no_hooks(mut self) -> Self {
self.flags |= SignalFlags::NO_HOOKS;
self
}
// rustdoc-stripper-ignore-next
/// Varargs signal emission will always collect the arguments, even if there are no signal
/// handlers connected.
pub fn must_collect(mut self) -> Self {
self.flags |= SignalFlags::MUST_COLLECT;
self
}
// rustdoc-stripper-ignore-next
/// The signal is deprecated and will be removed in a future version.
pub fn deprecated(mut self) -> Self {
self.flags |= SignalFlags::DEPRECATED;
self
}
// rustdoc-stripper-ignore-next
/// Explicitly set all flags.
///
/// This overrides previously set flags on this builder.
pub fn flags(mut self, flags: SignalFlags) -> Self {
self.flags = flags;
self
}
// rustdoc-stripper-ignore-next
/// Class handler for this signal.
pub fn class_handler<
F: Fn(&SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
>(
mut self,
func: F,
) -> Self {
self.class_handler = Some(Box::new(func));
self
}
// rustdoc-stripper-ignore-next
/// Accumulator for the return values of the signal.
///
/// This is called if multiple signal handlers are connected to the signal for accumulating the
/// return values into a single value.
pub fn accumulator<
F: Fn(&SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static,
>(
mut self,
func: F,
) -> Self {
self.accumulator = Some(Box::new(func));
self
}
// rustdoc-stripper-ignore-next
/// Build the signal.
///
/// This does not register the signal yet, which only happens as part of object type
/// registration.
#[must_use = "Signal returned from the builder must be used for it to be registered"]
pub fn build(self) -> Signal {
let flags = if self.flags
& (SignalFlags::RUN_FIRST | SignalFlags::RUN_LAST | SignalFlags::RUN_CLEANUP)
== SignalFlags::empty()
{
self.flags | SignalFlags::RUN_LAST
} else {
self.flags
};
Signal {
name: self.name,
flags,
param_types: self.param_types.to_vec(),
return_type: self.return_type,
registration: Mutex::new(SignalRegistration::Unregistered {
class_handler: self.class_handler,
accumulator: self.accumulator,
}),
}
}
}
impl Signal {
// rustdoc-stripper-ignore-next
/// Create a new builder for a signal.
pub fn builder(name: &str) -> SignalBuilder {
assert!(
is_canonical_pspec_name(name),
"{name} is not a valid canonical signal name",
);
SignalBuilder {
name: name.to_owned(),
param_types: Vec::default(),
return_type: <()>::static_type().into(),
flags: SignalFlags::empty(),
class_handler: None,
accumulator: None,
}
}
// rustdoc-stripper-ignore-next
/// Name of the signal.
#[inline]
pub fn name(&self) -> &str {
&self.name
}
// rustdoc-stripper-ignore-next
/// Flags of the signal.
#[inline]
pub fn flags(&self) -> SignalFlags {
self.flags
}
// rustdoc-stripper-ignore-next
/// Parameter types of the signal.
#[inline]
pub fn param_types(&self) -> &[SignalType] {
&self.param_types
}
// rustdoc-stripper-ignore-next
/// Return type of the signal.
#[inline]
pub fn return_type(&self) -> SignalType {
self.return_type
}
// rustdoc-stripper-ignore-next
/// Signal ID.
///
/// This will panic if called before the signal was registered.
#[inline]
pub fn signal_id(&self) -> SignalId {
match &*self.registration.lock().unwrap() {
SignalRegistration::Unregistered { .. } => panic!("Signal not registered yet"),
SignalRegistration::Registered { signal_id, .. } => *signal_id,
}
}
// rustdoc-stripper-ignore-next
/// Type this signal was registered for.
///
/// This will panic if called before the signal was registered.
#[inline]
pub fn type_(&self) -> Type {
match &*self.registration.lock().unwrap() {
SignalRegistration::Unregistered { .. } => panic!("Signal not registered yet"),
SignalRegistration::Registered { type_, .. } => *type_,
}
}
pub(super) fn register(&self, type_: Type) {
let mut registration = self.registration.lock().unwrap();
let (class_handler, accumulator) = match &mut *registration {
SignalRegistration::Unregistered {
class_handler,
accumulator,
} => (class_handler.take(), accumulator.take()),
SignalRegistration::Registered { .. } => unreachable!(),
};
let return_type = self.return_type;
let class_handler = class_handler.map(|class_handler| {
Closure::new(move |values| unsafe {
let instance = gobject_ffi::g_value_get_object(values[0].to_glib_none().0);
let res = class_handler(&SignalClassHandlerToken(instance as *mut _, return_type.into(), values.as_ptr()), values);
if return_type == Type::UNIT {
if let Some(ref v) = res {
panic!("Signal has no return value but class handler returned a value of type {}", v.type_());
}
} else {
match res {
None => {
panic!("Signal has a return value but class handler returned none");
}
Some(ref v) => {
assert!(v.type_().is_a(return_type.into()), "Signal has a return type of {} but class handler returned {}", Type::from(return_type), v.type_());
}
}
}
res
})
});
unsafe extern "C" fn accumulator_trampoline(
ihint: *mut gobject_ffi::GSignalInvocationHint,
return_accu: *mut gobject_ffi::GValue,
handler_return: *const gobject_ffi::GValue,
data: ffi::gpointer,
) -> ffi::gboolean {
let accumulator = &*(data as *const (
SignalType,
Box<
dyn Fn(&SignalInvocationHint, &mut Value, &Value) -> bool
+ Send
+ Sync
+ 'static,
>,
));
let return_accu = &mut *(return_accu as *mut Value);
let handler_return = &*(handler_return as *const Value);
let return_type = accumulator.0;
assert!(
handler_return.type_().is_a(return_type.into()),
"Signal has a return type of {} but handler returned {}",
Type::from(return_type),
handler_return.type_()
);
let res = (accumulator.1)(&SignalInvocationHint(*ihint), return_accu, handler_return)
.into_glib();
assert!(
return_accu.type_().is_a(return_type.into()),
"Signal has a return type of {} but accumulator returned {}",
Type::from(return_type),
return_accu.type_()
);
res
}
let (accumulator, accumulator_trampoline) =
if let (Some(accumulator), true) = (accumulator, return_type != Type::UNIT) {
(
Box::into_raw(Box::new((return_type, accumulator))),
Some::<unsafe extern "C" fn(_, _, _, _) -> _>(accumulator_trampoline),
)
} else {
(ptr::null_mut(), None)
};
unsafe {
let signal_id = gobject_ffi::g_signal_newv(
self.name.to_glib_none().0,
type_.into_glib(),
self.flags.into_glib(),
class_handler.to_glib_none().0,
accumulator_trampoline,
accumulator as ffi::gpointer,
None,
return_type.into_glib(),
self.param_types.len() as u32,
self.param_types.as_ptr() as *mut _,
);
*registration = SignalRegistration::Registered {
type_,
signal_id: SignalId::from_glib(signal_id),
};
}
}
}
File diff suppressed because it is too large. Load diff