gsk4/
render_replay.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use crate::{ffi, RenderNode};
4use glib::{prelude::*, translate::*};
5use std::boxed::Box as Box_;
6
7/// A facility to replay a [`RenderNode`][crate::RenderNode] and its children, potentially
8/// modifying them.
9///
10/// This is a utility tool to walk a rendernode tree. The most powerful way
11/// is to provide a function via [`set_node_filter()`][Self::set_node_filter()]
12/// to filter each individual node and then run
13/// [`filter_node()`][Self::filter_node()] on the nodes you want to filter.
14///
15/// An easier method exists to just walk the node tree and extract information
16/// without any modifications. If you want to do that, the functions
17/// [`set_node_foreach()`][Self::set_node_foreach()] exists. You can also call
18/// [`foreach_node()`][Self::foreach_node()] to run that function. Note that
19/// the previously mentioned complex functionality will still be invoked if you
20/// have set up a function for it, but its result will not be returned.
21///
22/// Here is an example that combines both approaches to print the whole tree:
23///
24/// **⚠️ The following code is in c ⚠️**
25///
26/// ```c
27/// #include <gtk/gtk.h>
28///
29/// static GskRenderNode *
30/// print_nodes (GskRenderReplay *replay,
31///              GskRenderNode   *node,
32///              gpointer         user_data)
33/// {
34///   int *depth = user_data;
35///   GskRenderNode *result;
36///
37///   g_print ("%*s%s\n", 2 * *depth, "", g_type_name_from_instance ((GTypeInstance *) node));
38///
39///   *depth += 1;
40///   result = gsk_render_replay_default (replay, node);
41///   *depth -= 1;
42///
43///   return result;
44/// }
45///
46/// int
47/// main (int argc, char *argv[])
48/// {
49///   GFile *file;
50///   GBytes *bytes;
51///   GskRenderNode *node;
52///   GskRenderReplay *replay;
53///   int depth = 0;
54///
55///   gtk_init ();
56///
57///   if (argc < 2)
58///     {
59///       g_print ("usage: %s NODEFILE\n", argv[0]);
60///       return 0;
61///     }
62///
63///   file = g_file_new_for_commandline_arg (argv[1]);
64///   bytes = g_file_load_bytes (file, NULL, NULL, NULL);
65///   g_object_unref (file);
66///   if (bytes == NULL)
67///     return 1;
68///
69///   node = gsk_render_node_deserialize (bytes, NULL, NULL);
70///   g_bytes_unref (bytes);
71///   if (node == NULL)
72///     return 1;
73///
74///   replay = gsk_render_replay_new ();
75///   gsk_render_replay_set_node_filter (replay, print_nodes, &depth, NULL);
76///   gsk_render_node_foreach_node (replay, node);
77///   gsk_render_node_unref (node);
78///
79///   return 0;
80/// }
81/// ```
82#[repr(C)]
83#[doc(alias = "GskRenderReplay")]
84pub struct RenderReplay(std::ptr::NonNull<ffi::GskRenderReplay>);
85
86impl Drop for RenderReplay {
87    fn drop(&mut self) {
88        unsafe {
89            ffi::gsk_render_replay_free(self.0.as_ptr());
90        }
91    }
92}
93
94impl RenderReplay {
95    /// Replays the node using the default method.
96    ///
97    /// The default method calls [`filter_node()`][Self::filter_node()]
98    /// on all its child nodes and the filter functions for all its
99    /// proeprties. If none of them are changed, it returns the passed
100    /// in node. Otherwise it constructs a new node with the changed
101    /// children and properties.
102    ///
103    /// It may not be possible to construct a new node when any of the
104    /// callbacks return NULL. In that case, this function will return
105    /// NULL, too.
106    /// ## `node`
107    /// the node to replay
108    ///
109    /// # Returns
110    ///
111    /// The replayed node
112    #[doc(alias = "gsk_render_replay_default")]
113    #[allow(clippy::should_implement_trait)]
114    pub fn default(&self, node: impl AsRef<RenderNode>) -> Option<RenderNode> {
115        unsafe {
116            from_glib_full(ffi::gsk_render_replay_default(
117                self.0.as_ptr(),
118                node.as_ref().to_glib_none().0,
119            ))
120        }
121    }
122
123    /// Filters a font using the current filter function.
124    /// ## `font`
125    /// The font to filter
126    ///
127    /// # Returns
128    ///
129    /// the filtered font
130    #[doc(alias = "gsk_render_replay_filter_font")]
131    pub fn filter_font(&self, font: &impl IsA<pango::Font>) -> pango::Font {
132        unsafe {
133            from_glib_full(ffi::gsk_render_replay_filter_font(
134                self.0.as_ptr(),
135                font.as_ref().to_glib_none().0,
136            ))
137        }
138    }
139
140    /// Replays a node using the replay's filter function.
141    ///
142    /// After the replay the node may be unchanged, or it may be
143    /// removed, which will result in [`None`] being returned.
144    ///
145    /// This function calls the registered callback in the following order:
146    ///
147    /// 1. If a foreach function is set, it is called first. If it returns
148    ///    false, this function immediately exits and returns the passed
149    ///    in node.
150    ///
151    /// 2. If a node filter is set, it is called and its result is returned.
152    ///
153    /// 3. [`default()`][Self::default()] is called and its result is
154    ///    returned.
155    /// ## `node`
156    /// the node to replay
157    ///
158    /// # Returns
159    ///
160    /// The replayed node
161    #[doc(alias = "gsk_render_replay_filter_node")]
162    pub fn filter_node(&self, node: impl AsRef<RenderNode>) -> Option<RenderNode> {
163        unsafe {
164            from_glib_full(ffi::gsk_render_replay_filter_node(
165                self.0.as_ptr(),
166                node.as_ref().to_glib_none().0,
167            ))
168        }
169    }
170
171    /// Filters a texture using the current filter function.
172    /// ## `texture`
173    /// The texture to filter
174    ///
175    /// # Returns
176    ///
177    /// the filtered texture
178    #[doc(alias = "gsk_render_replay_filter_texture")]
179    pub fn filter_texture(&self, texture: &impl IsA<gdk::Texture>) -> gdk::Texture {
180        unsafe {
181            from_glib_full(ffi::gsk_render_replay_filter_texture(
182                self.0.as_ptr(),
183                texture.as_ref().to_glib_none().0,
184            ))
185        }
186    }
187
188    /// Calls the filter and foreach functions for each node.
189    ///
190    /// This function calls [`filter_node()`][Self::filter_node()] internally,
191    /// but discards the result assuming no changes were made.
192    /// ## `node`
193    /// the node to replay
194    #[doc(alias = "gsk_render_replay_foreach_node")]
195    pub fn foreach_node(&self, node: impl AsRef<RenderNode>) {
196        unsafe {
197            ffi::gsk_render_replay_foreach_node(self.0.as_ptr(), node.as_ref().to_glib_none().0);
198        }
199    }
200
201    /// Sets a filter function to be called by [`default()`][Self::default()]
202    /// for nodes that contain fonts.
203    ///
204    /// You can call `GskRenderReplay::filter_font()` to filter
205    /// a font yourself.
206    /// ## `filter`
207    /// the font filter function
208    #[doc(alias = "gsk_render_replay_set_font_filter")]
209    pub fn set_font_filter<P: Fn(&RenderReplay, &pango::Font) -> pango::Font + 'static>(
210        &mut self,
211        filter: P,
212    ) {
213        let filter_data: Box_<P> = Box_::new(filter);
214        unsafe extern "C" fn filter_func<
215            P: Fn(&RenderReplay, &pango::Font) -> pango::Font + 'static,
216        >(
217            replay: *mut ffi::GskRenderReplay,
218            font: *mut pango::ffi::PangoFont,
219            user_data: glib::ffi::gpointer,
220        ) -> *mut pango::ffi::PangoFont {
221            let replay = RenderReplay(std::ptr::NonNull::new_unchecked(replay));
222            let font = from_glib_borrow(font);
223            let callback = &*(user_data as *mut P);
224            (*callback)(&replay, &font).into_glib_ptr()
225        }
226        let filter = Some(filter_func::<P> as _);
227        unsafe extern "C" fn user_destroy_func<
228            P: Fn(&RenderReplay, &pango::Font) -> pango::Font + 'static,
229        >(
230            data: glib::ffi::gpointer,
231        ) {
232            let _callback = Box_::from_raw(data as *mut P);
233        }
234        let destroy_call3 = Some(user_destroy_func::<P> as _);
235        let super_callback0: Box_<P> = filter_data;
236        unsafe {
237            ffi::gsk_render_replay_set_font_filter(
238                self.0.as_mut(),
239                filter,
240                Box_::into_raw(super_callback0) as *mut _,
241                destroy_call3,
242            );
243        }
244    }
245
246    #[doc(alias = "gsk_render_replay_set_font_filter")]
247    #[doc(alias = "set_font_filter")]
248    pub fn unset_font_filter(&mut self) {
249        unsafe {
250            ffi::gsk_render_replay_set_font_filter(
251                self.0.as_mut(),
252                None,
253                std::ptr::null_mut(),
254                None,
255            )
256        }
257    }
258
259    /// Sets the function to use as a node filter.
260    ///
261    /// This is the most complex function to use for replaying nodes.
262    /// It can either:
263    ///
264    /// * keep the node and just return it unchanged
265    ///
266    /// * create a replacement node and return that
267    ///
268    /// * discard the node by returning [`None`]
269    ///
270    /// * call [`default()`][Self::default()] to have the default handler
271    ///   run for this node, which calls your function on its children
272    /// ## `filter`
273    /// The function to call to replay nodes
274    #[doc(alias = "gsk_render_replay_set_node_filter")]
275    pub fn set_node_filter<P: Fn(&RenderReplay, &RenderNode) -> Option<RenderNode> + 'static>(
276        &mut self,
277        filter: P,
278    ) {
279        let filter_data: Box_<P> = Box_::new(filter);
280        unsafe extern "C" fn filter_func<
281            P: Fn(&RenderReplay, &RenderNode) -> Option<RenderNode> + 'static,
282        >(
283            replay: *mut ffi::GskRenderReplay,
284            node: *mut ffi::GskRenderNode,
285            user_data: glib::ffi::gpointer,
286        ) -> *mut ffi::GskRenderNode {
287            let replay = RenderReplay(std::ptr::NonNull::new_unchecked(replay));
288            let node = from_glib_borrow(node);
289            let callback = &*(user_data as *mut P);
290            (*callback)(&replay, &node).into_glib_ptr()
291        }
292        let filter = Some(filter_func::<P> as _);
293        unsafe extern "C" fn user_destroy_func<
294            P: Fn(&RenderReplay, &RenderNode) -> Option<RenderNode> + 'static,
295        >(
296            data: glib::ffi::gpointer,
297        ) {
298            let _callback = Box_::from_raw(data as *mut P);
299        }
300        let destroy_call3 = Some(user_destroy_func::<P> as _);
301        let super_callback0: Box_<P> = filter_data;
302        unsafe {
303            ffi::gsk_render_replay_set_node_filter(
304                self.0.as_mut(),
305                filter,
306                Box_::into_raw(super_callback0) as *mut _,
307                destroy_call3,
308            );
309        }
310    }
311
312    #[doc(alias = "gsk_render_replay_set_node_filter")]
313    #[doc(alias = "set_node_filter")]
314    pub fn unset_node_filter(&mut self) {
315        unsafe {
316            ffi::gsk_render_replay_set_node_filter(
317                self.0.as_mut(),
318                None,
319                std::ptr::null_mut(),
320                None,
321            )
322        }
323    }
324
325    /// Sets the function to call for every node.
326    ///
327    /// This function is called before the node filter, so if it returns
328    /// FALSE, the node filter will never be called.
329    /// ## `foreach`
330    /// the function to call for all nodes
331    #[doc(alias = "gsk_render_replay_set_node_foreach")]
332    pub fn set_node_foreach<P: Fn(&RenderReplay, &RenderNode) -> glib::ControlFlow + 'static>(
333        &mut self,
334        foreach: P,
335    ) {
336        let foreach_data: Box_<P> = Box_::new(foreach);
337        unsafe extern "C" fn foreach_func<
338            P: Fn(&RenderReplay, &RenderNode) -> glib::ControlFlow + 'static,
339        >(
340            replay: *mut ffi::GskRenderReplay,
341            node: *mut ffi::GskRenderNode,
342            user_data: glib::ffi::gpointer,
343        ) -> glib::ffi::gboolean {
344            let replay = RenderReplay(std::ptr::NonNull::new_unchecked(replay));
345            let node = from_glib_borrow(node);
346            let callback = &*(user_data as *mut P);
347            (*callback)(&replay, &node).into_glib()
348        }
349        let foreach = Some(foreach_func::<P> as _);
350        unsafe extern "C" fn user_destroy_func<
351            P: Fn(&RenderReplay, &RenderNode) -> glib::ControlFlow + 'static,
352        >(
353            data: glib::ffi::gpointer,
354        ) {
355            let _callback = Box_::from_raw(data as *mut P);
356        }
357        let destroy_call3 = Some(user_destroy_func::<P> as _);
358        let super_callback0: Box_<P> = foreach_data;
359        unsafe {
360            ffi::gsk_render_replay_set_node_foreach(
361                self.0.as_mut(),
362                foreach,
363                Box_::into_raw(super_callback0) as *mut _,
364                destroy_call3,
365            );
366        }
367    }
368
369    #[doc(alias = "gsk_render_replay_set_node_foreach")]
370    #[doc(alias = "set_node_foreach")]
371    pub fn unset_foreach_node(&mut self) {
372        unsafe {
373            ffi::gsk_render_replay_set_node_foreach(
374                self.0.as_mut(),
375                None,
376                std::ptr::null_mut(),
377                None,
378            )
379        }
380    }
381
382    /// Sets a filter function to be called by [`default()`][Self::default()]
383    /// for nodes that contain textures.
384    ///
385    /// You can call `GskRenderReplay::filter_texture()` to filter
386    /// a texture yourself.
387    /// ## `filter`
388    /// the texture filter function
389    #[doc(alias = "gsk_render_replay_set_texture_filter")]
390    pub fn set_texture_filter<P: Fn(&RenderReplay, &gdk::Texture) -> gdk::Texture + 'static>(
391        &mut self,
392        filter: P,
393    ) {
394        let filter_data: Box_<P> = Box_::new(filter);
395        unsafe extern "C" fn filter_func<
396            P: Fn(&RenderReplay, &gdk::Texture) -> gdk::Texture + 'static,
397        >(
398            replay: *mut ffi::GskRenderReplay,
399            texture: *mut gdk::ffi::GdkTexture,
400            user_data: glib::ffi::gpointer,
401        ) -> *mut gdk::ffi::GdkTexture {
402            let replay = RenderReplay(std::ptr::NonNull::new_unchecked(replay));
403            let texture = from_glib_borrow(texture);
404            let callback = &*(user_data as *mut P);
405            (*callback)(&replay, &texture).into_glib_ptr()
406        }
407        let filter = Some(filter_func::<P> as _);
408        unsafe extern "C" fn user_destroy_func<
409            P: Fn(&RenderReplay, &gdk::Texture) -> gdk::Texture + 'static,
410        >(
411            data: glib::ffi::gpointer,
412        ) {
413            let _callback = Box_::from_raw(data as *mut P);
414        }
415        let destroy_call3 = Some(user_destroy_func::<P> as _);
416        let super_callback0: Box_<P> = filter_data;
417        unsafe {
418            ffi::gsk_render_replay_set_texture_filter(
419                self.0.as_mut(),
420                filter,
421                Box_::into_raw(super_callback0) as *mut _,
422                destroy_call3,
423            );
424        }
425    }
426
427    #[doc(alias = "gsk_render_replay_set_texture_filter")]
428    #[doc(alias = "set_texture_filter")]
429    pub fn unset_texture_filter(&mut self) {
430        unsafe {
431            ffi::gsk_render_replay_set_texture_filter(
432                self.0.as_mut(),
433                None,
434                std::ptr::null_mut(),
435                None,
436            )
437        }
438    }
439
440    /// Creates a new replay object to replay nodes.
441    ///
442    /// # Returns
443    ///
444    /// A new replay object to replay nodes
445    #[doc(alias = "gsk_render_replay_new")]
446    pub fn new() -> RenderReplay {
447        assert_initialized_main_thread!();
448        unsafe {
449            RenderReplay(std::ptr::NonNull::new_unchecked(
450                ffi::gsk_render_replay_new(),
451            ))
452        }
453    }
454}
455
456#[cfg(feature = "v4_22")]
457#[cfg_attr(docsrs, doc(cfg(feature = "v4_22")))]
458impl Default for RenderReplay {
459    fn default() -> Self {
460        Self::new()
461    }
462}