gdk_pixbuf/subclass/
pixbuf_animation.rs1use 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 {
28        self.parent_is_static_image()
29    }
30
31    fn static_image(&self) -> Option<Pixbuf> {
47        self.parent_static_image()
48    }
49
50    fn size(&self) -> (i32, i32) {
52        self.parent_size()
53    }
54
55    fn iter(&self, start_time: SystemTime) -> PixbufAnimationIter {
96        self.parent_iter(start_time)
97    }
98}
99
100pub trait PixbufAnimationImplExt: PixbufAnimationImpl {
101    fn parent_is_static_image(&self) -> bool {
102        unsafe {
103            let data = Self::type_data();
104            let parent_class = data.as_ref().parent_class() as *mut ffi::GdkPixbufAnimationClass;
105            let f = (*parent_class)
106                .is_static_image
107                .expect("No parent class implementation for \"is_static_image\"");
108
109            from_glib(f(self
110                .obj()
111                .unsafe_cast_ref::<PixbufAnimation>()
112                .to_glib_none()
113                .0))
114        }
115    }
116
117    fn parent_static_image(&self) -> Option<Pixbuf> {
118        unsafe {
119            let data = Self::type_data();
120            let parent_class = data.as_ref().parent_class() as *mut ffi::GdkPixbufAnimationClass;
121            let f = (*parent_class)
122                .get_static_image
123                .expect("No parent class implementation for \"get_static_image\"");
124
125            from_glib_none(f(self
126                .obj()
127                .unsafe_cast_ref::<PixbufAnimation>()
128                .to_glib_none()
129                .0))
130        }
131    }
132
133    fn parent_size(&self) -> (i32, i32) {
134        unsafe {
135            let data = Self::type_data();
136            let parent_class = data.as_ref().parent_class() as *mut ffi::GdkPixbufAnimationClass;
137            let f = (*parent_class)
138                .get_size
139                .expect("No parent class implementation for \"get_size\"");
140            let mut width = MaybeUninit::uninit();
141            let mut height = MaybeUninit::uninit();
142            f(
143                self.obj()
144                    .unsafe_cast_ref::<PixbufAnimation>()
145                    .to_glib_none()
146                    .0,
147                width.as_mut_ptr(),
148                height.as_mut_ptr(),
149            );
150            (width.assume_init(), height.assume_init())
151        }
152    }
153
154    fn parent_iter(&self, start_time: SystemTime) -> PixbufAnimationIter {
155        unsafe {
156            let data = Self::type_data();
157            let parent_class = data.as_ref().parent_class() as *mut ffi::GdkPixbufAnimationClass;
158            let f = (*parent_class)
159                .get_iter
160                .expect("No parent class implementation for \"get_iter\"");
161
162            let diff = start_time
163                .duration_since(SystemTime::UNIX_EPOCH)
164                .expect("failed to convert time");
165            let time = glib::ffi::GTimeVal {
166                tv_sec: diff.as_secs() as _,
167                tv_usec: diff.subsec_micros() as _,
168            };
169            from_glib_full(f(
170                self.obj()
171                    .unsafe_cast_ref::<PixbufAnimation>()
172                    .to_glib_none()
173                    .0,
174                &time,
175            ))
176        }
177    }
178}
179
180impl<T: PixbufAnimationImpl> PixbufAnimationImplExt for T {}
181
182unsafe impl<T: PixbufAnimationImpl> IsSubclassable<T> for PixbufAnimation {
183    fn class_init(class: &mut ::glib::Class<Self>) {
184        Self::parent_class_init::<T>(class);
185
186        let klass = class.as_mut();
187        klass.get_static_image = Some(animation_get_static_image::<T>);
188        klass.get_size = Some(animation_get_size::<T>);
189        klass.get_iter = Some(animation_get_iter::<T>);
190        klass.is_static_image = Some(animation_is_static_image::<T>);
191    }
192}
193
194unsafe extern "C" fn animation_is_static_image<T: PixbufAnimationImpl>(
195    ptr: *mut ffi::GdkPixbufAnimation,
196) -> glib::ffi::gboolean {
197    let instance = &*(ptr as *mut T::Instance);
198    let imp = instance.imp();
199
200    imp.is_static_image().into_glib()
201}
202
203unsafe extern "C" fn animation_get_size<T: PixbufAnimationImpl>(
204    ptr: *mut ffi::GdkPixbufAnimation,
205    width_ptr: *mut libc::c_int,
206    height_ptr: *mut libc::c_int,
207) {
208    if width_ptr.is_null() && height_ptr.is_null() {
209        return;
210    }
211
212    let instance = &*(ptr as *mut T::Instance);
213    let imp = instance.imp();
214
215    let (width, height) = imp.size();
216    if !width_ptr.is_null() {
217        *width_ptr = width;
218    }
219    if !height_ptr.is_null() {
220        *height_ptr = height;
221    }
222}
223
224unsafe extern "C" fn animation_get_static_image<T: PixbufAnimationImpl>(
225    ptr: *mut ffi::GdkPixbufAnimation,
226) -> *mut ffi::GdkPixbuf {
227    let instance = &*(ptr as *mut T::Instance);
228    let imp = instance.imp();
229
230    let instance = imp.obj();
231    let static_image = imp.static_image();
232    let static_image_quark = {
235        static QUARK: OnceLock<glib::Quark> = OnceLock::new();
236        *QUARK.get_or_init(|| glib::Quark::from_str("gtk-rs-subclass-static-image"))
237    };
238    if let Some(old_image) = instance.qdata::<Option<Pixbuf>>(static_image_quark) {
239        let old_image = old_image.as_ref();
240
241        if let Some(old_image) = old_image {
242            assert_eq!(
243                Some(old_image),
244                static_image.as_ref(),
245                "Did not return same static image again"
246            );
247        }
248    }
249    instance.set_qdata(static_image_quark, static_image.clone());
250    static_image.to_glib_none().0
251}
252
253unsafe extern "C" fn animation_get_iter<T: PixbufAnimationImpl>(
254    ptr: *mut ffi::GdkPixbufAnimation,
255    start_time_ptr: *const glib::ffi::GTimeVal,
256) -> *mut ffi::GdkPixbufAnimationIter {
257    let instance = &*(ptr as *mut T::Instance);
258    let imp = instance.imp();
259
260    let start_time = SystemTime::UNIX_EPOCH
261        + Duration::from_secs((*start_time_ptr).tv_sec.try_into().unwrap())
262        + Duration::from_micros((*start_time_ptr).tv_usec.try_into().unwrap());
263
264    imp.iter(start_time).into_glib_ptr()
265}