Skip to main content

gdk4/subclass/
content_provider.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3// rustdoc-stripper-ignore-next
4//! Traits intended for subclassing [`ContentProvider`].
5
6use 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    /// Emits the ::content-changed signal.
14    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    /// Gets the formats that the provider can provide its current contents in.
27    ///
28    /// # Returns
29    ///
30    /// The formats of the provider
31    fn formats(&self) -> ContentFormats {
32        self.parent_formats()
33    }
34
35    /// Gets the formats that the provider suggests other applications to store
36    /// the data in.
37    ///
38    /// An example of such an application would be a clipboard manager.
39    ///
40    /// This can be assumed to be a subset of [`ContentProviderExt::formats()`][crate::prelude::ContentProviderExt::formats()].
41    ///
42    /// # Returns
43    ///
44    /// The storable formats of the provider
45    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    /// Gets the contents of @self stored in @value.
59    ///
60    /// The @value will have been initialized to the `GType` the value should be
61    /// provided in. This given `GType` does not need to be listed in the formats
62    /// returned by [`ContentProviderExt::formats()`][crate::prelude::ContentProviderExt::formats()]. However, if the
63    /// given `GType` is not supported, this operation can fail and
64    /// `G_IO_ERROR_NOT_SUPPORTED` will be reported.
65    ///
66    /// # Returns
67    ///
68    /// [`true`] if the value was set successfully. Otherwise
69    ///   @error will be set to describe the failure.
70    ///
71    /// ## `value`
72    /// the `GValue` to fill
73    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}