gdk_pixbuf/subclass/
pixbuf_animation.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3// rustdoc-stripper-ignore-next
4//! Traits intended for subclassing [`PixbufAnimation`].
5
6use std::{
7    mem::MaybeUninit,
8    sync::OnceLock,
9    time::{Duration, SystemTime},
10};
11
12use glib::{prelude::*, subclass::prelude::*, translate::*};
13
14use crate::{ffi, Pixbuf, PixbufAnimation, PixbufAnimationIter};
15
16pub trait PixbufAnimationImpl: ObjectImpl {
17    fn is_static_image(&self) -> bool {
18        self.parent_is_static_image()
19    }
20
21    fn static_image(&self) -> Option<Pixbuf> {
22        self.parent_static_image()
23    }
24
25    fn size(&self) -> (i32, i32) {
26        self.parent_size()
27    }
28
29    fn iter(&self, start_time: SystemTime) -> PixbufAnimationIter {
30        self.parent_iter(start_time)
31    }
32}
33
34mod sealed {
35    pub trait Sealed {}
36    impl<T: super::PixbufAnimationImplExt> Sealed for T {}
37}
38
39pub trait PixbufAnimationImplExt: sealed::Sealed + ObjectSubclass {
40    fn parent_is_static_image(&self) -> bool {
41        unsafe {
42            let data = Self::type_data();
43            let parent_class = data.as_ref().parent_class() as *mut ffi::GdkPixbufAnimationClass;
44            let f = (*parent_class)
45                .is_static_image
46                .expect("No parent class implementation for \"is_static_image\"");
47
48            from_glib(f(self
49                .obj()
50                .unsafe_cast_ref::<PixbufAnimation>()
51                .to_glib_none()
52                .0))
53        }
54    }
55
56    fn parent_static_image(&self) -> Option<Pixbuf> {
57        unsafe {
58            let data = Self::type_data();
59            let parent_class = data.as_ref().parent_class() as *mut ffi::GdkPixbufAnimationClass;
60            let f = (*parent_class)
61                .get_static_image
62                .expect("No parent class implementation for \"get_static_image\"");
63
64            from_glib_none(f(self
65                .obj()
66                .unsafe_cast_ref::<PixbufAnimation>()
67                .to_glib_none()
68                .0))
69        }
70    }
71
72    fn parent_size(&self) -> (i32, i32) {
73        unsafe {
74            let data = Self::type_data();
75            let parent_class = data.as_ref().parent_class() as *mut ffi::GdkPixbufAnimationClass;
76            let f = (*parent_class)
77                .get_size
78                .expect("No parent class implementation for \"get_size\"");
79            let mut width = MaybeUninit::uninit();
80            let mut height = MaybeUninit::uninit();
81            f(
82                self.obj()
83                    .unsafe_cast_ref::<PixbufAnimation>()
84                    .to_glib_none()
85                    .0,
86                width.as_mut_ptr(),
87                height.as_mut_ptr(),
88            );
89            (width.assume_init(), height.assume_init())
90        }
91    }
92
93    fn parent_iter(&self, start_time: SystemTime) -> PixbufAnimationIter {
94        unsafe {
95            let data = Self::type_data();
96            let parent_class = data.as_ref().parent_class() as *mut ffi::GdkPixbufAnimationClass;
97            let f = (*parent_class)
98                .get_iter
99                .expect("No parent class implementation for \"get_iter\"");
100
101            let diff = start_time
102                .duration_since(SystemTime::UNIX_EPOCH)
103                .expect("failed to convert time");
104            let time = glib::ffi::GTimeVal {
105                tv_sec: diff.as_secs() as _,
106                tv_usec: diff.subsec_micros() as _,
107            };
108            from_glib_full(f(
109                self.obj()
110                    .unsafe_cast_ref::<PixbufAnimation>()
111                    .to_glib_none()
112                    .0,
113                &time,
114            ))
115        }
116    }
117}
118
119impl<T: PixbufAnimationImpl> PixbufAnimationImplExt for T {}
120
121unsafe impl<T: PixbufAnimationImpl> IsSubclassable<T> for PixbufAnimation {
122    fn class_init(class: &mut ::glib::Class<Self>) {
123        Self::parent_class_init::<T>(class);
124
125        let klass = class.as_mut();
126        klass.get_static_image = Some(animation_get_static_image::<T>);
127        klass.get_size = Some(animation_get_size::<T>);
128        klass.get_iter = Some(animation_get_iter::<T>);
129        klass.is_static_image = Some(animation_is_static_image::<T>);
130    }
131}
132
133unsafe extern "C" fn animation_is_static_image<T: PixbufAnimationImpl>(
134    ptr: *mut ffi::GdkPixbufAnimation,
135) -> glib::ffi::gboolean {
136    let instance = &*(ptr as *mut T::Instance);
137    let imp = instance.imp();
138
139    imp.is_static_image().into_glib()
140}
141
142unsafe extern "C" fn animation_get_size<T: PixbufAnimationImpl>(
143    ptr: *mut ffi::GdkPixbufAnimation,
144    width_ptr: *mut libc::c_int,
145    height_ptr: *mut libc::c_int,
146) {
147    if width_ptr.is_null() && height_ptr.is_null() {
148        return;
149    }
150
151    let instance = &*(ptr as *mut T::Instance);
152    let imp = instance.imp();
153
154    let (width, height) = imp.size();
155    if !width_ptr.is_null() {
156        *width_ptr = width;
157    }
158    if !height_ptr.is_null() {
159        *height_ptr = height;
160    }
161}
162
163unsafe extern "C" fn animation_get_static_image<T: PixbufAnimationImpl>(
164    ptr: *mut ffi::GdkPixbufAnimation,
165) -> *mut ffi::GdkPixbuf {
166    let instance = &*(ptr as *mut T::Instance);
167    let imp = instance.imp();
168
169    let instance = imp.obj();
170    let static_image = imp.static_image();
171    // Ensure that a) the static image stays alive as long as the animation instance and b) that
172    // the same static image is returned every time. This is a requirement by the gdk-pixbuf API.
173    let static_image_quark = {
174        static QUARK: OnceLock<glib::Quark> = OnceLock::new();
175        *QUARK.get_or_init(|| glib::Quark::from_str("gtk-rs-subclass-static-image"))
176    };
177    if let Some(old_image) = instance.qdata::<Option<Pixbuf>>(static_image_quark) {
178        let old_image = old_image.as_ref();
179
180        if let Some(old_image) = old_image {
181            assert_eq!(
182                Some(old_image),
183                static_image.as_ref(),
184                "Did not return same static image again"
185            );
186        }
187    }
188    instance.set_qdata(static_image_quark, static_image.clone());
189    static_image.to_glib_none().0
190}
191
192unsafe extern "C" fn animation_get_iter<T: PixbufAnimationImpl>(
193    ptr: *mut ffi::GdkPixbufAnimation,
194    start_time_ptr: *const glib::ffi::GTimeVal,
195) -> *mut ffi::GdkPixbufAnimationIter {
196    let instance = &*(ptr as *mut T::Instance);
197    let imp = instance.imp();
198
199    let start_time = SystemTime::UNIX_EPOCH
200        + Duration::from_secs((*start_time_ptr).tv_sec.try_into().unwrap())
201        + Duration::from_micros((*start_time_ptr).tv_usec.try_into().unwrap());
202
203    imp.iter(start_time).into_glib_ptr()
204}