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