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