Skip to content

Commit d191b45

Browse files
gio: Add LoadableIcon subclassing support
1 parent a919867 commit d191b45

File tree

2 files changed

+265
-0
lines changed

2 files changed

+265
-0
lines changed

gio/src/subclass/loadable_icon.rs

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
// Take a look at the license at the top of the repository in the LICENSE file.
2+
3+
use std::{future::Future, pin::Pin};
4+
5+
use crate::{
6+
subclass::prelude::*, AsyncResult, Cancellable, GioFuture, InputStream, LoadableIcon, LocalTask,
7+
};
8+
use glib::{prelude::*, translate::*, GString};
9+
10+
pub trait LoadableIconImpl: IconImpl {
11+
fn load(
12+
&self,
13+
size: i32,
14+
cancellable: Option<&Cancellable>,
15+
) -> Result<(InputStream, Option<GString>), glib::Error> {
16+
self.parent_load(size, cancellable)
17+
}
18+
19+
fn load_future(
20+
&self,
21+
size: i32,
22+
) -> Pin<Box<dyn Future<Output = Result<(InputStream, Option<GString>), glib::Error>> + 'static>>
23+
{
24+
self.parent_load_future(size)
25+
}
26+
}
27+
28+
pub trait LoadableIconImplExt: ObjectSubclass {
29+
fn parent_load(
30+
&self,
31+
size: i32,
32+
cancellable: Option<&Cancellable>,
33+
) -> Result<(InputStream, Option<GString>), glib::Error>;
34+
35+
fn parent_load_async<C>(&self, size: i32, cancellable: Option<&Cancellable>, callback: C)
36+
where
37+
C: FnOnce(Result<(InputStream, Option<GString>), glib::Error>) + 'static;
38+
39+
fn parent_load_future(
40+
&self,
41+
size: i32,
42+
) -> Pin<Box<dyn Future<Output = Result<(InputStream, Option<GString>), glib::Error>> + 'static>>;
43+
}
44+
45+
impl<T: LoadableIconImpl> LoadableIconImplExt for T {
46+
fn parent_load(
47+
&self,
48+
size: i32,
49+
cancellable: Option<&Cancellable>,
50+
) -> Result<(InputStream, Option<GString>), glib::Error> {
51+
unsafe {
52+
let type_data = Self::type_data();
53+
let parent_iface = type_data.as_ref().parent_interface::<LoadableIcon>()
54+
as *const ffi::GLoadableIconIface;
55+
56+
let func = (*parent_iface)
57+
.load
58+
.expect("No parent iface implementation for \"load\"");
59+
let mut err = std::ptr::null_mut();
60+
let mut string = std::ptr::null_mut();
61+
let stream = func(
62+
self.obj()
63+
.unsafe_cast_ref::<LoadableIcon>()
64+
.to_glib_none()
65+
.0,
66+
size,
67+
&mut string,
68+
cancellable.to_glib_none().0,
69+
&mut err,
70+
);
71+
if err.is_null() {
72+
Ok((from_glib_full(stream), from_glib_full(string)))
73+
} else {
74+
Err(from_glib_full(err))
75+
}
76+
}
77+
}
78+
79+
fn parent_load_async<C>(&self, size: i32, cancellable: Option<&Cancellable>, callback: C)
80+
where
81+
C: FnOnce(Result<(InputStream, Option<GString>), glib::Error>) + 'static,
82+
{
83+
unsafe {
84+
let main_context = glib::MainContext::ref_thread_default();
85+
let is_main_context_owner = main_context.is_owner();
86+
let has_acquired_main_context = (!is_main_context_owner)
87+
.then(|| main_context.acquire().ok())
88+
.flatten();
89+
assert!(
90+
is_main_context_owner || has_acquired_main_context.is_some(),
91+
"Async operations only allowed if the thread is owning the MainContext"
92+
);
93+
94+
let type_data = T::type_data();
95+
let parent_iface = type_data.as_ref().parent_interface::<LoadableIcon>()
96+
as *const ffi::GLoadableIconIface;
97+
let f = (*parent_iface)
98+
.load_async
99+
.expect("no parent \"load_async\" implementation");
100+
let finish = (*parent_iface)
101+
.load_finish
102+
.expect("no parent \"load_finish\" implementation");
103+
104+
let user_data: Box<(glib::thread_guard::ThreadGuard<C>, _)> =
105+
Box::new((glib::thread_guard::ThreadGuard::new(callback), finish));
106+
107+
unsafe extern "C" fn parent_load_async_trampoline<R>(
108+
source_object_ptr: *mut glib::gobject_ffi::GObject,
109+
res: *mut ffi::GAsyncResult,
110+
user_data: glib::ffi::gpointer,
111+
) where
112+
R: FnOnce(Result<(InputStream, Option<GString>), glib::Error>) + 'static,
113+
{
114+
let mut error = std::ptr::null_mut();
115+
let cb: Box<(
116+
glib::thread_guard::ThreadGuard<R>,
117+
fn(
118+
*mut ffi::GLoadableIcon,
119+
*mut ffi::GAsyncResult,
120+
*mut *mut libc::c_char,
121+
*mut *mut glib::ffi::GError,
122+
) -> *mut ffi::GInputStream,
123+
)> = Box::from_raw(user_data as *mut _);
124+
let mut typeptr = std::ptr::null_mut();
125+
let stream = cb.1(source_object_ptr as _, res, &mut typeptr, &mut error);
126+
let result = if error.is_null() {
127+
Ok((from_glib_full(stream), from_glib_full(typeptr)))
128+
} else {
129+
Err(from_glib_full(error))
130+
};
131+
let cb = cb.0.into_inner();
132+
cb(result);
133+
}
134+
135+
let callback = parent_load_async_trampoline::<C>;
136+
f(
137+
self.obj()
138+
.unsafe_cast_ref::<LoadableIcon>()
139+
.to_glib_none()
140+
.0,
141+
size,
142+
cancellable.to_glib_none().0,
143+
Some(callback),
144+
Box::into_raw(user_data) as *mut _,
145+
);
146+
}
147+
}
148+
149+
fn parent_load_future(
150+
&self,
151+
size: i32,
152+
) -> Pin<Box<dyn Future<Output = Result<(InputStream, Option<GString>), glib::Error>> + 'static>>
153+
{
154+
Box::pin(GioFuture::new(
155+
&self.ref_counted(),
156+
move |imp, cancellable, send| {
157+
imp.parent_load_async(size, Some(cancellable), move |res| {
158+
send.resolve(res);
159+
});
160+
},
161+
))
162+
}
163+
}
164+
165+
unsafe impl<T: LoadableIconImpl> IsImplementable<T> for LoadableIcon {
166+
fn interface_init(iface: &mut glib::Interface<Self>) {
167+
let iface = iface.as_mut();
168+
169+
iface.load = Some(icon_load::<T>);
170+
iface.load_async = Some(icon_load_async::<T>);
171+
iface.load_finish = Some(icon_load_finish::<T>);
172+
}
173+
}
174+
175+
unsafe extern "C" fn icon_load<T: LoadableIconImpl>(
176+
icon: *mut ffi::GLoadableIcon,
177+
size: i32,
178+
typeptr: *mut *mut libc::c_char,
179+
cancellableptr: *mut ffi::GCancellable,
180+
errorptr: *mut *mut glib::ffi::GError,
181+
) -> *mut ffi::GInputStream {
182+
let instance = &*(icon as *mut T::Instance);
183+
let imp = instance.imp();
184+
185+
let cancellable: Borrowed<Option<Cancellable>> = from_glib_borrow(cancellableptr);
186+
187+
let ret = imp.load(size, cancellable.as_ref().as_ref());
188+
match ret {
189+
Ok((stream, icon_type)) => {
190+
if !typeptr.is_null() {
191+
*typeptr = icon_type.to_glib_none().0;
192+
}
193+
194+
stream.to_glib_full()
195+
}
196+
Err(err) => {
197+
*errorptr = err.into_glib_ptr();
198+
*typeptr = std::ptr::null_mut();
199+
200+
std::ptr::null_mut()
201+
}
202+
}
203+
}
204+
205+
#[derive(Clone, glib::Boxed)]
206+
#[boxed_type(name = "GLoadableIconReturnType")]
207+
// Needed for having the required Value traits by GTask API
208+
struct LoadableIconReturnType(InputStream, Option<GString>);
209+
210+
unsafe extern "C" fn icon_load_async<T: LoadableIconImpl>(
211+
icon: *mut ffi::GLoadableIcon,
212+
size: i32,
213+
cancellableptr: *mut ffi::GCancellable,
214+
callbackptr: ffi::GAsyncReadyCallback,
215+
dataptr: glib::ffi::gpointer,
216+
) {
217+
let instance = &*(icon as *mut T::Instance);
218+
let imp = instance.imp();
219+
let wrap: LoadableIcon = from_glib_none(icon);
220+
let cancellable: Option<Cancellable> = from_glib_none(cancellableptr);
221+
222+
let closure = move |task: LocalTask<LoadableIconReturnType>,
223+
source_object: Option<&glib::Object>| {
224+
let result: *mut ffi::GAsyncResult = task.upcast_ref::<AsyncResult>().to_glib_none().0;
225+
let source_object: *mut glib::gobject_ffi::GObject = source_object.to_glib_none().0;
226+
callbackptr.unwrap()(source_object, result, dataptr)
227+
};
228+
229+
let t = LocalTask::new(
230+
Some(wrap.upcast_ref::<glib::Object>()),
231+
cancellable.as_ref(),
232+
closure,
233+
);
234+
235+
glib::MainContext::default().spawn_local(async move {
236+
let res = imp.load_future(size).await;
237+
t.return_result(res.map(|(s, t)| LoadableIconReturnType(s, t)));
238+
});
239+
}
240+
241+
unsafe extern "C" fn icon_load_finish<T: LoadableIconImpl>(
242+
_icon: *mut ffi::GLoadableIcon,
243+
resultptr: *mut ffi::GAsyncResult,
244+
typeptr: *mut *mut libc::c_char,
245+
errorptr: *mut *mut glib::ffi::GError,
246+
) -> *mut ffi::GInputStream {
247+
let res: AsyncResult = from_glib_none(resultptr);
248+
let t = res.downcast::<LocalTask<LoadableIconReturnType>>().unwrap();
249+
let ret = t.propagate();
250+
match ret {
251+
Ok(rt) => {
252+
let (stream, icon_type) = (rt.0, rt.1);
253+
if !typeptr.is_null() {
254+
*typeptr = icon_type.to_glib_full();
255+
}
256+
stream.to_glib_full()
257+
}
258+
Err(err) => {
259+
*errorptr = err.into_glib_ptr();
260+
std::ptr::null_mut()
261+
}
262+
}
263+
}

gio/src/subclass/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ mod initable;
1111
mod input_stream;
1212
mod io_stream;
1313
mod list_model;
14+
mod loadable_icon;
1415
mod output_stream;
1516
mod seekable;
1617

@@ -32,6 +33,7 @@ pub mod prelude {
3233
input_stream::{InputStreamImpl, InputStreamImplExt},
3334
io_stream::{IOStreamImpl, IOStreamImplExt},
3435
list_model::{ListModelImpl, ListModelImplExt},
36+
loadable_icon::{LoadableIconImpl, LoadableIconImplExt},
3537
output_stream::{OutputStreamImpl, OutputStreamImplExt},
3638
seekable::{SeekableImpl, SeekableImplExt},
3739
};

0 commit comments

Comments
 (0)