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