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) {
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 let mut error = std::ptr::null_mut();
202 let cb: Box<(
203 glib::thread_guard::ThreadGuard<R>,
204 fn(
205 *mut ffi::GdkContentProvider,
206 *mut gio::ffi::GAsyncResult,
207 *mut *mut glib::ffi::GError,
208 ) -> glib::ffi::gboolean,
209 )> = Box::from_raw(user_data as *mut _);
210 cb.1(source_object_ptr as _, res, &mut error);
211 let result = if error.is_null() {
212 Ok(())
213 } else {
214 Err(from_glib_full(error))
215 };
216 let cb = cb.0.into_inner();
217 cb(result);
218 }
219
220 let cancellable = cancellable.map(|p| p.as_ref());
221 let callback = parent_write_mime_type_async_trampoline::<R>;
222 f(
223 self.obj()
224 .unsafe_cast_ref::<ContentProvider>()
225 .to_glib_none()
226 .0,
227 mime_type.to_glib_none().0,
228 stream.to_glib_none().0,
229 io_priority.into_glib(),
230 cancellable.to_glib_none().0,
231 Some(callback),
232 Box::into_raw(user_data) as *mut _,
233 );
234 }
235 }
236
237 fn parent_write_mime_type_future(
238 &self,
239 mime_type: &str,
240 stream: &gio::OutputStream,
241 io_priority: glib::Priority,
242 ) -> Pin<Box<dyn Future<Output = Result<(), glib::Error>> + 'static>> {
243 let stream = stream.clone();
244 let mime_type = String::from(mime_type);
245 Box::pin(gio::GioFuture::new(
246 &self.ref_counted(),
247 move |imp, cancellable, send| {
248 imp.parent_write_mime_type_async(
249 &mime_type,
250 &stream,
251 io_priority,
252 Some(cancellable),
253 move |res| {
254 send.resolve(res);
255 },
256 );
257 },
258 ))
259 }
260
261 fn parent_value(&self, type_: glib::Type) -> Result<Value, glib::Error> {
262 unsafe {
263 let data = Self::type_data();
264 let parent_class = data.as_ref().parent_class() as *mut ffi::GdkContentProviderClass;
265 let f = (*parent_class)
266 .get_value
267 .expect("no parent \"get_value\" implementation");
268 let mut value = Value::from_type(type_);
269
270 let mut error = std::ptr::null_mut();
271 f(
272 self.obj()
273 .unsafe_cast_ref::<ContentProvider>()
274 .to_glib_none()
275 .0,
276 value.to_glib_none_mut().0,
277 &mut error,
278 );
279
280 if error.is_null() {
281 Ok(value)
282 } else {
283 Err(from_glib_full(error))
284 }
285 }
286 }
287}
288
289impl<T: ContentProviderImpl> ContentProviderImplExt for T {}
290
291unsafe impl<T: ContentProviderImpl> IsSubclassable<T> for ContentProvider {
292 fn class_init(class: &mut glib::Class<Self>) {
293 Self::parent_class_init::<T>(class);
294
295 let klass = class.as_mut();
296 klass.content_changed = Some(content_provider_content_changed::<T>);
297 klass.attach_clipboard = Some(content_provider_attach_clipboard::<T>);
298 klass.detach_clipboard = Some(content_provider_detach_clipboard::<T>);
299 klass.ref_formats = Some(content_provider_formats::<T>);
300 klass.ref_storable_formats = Some(content_provider_storable_formats::<T>);
301 klass.write_mime_type_async = Some(content_provider_write_mime_type_async::<T>);
302 klass.write_mime_type_finish = Some(content_provider_write_mime_type_finish);
303 klass.get_value = Some(content_provider_get_value::<T>);
304 }
305}
306
307unsafe extern "C" fn content_provider_content_changed<T: ContentProviderImpl>(
308 ptr: *mut ffi::GdkContentProvider,
309) {
310 let instance = &*(ptr as *mut T::Instance);
311 let imp = instance.imp();
312
313 imp.content_changed()
314}
315
316unsafe extern "C" fn content_provider_attach_clipboard<T: ContentProviderImpl>(
317 ptr: *mut ffi::GdkContentProvider,
318 clipboard_ptr: *mut ffi::GdkClipboard,
319) {
320 let instance = &*(ptr as *mut T::Instance);
321 let imp = instance.imp();
322 let clipboard = from_glib_borrow(clipboard_ptr);
323
324 imp.attach_clipboard(&clipboard)
325}
326
327unsafe extern "C" fn content_provider_detach_clipboard<T: ContentProviderImpl>(
328 ptr: *mut ffi::GdkContentProvider,
329 clipboard_ptr: *mut ffi::GdkClipboard,
330) {
331 let instance = &*(ptr as *mut T::Instance);
332 let imp = instance.imp();
333 let clipboard = from_glib_borrow(clipboard_ptr);
334
335 imp.detach_clipboard(&clipboard)
336}
337
338unsafe extern "C" fn content_provider_formats<T: ContentProviderImpl>(
339 ptr: *mut ffi::GdkContentProvider,
340) -> *mut ffi::GdkContentFormats {
341 let instance = &*(ptr as *mut T::Instance);
342 let imp = instance.imp();
343
344 imp.formats().into_glib_ptr()
345}
346
347unsafe extern "C" fn content_provider_storable_formats<T: ContentProviderImpl>(
348 ptr: *mut ffi::GdkContentProvider,
349) -> *mut ffi::GdkContentFormats {
350 let instance = &*(ptr as *mut T::Instance);
351 let imp = instance.imp();
352
353 imp.storable_formats().into_glib_ptr()
354}
355
356unsafe extern "C" fn content_provider_write_mime_type_async<T: ContentProviderImpl>(
357 ptr: *mut ffi::GdkContentProvider,
358 mime_type_ptr: *const libc::c_char,
359 stream_ptr: *mut gio::ffi::GOutputStream,
360 priority: libc::c_int,
361 cancellable_ptr: *mut gio::ffi::GCancellable,
362 callback: gio::ffi::GAsyncReadyCallback,
363 user_data: glib::ffi::gpointer,
364) {
365 let instance = &*(ptr as *mut T::Instance);
366 let imp = instance.imp();
367 let wrap: ContentProvider = from_glib_none(ptr);
368 let cancellable: Option<gio::Cancellable> = from_glib_none(cancellable_ptr);
369 let mime_type: glib::GString = from_glib_none(mime_type_ptr);
370 let stream: gio::OutputStream = from_glib_none(stream_ptr);
371
372 let closure = move |task: gio::LocalTask<bool>, source_object: Option<&glib::Object>| {
373 let result: *mut gio::ffi::GAsyncResult =
374 task.upcast_ref::<gio::AsyncResult>().to_glib_none().0;
375 let source_object: *mut glib::gobject_ffi::GObject = source_object.to_glib_none().0;
376 callback.unwrap()(source_object, result, user_data)
377 };
378
379 let t = gio::LocalTask::new(
380 Some(wrap.upcast_ref::<glib::Object>()),
381 cancellable.as_ref(),
382 closure,
383 );
384
385 glib::MainContext::default().spawn_local(async move {
386 let res = imp
387 .write_mime_type_future(
388 mime_type.as_str(),
389 stream.unsafe_cast_ref::<gio::OutputStream>(),
390 from_glib(priority),
391 )
392 .await;
393 t.return_result(res.map(|_t| true));
394 });
395}
396
397unsafe extern "C" fn content_provider_write_mime_type_finish(
398 _ptr: *mut ffi::GdkContentProvider,
399 res_ptr: *mut gio::ffi::GAsyncResult,
400 error_ptr: *mut *mut glib::ffi::GError,
401) -> glib::ffi::gboolean {
402 let res: gio::AsyncResult = from_glib_none(res_ptr);
403 let t = res.downcast::<gio::LocalTask<bool>>().unwrap();
404 let ret = t.propagate();
405 match ret {
406 Ok(v) => {
407 debug_assert!(v);
408 true.into_glib()
409 }
410 Err(e) => {
411 if !error_ptr.is_null() {
412 *error_ptr = e.into_glib_ptr();
413 }
414 false.into_glib()
415 }
416 }
417}
418
419unsafe extern "C" fn content_provider_get_value<T: ContentProviderImpl>(
420 ptr: *mut ffi::GdkContentProvider,
421 value_ptr: *mut glib::gobject_ffi::GValue,
422 error_ptr: *mut *mut glib::ffi::GError,
423) -> glib::ffi::gboolean {
424 let instance = &*(ptr as *mut T::Instance);
425 let imp = instance.imp();
426 let value: Value = from_glib_none(value_ptr);
427
428 let ret = imp.value(value.type_());
429 match ret {
430 Ok(v) => {
431 glib::gobject_ffi::g_value_copy(v.to_glib_none().0, value_ptr);
432 true.into_glib()
433 }
434 Err(e) => {
435 if !error_ptr.is_null() {
436 *error_ptr = e.into_glib_ptr();
437 }
438 false.into_glib()
439 }
440 }
441}