gdk_pixbuf/subclass/
pixbuf_animation_iter.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 [`PixbufAnimationIter`].
5
6use std::{
7    sync::OnceLock,
8    time::{Duration, SystemTime},
9};
10
11use glib::{prelude::*, subclass::prelude::*, translate::*};
12
13use crate::{ffi, Pixbuf, PixbufAnimationIter};
14
15pub trait PixbufAnimationIterImpl:
16    ObjectImpl + ObjectSubclass<Type: IsA<PixbufAnimationIter>>
17{
18    // rustdoc-stripper-ignore-next
19    /// Time in milliseconds, returning `None` implies showing the same pixbuf forever.
20    fn delay_time(&self) -> Option<Duration> {
21        self.parent_delay_time()
22    }
23
24    fn pixbuf(&self) -> Pixbuf {
25        self.parent_pixbuf()
26    }
27
28    fn on_currently_loading_frame(&self) -> bool {
29        self.parent_on_currently_loading_frame()
30    }
31
32    fn advance(&self, current_time: SystemTime) -> bool {
33        self.parent_advance(current_time)
34    }
35}
36
37pub trait PixbufAnimationIterImplExt: PixbufAnimationIterImpl {
38    fn parent_delay_time(&self) -> Option<Duration> {
39        unsafe {
40            let data = Self::type_data();
41            let parent_class =
42                data.as_ref().parent_class() as *mut ffi::GdkPixbufAnimationIterClass;
43            let f = (*parent_class)
44                .get_delay_time
45                .expect("No parent class implementation for \"get_delay_time\"");
46
47            let time = f(self
48                .obj()
49                .unsafe_cast_ref::<PixbufAnimationIter>()
50                .to_glib_none()
51                .0);
52            if time < 0 {
53                None
54            } else {
55                Some(Duration::from_millis(time as u64))
56            }
57        }
58    }
59
60    fn parent_pixbuf(&self) -> Pixbuf {
61        unsafe {
62            let data = Self::type_data();
63            let parent_class =
64                data.as_ref().parent_class() as *mut ffi::GdkPixbufAnimationIterClass;
65            let f = (*parent_class)
66                .get_pixbuf
67                .expect("No parent class implementation for \"get_pixbuf\"");
68
69            from_glib_none(f(self
70                .obj()
71                .unsafe_cast_ref::<PixbufAnimationIter>()
72                .to_glib_none()
73                .0))
74        }
75    }
76
77    fn parent_on_currently_loading_frame(&self) -> bool {
78        unsafe {
79            let data = Self::type_data();
80            let parent_class =
81                data.as_ref().parent_class() as *mut ffi::GdkPixbufAnimationIterClass;
82            let f = (*parent_class)
83                .on_currently_loading_frame
84                .expect("No parent class implementation for \"on_currently_loading_frame\"");
85
86            from_glib(f(self
87                .obj()
88                .unsafe_cast_ref::<PixbufAnimationIter>()
89                .to_glib_none()
90                .0))
91        }
92    }
93
94    fn parent_advance(&self, current_time: SystemTime) -> bool {
95        unsafe {
96            let data = Self::type_data();
97            let parent_class =
98                data.as_ref().parent_class() as *mut ffi::GdkPixbufAnimationIterClass;
99            let f = (*parent_class)
100                .advance
101                .expect("No parent class implementation for \"advance\"");
102
103            let diff = current_time
104                .duration_since(SystemTime::UNIX_EPOCH)
105                .expect("failed to convert time");
106            let time = glib::ffi::GTimeVal {
107                tv_sec: diff.as_secs() as _,
108                tv_usec: diff.subsec_micros() as _,
109            };
110            from_glib(f(
111                self.obj()
112                    .unsafe_cast_ref::<PixbufAnimationIter>()
113                    .to_glib_none()
114                    .0,
115                &time,
116            ))
117        }
118    }
119}
120
121impl<T: PixbufAnimationIterImpl> PixbufAnimationIterImplExt for T {}
122
123unsafe impl<T: PixbufAnimationIterImpl> IsSubclassable<T> for PixbufAnimationIter {
124    fn class_init(class: &mut ::glib::Class<Self>) {
125        Self::parent_class_init::<T>(class);
126
127        let klass = class.as_mut();
128        klass.get_delay_time = Some(animation_iter_get_delay_time::<T>);
129        klass.get_pixbuf = Some(animation_iter_get_pixbuf::<T>);
130        klass.on_currently_loading_frame = Some(animation_iter_on_currently_loading_frame::<T>);
131        klass.advance = Some(animation_iter_advance::<T>);
132    }
133}
134
135unsafe extern "C" fn animation_iter_get_delay_time<T: PixbufAnimationIterImpl>(
136    ptr: *mut ffi::GdkPixbufAnimationIter,
137) -> i32 {
138    let instance = &*(ptr as *mut T::Instance);
139    let imp = instance.imp();
140
141    imp.delay_time().map(|t| t.as_millis() as i32).unwrap_or(-1)
142}
143
144unsafe extern "C" fn animation_iter_get_pixbuf<T: PixbufAnimationIterImpl>(
145    ptr: *mut ffi::GdkPixbufAnimationIter,
146) -> *mut ffi::GdkPixbuf {
147    let instance = &*(ptr as *mut T::Instance);
148    let imp = instance.imp();
149
150    let pixbuf = imp.pixbuf();
151    // Ensure that the pixbuf stays alive until the next call
152    let pixbuf_quark = {
153        static QUARK: OnceLock<glib::Quark> = OnceLock::new();
154        *QUARK.get_or_init(|| glib::Quark::from_str("gtk-rs-subclass-pixbuf"))
155    };
156    imp.obj().set_qdata(pixbuf_quark, pixbuf.clone());
157    pixbuf.to_glib_none().0
158}
159
160unsafe extern "C" fn animation_iter_on_currently_loading_frame<T: PixbufAnimationIterImpl>(
161    ptr: *mut ffi::GdkPixbufAnimationIter,
162) -> glib::ffi::gboolean {
163    let instance = &*(ptr as *mut T::Instance);
164    let imp = instance.imp();
165
166    imp.on_currently_loading_frame().into_glib()
167}
168
169unsafe extern "C" fn animation_iter_advance<T: PixbufAnimationIterImpl>(
170    ptr: *mut ffi::GdkPixbufAnimationIter,
171    current_time_ptr: *const glib::ffi::GTimeVal,
172) -> glib::ffi::gboolean {
173    let instance = &*(ptr as *mut T::Instance);
174    let imp = instance.imp();
175
176    let current_time = SystemTime::UNIX_EPOCH
177        + Duration::from_secs((*current_time_ptr).tv_sec.try_into().unwrap())
178        + Duration::from_micros((*current_time_ptr).tv_usec.try_into().unwrap());
179
180    imp.advance(current_time).into_glib()
181}