Skip to main content

gdk4/
functions.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{future, pin::Pin, ptr};
4
5use glib::translate::*;
6
7pub use crate::auto::functions::*;
8use crate::{ContentDeserializer, ContentSerializer, ffi, prelude::*};
9
10#[repr(C, packed)]
11pub struct GRange(pub i32, pub i32);
12
13/// s text.
14///
15/// Note that the regions returned correspond to logical extents of the text
16/// ranges, not ink extents. So the drawn layout may in fact touch areas out of
17/// the clip region.  The clip region is mainly useful for highlightling parts
18/// of text, such as when text is selected.
19/// ## `layout`
20/// a [`pango::Layout`][crate::pango::Layout]
21/// ## `x_origin`
22/// X pixel where you intend to draw the layout with this clip
23/// ## `y_origin`
24/// Y pixel where you intend to draw the layout with this clip
25/// ## `index_ranges`
26/// array of byte indexes into the layout, where even members of array are start indexes and odd elements are end indexes
27///
28/// # Returns
29///
30/// a clip region containing the given ranges
31#[doc(alias = "gdk_pango_layout_get_clip_region")]
32pub fn pango_layout_get_clip_region(
33    layout: &pango::Layout,
34    x_origin: i32,
35    y_origin: i32,
36    index_ranges: &[GRange],
37) -> cairo::Region {
38    assert_initialized_main_thread!();
39
40    let ptr: *const i32 = index_ranges.as_ptr() as _;
41    unsafe {
42        from_glib_full(ffi::gdk_pango_layout_get_clip_region(
43            layout.to_glib_none().0,
44            x_origin,
45            y_origin,
46            ptr,
47            (index_ranges.len() / 2) as i32,
48        ))
49    }
50}
51
52/// Reads content from the given input stream and deserialize it, asynchronously.
53///
54/// The default I/O priority is `G_PRIORITY_DEFAULT` (i.e. 0), and lower numbers
55/// indicate a higher priority.
56/// ## `stream`
57/// a `GInputStream` to read the serialized content from
58/// ## `mime_type`
59/// the mime type to deserialize from
60/// ## `type_`
61/// the GType to deserialize from
62/// ## `io_priority`
63/// the I/O priority of the operation
64/// ## `cancellable`
65/// optional `GCancellable` object
66/// ## `callback`
67/// callback to call when the operation is done
68#[doc(alias = "gdk_content_deserialize_async")]
69pub fn content_deserialize_async<R: FnOnce(Result<glib::Value, glib::Error>) + 'static>(
70    stream: &impl IsA<gio::InputStream>,
71    mime_type: &str,
72    type_: glib::types::Type,
73    io_priority: glib::Priority,
74    cancellable: Option<&impl IsA<gio::Cancellable>>,
75    callback: R,
76) {
77    assert_initialized_main_thread!();
78    let main_context = glib::MainContext::ref_thread_default();
79    let is_main_context_owner = main_context.is_owner();
80    let has_acquired_main_context = (!is_main_context_owner)
81        .then(|| main_context.acquire().ok())
82        .flatten();
83    assert!(
84        is_main_context_owner || has_acquired_main_context.is_some(),
85        "Async operations only allowed if the thread is owning the MainContext"
86    );
87
88    let user_data: Box<glib::thread_guard::ThreadGuard<R>> =
89        Box::new(glib::thread_guard::ThreadGuard::new(callback));
90    unsafe extern "C" fn content_deserialize_async_trampoline<
91        R: FnOnce(Result<glib::Value, glib::Error>) + 'static,
92    >(
93        _source_object: *mut glib::gobject_ffi::GObject,
94        res: *mut gio::ffi::GAsyncResult,
95        user_data: glib::ffi::gpointer,
96    ) {
97        unsafe {
98            let mut error = ptr::null_mut();
99            let mut value = glib::Value::uninitialized();
100            let _ =
101                ffi::gdk_content_deserialize_finish(res, value.to_glib_none_mut().0, &mut error);
102            let result = if error.is_null() {
103                Ok(value)
104            } else {
105                Err(from_glib_full(error))
106            };
107            let callback: Box<glib::thread_guard::ThreadGuard<R>> =
108                Box::from_raw(user_data as *mut _);
109            let callback = callback.into_inner();
110            callback(result);
111        }
112    }
113    let callback = content_deserialize_async_trampoline::<R>;
114    unsafe {
115        ffi::gdk_content_deserialize_async(
116            stream.as_ref().to_glib_none().0,
117            mime_type.to_glib_none().0,
118            type_.into_glib(),
119            io_priority.into_glib(),
120            cancellable.map(|p| p.as_ref()).to_glib_none().0,
121            Some(callback),
122            Box::into_raw(user_data) as *mut _,
123        );
124    }
125}
126
127pub fn content_deserialize_future(
128    stream: &(impl IsA<gio::InputStream> + Clone + 'static),
129    mime_type: &str,
130    type_: glib::types::Type,
131    io_priority: glib::Priority,
132) -> Pin<Box<dyn future::Future<Output = Result<glib::Value, glib::Error>> + 'static>> {
133    assert_initialized_main_thread!();
134
135    let stream = stream.clone();
136    let mime_type = String::from(mime_type);
137    Box::pin(gio::GioFuture::new(&(), move |_obj, cancellable, send| {
138        content_deserialize_async(
139            &stream,
140            &mime_type,
141            type_,
142            io_priority,
143            Some(cancellable),
144            move |res| {
145                send.resolve(res);
146            },
147        );
148    }))
149}
150
151/// Registers a function to deserialize object of a given type.
152///
153/// Since 4.20, when looking up a deserializer to use, GTK will
154/// use the last registered deserializer for a given mime type,
155/// so applications can override the built-in deserializers.
156/// ## `mime_type`
157/// the mime type which the function can deserialize from
158/// ## `type_`
159/// the type of objects that the function creates
160/// ## `deserialize`
161/// the callback
162/// ## `notify`
163/// destroy notify for @data
164#[doc(alias = "gdk_content_register_deserializer")]
165pub fn content_register_deserializer<
166    T: 'static,
167    P: Fn(&ContentDeserializer, &mut Option<T>) + 'static,
168>(
169    mime_type: &str,
170    type_: glib::types::Type,
171    deserialize: P,
172) {
173    assert_initialized_main_thread!();
174    let deserialize_data: Box<P> = Box::new(deserialize);
175    unsafe extern "C" fn deserialize_func<
176        T: 'static,
177        P: Fn(&ContentDeserializer, &mut Option<T>) + 'static,
178    >(
179        deserializer: *mut ffi::GdkContentDeserializer,
180    ) {
181        unsafe {
182            let deserializer: ContentDeserializer = from_glib_full(deserializer);
183            let callback: &P =
184                &*(ffi::gdk_content_deserializer_get_user_data(deserializer.to_glib_none().0)
185                    as *mut _);
186
187            let mut task_data: *mut Option<T> =
188                ffi::gdk_content_deserializer_get_task_data(deserializer.to_glib_none().0)
189                    as *mut _;
190            if task_data.is_null() {
191                unsafe extern "C" fn notify_func<T: 'static>(data: glib::ffi::gpointer) {
192                    unsafe {
193                        let _task_data: Box<Option<T>> = Box::from_raw(data as *mut _);
194                    }
195                }
196                task_data = Box::into_raw(Box::new(None));
197                ffi::gdk_content_deserializer_set_task_data(
198                    deserializer.to_glib_none().0,
199                    task_data as *mut _,
200                    Some(notify_func::<T>),
201                );
202            }
203
204            (*callback)(&deserializer, &mut *task_data);
205        }
206    }
207    let deserialize = Some(deserialize_func::<T, P> as _);
208    unsafe extern "C" fn notify_func<
209        T: 'static,
210        P: Fn(&ContentDeserializer, &mut Option<T>) + 'static,
211    >(
212        data: glib::ffi::gpointer,
213    ) {
214        unsafe {
215            let _callback: Box<P> = Box::from_raw(data as *mut _);
216        }
217    }
218    let destroy_call4 = Some(notify_func::<T, P> as _);
219    let super_callback0: Box<P> = deserialize_data;
220    unsafe {
221        ffi::gdk_content_register_deserializer(
222            mime_type.to_glib_none().0,
223            type_.into_glib(),
224            deserialize,
225            Box::into_raw(super_callback0) as *mut _,
226            destroy_call4,
227        );
228    }
229}
230
231/// Registers a function to serialize objects of a given type.
232///
233/// Since 4.20, when looking up a serializer to use, GTK will
234/// use the last registered serializer for a given mime type,
235/// so applications can override the built-in serializers.
236/// ## `type_`
237/// the type of objects that the function can serialize
238/// ## `mime_type`
239/// the mime type to serialize to
240/// ## `serialize`
241/// the callback
242/// ## `notify`
243/// destroy notify for @data
244#[doc(alias = "gdk_content_register_serializer")]
245pub fn content_register_serializer<
246    T: 'static,
247    P: Fn(&ContentSerializer, &mut Option<T>) + 'static,
248>(
249    type_: glib::types::Type,
250    mime_type: &str,
251    serialize: P,
252) {
253    assert_initialized_main_thread!();
254    let serialize_data: Box<P> = Box::new(serialize);
255    unsafe extern "C" fn serialize_func<
256        T: 'static,
257        P: Fn(&ContentSerializer, &mut Option<T>) + 'static,
258    >(
259        serializer: *mut ffi::GdkContentSerializer,
260    ) {
261        unsafe {
262            let serializer: ContentSerializer = from_glib_full(serializer);
263            let callback: &P =
264                &*(ffi::gdk_content_serializer_get_user_data(serializer.to_glib_none().0)
265                    as *mut _);
266
267            let mut task_data: *mut Option<T> =
268                ffi::gdk_content_serializer_get_task_data(serializer.to_glib_none().0) as *mut _;
269            if task_data.is_null() {
270                unsafe extern "C" fn notify_func<T: 'static>(data: glib::ffi::gpointer) {
271                    unsafe {
272                        let _task_data: Box<Option<T>> = Box::from_raw(data as *mut _);
273                    }
274                }
275                task_data = Box::into_raw(Box::new(None));
276                ffi::gdk_content_serializer_set_task_data(
277                    serializer.to_glib_none().0,
278                    task_data as *mut _,
279                    Some(notify_func::<T>),
280                );
281            }
282
283            (*callback)(&serializer, &mut *task_data);
284        }
285    }
286    let serialize = Some(serialize_func::<T, P> as _);
287    unsafe extern "C" fn notify_func<
288        T: 'static,
289        P: Fn(&ContentSerializer, &mut Option<T>) + 'static,
290    >(
291        data: glib::ffi::gpointer,
292    ) {
293        unsafe {
294            let _callback: Box<P> = Box::from_raw(data as *mut _);
295        }
296    }
297    let destroy_call4 = Some(notify_func::<T, P> as _);
298    let super_callback0: Box<P> = serialize_data;
299    unsafe {
300        ffi::gdk_content_register_serializer(
301            type_.into_glib(),
302            mime_type.to_glib_none().0,
303            serialize,
304            Box::into_raw(super_callback0) as *mut _,
305            destroy_call4,
306        );
307    }
308}
309
310/// Serialize content and write it to the given output stream, asynchronously.
311///
312/// The default I/O priority is `G_PRIORITY_DEFAULT` (i.e. 0), and lower numbers
313/// indicate a higher priority.
314/// ## `stream`
315/// a `GOutputStream` to write the serialized content to
316/// ## `mime_type`
317/// the mime type to serialize to
318/// ## `value`
319/// the content to serialize
320/// ## `io_priority`
321/// the I/O priority of the operation
322/// ## `cancellable`
323/// optional `GCancellable` object
324/// ## `callback`
325/// callback to call when the operation is done
326#[doc(alias = "gdk_content_serialize_async")]
327pub fn content_serialize_async<R: FnOnce(Result<(), glib::Error>) + 'static>(
328    stream: &impl IsA<gio::OutputStream>,
329    mime_type: &str,
330    value: &glib::Value,
331    io_priority: glib::Priority,
332    cancellable: Option<&impl IsA<gio::Cancellable>>,
333    callback: R,
334) {
335    assert_initialized_main_thread!();
336    let main_context = glib::MainContext::ref_thread_default();
337    let is_main_context_owner = main_context.is_owner();
338    let has_acquired_main_context = (!is_main_context_owner)
339        .then(|| main_context.acquire().ok())
340        .flatten();
341    assert!(
342        is_main_context_owner || has_acquired_main_context.is_some(),
343        "Async operations only allowed if the thread is owning the MainContext"
344    );
345    let user_data: Box<glib::thread_guard::ThreadGuard<R>> =
346        Box::new(glib::thread_guard::ThreadGuard::new(callback));
347    unsafe extern "C" fn content_serialize_async_trampoline<
348        R: FnOnce(Result<(), glib::Error>) + 'static,
349    >(
350        _source_object: *mut glib::gobject_ffi::GObject,
351        res: *mut gio::ffi::GAsyncResult,
352        user_data: glib::ffi::gpointer,
353    ) {
354        unsafe {
355            let mut error = ptr::null_mut();
356            let _ = ffi::gdk_content_serialize_finish(res, &mut error);
357            let result = if error.is_null() {
358                Ok(())
359            } else {
360                Err(from_glib_full(error))
361            };
362            let callback: Box<glib::thread_guard::ThreadGuard<R>> =
363                Box::from_raw(user_data as *mut _);
364            let callback = callback.into_inner();
365            callback(result);
366        }
367    }
368    let callback = content_serialize_async_trampoline::<R>;
369    unsafe {
370        ffi::gdk_content_serialize_async(
371            stream.as_ref().to_glib_none().0,
372            mime_type.to_glib_none().0,
373            value.to_glib_none().0,
374            io_priority.into_glib(),
375            cancellable.map(|p| p.as_ref()).to_glib_none().0,
376            Some(callback),
377            Box::into_raw(user_data) as *mut _,
378        );
379    }
380}
381
382pub fn content_serialize_future(
383    stream: &(impl IsA<gio::OutputStream> + Clone + 'static),
384    mime_type: &str,
385    value: &glib::Value,
386    io_priority: glib::Priority,
387) -> Pin<Box<dyn future::Future<Output = Result<(), glib::Error>> + 'static>> {
388    assert_initialized_main_thread!();
389
390    let stream = stream.clone();
391    let mime_type = String::from(mime_type);
392    let value = value.clone();
393    Box::pin(gio::GioFuture::new(&(), move |_obj, cancellable, send| {
394        content_serialize_async(
395            &stream,
396            &mime_type,
397            &value,
398            io_priority,
399            Some(cancellable),
400            move |res| {
401                send.resolve(res);
402            },
403        );
404    }))
405}
406
407/// s text.
408/// The clip region will include space to the left or right of the line
409/// (to the layout bounding box) if you have indexes above or below the
410/// indexes contained inside the line. This is to draw the selection all
411/// the way to the side of the layout. However, the clip region is in line
412/// coordinates, not layout coordinates.
413///
414/// Note that the regions returned correspond to logical extents of the text
415/// ranges, not ink extents. So the drawn line may in fact touch areas out of
416/// the clip region.  The clip region is mainly useful for highlightling parts
417/// of text, such as when text is selected.
418/// ## `line`
419/// a [`pango::LayoutLine`][crate::pango::LayoutLine]
420/// ## `x_origin`
421/// X pixel where you intend to draw the layout line with this clip
422/// ## `y_origin`
423/// baseline pixel where you intend to draw the layout line with this clip
424/// ## `index_ranges`
425/// array of byte indexes into the layout, where even
426///   members of array are start indexes and odd elements are end indexes
427///
428/// # Returns
429///
430/// a clip region containing the given ranges
431#[doc(alias = "gdk_pango_layout_line_get_clip_region")]
432pub fn pango_layout_line_get_clip_region(
433    line: &pango::LayoutLine,
434    x_origin: i32,
435    y_origin: i32,
436    index_ranges: &[GRange],
437) -> cairo::Region {
438    assert_initialized_main_thread!();
439
440    let ptr: *const i32 = index_ranges.as_ptr() as _;
441    unsafe {
442        from_glib_full(ffi::gdk_pango_layout_line_get_clip_region(
443            line.to_glib_none().0,
444            x_origin,
445            y_origin,
446            mut_override(ptr),
447            (index_ranges.len() / 2) as i32,
448        ))
449    }
450}