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