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