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::{translate::*, Value};
9
10use crate::{ffi, prelude::*, subclass::prelude::*, Clipboard, ContentFormats, ContentProvider};
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                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}