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