1/* GSK - The GTK Scene Kit
2 *
3 * Copyright 2016 Endless
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19/**
20 * GskRenderNode: (ref-func gsk_render_node_ref) (unref-func gsk_render_node_unref) (set-value-func gsk_value_set_render_node) (get-value-func gsk_value_get_render_node)
21 *
22 * `GskRenderNode` is the basic block in a scene graph to be
23 * rendered using [class@Gsk.Renderer].
24 *
25 * Each node has a parent, except the top-level node; each node may have
26 * children nodes.
27 *
28 * Each node has an associated drawing surface, which has the size of
29 * the rectangle set when creating it.
30 *
31 * Render nodes are meant to be transient; once they have been associated
32 * to a [class@Gsk.Renderer] it's safe to release any reference you have on
33 * them. All [class@Gsk.RenderNode]s are immutable, you can only specify their
34 * properties during construction.
35 */
36
37#include "config.h"
38
39#include "gskrendernodeprivate.h"
40
41#include "gskdebugprivate.h"
42#include "gskrendererprivate.h"
43#include "gskrendernodeparserprivate.h"
44
45#include <graphene-gobject.h>
46
47#include <math.h>
48
49#include <gobject/gvaluecollector.h>
50
51G_DEFINE_QUARK (gsk-serialization-error-quark, gsk_serialization_error)
52
53#define GSK_RENDER_NODE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSK_TYPE_RENDER_NODE, GskRenderNodeClass))
54
55
56static void
57value_render_node_init (GValue *value)
58{
59 value->data[0].v_pointer = NULL;
60}
61
62static void
63value_render_node_free_value (GValue *value)
64{
65 if (value->data[0].v_pointer != NULL)
66 gsk_render_node_unref (node: value->data[0].v_pointer);
67}
68
69static void
70value_render_node_copy_value (const GValue *src,
71 GValue *dst)
72{
73 if (src->data[0].v_pointer != NULL)
74 dst->data[0].v_pointer = gsk_render_node_ref (node: src->data[0].v_pointer);
75 else
76 dst->data[0].v_pointer = NULL;
77}
78
79static gpointer
80value_render_node_peek_pointer (const GValue *value)
81{
82 return value->data[0].v_pointer;
83}
84
85static char *
86value_render_node_collect_value (GValue *value,
87 guint n_collect_values,
88 GTypeCValue *collect_values,
89 guint collect_flags)
90{
91 GskRenderNode *node = collect_values[0].v_pointer;
92
93 if (node == NULL)
94 {
95 value->data[0].v_pointer = NULL;
96 return NULL;
97 }
98
99 if (node->parent_instance.g_class == NULL)
100 return g_strconcat (string1: "invalid unclassed GskRenderNode pointer for "
101 "value type '",
102 G_VALUE_TYPE_NAME (value),
103 "'",
104 NULL);
105
106 value->data[0].v_pointer = gsk_render_node_ref (node);
107
108 return NULL;
109}
110
111static char *
112value_render_node_lcopy_value (const GValue *value,
113 guint n_collect_values,
114 GTypeCValue *collect_values,
115 guint collect_flags)
116{
117 GskRenderNode **node_p = collect_values[0].v_pointer;
118
119 if (node_p == NULL)
120 return g_strconcat (string1: "value location for '",
121 G_VALUE_TYPE_NAME (value),
122 "' passed as NULL",
123 NULL);
124
125 if (value->data[0].v_pointer == NULL)
126 *node_p = NULL;
127 else if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
128 *node_p = value->data[0].v_pointer;
129 else
130 *node_p = gsk_render_node_ref (node: value->data[0].v_pointer);
131
132 return NULL;
133}
134
135static void
136gsk_render_node_finalize (GskRenderNode *self)
137{
138 g_type_free_instance (instance: (GTypeInstance *) self);
139}
140
141static void
142gsk_render_node_real_draw (GskRenderNode *node,
143 cairo_t *cr)
144{
145}
146
147static gboolean
148gsk_render_node_real_can_diff (const GskRenderNode *node1,
149 const GskRenderNode *node2)
150{
151 return FALSE;
152}
153
154static void
155gsk_render_node_real_diff (GskRenderNode *node1,
156 GskRenderNode *node2,
157 cairo_region_t *region)
158{
159}
160
161static void
162gsk_render_node_class_init (GskRenderNodeClass *klass)
163{
164 klass->node_type = GSK_NOT_A_RENDER_NODE;
165 klass->finalize = gsk_render_node_finalize;
166 klass->draw = gsk_render_node_real_draw;
167 klass->can_diff = gsk_render_node_real_can_diff;
168 klass->diff = gsk_render_node_real_diff;
169}
170
171static void
172gsk_render_node_init (GskRenderNode *self)
173{
174 g_atomic_ref_count_init (arc: &self->ref_count);
175}
176
177GType
178gsk_render_node_get_type (void)
179{
180 static gsize render_node_type__volatile;
181
182 if (g_once_init_enter (&render_node_type__volatile))
183 {
184 static const GTypeFundamentalInfo finfo = {
185 (G_TYPE_FLAG_CLASSED |
186 G_TYPE_FLAG_INSTANTIATABLE |
187 G_TYPE_FLAG_DERIVABLE |
188 G_TYPE_FLAG_DEEP_DERIVABLE),
189 };
190
191 static const GTypeValueTable value_table = {
192 value_render_node_init,
193 value_render_node_free_value,
194 value_render_node_copy_value,
195 value_render_node_peek_pointer,
196 "p",
197 value_render_node_collect_value,
198 "p",
199 value_render_node_lcopy_value,
200 };
201
202 const GTypeInfo node_info = {
203 /* Class */
204 sizeof (GskRenderNodeClass),
205 (GBaseInitFunc) NULL,
206 (GBaseFinalizeFunc) NULL,
207 (GClassInitFunc) gsk_render_node_class_init,
208 (GClassFinalizeFunc) NULL,
209 NULL,
210
211 /* Instance */
212 sizeof (GskRenderNode),
213 0,
214 (GInstanceInitFunc) gsk_render_node_init,
215
216 /* GValue */
217 &value_table,
218 };
219
220 GType render_node_type =
221 g_type_register_fundamental (type_id: g_type_fundamental_next (),
222 type_name: g_intern_static_string (string: "GskRenderNode"),
223 info: &node_info, finfo: &finfo,
224 flags: G_TYPE_FLAG_ABSTRACT);
225
226 g_once_init_leave (&render_node_type__volatile, render_node_type);
227 }
228
229 return render_node_type__volatile;
230}
231
232typedef struct
233{
234 GskRenderNodeType node_type;
235
236 void (* finalize) (GskRenderNode *node);
237 void (* draw) (GskRenderNode *node,
238 cairo_t *cr);
239 gboolean (* can_diff) (const GskRenderNode *node1,
240 const GskRenderNode *node2);
241 void (* diff) (GskRenderNode *node1,
242 GskRenderNode *node2,
243 cairo_region_t *region);
244} RenderNodeClassData;
245
246static void
247gsk_render_node_generic_class_init (gpointer g_class,
248 gpointer class_data)
249{
250 GskRenderNodeClass *node_class = g_class;
251 RenderNodeClassData *node_data = class_data;
252
253 /* Mandatory */
254 node_class->node_type = node_data->node_type;
255
256 /* Optional */
257 if (node_data->finalize != NULL)
258 node_class->finalize = node_data->finalize;
259 if (node_data->can_diff != NULL)
260 node_class->can_diff = node_data->can_diff;
261
262 /* Mandatory */
263 node_class->draw = node_data->draw;
264 node_class->diff = node_data->diff;
265
266 g_free (mem: node_data);
267}
268
269static gboolean
270gsk_render_node_can_diff_true (const GskRenderNode *node1,
271 const GskRenderNode *node2)
272{
273 return TRUE;
274}
275
276/*< private >
277 * gsk_render_node_type_register_static:
278 * @node_name: the name of the node
279 * @node_info: type information of the node
280 *
281 * Registers a new `GskRenderNode` type for the given @node_name using
282 * the type information in @node_info.
283 *
284 * Returns: the newly registered GType
285 */
286GType
287gsk_render_node_type_register_static (const char *node_name,
288 const GskRenderNodeTypeInfo *node_info)
289{
290 GTypeInfo info;
291
292 info.class_size = sizeof (GskRenderNodeClass);
293 info.base_init = NULL;
294 info.base_finalize = NULL;
295 info.class_init = gsk_render_node_generic_class_init;
296 info.class_finalize = NULL;
297
298 /* Avoid having a class_init() and a class struct for every GskRenderNode,
299 * by passing the various virtual functions and class data when initializing
300 * the base class
301 */
302 info.class_data = g_new (RenderNodeClassData, 1);
303 ((RenderNodeClassData *) info.class_data)->node_type = node_info->node_type;
304 ((RenderNodeClassData *) info.class_data)->finalize = node_info->finalize;
305 ((RenderNodeClassData *) info.class_data)->draw = node_info->draw;
306 ((RenderNodeClassData *) info.class_data)->can_diff = node_info->can_diff != NULL
307 ? node_info->can_diff
308 : gsk_render_node_can_diff_true;
309 ((RenderNodeClassData *) info.class_data)->diff = node_info->diff != NULL
310 ? node_info->diff
311 : gsk_render_node_diff_impossible;
312
313 info.instance_size = node_info->instance_size;
314 info.n_preallocs = 0;
315 info.instance_init = (GInstanceInitFunc) node_info->instance_init;
316 info.value_table = NULL;
317
318 return g_type_register_static (GSK_TYPE_RENDER_NODE, type_name: node_name, info: &info, flags: 0);
319}
320
321/*< private >
322 * gsk_render_node_alloc:
323 * @node_type: the `GskRenderNode`Type to instantiate
324 *
325 * Instantiates a new `GskRenderNode` for the given @node_type.
326 *
327 * Returns: (transfer full) (type GskRenderNode): the newly created `GskRenderNode`
328 */
329gpointer
330gsk_render_node_alloc (GskRenderNodeType node_type)
331{
332 g_return_val_if_fail (node_type > GSK_NOT_A_RENDER_NODE, NULL);
333 g_return_val_if_fail (node_type < GSK_RENDER_NODE_TYPE_N_TYPES, NULL);
334
335 g_assert (gsk_render_node_types[node_type] != G_TYPE_INVALID);
336 return g_type_create_instance (type: gsk_render_node_types[node_type]);
337}
338
339/**
340 * gsk_render_node_ref:
341 * @node: a `GskRenderNode`
342 *
343 * Acquires a reference on the given `GskRenderNode`.
344 *
345 * Returns: (transfer full): the `GskRenderNode` with an additional reference
346 */
347GskRenderNode *
348gsk_render_node_ref (GskRenderNode *node)
349{
350 g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
351
352 g_atomic_ref_count_inc (arc: &node->ref_count);
353
354 return node;
355}
356
357/**
358 * gsk_render_node_unref:
359 * @node: (transfer full): a `GskRenderNode`
360 *
361 * Releases a reference on the given `GskRenderNode`.
362 *
363 * If the reference was the last, the resources associated to the @node are
364 * freed.
365 */
366void
367gsk_render_node_unref (GskRenderNode *node)
368{
369 g_return_if_fail (GSK_IS_RENDER_NODE (node));
370
371 if (g_atomic_ref_count_dec (arc: &node->ref_count))
372 GSK_RENDER_NODE_GET_CLASS (node)->finalize (node);
373}
374
375
376/**
377 * gsk_render_node_get_node_type:
378 * @node: a `GskRenderNode`
379 *
380 * Returns the type of the @node.
381 *
382 * Returns: the type of the `GskRenderNode`
383 */
384GskRenderNodeType
385gsk_render_node_get_node_type (const GskRenderNode *node)
386{
387 g_return_val_if_fail (GSK_IS_RENDER_NODE (node), GSK_NOT_A_RENDER_NODE);
388
389 return GSK_RENDER_NODE_GET_CLASS (node)->node_type;
390}
391
392G_GNUC_PURE static inline
393GskRenderNodeType
394_gsk_render_node_get_node_type (const GskRenderNode *node)
395{
396 return GSK_RENDER_NODE_GET_CLASS (node)->node_type;
397}
398
399/**
400 * gsk_render_node_get_bounds:
401 * @node: a `GskRenderNode`
402 * @bounds: (out caller-allocates): return location for the boundaries
403 *
404 * Retrieves the boundaries of the @node.
405 *
406 * The node will not draw outside of its boundaries.
407 */
408void
409gsk_render_node_get_bounds (GskRenderNode *node,
410 graphene_rect_t *bounds)
411{
412 g_return_if_fail (GSK_IS_RENDER_NODE (node));
413 g_return_if_fail (bounds != NULL);
414
415 graphene_rect_init_from_rect (r: bounds, src: &node->bounds);
416}
417
418/**
419 * gsk_render_node_draw:
420 * @node: a `GskRenderNode`
421 * @cr: cairo context to draw to
422 *
423 * Draw the contents of @node to the given cairo context.
424 *
425 * Typically, you'll use this function to implement fallback rendering
426 * of `GskRenderNode`s on an intermediate Cairo context, instead of using
427 * the drawing context associated to a [class@Gdk.Surface]'s rendering buffer.
428 *
429 * For advanced nodes that cannot be supported using Cairo, in particular
430 * for nodes doing 3D operations, this function may fail.
431 **/
432void
433gsk_render_node_draw (GskRenderNode *node,
434 cairo_t *cr)
435{
436 g_return_if_fail (GSK_IS_RENDER_NODE (node));
437 g_return_if_fail (cr != NULL);
438 g_return_if_fail (cairo_status (cr) == CAIRO_STATUS_SUCCESS);
439
440 cairo_save (cr);
441
442 GSK_NOTE (CAIRO, g_message ("Rendering node %s[%p]",
443 g_type_name_from_instance ((GTypeInstance *) node),
444 node));
445
446 GSK_RENDER_NODE_GET_CLASS (node)->draw (node, cr);
447
448#ifdef G_ENABLE_DEBUG
449 if (GSK_DEBUG_CHECK (GEOMETRY))
450 {
451 cairo_set_operator (cr, op: CAIRO_OPERATOR_OVER);
452 cairo_rectangle (cr, x: node->bounds.origin.x - 1, y: node->bounds.origin.y - 1,
453 width: node->bounds.size.width + 2, height: node->bounds.size.height + 2);
454 cairo_set_line_width (cr, width: 2);
455 cairo_set_source_rgba (cr, red: 0, green: 0, blue: 0, alpha: 0.5);
456 cairo_stroke (cr);
457 }
458#endif
459
460 cairo_restore (cr);
461
462 if (cairo_status (cr))
463 {
464 g_warning ("drawing failure for render node %s: %s",
465 g_type_name_from_instance ((GTypeInstance *) node),
466 cairo_status_to_string (cairo_status (cr)));
467 }
468}
469
470/*
471 * gsk_render_node_can_diff:
472 * @node1: a `GskRenderNode`
473 * @node2: the `GskRenderNode` to compare with
474 *
475 * Checks if two render nodes can be expected to be compared via
476 * gsk_render_node_diff().
477 *
478 * The node diffing algorithm uses this function to match up similar
479 * nodes to compare when trying to minimize the resulting region.
480 *
481 * Nodes of different type always return %FALSE here.
482 *
483 * Returns: %TRUE if @node1 and @node2 can be expected to be compared
484 **/
485gboolean
486gsk_render_node_can_diff (const GskRenderNode *node1,
487 const GskRenderNode *node2)
488{
489 if (node1 == node2)
490 return TRUE;
491
492 if (_gsk_render_node_get_node_type (node: node1) == _gsk_render_node_get_node_type (node: node2))
493 return GSK_RENDER_NODE_GET_CLASS (node1)->can_diff (node1, node2);
494
495 if (_gsk_render_node_get_node_type (node: node1) == GSK_CONTAINER_NODE ||
496 _gsk_render_node_get_node_type (node: node2) == GSK_CONTAINER_NODE)
497 return TRUE;
498
499 return FALSE;
500}
501
502static void
503rectangle_init_from_graphene (cairo_rectangle_int_t *cairo,
504 const graphene_rect_t *graphene)
505{
506 cairo->x = floorf (x: graphene->origin.x);
507 cairo->y = floorf (x: graphene->origin.y);
508 cairo->width = ceilf (x: graphene->origin.x + graphene->size.width) - cairo->x;
509 cairo->height = ceilf (x: graphene->origin.y + graphene->size.height) - cairo->y;
510}
511
512void
513gsk_render_node_diff_impossible (GskRenderNode *node1,
514 GskRenderNode *node2,
515 cairo_region_t *region)
516{
517 cairo_rectangle_int_t rect;
518
519 rectangle_init_from_graphene (cairo: &rect, graphene: &node1->bounds);
520 cairo_region_union_rectangle (dst: region, rectangle: &rect);
521 rectangle_init_from_graphene (cairo: &rect, graphene: &node2->bounds);
522 cairo_region_union_rectangle (dst: region, rectangle: &rect);
523}
524
525/**
526 * gsk_render_node_diff:
527 * @node1: a `GskRenderNode`
528 * @node2: the `GskRenderNode` to compare with
529 * @region: a `cairo_region_t` to add the differences to
530 *
531 * Compares @node1 and @node2 trying to compute the minimal region of changes.
532 *
533 * In the worst case, this is the union of the bounds of @node1 and @node2.
534 *
535 * This function is used to compute the area that needs to be redrawn when
536 * the previous contents were drawn by @node1 and the new contents should
537 * correspond to @node2. As such, it is important that this comparison is
538 * faster than the time it takes to actually do the redraw.
539 *
540 * Note that the passed in @region may already contain previous results from
541 * previous node comparisons, so this function call will only add to it.
542 **/
543void
544gsk_render_node_diff (GskRenderNode *node1,
545 GskRenderNode *node2,
546 cairo_region_t *region)
547{
548 if (node1 == node2)
549 return;
550
551 if (_gsk_render_node_get_node_type (node: node1) == _gsk_render_node_get_node_type (node: node2))
552 GSK_RENDER_NODE_GET_CLASS (node1)->diff (node1, node2, region);
553
554 else if (_gsk_render_node_get_node_type (node: node1) == GSK_CONTAINER_NODE)
555 gsk_container_node_diff_with (container: node1, other: node2, region);
556 else if (_gsk_render_node_get_node_type (node: node2) == GSK_CONTAINER_NODE)
557 gsk_container_node_diff_with (container: node2, other: node1, region);
558 else
559 gsk_render_node_diff_impossible (node1, node2, region);
560}
561
562/**
563 * gsk_render_node_write_to_file:
564 * @node: a `GskRenderNode`
565 * @filename: (type filename): the file to save it to.
566 * @error: Return location for a potential error
567 *
568 * This function is equivalent to calling [method@Gsk.RenderNode.serialize]
569 * followed by [func@GLib.file_set_contents].
570 *
571 * See those two functions for details on the arguments.
572 *
573 * It is mostly intended for use inside a debugger to quickly dump a render
574 * node to a file for later inspection.
575 *
576 * Returns: %TRUE if saving was successful
577 **/
578gboolean
579gsk_render_node_write_to_file (GskRenderNode *node,
580 const char *filename,
581 GError **error)
582{
583 GBytes *bytes;
584 gboolean result;
585
586 g_return_val_if_fail (GSK_IS_RENDER_NODE (node), FALSE);
587 g_return_val_if_fail (filename != NULL, FALSE);
588 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
589
590 bytes = gsk_render_node_serialize (node);
591 result = g_file_set_contents (filename,
592 contents: g_bytes_get_data (bytes, NULL),
593 length: g_bytes_get_size (bytes),
594 error);
595 g_bytes_unref (bytes);
596
597 return result;
598}
599
600/**
601 * gsk_render_node_deserialize:
602 * @bytes: the bytes containing the data
603 * @error_func: (nullable) (scope call): Callback on parsing errors
604 * @user_data: (closure error_func): user_data for @error_func
605 *
606 * Loads data previously created via [method@Gsk.RenderNode.serialize].
607 *
608 * For a discussion of the supported format, see that function.
609 *
610 * Returns: (nullable) (transfer full): a new `GskRenderNode`
611 */
612GskRenderNode *
613gsk_render_node_deserialize (GBytes *bytes,
614 GskParseErrorFunc error_func,
615 gpointer user_data)
616{
617 GskRenderNode *node = NULL;
618
619 node = gsk_render_node_deserialize_from_bytes (bytes, error_func, user_data);
620
621 return node;
622}
623
624/**
625 * gsk_value_set_render_node:
626 * @value: a [struct@GObject.Value] initialized with type `GSK_TYPE_RENDER_NODE`
627 * @node: a `GskRenderNode`
628 *
629 * Stores the given `GskRenderNode` inside `value`.
630 *
631 * The [struct@GObject.Value] will acquire a reference to the `node`.
632 *
633 * Since: 4.6
634 */
635void
636gsk_value_set_render_node (GValue *value,
637 GskRenderNode *node)
638{
639 GskRenderNode *old_node;
640
641 g_return_if_fail (G_VALUE_HOLDS (value, GSK_TYPE_RENDER_NODE));
642
643 old_node = value->data[0].v_pointer;
644
645 if (node != NULL)
646 {
647 g_return_if_fail (GSK_IS_RENDER_NODE (node));
648
649 value->data[0].v_pointer = gsk_render_node_ref (node);
650 }
651 else
652 {
653 value->data[0].v_pointer = NULL;
654 }
655
656 if (old_node != NULL)
657 gsk_render_node_unref (node: old_node);
658}
659
660/**
661 * gsk_value_take_render_node:
662 * @value: a [struct@GObject.Value] initialized with type `GSK_TYPE_RENDER_NODE`
663 * @node: (transfer full) (nullable): a `GskRenderNode`
664 *
665 * Stores the given `GskRenderNode` inside `value`.
666 *
667 * This function transfers the ownership of the `node` to the `GValue`.
668 *
669 * Since: 4.6
670 */
671void
672gsk_value_take_render_node (GValue *value,
673 GskRenderNode *node)
674{
675 GskRenderNode *old_node;
676
677 g_return_if_fail (G_VALUE_HOLDS (value, GSK_TYPE_RENDER_NODE));
678
679 old_node = value->data[0].v_pointer;
680
681 if (node != NULL)
682 {
683 g_return_if_fail (GSK_IS_RENDER_NODE (node));
684
685 value->data[0].v_pointer = node;
686 }
687 else
688 {
689 value->data[0].v_pointer = NULL;
690 }
691
692 if (old_node != NULL)
693 gsk_render_node_unref (node: old_node);
694}
695
696/**
697 * gsk_value_get_render_node:
698 * @value: a `GValue` initialized with type `GSK_TYPE_RENDER_NODE`
699 *
700 * Retrieves the `GskRenderNode` stored inside the given `value`.
701 *
702 * Returns: (transfer none) (nullable): a `GskRenderNode`
703 *
704 * Since: 4.6
705 */
706GskRenderNode *
707gsk_value_get_render_node (const GValue *value)
708{
709 g_return_val_if_fail (G_VALUE_HOLDS (value, GSK_TYPE_RENDER_NODE), NULL);
710
711 return value->data[0].v_pointer;
712}
713
714/**
715 * gsk_value_dup_render_node:
716 * @value: a [struct@GObject.Value] initialized with type `GSK_TYPE_RENDER_NODE`
717 *
718 * Retrieves the `GskRenderNode` stored inside the given `value`, and acquires
719 * a reference to it.
720 *
721 * Returns: (transfer full) (nullable): a `GskRenderNode`
722 *
723 * Since: 4.6
724 */
725GskRenderNode *
726gsk_value_dup_render_node (const GValue *value)
727{
728 g_return_val_if_fail (G_VALUE_HOLDS (value, GSK_TYPE_RENDER_NODE), NULL);
729
730 if (value->data[0].v_pointer == NULL)
731 return NULL;
732
733 return gsk_render_node_ref (node: value->data[0].v_pointer);
734}
735
736gboolean
737gsk_render_node_prefers_high_depth (const GskRenderNode *node)
738{
739 return node->prefers_high_depth;
740}
741

source code of gtk/gsk/gskrendernode.c