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