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 {
18 self.parent_is_static_image()
19 }
20
21 fn static_image(&self) -> Option<Pixbuf> {
22 self.parent_static_image()
23 }
24
25 fn size(&self) -> (i32, i32) {
26 self.parent_size()
27 }
28
29 fn iter(&self, start_time: SystemTime) -> PixbufAnimationIter {
30 self.parent_iter(start_time)
31 }
32}
33
34pub trait PixbufAnimationImplExt: PixbufAnimationImpl {
35 fn parent_is_static_image(&self) -> bool {
36 unsafe {
37 let data = Self::type_data();
38 let parent_class = data.as_ref().parent_class() as *mut ffi::GdkPixbufAnimationClass;
39 let f = (*parent_class)
40 .is_static_image
41 .expect("No parent class implementation for \"is_static_image\"");
42
43 from_glib(f(self
44 .obj()
45 .unsafe_cast_ref::<PixbufAnimation>()
46 .to_glib_none()
47 .0))
48 }
49 }
50
51 fn parent_static_image(&self) -> Option<Pixbuf> {
52 unsafe {
53 let data = Self::type_data();
54 let parent_class = data.as_ref().parent_class() as *mut ffi::GdkPixbufAnimationClass;
55 let f = (*parent_class)
56 .get_static_image
57 .expect("No parent class implementation for \"get_static_image\"");
58
59 from_glib_none(f(self
60 .obj()
61 .unsafe_cast_ref::<PixbufAnimation>()
62 .to_glib_none()
63 .0))
64 }
65 }
66
67 fn parent_size(&self) -> (i32, i32) {
68 unsafe {
69 let data = Self::type_data();
70 let parent_class = data.as_ref().parent_class() as *mut ffi::GdkPixbufAnimationClass;
71 let f = (*parent_class)
72 .get_size
73 .expect("No parent class implementation for \"get_size\"");
74 let mut width = MaybeUninit::uninit();
75 let mut height = MaybeUninit::uninit();
76 f(
77 self.obj()
78 .unsafe_cast_ref::<PixbufAnimation>()
79 .to_glib_none()
80 .0,
81 width.as_mut_ptr(),
82 height.as_mut_ptr(),
83 );
84 (width.assume_init(), height.assume_init())
85 }
86 }
87
88 fn parent_iter(&self, start_time: SystemTime) -> PixbufAnimationIter {
89 unsafe {
90 let data = Self::type_data();
91 let parent_class = data.as_ref().parent_class() as *mut ffi::GdkPixbufAnimationClass;
92 let f = (*parent_class)
93 .get_iter
94 .expect("No parent class implementation for \"get_iter\"");
95
96 let diff = start_time
97 .duration_since(SystemTime::UNIX_EPOCH)
98 .expect("failed to convert time");
99 let time = glib::ffi::GTimeVal {
100 tv_sec: diff.as_secs() as _,
101 tv_usec: diff.subsec_micros() as _,
102 };
103 from_glib_full(f(
104 self.obj()
105 .unsafe_cast_ref::<PixbufAnimation>()
106 .to_glib_none()
107 .0,
108 &time,
109 ))
110 }
111 }
112}
113
114impl<T: PixbufAnimationImpl> PixbufAnimationImplExt for T {}
115
116unsafe impl<T: PixbufAnimationImpl> IsSubclassable<T> for PixbufAnimation {
117 fn class_init(class: &mut ::glib::Class<Self>) {
118 Self::parent_class_init::<T>(class);
119
120 let klass = class.as_mut();
121 klass.get_static_image = Some(animation_get_static_image::<T>);
122 klass.get_size = Some(animation_get_size::<T>);
123 klass.get_iter = Some(animation_get_iter::<T>);
124 klass.is_static_image = Some(animation_is_static_image::<T>);
125 }
126}
127
128unsafe extern "C" fn animation_is_static_image<T: PixbufAnimationImpl>(
129 ptr: *mut ffi::GdkPixbufAnimation,
130) -> glib::ffi::gboolean {
131 let instance = &*(ptr as *mut T::Instance);
132 let imp = instance.imp();
133
134 imp.is_static_image().into_glib()
135}
136
137unsafe extern "C" fn animation_get_size<T: PixbufAnimationImpl>(
138 ptr: *mut ffi::GdkPixbufAnimation,
139 width_ptr: *mut libc::c_int,
140 height_ptr: *mut libc::c_int,
141) {
142 if width_ptr.is_null() && height_ptr.is_null() {
143 return;
144 }
145
146 let instance = &*(ptr as *mut T::Instance);
147 let imp = instance.imp();
148
149 let (width, height) = imp.size();
150 if !width_ptr.is_null() {
151 *width_ptr = width;
152 }
153 if !height_ptr.is_null() {
154 *height_ptr = height;
155 }
156}
157
158unsafe extern "C" fn animation_get_static_image<T: PixbufAnimationImpl>(
159 ptr: *mut ffi::GdkPixbufAnimation,
160) -> *mut ffi::GdkPixbuf {
161 let instance = &*(ptr as *mut T::Instance);
162 let imp = instance.imp();
163
164 let instance = imp.obj();
165 let static_image = imp.static_image();
166 let static_image_quark = {
169 static QUARK: OnceLock<glib::Quark> = OnceLock::new();
170 *QUARK.get_or_init(|| glib::Quark::from_str("gtk-rs-subclass-static-image"))
171 };
172 if let Some(old_image) = instance.qdata::<Option<Pixbuf>>(static_image_quark) {
173 let old_image = old_image.as_ref();
174
175 if let Some(old_image) = old_image {
176 assert_eq!(
177 Some(old_image),
178 static_image.as_ref(),
179 "Did not return same static image again"
180 );
181 }
182 }
183 instance.set_qdata(static_image_quark, static_image.clone());
184 static_image.to_glib_none().0
185}
186
187unsafe extern "C" fn animation_get_iter<T: PixbufAnimationImpl>(
188 ptr: *mut ffi::GdkPixbufAnimation,
189 start_time_ptr: *const glib::ffi::GTimeVal,
190) -> *mut ffi::GdkPixbufAnimationIter {
191 let instance = &*(ptr as *mut T::Instance);
192 let imp = instance.imp();
193
194 let start_time = SystemTime::UNIX_EPOCH
195 + Duration::from_secs((*start_time_ptr).tv_sec.try_into().unwrap())
196 + Duration::from_micros((*start_time_ptr).tv_usec.try_into().unwrap());
197
198 imp.iter(start_time).into_glib_ptr()
199}