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`](crate::ContentProvider).
5
6use 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    /// 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
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}