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