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