diff --git a/gdk-pixbuf/src/lib.rs b/gdk-pixbuf/src/lib.rs index 27d7f8e08937..64678b2fd699 100644 --- a/gdk-pixbuf/src/lib.rs +++ b/gdk-pixbuf/src/lib.rs @@ -11,6 +11,8 @@ pub use glib; #[allow(unused_imports)] mod auto; +pub mod subclass; + mod pixbuf; mod pixbuf_animation; mod pixbuf_animation_iter; diff --git a/gdk-pixbuf/src/pixbuf_animation_iter.rs b/gdk-pixbuf/src/pixbuf_animation_iter.rs index 2f840febf480..cac066665061 100644 --- a/gdk-pixbuf/src/pixbuf_animation_iter.rs +++ b/gdk-pixbuf/src/pixbuf_animation_iter.rs @@ -7,7 +7,7 @@ use std::time::SystemTime; glib::wrapper! { #[doc(alias = "GdkPixbufAnimationIter")] - pub struct PixbufAnimationIter(Object); + pub struct PixbufAnimationIter(Object); match fn { type_ => || ffi::gdk_pixbuf_animation_iter_get_type(), diff --git a/gdk-pixbuf/src/subclass/mod.rs b/gdk-pixbuf/src/subclass/mod.rs new file mode 100644 index 000000000000..78d40bed1f43 --- /dev/null +++ b/gdk-pixbuf/src/subclass/mod.rs @@ -0,0 +1,17 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +// rustdoc-stripper-ignore-next +//! Traits intended for creating custom types. + +pub mod pixbuf_animation; +pub mod pixbuf_animation_iter; +pub mod pixbuf_loader; + +pub mod prelude { + pub use gio::subclass::prelude::*; + pub use glib::subclass::prelude::*; + + pub use super::pixbuf_animation::{PixbufAnimationImpl, PixbufAnimationImplExt}; + pub use super::pixbuf_animation_iter::{PixbufAnimationIterImpl, PixbufAnimationIterImplExt}; + pub use super::pixbuf_loader::{PixbufLoaderImpl, PixbufLoaderImplExt}; +} diff --git a/gdk-pixbuf/src/subclass/pixbuf_animation.rs b/gdk-pixbuf/src/subclass/pixbuf_animation.rs new file mode 100644 index 000000000000..b7452b9c6ac2 --- /dev/null +++ b/gdk-pixbuf/src/subclass/pixbuf_animation.rs @@ -0,0 +1,170 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +// rustdoc-stripper-ignore-next +//! Traits intended for subclassing [`PixbufAnimation`](crate::PixbufAnimation). + +use std::mem::MaybeUninit; +use std::time::Duration; + +use crate::{Pixbuf, PixbufAnimation, PixbufAnimationIter}; +use glib::subclass::prelude::*; +use glib::translate::*; +use glib::Cast; + +pub trait PixbufAnimationImpl: ObjectImpl { + fn is_static_image(&self) -> bool { + self.parent_is_static_image() + } + + fn static_image(&self) -> Option { + self.parent_static_image() + } + + fn size(&self) -> (i32, i32) { + self.parent_size() + } + + fn iter(&self, start_time: Duration) -> PixbufAnimationIter { + self.parent_iter(start_time) + } +} + +pub trait PixbufAnimationImplExt: ObjectSubclass { + fn parent_is_static_image(&self) -> bool; + fn parent_static_image(&self) -> Option; + fn parent_size(&self) -> (i32, i32); + fn parent_iter(&self, start_time: Duration) -> PixbufAnimationIter; +} + +impl PixbufAnimationImplExt for T { + fn parent_is_static_image(&self) -> bool { + unsafe { + let data = T::type_data(); + let parent_class = data.as_ref().parent_class() as *mut ffi::GdkPixbufAnimationClass; + let f = (*parent_class) + .is_static_image + .expect("No parent class implementation for \"is_static_image\""); + + from_glib(f(self + .obj() + .unsafe_cast_ref::() + .to_glib_none() + .0)) + } + } + + fn parent_static_image(&self) -> Option { + unsafe { + let data = T::type_data(); + let parent_class = data.as_ref().parent_class() as *mut ffi::GdkPixbufAnimationClass; + let f = (*parent_class) + .get_static_image + .expect("No parent class implementation for \"get_static_image\""); + + from_glib_none(f(self + .obj() + .unsafe_cast_ref::() + .to_glib_none() + .0)) + } + } + + fn parent_size(&self) -> (i32, i32) { + unsafe { + let data = T::type_data(); + let parent_class = data.as_ref().parent_class() as *mut ffi::GdkPixbufAnimationClass; + let f = (*parent_class) + .get_size + .expect("No parent class implementation for \"get_size\""); + let mut width = MaybeUninit::uninit(); + let mut height = MaybeUninit::uninit(); + f( + self.obj() + .unsafe_cast_ref::() + .to_glib_none() + .0, + width.as_mut_ptr(), + height.as_mut_ptr(), + ); + (width.assume_init(), height.assume_init()) + } + } + + fn parent_iter(&self, start_time: Duration) -> PixbufAnimationIter { + unsafe { + let data = T::type_data(); + let parent_class = data.as_ref().parent_class() as *mut ffi::GdkPixbufAnimationClass; + let f = (*parent_class) + .get_iter + .expect("No parent class implementation for \"get_iter\""); + + let time = glib::ffi::GTimeVal { + tv_sec: start_time.as_secs() as _, + tv_usec: start_time.subsec_micros() as _, + }; + from_glib_full(f( + self.obj() + .unsafe_cast_ref::() + .to_glib_none() + .0, + &time as *const _, + )) + } + } +} + +unsafe impl IsSubclassable for PixbufAnimation { + fn class_init(class: &mut ::glib::Class) { + Self::parent_class_init::(class); + + let klass = class.as_mut(); + klass.get_static_image = Some(animation_get_static_image::); + klass.get_size = Some(animation_get_size::); + klass.get_iter = Some(animation_get_iter::); + klass.is_static_image = Some(animation_is_static_image::); + } +} + +unsafe extern "C" fn animation_is_static_image( + ptr: *mut ffi::GdkPixbufAnimation, +) -> glib::ffi::gboolean { + let instance = &*(ptr as *mut T::Instance); + let imp = instance.imp(); + + imp.is_static_image().into_glib() +} + +unsafe extern "C" fn animation_get_size( + ptr: *mut ffi::GdkPixbufAnimation, + width_ptr: *mut libc::c_int, + height_ptr: *mut libc::c_int, +) { + let instance = &*(ptr as *mut T::Instance); + let imp = instance.imp(); + + let (width, height) = imp.size(); + *width_ptr = width; + *height_ptr = height; +} + +unsafe extern "C" fn animation_get_static_image( + ptr: *mut ffi::GdkPixbufAnimation, +) -> *mut ffi::GdkPixbuf { + let instance = &*(ptr as *mut T::Instance); + let imp = instance.imp(); + + imp.static_image().to_glib_none().0 +} + +unsafe extern "C" fn animation_get_iter( + ptr: *mut ffi::GdkPixbufAnimation, + start_time_ptr: *const glib::ffi::GTimeVal, +) -> *mut ffi::GdkPixbufAnimationIter { + let instance = &*(ptr as *mut T::Instance); + let imp = instance.imp(); + + let total = Duration::from_secs((*start_time_ptr).tv_sec.try_into().unwrap()) + + Duration::from_micros((*start_time_ptr).tv_usec.try_into().unwrap()); + + imp.iter(total).to_glib_full() +} diff --git a/gdk-pixbuf/src/subclass/pixbuf_animation_iter.rs b/gdk-pixbuf/src/subclass/pixbuf_animation_iter.rs new file mode 100644 index 000000000000..72e037342cf9 --- /dev/null +++ b/gdk-pixbuf/src/subclass/pixbuf_animation_iter.rs @@ -0,0 +1,172 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +// rustdoc-stripper-ignore-next +//! Traits intended for subclassing [`PixbufAnimationIter`](crate::PixbufAnimationIter). + +use std::time::Duration; + +use glib::subclass::prelude::*; +use glib::translate::*; +use glib::Cast; + +use crate::{Pixbuf, PixbufAnimationIter}; + +pub trait PixbufAnimationIterImpl: ObjectImpl { + // rustdoc-stripper-ignore-next + /// Time in milliseconds, returning `None` implies showing the same pixbuf forever. + fn delay_time(&self) -> Option { + self.parent_delay_time() + } + + fn pixbuf(&self) -> Pixbuf { + self.parent_pixbuf() + } + + fn on_currently_loading_frame(&self) -> bool { + self.parent_on_currently_loading_frame() + } + + fn advance(&self, time: Duration) -> bool { + self.parent_advance(time) + } +} + +pub trait PixbufAnimationIterImplExt: ObjectSubclass { + fn parent_delay_time(&self) -> Option; + fn parent_pixbuf(&self) -> Pixbuf; + fn parent_on_currently_loading_frame(&self) -> bool; + fn parent_advance(&self, time: Duration) -> bool; +} + +impl PixbufAnimationIterImplExt for T { + fn parent_delay_time(&self) -> Option { + unsafe { + let data = T::type_data(); + let parent_class = + data.as_ref().parent_class() as *mut ffi::GdkPixbufAnimationIterClass; + let f = (*parent_class) + .get_delay_time + .expect("No parent class implementation for \"get_delay_time\""); + + let time = f(self + .obj() + .unsafe_cast_ref::() + .to_glib_none() + .0); + if time == -1 { + None + } else { + Some(Duration::from_millis(time.try_into().unwrap())) + } + } + } + + fn parent_pixbuf(&self) -> Pixbuf { + unsafe { + let data = T::type_data(); + let parent_class = + data.as_ref().parent_class() as *mut ffi::GdkPixbufAnimationIterClass; + let f = (*parent_class) + .get_pixbuf + .expect("No parent class implementation for \"get_pixbuf\""); + + from_glib_none(f(self + .obj() + .unsafe_cast_ref::() + .to_glib_none() + .0)) + } + } + + fn parent_on_currently_loading_frame(&self) -> bool { + unsafe { + let data = T::type_data(); + let parent_class = + data.as_ref().parent_class() as *mut ffi::GdkPixbufAnimationIterClass; + let f = (*parent_class) + .on_currently_loading_frame + .expect("No parent class implementation for \"on_currently_loading_frame\""); + + from_glib(f(self + .obj() + .unsafe_cast_ref::() + .to_glib_none() + .0)) + } + } + + fn parent_advance(&self, time: Duration) -> bool { + unsafe { + let data = T::type_data(); + let parent_class = + data.as_ref().parent_class() as *mut ffi::GdkPixbufAnimationIterClass; + let f = (*parent_class) + .advance + .expect("No parent class implementation for \"advance\""); + + let time = glib::ffi::GTimeVal { + tv_sec: time.as_secs() as _, + tv_usec: time.subsec_micros() as _, + }; + from_glib(f( + self.obj() + .unsafe_cast_ref::() + .to_glib_none() + .0, + &time as *const _, + )) + } + } +} + +unsafe impl IsSubclassable for PixbufAnimationIter { + fn class_init(class: &mut ::glib::Class) { + Self::parent_class_init::(class); + + let klass = class.as_mut(); + klass.get_delay_time = Some(animation_iter_get_delay_time::); + klass.get_pixbuf = Some(animation_iter_get_pixbuf::); + klass.on_currently_loading_frame = Some(animation_iter_on_currently_loading_frame::); + klass.advance = Some(animation_iter_advance::); + } +} + +unsafe extern "C" fn animation_iter_get_delay_time( + ptr: *mut ffi::GdkPixbufAnimationIter, +) -> i32 { + let instance = &*(ptr as *mut T::Instance); + let imp = instance.imp(); + + imp.delay_time().map(|t| t.as_millis() as i32).unwrap_or(-1) +} + +unsafe extern "C" fn animation_iter_get_pixbuf( + ptr: *mut ffi::GdkPixbufAnimationIter, +) -> *mut ffi::GdkPixbuf { + let instance = &*(ptr as *mut T::Instance); + let imp = instance.imp(); + + imp.pixbuf().to_glib_none().0 +} + +unsafe extern "C" fn animation_iter_on_currently_loading_frame( + ptr: *mut ffi::GdkPixbufAnimationIter, +) -> glib::ffi::gboolean { + let instance = &*(ptr as *mut T::Instance); + let imp = instance.imp(); + + imp.on_currently_loading_frame().into_glib() +} + +unsafe extern "C" fn animation_iter_advance( + ptr: *mut ffi::GdkPixbufAnimationIter, + time_ptr: *const glib::ffi::GTimeVal, +) -> glib::ffi::gboolean { + let instance = &*(ptr as *mut T::Instance); + let imp = instance.imp(); + + let total = Duration::from_secs((*time_ptr).tv_sec.try_into().unwrap()) + + Duration::from_micros((*time_ptr).tv_usec.try_into().unwrap()); + + imp.advance(total).into_glib() +} diff --git a/gdk-pixbuf/src/subclass/pixbuf_loader.rs b/gdk-pixbuf/src/subclass/pixbuf_loader.rs new file mode 100644 index 000000000000..e31e7e11b923 --- /dev/null +++ b/gdk-pixbuf/src/subclass/pixbuf_loader.rs @@ -0,0 +1,150 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +// rustdoc-stripper-ignore-next +//! Traits intended for subclassing [`PixbufLoader`](crate::PixbufLoader). + +use crate::PixbufLoader; +use glib::subclass::prelude::*; +use glib::translate::*; +use glib::Cast; + +pub trait PixbufLoaderImpl: ObjectImpl { + fn size_prepared(&self, width: i32, height: i32) { + self.parent_size_prepared(width, height) + } + + fn area_prepared(&self) { + self.parent_area_prepared() + } + + fn area_updated(&self, x: i32, y: i32, width: i32, height: i32) { + self.parent_area_updated(x, y, width, height) + } + + fn closed(&self) { + self.parent_closed() + } +} + +pub trait PixbufLoaderImplExt: ObjectSubclass { + fn parent_size_prepared(&self, width: i32, height: i32); + fn parent_area_prepared(&self); + fn parent_area_updated(&self, x: i32, y: i32, width: i32, height: i32); + fn parent_closed(&self); +} + +impl PixbufLoaderImplExt for T { + fn parent_size_prepared(&self, width: i32, height: i32) { + unsafe { + let data = T::type_data(); + let parent_class = data.as_ref().parent_class() as *mut ffi::GdkPixbufLoaderClass; + if let Some(f) = (*parent_class).size_prepared { + f( + self.obj() + .unsafe_cast_ref::() + .to_glib_none() + .0, + width, + height, + ) + } + } + } + + fn parent_area_prepared(&self) { + unsafe { + let data = T::type_data(); + let parent_class = data.as_ref().parent_class() as *mut ffi::GdkPixbufLoaderClass; + if let Some(f) = (*parent_class).area_prepared { + f(self + .obj() + .unsafe_cast_ref::() + .to_glib_none() + .0) + } + } + } + + fn parent_area_updated(&self, x: i32, y: i32, width: i32, height: i32) { + unsafe { + let data = T::type_data(); + let parent_class = data.as_ref().parent_class() as *mut ffi::GdkPixbufLoaderClass; + if let Some(f) = (*parent_class).area_updated { + f( + self.obj() + .unsafe_cast_ref::() + .to_glib_none() + .0, + x, + y, + width, + height, + ) + } + } + } + + fn parent_closed(&self) { + unsafe { + let data = T::type_data(); + let parent_class = data.as_ref().parent_class() as *mut ffi::GdkPixbufLoaderClass; + if let Some(f) = (*parent_class).closed { + f(self + .obj() + .unsafe_cast_ref::() + .to_glib_none() + .0) + } + } + } +} + +unsafe impl IsSubclassable for PixbufLoader { + fn class_init(class: &mut ::glib::Class) { + Self::parent_class_init::(class); + + let klass = class.as_mut(); + klass.size_prepared = Some(loader_size_prepared::); + klass.area_prepared = Some(loader_area_prepared::); + klass.area_updated = Some(loader_area_updated::); + klass.closed = Some(loader_closed::); + } +} + +unsafe extern "C" fn loader_size_prepared( + ptr: *mut ffi::GdkPixbufLoader, + width: i32, + height: i32, +) { + let instance = &*(ptr as *mut T::Instance); + let imp = instance.imp(); + + imp.size_prepared(width, height) +} + +unsafe extern "C" fn loader_area_prepared(ptr: *mut ffi::GdkPixbufLoader) { + let instance = &*(ptr as *mut T::Instance); + let imp = instance.imp(); + + imp.area_prepared(); +} + +unsafe extern "C" fn loader_area_updated( + ptr: *mut ffi::GdkPixbufLoader, + x: i32, + y: i32, + width: i32, + height: i32, +) { + let instance = &*(ptr as *mut T::Instance); + let imp = instance.imp(); + + imp.area_updated(x, y, width, height) +} + +unsafe extern "C" fn loader_closed(ptr: *mut ffi::GdkPixbufLoader) { + let instance = &*(ptr as *mut T::Instance); + let imp = instance.imp(); + + imp.closed() +} diff --git a/glib/src/boxed.rs b/glib/src/boxed.rs index d74ccadc2fe1..b4d0963bec79 100644 --- a/glib/src/boxed.rs +++ b/glib/src/boxed.rs @@ -36,6 +36,7 @@ macro_rules! glib_boxed_wrapper { } impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $name $(<$($generic),+>)? { + #[doc = "Return the inner pointer to the underlying C value."] pub fn as_ptr(&self) -> *mut $ffi_name { $crate::translate::ToGlibPtr::to_glib_none(&self.inner).0 as *mut _ } diff --git a/glib/src/shared.rs b/glib/src/shared.rs index 5e2bc2d7ef30..d0df33a8036c 100644 --- a/glib/src/shared.rs +++ b/glib/src/shared.rs @@ -34,6 +34,7 @@ macro_rules! glib_shared_wrapper { } impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $name $(<$($generic),+>)? { + #[doc = "Return the inner pointer to the underlying C value."] pub fn as_ptr(&self) -> *mut $ffi_name { $crate::translate::ToGlibPtr::to_glib_none(&self.inner).0 as *mut _ } diff --git a/glib/src/subclass/object.rs b/glib/src/subclass/object.rs index 6d7beb5173d6..ca1409f44b11 100644 --- a/glib/src/subclass/object.rs +++ b/glib/src/subclass/object.rs @@ -33,6 +33,8 @@ pub trait ObjectImpl: ObjectSubclass + ObjectImplExt { /// /// 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!() } @@ -42,6 +44,8 @@ pub trait ObjectImpl: ObjectSubclass + ObjectImplExt { /// /// 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!() diff --git a/glib/src/subclass/types.rs b/glib/src/subclass/types.rs index 5ec2b11b732a..3931edad57c7 100644 --- a/glib/src/subclass/types.rs +++ b/glib/src/subclass/types.rs @@ -968,10 +968,9 @@ pub fn register_type() -> Type { // Must not be a dangling pointer so let's create some uninitialized memory let priv_ = std::mem::MaybeUninit::>::uninit(); let ptr = priv_.as_ptr(); - // FIXME: Technically UB but we'd need std::ptr::raw_const for this - let imp_ptr = &(*ptr).imp as *const _ as *const u8; + let imp_ptr = std::ptr::addr_of!((*ptr).imp) as *const u8; let ptr = ptr as *const u8; - imp_ptr as isize - ptr as isize + imp_ptr.offset_from(ptr) }; let iface_types = T::Interfaces::iface_infos();