1use std::{future::Future, pin::Pin};
7
8use glib::{translate::*, Value};
9
10use crate::{ffi, prelude::*, subclass::prelude::*, Clipboard, ContentFormats, ContentProvider};
11
12pub trait ContentProviderImpl: ObjectImpl + ObjectSubclass<Type: IsA<ContentProvider>> {
13 fn content_changed(&self) {
14 self.parent_content_changed()
15 }
16
17 fn attach_clipboard(&self, clipboard: &Clipboard) {
18 self.parent_attach_clipboard(clipboard)
19 }
20
21 fn detach_clipboard(&self, clipboard: &Clipboard) {
22 self.parent_detach_clipboard(clipboard)
23 }
24
25 fn formats(&self) -> ContentFormats {
26 self.parent_formats()
27 }
28
29 fn storable_formats(&self) -> ContentFormats {
30 self.parent_storable_formats()
31 }
32
33 fn write_mime_type_future(
34 &self,
35 mime_type: &str,
36 stream: &gio::OutputStream,
37 io_priority: glib::Priority,
38 ) -> Pin<Box<dyn Future<Output = Result<(), glib::Error>> + 'static>> {
39 self.parent_write_mime_type_future(mime_type, stream, io_priority)
40 }
41
42 fn value(&self, type_: glib::Type) -> Result<Value, glib::Error> {
43 self.parent_value(type_)
44 }
45}
46
47pub trait ContentProviderImplExt: ContentProviderImpl {
48 fn parent_content_changed(&self) {
49 unsafe {
50 let data = Self::type_data();
51 let parent_class = data.as_ref().parent_class() as *mut ffi::GdkContentProviderClass;
52 if let Some(f) = (*parent_class).content_changed {
53 f(self
54 .obj()
55 .unsafe_cast_ref::<ContentProvider>()
56 .to_glib_none()
57 .0)
58 }
59 }
60 }
61
62 fn parent_attach_clipboard(&self, clipboard: &Clipboard) {
63 unsafe {
64 let data = Self::type_data();
65 let parent_class = data.as_ref().parent_class() as *mut ffi::GdkContentProviderClass;
66 if let Some(f) = (*parent_class).attach_clipboard {
67 f(
68 self.obj()
69 .unsafe_cast_ref::<ContentProvider>()
70 .to_glib_none()
71 .0,
72 clipboard.to_glib_none().0,
73 )
74 }
75 }
76 }
77
78 fn parent_detach_clipboard(&self, clipboard: &Clipboard) {
79 unsafe {
80 let data = Self::type_data();
81 let parent_class = data.as_ref().parent_class() as *mut ffi::GdkContentProviderClass;
82 if let Some(f) = (*parent_class).detach_clipboard {
83 f(
84 self.obj()
85 .unsafe_cast_ref::<ContentProvider>()
86 .to_glib_none()
87 .0,
88 clipboard.to_glib_none().0,
89 )
90 }
91 }
92 }
93
94 fn parent_formats(&self) -> ContentFormats {
95 unsafe {
96 let data = Self::type_data();
97 let parent_class = data.as_ref().parent_class() as *mut ffi::GdkContentProviderClass;
98 let f = (*parent_class)
99 .ref_formats
100 .expect("no parent \"ref_formats\" implementation");
101 let ret = f(self
102 .obj()
103 .unsafe_cast_ref::<ContentProvider>()
104 .to_glib_none()
105 .0);
106
107 from_glib_full(ret)
108 }
109 }
110
111 fn parent_storable_formats(&self) -> ContentFormats {
112 unsafe {
113 let data = Self::type_data();
114 let parent_class = data.as_ref().parent_class() as *mut ffi::GdkContentProviderClass;
115 let f = (*parent_class)
116 .ref_storable_formats
117 .expect("no parent \"ref_storable_formats\" implementation");
118 let ret = f(self
119 .obj()
120 .unsafe_cast_ref::<ContentProvider>()
121 .to_glib_none()
122 .0);
123
124 from_glib_full(ret)
125 }
126 }
127
128 #[allow(clippy::type_complexity)]
129 fn parent_write_mime_type_async<
130 Q: IsA<gio::Cancellable>,
131 R: FnOnce(Result<(), glib::Error>) + 'static,
132 >(
133 &self,
134 mime_type: &str,
135 stream: &gio::OutputStream,
136 io_priority: glib::Priority,
137 cancellable: Option<&Q>,
138 callback: R,
139 ) {
140 unsafe {
141 let main_context = glib::MainContext::ref_thread_default();
142 let is_main_context_owner = main_context.is_owner();
143 let has_acquired_main_context = (!is_main_context_owner)
144 .then(|| main_context.acquire().ok())
145 .flatten();
146 assert!(
147 is_main_context_owner || has_acquired_main_context.is_some(),
148 "Async operations only allowed if the thread is owning the MainContext"
149 );
150
151 let data = Self::type_data();
152 let parent_class = data.as_ref().parent_class() as *mut ffi::GdkContentProviderClass;
153 let f = (*parent_class)
154 .write_mime_type_async
155 .expect("no parent \"write_mime_type_async\" implementation");
156 let finish = (*parent_class)
157 .write_mime_type_finish
158 .expect("no parent \"write_mime_type_finish\" implementation");
159
160 let user_data: Box<(glib::thread_guard::ThreadGuard<R>, _)> =
161 Box::new((glib::thread_guard::ThreadGuard::new(callback), finish));
162
163 unsafe extern "C" fn parent_write_mime_type_async_trampoline<
164 R: FnOnce(Result<(), glib::Error>) + 'static,
165 >(
166 source_object_ptr: *mut glib::gobject_ffi::GObject,
167 res: *mut gio::ffi::GAsyncResult,
168 user_data: glib::ffi::gpointer,
169 ) {
170 let mut error = std::ptr::null_mut();
171 let cb: Box<(
172 glib::thread_guard::ThreadGuard<R>,
173 fn(
174 *mut ffi::GdkContentProvider,
175 *mut gio::ffi::GAsyncResult,
176 *mut *mut glib::ffi::GError,
177 ) -> glib::ffi::gboolean,
178 )> = Box::from_raw(user_data as *mut _);
179 cb.1(source_object_ptr as _, res, &mut error);
180 let result = if error.is_null() {
181 Ok(())
182 } else {
183 Err(from_glib_full(error))
184 };
185 let cb = cb.0.into_inner();
186 cb(result);
187 }
188
189 let cancellable = cancellable.map(|p| p.as_ref());
190 let callback = parent_write_mime_type_async_trampoline::<R>;
191 f(
192 self.obj()
193 .unsafe_cast_ref::<ContentProvider>()
194 .to_glib_none()
195 .0,
196 mime_type.to_glib_none().0,
197 stream.to_glib_none().0,
198 io_priority.into_glib(),
199 cancellable.to_glib_none().0,
200 Some(callback),
201 Box::into_raw(user_data) as *mut _,
202 );
203 }
204 }
205
206 fn parent_write_mime_type_future(
207 &self,
208 mime_type: &str,
209 stream: &gio::OutputStream,
210 io_priority: glib::Priority,
211 ) -> Pin<Box<dyn Future<Output = Result<(), glib::Error>> + 'static>> {
212 let stream = stream.clone();
213 let mime_type = String::from(mime_type);
214 Box::pin(gio::GioFuture::new(
215 &self.ref_counted(),
216 move |imp, cancellable, send| {
217 imp.parent_write_mime_type_async(
218 &mime_type,
219 &stream,
220 io_priority,
221 Some(cancellable),
222 move |res| {
223 send.resolve(res);
224 },
225 );
226 },
227 ))
228 }
229
230 fn parent_value(&self, type_: glib::Type) -> Result<Value, glib::Error> {
231 unsafe {
232 let data = Self::type_data();
233 let parent_class = data.as_ref().parent_class() as *mut ffi::GdkContentProviderClass;
234 let f = (*parent_class)
235 .get_value
236 .expect("no parent \"get_value\" implementation");
237 let mut value = Value::from_type(type_);
238
239 let mut error = std::ptr::null_mut();
240 f(
241 self.obj()
242 .unsafe_cast_ref::<ContentProvider>()
243 .to_glib_none()
244 .0,
245 value.to_glib_none_mut().0,
246 &mut error,
247 );
248
249 if error.is_null() {
250 Ok(value)
251 } else {
252 Err(from_glib_full(error))
253 }
254 }
255 }
256}
257
258impl<T: ContentProviderImpl> ContentProviderImplExt for T {}
259
260unsafe impl<T: ContentProviderImpl> IsSubclassable<T> for ContentProvider {
261 fn class_init(class: &mut glib::Class<Self>) {
262 Self::parent_class_init::<T>(class);
263
264 let klass = class.as_mut();
265 klass.content_changed = Some(content_provider_content_changed::<T>);
266 klass.attach_clipboard = Some(content_provider_attach_clipboard::<T>);
267 klass.detach_clipboard = Some(content_provider_detach_clipboard::<T>);
268 klass.ref_formats = Some(content_provider_formats::<T>);
269 klass.ref_storable_formats = Some(content_provider_storable_formats::<T>);
270 klass.write_mime_type_async = Some(content_provider_write_mime_type_async::<T>);
271 klass.write_mime_type_finish = Some(content_provider_write_mime_type_finish);
272 klass.get_value = Some(content_provider_get_value::<T>);
273 }
274}
275
276unsafe extern "C" fn content_provider_content_changed<T: ContentProviderImpl>(
277 ptr: *mut ffi::GdkContentProvider,
278) {
279 let instance = &*(ptr as *mut T::Instance);
280 let imp = instance.imp();
281
282 imp.content_changed()
283}
284
285unsafe extern "C" fn content_provider_attach_clipboard<T: ContentProviderImpl>(
286 ptr: *mut ffi::GdkContentProvider,
287 clipboard_ptr: *mut ffi::GdkClipboard,
288) {
289 let instance = &*(ptr as *mut T::Instance);
290 let imp = instance.imp();
291 let clipboard = from_glib_borrow(clipboard_ptr);
292
293 imp.attach_clipboard(&clipboard)
294}
295
296unsafe extern "C" fn content_provider_detach_clipboard<T: ContentProviderImpl>(
297 ptr: *mut ffi::GdkContentProvider,
298 clipboard_ptr: *mut ffi::GdkClipboard,
299) {
300 let instance = &*(ptr as *mut T::Instance);
301 let imp = instance.imp();
302 let clipboard = from_glib_borrow(clipboard_ptr);
303
304 imp.detach_clipboard(&clipboard)
305}
306
307unsafe extern "C" fn content_provider_formats<T: ContentProviderImpl>(
308 ptr: *mut ffi::GdkContentProvider,
309) -> *mut ffi::GdkContentFormats {
310 let instance = &*(ptr as *mut T::Instance);
311 let imp = instance.imp();
312
313 imp.formats().into_glib_ptr()
314}
315
316unsafe extern "C" fn content_provider_storable_formats<T: ContentProviderImpl>(
317 ptr: *mut ffi::GdkContentProvider,
318) -> *mut ffi::GdkContentFormats {
319 let instance = &*(ptr as *mut T::Instance);
320 let imp = instance.imp();
321
322 imp.storable_formats().into_glib_ptr()
323}
324
325unsafe extern "C" fn content_provider_write_mime_type_async<T: ContentProviderImpl>(
326 ptr: *mut ffi::GdkContentProvider,
327 mime_type_ptr: *const libc::c_char,
328 stream_ptr: *mut gio::ffi::GOutputStream,
329 priority: libc::c_int,
330 cancellable_ptr: *mut gio::ffi::GCancellable,
331 callback: gio::ffi::GAsyncReadyCallback,
332 user_data: glib::ffi::gpointer,
333) {
334 let instance = &*(ptr as *mut T::Instance);
335 let imp = instance.imp();
336 let wrap: ContentProvider = from_glib_none(ptr);
337 let cancellable: Option<gio::Cancellable> = from_glib_none(cancellable_ptr);
338 let mime_type: glib::GString = from_glib_none(mime_type_ptr);
339 let stream: gio::OutputStream = from_glib_none(stream_ptr);
340
341 let closure = move |task: gio::LocalTask<bool>, source_object: Option<&glib::Object>| {
342 let result: *mut gio::ffi::GAsyncResult =
343 task.upcast_ref::<gio::AsyncResult>().to_glib_none().0;
344 let source_object: *mut glib::gobject_ffi::GObject = source_object.to_glib_none().0;
345 callback.unwrap()(source_object, result, user_data)
346 };
347
348 let t = gio::LocalTask::new(
349 Some(wrap.upcast_ref::<glib::Object>()),
350 cancellable.as_ref(),
351 closure,
352 );
353
354 glib::MainContext::default().spawn_local(async move {
355 let res = imp
356 .write_mime_type_future(
357 mime_type.as_str(),
358 stream.unsafe_cast_ref::<gio::OutputStream>(),
359 from_glib(priority),
360 )
361 .await;
362 t.return_result(res.map(|_t| true));
363 });
364}
365
366unsafe extern "C" fn content_provider_write_mime_type_finish(
367 _ptr: *mut ffi::GdkContentProvider,
368 res_ptr: *mut gio::ffi::GAsyncResult,
369 error_ptr: *mut *mut glib::ffi::GError,
370) -> glib::ffi::gboolean {
371 let res: gio::AsyncResult = from_glib_none(res_ptr);
372 let t = res.downcast::<gio::LocalTask<bool>>().unwrap();
373 let ret = t.propagate();
374 match ret {
375 Ok(v) => {
376 debug_assert!(v);
377 true.into_glib()
378 }
379 Err(e) => {
380 if !error_ptr.is_null() {
381 *error_ptr = e.into_glib_ptr();
382 }
383 false.into_glib()
384 }
385 }
386}
387
388unsafe extern "C" fn content_provider_get_value<T: ContentProviderImpl>(
389 ptr: *mut ffi::GdkContentProvider,
390 value_ptr: *mut glib::gobject_ffi::GValue,
391 error_ptr: *mut *mut glib::ffi::GError,
392) -> glib::ffi::gboolean {
393 let instance = &*(ptr as *mut T::Instance);
394 let imp = instance.imp();
395 let value: Value = from_glib_none(value_ptr);
396
397 let ret = imp.value(value.type_());
398 match ret {
399 Ok(v) => {
400 glib::gobject_ffi::g_value_copy(v.to_glib_none().0, value_ptr);
401 true.into_glib()
402 }
403 Err(e) => {
404 if !error_ptr.is_null() {
405 *error_ptr = e.into_glib_ptr();
406 }
407 false.into_glib()
408 }
409 }
410}