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::{ffi, prelude::*, ContentDeserializer, ContentSerializer};
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        let mut error = ptr::null_mut();
102        let mut value = glib::Value::uninitialized();
103        let _ = ffi::gdk_content_deserialize_finish(res, value.to_glib_none_mut().0, &mut error);
104        let result = if error.is_null() {
105            Ok(value)
106        } else {
107            Err(from_glib_full(error))
108        };
109        let callback: Box<glib::thread_guard::ThreadGuard<R>> = Box::from_raw(user_data as *mut _);
110        let callback = callback.into_inner();
111        callback(result);
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        let deserializer: ContentDeserializer = from_glib_full(deserializer);
182        let callback: &P =
183            &*(ffi::gdk_content_deserializer_get_user_data(deserializer.to_glib_none().0)
184                as *mut _);
185
186        let mut task_data: *mut Option<T> =
187            ffi::gdk_content_deserializer_get_task_data(deserializer.to_glib_none().0) as *mut _;
188        if task_data.is_null() {
189            unsafe extern "C" fn notify_func<T: 'static>(data: glib::ffi::gpointer) {
190                let _task_data: Box<Option<T>> = Box::from_raw(data as *mut _);
191            }
192            task_data = Box::into_raw(Box::new(None));
193            ffi::gdk_content_deserializer_set_task_data(
194                deserializer.to_glib_none().0,
195                task_data as *mut _,
196                Some(notify_func::<T>),
197            );
198        }
199
200        (*callback)(&deserializer, &mut *task_data);
201    }
202    let deserialize = Some(deserialize_func::<T, P> as _);
203    unsafe extern "C" fn notify_func<
204        T: 'static,
205        P: Fn(&ContentDeserializer, &mut Option<T>) + 'static,
206    >(
207        data: glib::ffi::gpointer,
208    ) {
209        let _callback: Box<P> = Box::from_raw(data as *mut _);
210    }
211    let destroy_call4 = Some(notify_func::<T, P> as _);
212    let super_callback0: Box<P> = deserialize_data;
213    unsafe {
214        ffi::gdk_content_register_deserializer(
215            mime_type.to_glib_none().0,
216            type_.into_glib(),
217            deserialize,
218            Box::into_raw(super_callback0) as *mut _,
219            destroy_call4,
220        );
221    }
222}
223
224/// Registers a function to serialize objects of a given type.
225///
226/// Since 4.20, when looking up a serializer to use, GTK will
227/// use the last registered serializer for a given mime type,
228/// so applications can override the built-in serializers.
229/// ## `type_`
230/// the type of objects that the function can serialize
231/// ## `mime_type`
232/// the mime type to serialize to
233/// ## `serialize`
234/// the callback
235/// ## `notify`
236/// destroy notify for @data
237#[doc(alias = "gdk_content_register_serializer")]
238pub fn content_register_serializer<
239    T: 'static,
240    P: Fn(&ContentSerializer, &mut Option<T>) + 'static,
241>(
242    type_: glib::types::Type,
243    mime_type: &str,
244    serialize: P,
245) {
246    assert_initialized_main_thread!();
247    let serialize_data: Box<P> = Box::new(serialize);
248    unsafe extern "C" fn serialize_func<
249        T: 'static,
250        P: Fn(&ContentSerializer, &mut Option<T>) + 'static,
251    >(
252        serializer: *mut ffi::GdkContentSerializer,
253    ) {
254        let serializer: ContentSerializer = from_glib_full(serializer);
255        let callback: &P =
256            &*(ffi::gdk_content_serializer_get_user_data(serializer.to_glib_none().0) as *mut _);
257
258        let mut task_data: *mut Option<T> =
259            ffi::gdk_content_serializer_get_task_data(serializer.to_glib_none().0) as *mut _;
260        if task_data.is_null() {
261            unsafe extern "C" fn notify_func<T: 'static>(data: glib::ffi::gpointer) {
262                let _task_data: Box<Option<T>> = Box::from_raw(data as *mut _);
263            }
264            task_data = Box::into_raw(Box::new(None));
265            ffi::gdk_content_serializer_set_task_data(
266                serializer.to_glib_none().0,
267                task_data as *mut _,
268                Some(notify_func::<T>),
269            );
270        }
271
272        (*callback)(&serializer, &mut *task_data);
273    }
274    let serialize = Some(serialize_func::<T, P> as _);
275    unsafe extern "C" fn notify_func<
276        T: 'static,
277        P: Fn(&ContentSerializer, &mut Option<T>) + 'static,
278    >(
279        data: glib::ffi::gpointer,
280    ) {
281        let _callback: Box<P> = Box::from_raw(data as *mut _);
282    }
283    let destroy_call4 = Some(notify_func::<T, P> as _);
284    let super_callback0: Box<P> = serialize_data;
285    unsafe {
286        ffi::gdk_content_register_serializer(
287            type_.into_glib(),
288            mime_type.to_glib_none().0,
289            serialize,
290            Box::into_raw(super_callback0) as *mut _,
291            destroy_call4,
292        );
293    }
294}
295
296/// Serialize content and write it to the given output stream, asynchronously.
297///
298/// The default I/O priority is `G_PRIORITY_DEFAULT` (i.e. 0), and lower numbers
299/// indicate a higher priority.
300/// ## `stream`
301/// a `GOutputStream` to write the serialized content to
302/// ## `mime_type`
303/// the mime type to serialize to
304/// ## `value`
305/// the content to serialize
306/// ## `io_priority`
307/// the I/O priority of the operation
308/// ## `cancellable`
309/// optional `GCancellable` object
310/// ## `callback`
311/// callback to call when the operation is done
312#[doc(alias = "gdk_content_serialize_async")]
313pub fn content_serialize_async<R: FnOnce(Result<(), glib::Error>) + 'static>(
314    stream: &impl IsA<gio::OutputStream>,
315    mime_type: &str,
316    value: &glib::Value,
317    io_priority: glib::Priority,
318    cancellable: Option<&impl IsA<gio::Cancellable>>,
319    callback: R,
320) {
321    assert_initialized_main_thread!();
322    let main_context = glib::MainContext::ref_thread_default();
323    let is_main_context_owner = main_context.is_owner();
324    let has_acquired_main_context = (!is_main_context_owner)
325        .then(|| main_context.acquire().ok())
326        .flatten();
327    assert!(
328        is_main_context_owner || has_acquired_main_context.is_some(),
329        "Async operations only allowed if the thread is owning the MainContext"
330    );
331    let user_data: Box<glib::thread_guard::ThreadGuard<R>> =
332        Box::new(glib::thread_guard::ThreadGuard::new(callback));
333    unsafe extern "C" fn content_serialize_async_trampoline<
334        R: FnOnce(Result<(), glib::Error>) + 'static,
335    >(
336        _source_object: *mut glib::gobject_ffi::GObject,
337        res: *mut gio::ffi::GAsyncResult,
338        user_data: glib::ffi::gpointer,
339    ) {
340        let mut error = ptr::null_mut();
341        let _ = ffi::gdk_content_serialize_finish(res, &mut error);
342        let result = if error.is_null() {
343            Ok(())
344        } else {
345            Err(from_glib_full(error))
346        };
347        let callback: Box<glib::thread_guard::ThreadGuard<R>> = Box::from_raw(user_data as *mut _);
348        let callback = callback.into_inner();
349        callback(result);
350    }
351    let callback = content_serialize_async_trampoline::<R>;
352    unsafe {
353        ffi::gdk_content_serialize_async(
354            stream.as_ref().to_glib_none().0,
355            mime_type.to_glib_none().0,
356            value.to_glib_none().0,
357            io_priority.into_glib(),
358            cancellable.map(|p| p.as_ref()).to_glib_none().0,
359            Some(callback),
360            Box::into_raw(user_data) as *mut _,
361        );
362    }
363}
364
365pub fn content_serialize_future(
366    stream: &(impl IsA<gio::OutputStream> + Clone + 'static),
367    mime_type: &str,
368    value: &glib::Value,
369    io_priority: glib::Priority,
370) -> Pin<Box<dyn future::Future<Output = Result<(), glib::Error>> + 'static>> {
371    assert_initialized_main_thread!();
372
373    let stream = stream.clone();
374    let mime_type = String::from(mime_type);
375    let value = value.clone();
376    Box::pin(gio::GioFuture::new(&(), move |_obj, cancellable, send| {
377        content_serialize_async(
378            &stream,
379            &mime_type,
380            &value,
381            io_priority,
382            Some(cancellable),
383            move |res| {
384                send.resolve(res);
385            },
386        );
387    }))
388}
389
390/// Obtains a clip region which contains the areas where the given
391/// ranges of text would be drawn.
392///
393/// @x_origin and @y_origin are the top left position of the layout.
394/// @index_ranges should contain ranges of bytes in the layout’s text.
395/// The clip region will include space to the left or right of the line
396/// (to the layout bounding box) if you have indexes above or below the
397/// indexes contained inside the line. This is to draw the selection all
398/// the way to the side of the layout. However, the clip region is in line
399/// coordinates, not layout coordinates.
400///
401/// Note that the regions returned correspond to logical extents of the text
402/// ranges, not ink extents. So the drawn line may in fact touch areas out of
403/// the clip region.  The clip region is mainly useful for highlightling parts
404/// of text, such as when text is selected.
405/// ## `line`
406/// a [`pango::LayoutLine`][crate::pango::LayoutLine]
407/// ## `x_origin`
408/// X pixel where you intend to draw the layout line with this clip
409/// ## `y_origin`
410/// baseline pixel where you intend to draw the layout line with this clip
411/// ## `index_ranges`
412/// array of byte indexes into the layout, where even
413///   members of array are start indexes and odd elements are end indexes
414///
415/// # Returns
416///
417/// a clip region containing the given ranges
418#[doc(alias = "gdk_pango_layout_line_get_clip_region")]
419pub fn pango_layout_line_get_clip_region(
420    line: &pango::LayoutLine,
421    x_origin: i32,
422    y_origin: i32,
423    index_ranges: &[GRange],
424) -> cairo::Region {
425    assert_initialized_main_thread!();
426
427    let ptr: *const i32 = index_ranges.as_ptr() as _;
428    unsafe {
429        from_glib_full(ffi::gdk_pango_layout_line_get_clip_region(
430            line.to_glib_none().0,
431            x_origin,
432            y_origin,
433            mut_override(ptr),
434            (index_ranges.len() / 2) as i32,
435        ))
436    }
437}