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 + 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}