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 | * GskRenderer: |
21 | * |
22 | * `GskRenderer` is a class that renders a scene graph defined via a |
23 | * tree of [class@Gsk.RenderNode] instances. |
24 | * |
25 | * Typically you will use a `GskRenderer` instance to repeatedly call |
26 | * [method@Gsk.Renderer.render] to update the contents of its associated |
27 | * [class@Gdk.Surface]. |
28 | * |
29 | * It is necessary to realize a `GskRenderer` instance using |
30 | * [method@Gsk.Renderer.realize] before calling [method@Gsk.Renderer.render], |
31 | * in order to create the appropriate windowing system resources needed |
32 | * to render the scene. |
33 | */ |
34 | |
35 | #include "config.h" |
36 | |
37 | #include "gskrendererprivate.h" |
38 | |
39 | #include "gskcairorenderer.h" |
40 | #include "gskdebugprivate.h" |
41 | #include "gl/gskglrenderer.h" |
42 | #include "gskprofilerprivate.h" |
43 | #include "gskrendernodeprivate.h" |
44 | |
45 | #include "gskenumtypes.h" |
46 | |
47 | #include <graphene-gobject.h> |
48 | #include <cairo-gobject.h> |
49 | #include <gdk/gdk.h> |
50 | |
51 | #ifdef GDK_WINDOWING_X11 |
52 | #include <gdk/x11/gdkx.h> |
53 | #endif |
54 | #ifdef GDK_WINDOWING_WAYLAND |
55 | #include <gdk/wayland/gdkwayland.h> |
56 | #endif |
57 | #ifdef GDK_WINDOWING_BROADWAY |
58 | #include "broadway/gskbroadwayrenderer.h" |
59 | #endif |
60 | #ifdef GDK_RENDERING_VULKAN |
61 | #include "vulkan/gskvulkanrenderer.h" |
62 | #endif |
63 | #ifdef GDK_WINDOWING_MACOS |
64 | #include <gdk/macos/gdkmacos.h> |
65 | #endif |
66 | #ifdef GDK_WINDOWING_WIN32 |
67 | #include <gdk/win32/gdkwin32.h> |
68 | |
69 | /* Remove these lines when OpenGL/ES 2.0 shader is ready */ |
70 | #include "win32/gdkprivate-win32.h" |
71 | #include "win32/gdkdisplay-win32.h" |
72 | #endif |
73 | |
74 | typedef struct |
75 | { |
76 | GObject parent_instance; |
77 | |
78 | GdkSurface *surface; |
79 | GskRenderNode *prev_node; |
80 | GskRenderNode *root_node; |
81 | |
82 | GskProfiler *profiler; |
83 | |
84 | GskDebugFlags debug_flags; |
85 | |
86 | gboolean is_realized : 1; |
87 | } GskRendererPrivate; |
88 | |
89 | G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GskRenderer, gsk_renderer, G_TYPE_OBJECT) |
90 | |
91 | enum { |
92 | PROP_0, |
93 | PROP_REALIZED, |
94 | PROP_SURFACE, |
95 | |
96 | N_PROPS |
97 | }; |
98 | |
99 | static GParamSpec *gsk_renderer_properties[N_PROPS]; |
100 | |
101 | #define GSK_RENDERER_WARN_NOT_IMPLEMENTED_METHOD(obj,method) \ |
102 | g_critical ("Renderer of type '%s' does not implement GskRenderer::" # method, G_OBJECT_TYPE_NAME (obj)) |
103 | |
104 | static gboolean |
105 | gsk_renderer_real_realize (GskRenderer *self, |
106 | GdkSurface *surface, |
107 | GError **error) |
108 | { |
109 | GSK_RENDERER_WARN_NOT_IMPLEMENTED_METHOD (self, realize); |
110 | return FALSE; |
111 | } |
112 | |
113 | static void |
114 | gsk_renderer_real_unrealize (GskRenderer *self) |
115 | { |
116 | GSK_RENDERER_WARN_NOT_IMPLEMENTED_METHOD (self, unrealize); |
117 | } |
118 | |
119 | static GdkTexture * |
120 | gsk_renderer_real_render_texture (GskRenderer *self, |
121 | GskRenderNode *root, |
122 | const graphene_rect_t *viewport) |
123 | { |
124 | GSK_RENDERER_WARN_NOT_IMPLEMENTED_METHOD (self, render_texture); |
125 | return NULL; |
126 | } |
127 | |
128 | static void |
129 | gsk_renderer_real_render (GskRenderer *self, |
130 | GskRenderNode *root, |
131 | const cairo_region_t *region) |
132 | { |
133 | GSK_RENDERER_WARN_NOT_IMPLEMENTED_METHOD (self, render); |
134 | } |
135 | |
136 | static void |
137 | gsk_renderer_dispose (GObject *gobject) |
138 | { |
139 | GskRenderer *self = GSK_RENDERER (gobject); |
140 | GskRendererPrivate *priv = gsk_renderer_get_instance_private (self); |
141 | |
142 | /* We can't just unrealize here because superclasses have already run dispose. |
143 | * So we insist that unrealize must be called before unreffing. */ |
144 | g_assert (!priv->is_realized); |
145 | |
146 | g_clear_object (&priv->profiler); |
147 | |
148 | G_OBJECT_CLASS (gsk_renderer_parent_class)->dispose (gobject); |
149 | } |
150 | |
151 | static void |
152 | gsk_renderer_get_property (GObject *gobject, |
153 | guint prop_id, |
154 | GValue *value, |
155 | GParamSpec *pspec) |
156 | { |
157 | GskRenderer *self = GSK_RENDERER (gobject); |
158 | GskRendererPrivate *priv = gsk_renderer_get_instance_private (self); |
159 | |
160 | switch (prop_id) |
161 | { |
162 | case PROP_REALIZED: |
163 | g_value_set_boolean (value, v_boolean: priv->is_realized); |
164 | break; |
165 | |
166 | case PROP_SURFACE: |
167 | g_value_set_object (value, v_object: priv->surface); |
168 | break; |
169 | |
170 | default: |
171 | G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); |
172 | break; |
173 | } |
174 | } |
175 | |
176 | static void |
177 | gsk_renderer_class_init (GskRendererClass *klass) |
178 | { |
179 | GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
180 | |
181 | klass->realize = gsk_renderer_real_realize; |
182 | klass->unrealize = gsk_renderer_real_unrealize; |
183 | klass->render = gsk_renderer_real_render; |
184 | klass->render_texture = gsk_renderer_real_render_texture; |
185 | |
186 | gobject_class->get_property = gsk_renderer_get_property; |
187 | gobject_class->dispose = gsk_renderer_dispose; |
188 | |
189 | /** |
190 | * GskRenderer:realized: (attributes org.gtk.Property.get=gsk_renderer_is_realized) |
191 | * |
192 | * Whether the renderer has been associated with a surface or draw context. |
193 | */ |
194 | gsk_renderer_properties[PROP_REALIZED] = |
195 | g_param_spec_boolean (name: "realized" , |
196 | nick: "Realized" , |
197 | blurb: "The renderer has been associated with a surface or draw context" , |
198 | FALSE, |
199 | flags: G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); |
200 | |
201 | /** |
202 | * GskRenderer:surface: (attributes org.gtk.Property.get=gsk_renderer_get_surface) |
203 | * |
204 | * The surface associated with renderer. |
205 | */ |
206 | gsk_renderer_properties[PROP_SURFACE] = |
207 | g_param_spec_object (name: "surface" , |
208 | nick: "Surface" , |
209 | blurb: "The surface associated to the renderer" , |
210 | GDK_TYPE_SURFACE, |
211 | flags: G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); |
212 | |
213 | g_object_class_install_properties (oclass: gobject_class, n_pspecs: N_PROPS, pspecs: gsk_renderer_properties); |
214 | } |
215 | |
216 | static void |
217 | gsk_renderer_init (GskRenderer *self) |
218 | { |
219 | GskRendererPrivate *priv = gsk_renderer_get_instance_private (self); |
220 | |
221 | priv->profiler = gsk_profiler_new (); |
222 | priv->debug_flags = gsk_get_debug_flags (); |
223 | } |
224 | |
225 | /** |
226 | * gsk_renderer_get_surface: (attributes org.gtk.Method.get_property=surface) |
227 | * @renderer: a `GskRenderer` |
228 | * |
229 | * Retrieves the `GdkSurface` set using gsk_enderer_realize(). |
230 | * |
231 | * If the renderer has not been realized yet, %NULL will be returned. |
232 | * |
233 | * Returns: (transfer none) (nullable): a `GdkSurface` |
234 | */ |
235 | GdkSurface * |
236 | gsk_renderer_get_surface (GskRenderer *renderer) |
237 | { |
238 | GskRendererPrivate *priv = gsk_renderer_get_instance_private (self: renderer); |
239 | |
240 | g_return_val_if_fail (GSK_IS_RENDERER (renderer), NULL); |
241 | |
242 | return priv->surface; |
243 | } |
244 | |
245 | /*< private > |
246 | * gsk_renderer_get_root_node: |
247 | * @renderer: a `GskRenderer` |
248 | * |
249 | * Retrieves the `GskRenderNode` used by @renderer. |
250 | * |
251 | * Returns: (transfer none) (nullable): a `GskRenderNode` |
252 | */ |
253 | GskRenderNode * |
254 | gsk_renderer_get_root_node (GskRenderer *renderer) |
255 | { |
256 | GskRendererPrivate *priv = gsk_renderer_get_instance_private (self: renderer); |
257 | |
258 | g_return_val_if_fail (GSK_IS_RENDERER (renderer), NULL); |
259 | |
260 | return priv->root_node; |
261 | } |
262 | |
263 | /** |
264 | * gsk_renderer_is_realized: (attributes org.gtk.Method.get_property=realized) |
265 | * @renderer: a `GskRenderer` |
266 | * |
267 | * Checks whether the @renderer is realized or not. |
268 | * |
269 | * Returns: %TRUE if the `GskRenderer` was realized, and %FALSE otherwise |
270 | */ |
271 | gboolean |
272 | gsk_renderer_is_realized (GskRenderer *renderer) |
273 | { |
274 | GskRendererPrivate *priv = gsk_renderer_get_instance_private (self: renderer); |
275 | |
276 | g_return_val_if_fail (GSK_IS_RENDERER (renderer), FALSE); |
277 | |
278 | return priv->is_realized; |
279 | } |
280 | |
281 | /** |
282 | * gsk_renderer_realize: |
283 | * @renderer: a `GskRenderer` |
284 | * @surface: (nullable): the `GdkSurface` renderer will be used on |
285 | * @error: return location for an error |
286 | * |
287 | * Creates the resources needed by the @renderer to render the scene |
288 | * graph. |
289 | * |
290 | * Since GTK 4.6, the surface may be `NULL`, which allows using |
291 | * renderers without having to create a surface. |
292 | * |
293 | * Note that it is mandatory to call [method@Gsk.Renderer.unrealize] before |
294 | * destroying the renderer. |
295 | * |
296 | * Returns: Whether the renderer was successfully realized |
297 | */ |
298 | gboolean |
299 | gsk_renderer_realize (GskRenderer *renderer, |
300 | GdkSurface *surface, |
301 | GError **error) |
302 | { |
303 | GskRendererPrivate *priv = gsk_renderer_get_instance_private (self: renderer); |
304 | |
305 | g_return_val_if_fail (GSK_IS_RENDERER (renderer), FALSE); |
306 | g_return_val_if_fail (!gsk_renderer_is_realized (renderer), FALSE); |
307 | g_return_val_if_fail (surface == NULL || GDK_IS_SURFACE (surface), FALSE); |
308 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); |
309 | |
310 | if (surface) |
311 | priv->surface = g_object_ref (surface); |
312 | |
313 | if (!GSK_RENDERER_GET_CLASS (renderer)->realize (renderer, surface, error)) |
314 | { |
315 | g_clear_object (&priv->surface); |
316 | return FALSE; |
317 | } |
318 | |
319 | priv->is_realized = TRUE; |
320 | return TRUE; |
321 | } |
322 | |
323 | /** |
324 | * gsk_renderer_unrealize: |
325 | * @renderer: a `GskRenderer` |
326 | * |
327 | * Releases all the resources created by gsk_renderer_realize(). |
328 | */ |
329 | void |
330 | gsk_renderer_unrealize (GskRenderer *renderer) |
331 | { |
332 | GskRendererPrivate *priv = gsk_renderer_get_instance_private (self: renderer); |
333 | |
334 | g_return_if_fail (GSK_IS_RENDERER (renderer)); |
335 | |
336 | if (!priv->is_realized) |
337 | return; |
338 | |
339 | GSK_RENDERER_GET_CLASS (renderer)->unrealize (renderer); |
340 | |
341 | g_clear_pointer (&priv->prev_node, gsk_render_node_unref); |
342 | |
343 | priv->is_realized = FALSE; |
344 | } |
345 | |
346 | /** |
347 | * gsk_renderer_render_texture: |
348 | * @renderer: a realized `GskRenderer` |
349 | * @root: a `GskRenderNode` |
350 | * @viewport: (nullable): the section to draw or %NULL to use @root's bounds |
351 | * |
352 | * Renders the scene graph, described by a tree of `GskRenderNode` instances, |
353 | * to a `GdkTexture`. |
354 | * |
355 | * The @renderer will acquire a reference on the `GskRenderNode` tree while |
356 | * the rendering is in progress. |
357 | * |
358 | * If you want to apply any transformations to @root, you should put it into a |
359 | * transform node and pass that node instead. |
360 | * |
361 | * Returns: (transfer full): a `GdkTexture` with the rendered contents of @root. |
362 | */ |
363 | GdkTexture * |
364 | gsk_renderer_render_texture (GskRenderer *renderer, |
365 | GskRenderNode *root, |
366 | const graphene_rect_t *viewport) |
367 | { |
368 | GskRendererPrivate *priv = gsk_renderer_get_instance_private (self: renderer); |
369 | graphene_rect_t real_viewport; |
370 | GdkTexture *texture; |
371 | |
372 | g_return_val_if_fail (GSK_IS_RENDERER (renderer), NULL); |
373 | g_return_val_if_fail (priv->is_realized, NULL); |
374 | g_return_val_if_fail (GSK_IS_RENDER_NODE (root), NULL); |
375 | g_return_val_if_fail (priv->root_node == NULL, NULL); |
376 | |
377 | priv->root_node = gsk_render_node_ref (node: root); |
378 | |
379 | if (viewport == NULL) |
380 | { |
381 | gsk_render_node_get_bounds (node: root, bounds: &real_viewport); |
382 | viewport = &real_viewport; |
383 | } |
384 | |
385 | texture = GSK_RENDERER_GET_CLASS (renderer)->render_texture (renderer, root, viewport); |
386 | |
387 | #ifdef G_ENABLE_DEBUG |
388 | if (GSK_RENDERER_DEBUG_CHECK (renderer, RENDERER)) |
389 | { |
390 | GString *buf = g_string_new (init: "*** Texture stats ***\n\n" ); |
391 | |
392 | gsk_profiler_append_counters (profiler: priv->profiler, buffer: buf); |
393 | g_string_append_c (buf, '\n'); |
394 | |
395 | gsk_profiler_append_timers (profiler: priv->profiler, buffer: buf); |
396 | g_string_append_c (buf, '\n'); |
397 | |
398 | g_print (format: "%s\n***\n\n" , buf->str); |
399 | |
400 | g_string_free (string: buf, TRUE); |
401 | } |
402 | #endif |
403 | |
404 | g_clear_pointer (&priv->root_node, gsk_render_node_unref); |
405 | |
406 | return texture; |
407 | } |
408 | |
409 | /** |
410 | * gsk_renderer_render: |
411 | * @renderer: a realized `GskRenderer` |
412 | * @root: a `GskRenderNode` |
413 | * @region: (nullable): the `cairo_region_t` that must be redrawn or %NULL |
414 | * for the whole window |
415 | * |
416 | * Renders the scene graph, described by a tree of `GskRenderNode` instances |
417 | * to the renderer's surface, ensuring that the given @region gets redrawn. |
418 | * |
419 | * If the renderer has no associated surface, this function does nothing. |
420 | * |
421 | * Renderers must ensure that changes of the contents given by the @root |
422 | * node as well as the area given by @region are redrawn. They are however |
423 | * free to not redraw any pixel outside of @region if they can guarantee that |
424 | * it didn't change. |
425 | * |
426 | * The @renderer will acquire a reference on the `GskRenderNode` tree while |
427 | * the rendering is in progress. |
428 | */ |
429 | void |
430 | gsk_renderer_render (GskRenderer *renderer, |
431 | GskRenderNode *root, |
432 | const cairo_region_t *region) |
433 | { |
434 | GskRendererPrivate *priv = gsk_renderer_get_instance_private (self: renderer); |
435 | cairo_region_t *clip; |
436 | |
437 | g_return_if_fail (GSK_IS_RENDERER (renderer)); |
438 | g_return_if_fail (priv->is_realized); |
439 | g_return_if_fail (GSK_IS_RENDER_NODE (root)); |
440 | g_return_if_fail (priv->root_node == NULL); |
441 | |
442 | if (priv->surface == NULL) |
443 | return; |
444 | |
445 | if (region == NULL || priv->prev_node == NULL || GSK_RENDERER_DEBUG_CHECK (renderer, FULL_REDRAW)) |
446 | { |
447 | clip = cairo_region_create_rectangle (rectangle: &(GdkRectangle) { |
448 | 0, 0, |
449 | gdk_surface_get_width (surface: priv->surface), |
450 | gdk_surface_get_height (surface: priv->surface) |
451 | }); |
452 | } |
453 | else |
454 | { |
455 | clip = cairo_region_copy (original: region); |
456 | gsk_render_node_diff (node1: priv->prev_node, node2: root, region: clip); |
457 | |
458 | if (cairo_region_is_empty (region: clip)) |
459 | { |
460 | cairo_region_destroy (region: clip); |
461 | return; |
462 | } |
463 | } |
464 | |
465 | priv->root_node = gsk_render_node_ref (node: root); |
466 | |
467 | GSK_RENDERER_GET_CLASS (renderer)->render (renderer, root, clip); |
468 | |
469 | #ifdef G_ENABLE_DEBUG |
470 | if (GSK_RENDERER_DEBUG_CHECK (renderer, RENDERER)) |
471 | { |
472 | GString *buf = g_string_new (init: "*** Frame stats ***\n\n" ); |
473 | |
474 | gsk_profiler_append_counters (profiler: priv->profiler, buffer: buf); |
475 | g_string_append_c (buf, '\n'); |
476 | |
477 | gsk_profiler_append_timers (profiler: priv->profiler, buffer: buf); |
478 | g_string_append_c (buf, '\n'); |
479 | |
480 | g_print (format: "%s\n***\n\n" , buf->str); |
481 | |
482 | g_string_free (string: buf, TRUE); |
483 | } |
484 | #endif |
485 | |
486 | g_clear_pointer (&priv->prev_node, gsk_render_node_unref); |
487 | cairo_region_destroy (region: clip); |
488 | priv->prev_node = priv->root_node; |
489 | priv->root_node = NULL; |
490 | } |
491 | |
492 | /*< private > |
493 | * gsk_renderer_get_profiler: |
494 | * @renderer: a `GskRenderer` |
495 | * |
496 | * Retrieves a pointer to the `GskProfiler` instance of the renderer. |
497 | * |
498 | * Returns: (transfer none): the profiler |
499 | */ |
500 | GskProfiler * |
501 | gsk_renderer_get_profiler (GskRenderer *renderer) |
502 | { |
503 | GskRendererPrivate *priv = gsk_renderer_get_instance_private (self: renderer); |
504 | |
505 | g_return_val_if_fail (GSK_IS_RENDERER (renderer), NULL); |
506 | |
507 | return priv->profiler; |
508 | } |
509 | |
510 | static GType |
511 | get_renderer_for_name (const char *renderer_name) |
512 | { |
513 | if (renderer_name == NULL) |
514 | return G_TYPE_INVALID; |
515 | #ifdef GDK_WINDOWING_BROADWAY |
516 | else if (g_ascii_strcasecmp (renderer_name, "broadway" ) == 0) |
517 | return GSK_TYPE_BROADWAY_RENDERER; |
518 | #endif |
519 | else if (g_ascii_strcasecmp (s1: renderer_name, s2: "cairo" ) == 0) |
520 | return GSK_TYPE_CAIRO_RENDERER; |
521 | else if (g_ascii_strcasecmp (s1: renderer_name, s2: "opengl" ) == 0 || |
522 | g_ascii_strcasecmp (s1: renderer_name, s2: "gl" ) == 0) |
523 | return GSK_TYPE_GL_RENDERER; |
524 | #ifdef GDK_RENDERING_VULKAN |
525 | else if (g_ascii_strcasecmp (renderer_name, "vulkan" ) == 0) |
526 | return GSK_TYPE_VULKAN_RENDERER; |
527 | #endif |
528 | else if (g_ascii_strcasecmp (s1: renderer_name, s2: "help" ) == 0) |
529 | { |
530 | g_print (format: "Supported arguments for GSK_RENDERER environment variable:\n" ); |
531 | #ifdef GDK_WINDOWING_BROADWAY |
532 | g_print ("broadway - Use the Broadway specific renderer\n" ); |
533 | #else |
534 | g_print (format: "broadway - Disabled during GTK build\n" ); |
535 | #endif |
536 | g_print (format: " cairo - Use the Cairo fallback renderer\n" ); |
537 | g_print (format: " opengl - Use the OpenGL renderer\n" ); |
538 | g_print (format: " gl - Use the OpenGL renderer\n" ); |
539 | #ifdef GDK_RENDERING_VULKAN |
540 | g_print (" vulkan - Use the Vulkan renderer\n" ); |
541 | #else |
542 | g_print (format: " vulkan - Disabled during GTK build\n" ); |
543 | #endif |
544 | g_print (format: " help - Print this help\n\n" ); |
545 | g_print (format: "Other arguments will cause a warning and be ignored.\n" ); |
546 | } |
547 | else |
548 | { |
549 | g_warning ("Unrecognized renderer \"%s\". Try GSK_RENDERER=help" , renderer_name); |
550 | } |
551 | |
552 | return G_TYPE_INVALID; |
553 | } |
554 | |
555 | static GType |
556 | get_renderer_for_display (GdkSurface *surface) |
557 | { |
558 | GdkDisplay *display = gdk_surface_get_display (surface); |
559 | const char *renderer_name; |
560 | |
561 | renderer_name = g_object_get_data (G_OBJECT (display), key: "gsk-renderer" ); |
562 | return get_renderer_for_name (renderer_name); |
563 | } |
564 | |
565 | static GType |
566 | get_renderer_for_env_var (GdkSurface *surface) |
567 | { |
568 | static GType env_var_type = G_TYPE_NONE; |
569 | |
570 | if (env_var_type == G_TYPE_NONE) |
571 | { |
572 | const char *renderer_name = g_getenv (variable: "GSK_RENDERER" ); |
573 | env_var_type = get_renderer_for_name (renderer_name); |
574 | } |
575 | |
576 | return env_var_type; |
577 | } |
578 | |
579 | static GType |
580 | get_renderer_for_backend (GdkSurface *surface) |
581 | { |
582 | #ifdef GDK_WINDOWING_X11 |
583 | if (GDK_IS_X11_SURFACE (surface)) |
584 | return GSK_TYPE_GL_RENDERER; |
585 | #endif |
586 | #ifdef GDK_WINDOWING_WAYLAND |
587 | if (GDK_IS_WAYLAND_SURFACE (surface)) |
588 | return GSK_TYPE_GL_RENDERER; |
589 | #endif |
590 | #ifdef GDK_WINDOWING_BROADWAY |
591 | if (GDK_IS_BROADWAY_SURFACE (surface)) |
592 | return GSK_TYPE_BROADWAY_RENDERER; |
593 | #endif |
594 | #ifdef GDK_WINDOWING_MACOS |
595 | if (GDK_IS_MACOS_SURFACE (surface)) |
596 | return GSK_TYPE_GL_RENDERER; |
597 | #endif |
598 | #ifdef GDK_WINDOWING_WIN32 |
599 | if (GDK_IS_WIN32_SURFACE (surface)) |
600 | return GSK_TYPE_GL_RENDERER; |
601 | #endif |
602 | |
603 | return G_TYPE_INVALID; |
604 | } |
605 | |
606 | static GType |
607 | get_renderer_fallback (GdkSurface *surface) |
608 | { |
609 | return GSK_TYPE_CAIRO_RENDERER; |
610 | } |
611 | |
612 | static struct { |
613 | GType (* get_renderer) (GdkSurface *surface); |
614 | } renderer_possibilities[] = { |
615 | { get_renderer_for_display }, |
616 | { get_renderer_for_env_var }, |
617 | { get_renderer_for_backend }, |
618 | { get_renderer_fallback }, |
619 | }; |
620 | |
621 | /** |
622 | * gsk_renderer_new_for_surface: |
623 | * @surface: a `GdkSurface` |
624 | * |
625 | * Creates an appropriate `GskRenderer` instance for the given @surface. |
626 | * |
627 | * If the `GSK_RENDERER` environment variable is set, GSK will |
628 | * try that renderer first, before trying the backend-specific |
629 | * default. The ultimate fallback is the cairo renderer. |
630 | * |
631 | * The renderer will be realized before it is returned. |
632 | * |
633 | * Returns: (transfer full) (nullable): a `GskRenderer` |
634 | */ |
635 | GskRenderer * |
636 | gsk_renderer_new_for_surface (GdkSurface *surface) |
637 | { |
638 | GType renderer_type; |
639 | GskRenderer *renderer; |
640 | GError *error = NULL; |
641 | guint i; |
642 | |
643 | g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL); |
644 | |
645 | for (i = 0; i < G_N_ELEMENTS (renderer_possibilities); i++) |
646 | { |
647 | renderer_type = renderer_possibilities[i].get_renderer (surface); |
648 | if (renderer_type == G_TYPE_INVALID) |
649 | continue; |
650 | |
651 | renderer = g_object_new (object_type: renderer_type, NULL); |
652 | |
653 | if (gsk_renderer_realize (renderer, surface, error: &error)) |
654 | { |
655 | GSK_RENDERER_NOTE (renderer, RENDERER, |
656 | g_message ("Using renderer of type '%s' for surface '%s'\n" , |
657 | G_OBJECT_TYPE_NAME (renderer), |
658 | G_OBJECT_TYPE_NAME (surface))); |
659 | return renderer; |
660 | } |
661 | |
662 | g_message ("Failed to realize renderer of type '%s' for surface '%s': %s\n" , |
663 | G_OBJECT_TYPE_NAME (renderer), |
664 | G_OBJECT_TYPE_NAME (surface), |
665 | error->message); |
666 | g_object_unref (object: renderer); |
667 | g_clear_error (err: &error); |
668 | } |
669 | |
670 | g_assert_not_reached (); |
671 | return NULL; |
672 | } |
673 | |
674 | GskDebugFlags |
675 | gsk_renderer_get_debug_flags (GskRenderer *renderer) |
676 | { |
677 | GskRendererPrivate *priv = gsk_renderer_get_instance_private (self: renderer); |
678 | |
679 | g_return_val_if_fail (GSK_IS_RENDERER (renderer), 0); |
680 | |
681 | return priv->debug_flags; |
682 | } |
683 | |
684 | void |
685 | gsk_renderer_set_debug_flags (GskRenderer *renderer, |
686 | GskDebugFlags flags) |
687 | { |
688 | GskRendererPrivate *priv = gsk_renderer_get_instance_private (self: renderer); |
689 | |
690 | g_return_if_fail (GSK_IS_RENDERER (renderer)); |
691 | |
692 | priv->debug_flags = flags; |
693 | } |
694 | |