1 /* GTK - The GIMP Toolkit
2  *
3  * gtkglarea.c: A GL drawing area
4  *
5  * Copyright © 2014  Emmanuele Bassi
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "config.h"
22 
23 #include "config.h"
24 #include "gtkglarea.h"
25 #include "gtkintl.h"
26 #include "gtkmarshalers.h"
27 #include "gtkmarshalers.h"
28 #include "gtkprivate.h"
29 #include "gtkrender.h"
30 #include "gtksnapshot.h"
31 #include "gtknative.h"
32 #include "gtkwidgetprivate.h"
33 
34 #include <epoxy/gl.h>
35 
36 /**
37  * GtkGLArea:
38  *
39  * `GtkGLArea` is a widget that allows drawing with OpenGL.
40  *
41  * ![An example GtkGLArea](glarea.png)
42  *
43  * `GtkGLArea` sets up its own [class@Gdk.GLContext], and creates a custom
44  * GL framebuffer that the widget will do GL rendering onto. It also ensures
45  * that this framebuffer is the default GL rendering target when rendering.
46  *
47  * In order to draw, you have to connect to the [signal@Gtk.GLArea::render]
48  * signal, or subclass `GtkGLArea` and override the GtkGLAreaClass.render
49  * virtual function.
50  *
51  * The `GtkGLArea` widget ensures that the `GdkGLContext` is associated with
52  * the widget's drawing area, and it is kept updated when the size and
53  * position of the drawing area changes.
54  *
55  * ## Drawing with GtkGLArea
56  *
57  * The simplest way to draw using OpenGL commands in a `GtkGLArea` is to
58  * create a widget instance and connect to the [signal@Gtk.GLArea::render] signal:
59  *
60  * The `render()` function will be called when the `GtkGLArea` is ready
61  * for you to draw its content:
62  *
63  * ```c
64  * static gboolean
65  * render (GtkGLArea *area, GdkGLContext *context)
66  * {
67  *   // inside this function it's safe to use GL; the given
68  *   // GdkGLContext has been made current to the drawable
69  *   // surface used by the `GtkGLArea` and the viewport has
70  *   // already been set to be the size of the allocation
71  *
72  *   // we can start by clearing the buffer
73  *   glClearColor (0, 0, 0, 0);
74  *   glClear (GL_COLOR_BUFFER_BIT);
75  *
76  *   // draw your object
77  *   // draw_an_object ();
78  *
79  *   // we completed our drawing; the draw commands will be
80  *   // flushed at the end of the signal emission chain, and
81  *   // the buffers will be drawn on the window
82  *   return TRUE;
83  * }
84  *
85  * void setup_glarea (void)
86  * {
87  *   // create a GtkGLArea instance
88  *   GtkWidget *gl_area = gtk_gl_area_new ();
89  *
90  *   // connect to the "render" signal
91  *   g_signal_connect (gl_area, "render", G_CALLBACK (render), NULL);
92  * }
93  * ```
94  *
95  * If you need to initialize OpenGL state, e.g. buffer objects or
96  * shaders, you should use the [signal@Gtk.Widget::realize] signal;
97  * you can use the [signal@Gtk.Widget::unrealize] signal to clean up.
98  * Since the `GdkGLContext` creation and initialization may fail, you
99  * will need to check for errors, using [method@Gtk.GLArea.get_error].
100  *
101  * An example of how to safely initialize the GL state is:
102  *
103  * ```c
104  * static void
105  * on_realize (GtkGLarea *area)
106  * {
107  *   // We need to make the context current if we want to
108  *   // call GL API
109  *   gtk_gl_area_make_current (area);
110  *
111  *   // If there were errors during the initialization or
112  *   // when trying to make the context current, this
113  *   // function will return a GError for you to catch
114  *   if (gtk_gl_area_get_error (area) != NULL)
115  *     return;
116  *
117  *   // You can also use gtk_gl_area_set_error() in order
118  *   // to show eventual initialization errors on the
119  *   // GtkGLArea widget itself
120  *   GError *internal_error = NULL;
121  *   init_buffer_objects (&error);
122  *   if (error != NULL)
123  *     {
124  *       gtk_gl_area_set_error (area, error);
125  *       g_error_free (error);
126  *       return;
127  *     }
128  *
129  *   init_shaders (&error);
130  *   if (error != NULL)
131  *     {
132  *       gtk_gl_area_set_error (area, error);
133  *       g_error_free (error);
134  *       return;
135  *     }
136  * }
137  * ```
138  *
139  * If you need to change the options for creating the `GdkGLContext`
140  * you should use the [signal@Gtk.GLArea::create-context] signal.
141  */
142 
143 typedef struct {
144   guint id;
145   int width;
146   int height;
147   GdkTexture *holder;
148 } Texture;
149 
150 typedef struct {
151   GdkGLContext *context;
152   GError *error;
153 
154   gboolean have_buffers;
155 
156   int required_gl_version;
157 
158   guint frame_buffer;
159   guint depth_stencil_buffer;
160   Texture *texture;
161   GList *textures;
162 
163   gboolean has_depth_buffer;
164   gboolean has_stencil_buffer;
165 
166   gboolean needs_resize;
167   gboolean needs_render;
168   gboolean auto_render;
169   gboolean use_es;
170 } GtkGLAreaPrivate;
171 
172 enum {
173   PROP_0,
174 
175   PROP_CONTEXT,
176   PROP_HAS_DEPTH_BUFFER,
177   PROP_HAS_STENCIL_BUFFER,
178   PROP_USE_ES,
179 
180   PROP_AUTO_RENDER,
181 
182   LAST_PROP
183 };
184 
185 static GParamSpec *obj_props[LAST_PROP] = { NULL, };
186 
187 enum {
188   RENDER,
189   RESIZE,
190   CREATE_CONTEXT,
191 
192   LAST_SIGNAL
193 };
194 
195 static void gtk_gl_area_allocate_buffers (GtkGLArea *area);
196 static void gtk_gl_area_allocate_texture (GtkGLArea *area);
197 
198 static guint area_signals[LAST_SIGNAL] = { 0, };
199 
G_DEFINE_TYPE_WITH_PRIVATE(GtkGLArea,gtk_gl_area,GTK_TYPE_WIDGET)200 G_DEFINE_TYPE_WITH_PRIVATE (GtkGLArea, gtk_gl_area, GTK_TYPE_WIDGET)
201 
202 static void
203 gtk_gl_area_set_property (GObject      *gobject,
204                           guint         prop_id,
205                           const GValue *value,
206                           GParamSpec   *pspec)
207 {
208   GtkGLArea *self = GTK_GL_AREA (gobject);
209 
210   switch (prop_id)
211     {
212     case PROP_AUTO_RENDER:
213       gtk_gl_area_set_auto_render (self, g_value_get_boolean (value));
214       break;
215 
216     case PROP_HAS_DEPTH_BUFFER:
217       gtk_gl_area_set_has_depth_buffer (self, g_value_get_boolean (value));
218       break;
219 
220     case PROP_HAS_STENCIL_BUFFER:
221       gtk_gl_area_set_has_stencil_buffer (self, g_value_get_boolean (value));
222       break;
223 
224     case PROP_USE_ES:
225       gtk_gl_area_set_use_es (self, g_value_get_boolean (value));
226       break;
227 
228     default:
229       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
230     }
231 }
232 
233 static void
gtk_gl_area_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * pspec)234 gtk_gl_area_get_property (GObject    *gobject,
235                           guint       prop_id,
236                           GValue     *value,
237                           GParamSpec *pspec)
238 {
239   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (GTK_GL_AREA (gobject));
240 
241   switch (prop_id)
242     {
243     case PROP_AUTO_RENDER:
244       g_value_set_boolean (value, priv->auto_render);
245       break;
246 
247     case PROP_HAS_DEPTH_BUFFER:
248       g_value_set_boolean (value, priv->has_depth_buffer);
249       break;
250 
251     case PROP_HAS_STENCIL_BUFFER:
252       g_value_set_boolean (value, priv->has_stencil_buffer);
253       break;
254 
255     case PROP_CONTEXT:
256       g_value_set_object (value, priv->context);
257       break;
258 
259     case PROP_USE_ES:
260       g_value_set_boolean (value, priv->use_es);
261       break;
262 
263     default:
264       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
265     }
266 }
267 
268 static void
gtk_gl_area_realize(GtkWidget * widget)269 gtk_gl_area_realize (GtkWidget *widget)
270 {
271   GtkGLArea *area = GTK_GL_AREA (widget);
272   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
273 
274   GTK_WIDGET_CLASS (gtk_gl_area_parent_class)->realize (widget);
275 
276   g_clear_error (&priv->error);
277   priv->context = NULL;
278   g_signal_emit (area, area_signals[CREATE_CONTEXT], 0, &priv->context);
279 
280   /* In case the signal failed, but did not set an error */
281   if (priv->context == NULL && priv->error == NULL)
282     g_set_error_literal (&priv->error, GDK_GL_ERROR,
283                          GDK_GL_ERROR_NOT_AVAILABLE,
284                          _("OpenGL context creation failed"));
285 
286   priv->needs_resize = TRUE;
287 }
288 
289 static void
gtk_gl_area_notify(GObject * object,GParamSpec * pspec)290 gtk_gl_area_notify (GObject    *object,
291                     GParamSpec *pspec)
292 {
293   if (strcmp (pspec->name, "scale-factor") == 0)
294     {
295       GtkGLArea *area = GTK_GL_AREA (object);
296       GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
297 
298       priv->needs_resize = TRUE;
299     }
300 
301   if (G_OBJECT_CLASS (gtk_gl_area_parent_class)->notify)
302     G_OBJECT_CLASS (gtk_gl_area_parent_class)->notify (object, pspec);
303 }
304 
305 static GdkGLContext *
gtk_gl_area_real_create_context(GtkGLArea * area)306 gtk_gl_area_real_create_context (GtkGLArea *area)
307 {
308   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
309   GtkWidget *widget = GTK_WIDGET (area);
310   GError *error = NULL;
311   GdkGLContext *context;
312 
313   context = gdk_surface_create_gl_context (gtk_native_get_surface (gtk_widget_get_native (widget)), &error);
314   if (error != NULL)
315     {
316       gtk_gl_area_set_error (area, error);
317       g_clear_object (&context);
318       g_clear_error (&error);
319       return NULL;
320     }
321 
322   gdk_gl_context_set_use_es (context, priv->use_es);
323   gdk_gl_context_set_required_version (context,
324                                        priv->required_gl_version / 10,
325                                        priv->required_gl_version % 10);
326 
327   gdk_gl_context_realize (context, &error);
328   if (error != NULL)
329     {
330       gtk_gl_area_set_error (area, error);
331       g_clear_object (&context);
332       g_clear_error (&error);
333       return NULL;
334     }
335 
336   return context;
337 }
338 
339 static void
gtk_gl_area_resize(GtkGLArea * area,int width,int height)340 gtk_gl_area_resize (GtkGLArea *area, int width, int height)
341 {
342   glViewport (0, 0, width, height);
343 }
344 
345 /*
346  * Creates all the buffer objects needed for rendering the scene
347  */
348 static void
gtk_gl_area_ensure_buffers(GtkGLArea * area)349 gtk_gl_area_ensure_buffers (GtkGLArea *area)
350 {
351   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
352   GtkWidget *widget = GTK_WIDGET (area);
353 
354   gtk_widget_realize (widget);
355 
356   if (priv->context == NULL)
357     return;
358 
359   if (priv->have_buffers)
360     return;
361 
362   priv->have_buffers = TRUE;
363 
364   glGenFramebuffers (1, &priv->frame_buffer);
365 
366   if ((priv->has_depth_buffer || priv->has_stencil_buffer))
367     {
368       if (priv->depth_stencil_buffer == 0)
369         glGenRenderbuffers (1, &priv->depth_stencil_buffer);
370     }
371   else if (priv->depth_stencil_buffer != 0)
372     {
373       /* Delete old depth/stencil buffer */
374       glDeleteRenderbuffers (1, &priv->depth_stencil_buffer);
375       priv->depth_stencil_buffer = 0;
376     }
377 
378   gtk_gl_area_allocate_buffers (area);
379 }
380 
381 static void
delete_one_texture(gpointer data)382 delete_one_texture (gpointer data)
383 {
384   Texture *texture = data;
385 
386   if (texture->holder)
387     gdk_gl_texture_release (GDK_GL_TEXTURE (texture->holder));
388 
389   if (texture->id != 0)
390     {
391       glDeleteTextures (1, &texture->id);
392       texture->id = 0;
393     }
394 
395   g_free (texture);
396 }
397 
398 static void
gtk_gl_area_ensure_texture(GtkGLArea * area)399 gtk_gl_area_ensure_texture (GtkGLArea *area)
400 {
401   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
402   GtkWidget *widget = GTK_WIDGET (area);
403 
404   gtk_widget_realize (widget);
405 
406   if (priv->context == NULL)
407     return;
408 
409   if (priv->texture == NULL)
410     {
411       GList *l, *link;
412 
413       l = priv->textures;
414       while (l)
415         {
416           Texture *texture = l->data;
417           link = l;
418           l = l->next;
419 
420           if (texture->holder)
421             continue;
422 
423           priv->textures = g_list_delete_link (priv->textures, link);
424 
425           if (priv->texture == NULL)
426             priv->texture = texture;
427           else
428             delete_one_texture (texture);
429         }
430     }
431 
432   if (priv->texture == NULL)
433     {
434       priv->texture = g_new (Texture, 1);
435 
436       priv->texture->width = 0;
437       priv->texture->height = 0;
438       priv->texture->holder = NULL;
439 
440       glGenTextures (1, &priv->texture->id);
441     }
442 
443   gtk_gl_area_allocate_texture (area);
444 }
445 
446 /*
447  * Allocates space of the right type and size for all the buffers
448  */
449 static void
gtk_gl_area_allocate_buffers(GtkGLArea * area)450 gtk_gl_area_allocate_buffers (GtkGLArea *area)
451 {
452   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
453   GtkWidget *widget = GTK_WIDGET (area);
454   int scale, width, height;
455 
456   if (priv->context == NULL)
457     return;
458 
459   scale = gtk_widget_get_scale_factor (widget);
460   width = gtk_widget_get_width (widget) * scale;
461   height = gtk_widget_get_height (widget) * scale;
462 
463   if (priv->has_depth_buffer || priv->has_stencil_buffer)
464     {
465       glBindRenderbuffer (GL_RENDERBUFFER, priv->depth_stencil_buffer);
466       if (priv->has_stencil_buffer)
467         glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
468       else
469         glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
470     }
471 
472   priv->needs_render = TRUE;
473 }
474 
475 static void
gtk_gl_area_allocate_texture(GtkGLArea * area)476 gtk_gl_area_allocate_texture (GtkGLArea *area)
477 {
478   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
479   GtkWidget *widget = GTK_WIDGET (area);
480   int scale, width, height;
481 
482   if (priv->context == NULL)
483     return;
484 
485   if (priv->texture == NULL)
486     return;
487 
488   g_assert (priv->texture->holder == NULL);
489 
490   scale = gtk_widget_get_scale_factor (widget);
491   width = gtk_widget_get_width (widget) * scale;
492   height = gtk_widget_get_height (widget) * scale;
493 
494   if (priv->texture->width != width ||
495       priv->texture->height != height)
496     {
497       glBindTexture (GL_TEXTURE_2D, priv->texture->id);
498       glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
499       glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
500       glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
501       glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
502 
503       if (gdk_gl_context_get_use_es (priv->context))
504         glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
505       else
506         glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
507 
508       priv->texture->width = width;
509       priv->texture->height = height;
510     }
511 }
512 
513 /**
514  * gtk_gl_area_attach_buffers:
515  * @area: a `GtkGLArea`
516  *
517  * Binds buffers to the framebuffer.
518  *
519  * Ensures that the @area framebuffer object is made the current draw
520  * and read target, and that all the required buffers for the @area
521  * are created and bound to the framebuffer.
522  *
523  * This function is automatically called before emitting the
524  * [signal@Gtk.GLArea::render] signal, and doesn't normally need to be
525  * called by application code.
526  */
527 void
gtk_gl_area_attach_buffers(GtkGLArea * area)528 gtk_gl_area_attach_buffers (GtkGLArea *area)
529 {
530   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
531 
532   g_return_if_fail (GTK_IS_GL_AREA (area));
533 
534   if (priv->context == NULL)
535     return;
536 
537   gtk_gl_area_make_current (area);
538 
539   if (priv->texture == NULL)
540     gtk_gl_area_ensure_texture (area);
541   else if (priv->needs_resize)
542     gtk_gl_area_allocate_texture (area);
543 
544   if (!priv->have_buffers)
545     gtk_gl_area_ensure_buffers (area);
546   else if (priv->needs_resize)
547     gtk_gl_area_allocate_buffers (area);
548 
549   glBindFramebuffer (GL_FRAMEBUFFER, priv->frame_buffer);
550 
551   if (priv->texture != NULL)
552     glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
553                             GL_TEXTURE_2D, priv->texture->id, 0);
554 
555   if (priv->depth_stencil_buffer)
556     {
557       if (priv->has_depth_buffer)
558         glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
559                                    GL_RENDERBUFFER, priv->depth_stencil_buffer);
560       if (priv->has_stencil_buffer)
561         glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
562                                    GL_RENDERBUFFER, priv->depth_stencil_buffer);
563     }
564 }
565 
566 static void
gtk_gl_area_delete_buffers(GtkGLArea * area)567 gtk_gl_area_delete_buffers (GtkGLArea *area)
568 {
569   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
570 
571   if (priv->context == NULL)
572     return;
573 
574   priv->have_buffers = FALSE;
575 
576   if (priv->depth_stencil_buffer != 0)
577     {
578       glDeleteRenderbuffers (1, &priv->depth_stencil_buffer);
579       priv->depth_stencil_buffer = 0;
580     }
581 
582   if (priv->frame_buffer != 0)
583     {
584       glBindFramebuffer (GL_FRAMEBUFFER, 0);
585       glDeleteFramebuffers (1, &priv->frame_buffer);
586       priv->frame_buffer = 0;
587     }
588 }
589 
590 static void
gtk_gl_area_delete_textures(GtkGLArea * area)591 gtk_gl_area_delete_textures (GtkGLArea *area)
592 {
593   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
594 
595   if (priv->texture)
596     {
597       delete_one_texture (priv->texture);
598       priv->texture = NULL;
599     }
600 
601   /* FIXME: we need to explicitly release all outstanding
602    * textures here, otherwise release_texture will get called
603    * later and access freed memory.
604    */
605   g_list_free_full (priv->textures, delete_one_texture);
606   priv->textures = NULL;
607 }
608 
609 static void
gtk_gl_area_unrealize(GtkWidget * widget)610 gtk_gl_area_unrealize (GtkWidget *widget)
611 {
612   GtkGLArea *area = GTK_GL_AREA (widget);
613   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
614 
615   if (priv->context != NULL)
616     {
617       gtk_gl_area_make_current (area);
618       gtk_gl_area_delete_buffers (area);
619       gtk_gl_area_delete_textures (area);
620 
621       /* Make sure to unset the context if current */
622       if (priv->context == gdk_gl_context_get_current ())
623         gdk_gl_context_clear_current ();
624     }
625 
626   g_clear_object (&priv->context);
627   g_clear_error (&priv->error);
628 
629   GTK_WIDGET_CLASS (gtk_gl_area_parent_class)->unrealize (widget);
630 }
631 
632 static void
gtk_gl_area_size_allocate(GtkWidget * widget,int width,int height,int baseline)633 gtk_gl_area_size_allocate (GtkWidget *widget,
634                            int        width,
635                            int        height,
636                            int        baseline)
637 {
638   GtkGLArea *area = GTK_GL_AREA (widget);
639   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
640 
641   GTK_WIDGET_CLASS (gtk_gl_area_parent_class)->size_allocate (widget, width, height, baseline);
642 
643   if (gtk_widget_get_realized (widget))
644     priv->needs_resize = TRUE;
645 }
646 
647 static void
gtk_gl_area_draw_error_screen(GtkGLArea * area,GtkSnapshot * snapshot,int width,int height)648 gtk_gl_area_draw_error_screen (GtkGLArea   *area,
649                                GtkSnapshot *snapshot,
650                                int          width,
651                                int          height)
652 {
653   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
654   PangoLayout *layout;
655   int layout_height;
656 
657   layout = gtk_widget_create_pango_layout (GTK_WIDGET (area),
658                                            priv->error->message);
659   pango_layout_set_width (layout, width * PANGO_SCALE);
660   pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
661   pango_layout_get_pixel_size (layout, NULL, &layout_height);
662 
663   gtk_snapshot_render_layout (snapshot,
664                               gtk_widget_get_style_context (GTK_WIDGET (area)),
665                               0, (height - layout_height) / 2,
666                               layout);
667 
668   g_object_unref (layout);
669 }
670 
671 static void
release_texture(gpointer data)672 release_texture (gpointer data)
673 {
674   Texture *texture = data;
675   texture->holder = NULL;
676 }
677 
678 static void
gtk_gl_area_snapshot(GtkWidget * widget,GtkSnapshot * snapshot)679 gtk_gl_area_snapshot (GtkWidget   *widget,
680                       GtkSnapshot *snapshot)
681 {
682   GtkGLArea *area = GTK_GL_AREA (widget);
683   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
684   gboolean unused;
685   int w, h, scale;
686   GLenum status;
687 
688   scale = gtk_widget_get_scale_factor (widget);
689   w = gtk_widget_get_width (widget) * scale;
690   h = gtk_widget_get_height (widget) * scale;
691 
692   if (w == 0 || h == 0)
693     return;
694 
695   if (priv->error != NULL)
696     {
697       gtk_gl_area_draw_error_screen (area,
698                                      snapshot,
699                                      gtk_widget_get_width (widget),
700                                      gtk_widget_get_height (widget));
701       return;
702     }
703 
704   if (priv->context == NULL)
705     return;
706 
707   gtk_gl_area_make_current (area);
708 
709   gtk_gl_area_attach_buffers (area);
710 
711  if (priv->has_depth_buffer)
712    glEnable (GL_DEPTH_TEST);
713  else
714    glDisable (GL_DEPTH_TEST);
715 
716   status = glCheckFramebufferStatus (GL_FRAMEBUFFER);
717   if (status == GL_FRAMEBUFFER_COMPLETE)
718     {
719       Texture *texture;
720 
721       if (priv->needs_render || priv->auto_render)
722         {
723           if (priv->needs_resize)
724             {
725               g_signal_emit (area, area_signals[RESIZE], 0, w, h, NULL);
726               priv->needs_resize = FALSE;
727             }
728 
729           g_signal_emit (area, area_signals[RENDER], 0, priv->context, &unused);
730         }
731 
732       priv->needs_render = FALSE;
733 
734       texture = priv->texture;
735       priv->texture = NULL;
736       priv->textures = g_list_prepend (priv->textures, texture);
737 
738       texture->holder = gdk_gl_texture_new (priv->context,
739                                             texture->id,
740                                             texture->width,
741                                             texture->height,
742                                             release_texture, texture);
743 
744       /* Our texture is rendered by OpenGL, so it is upside down,
745        * compared to what GSK expects, so flip it back.
746        */
747       gtk_snapshot_save (snapshot);
748       gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (0, gtk_widget_get_height (widget)));
749       gtk_snapshot_scale (snapshot, 1, -1);
750       gtk_snapshot_append_texture (snapshot,
751                                    texture->holder,
752                                    &GRAPHENE_RECT_INIT (0, 0,
753                                                         gtk_widget_get_width (widget),
754                                                         gtk_widget_get_height (widget)));
755       gtk_snapshot_restore (snapshot);
756 
757       g_object_unref (texture->holder);
758     }
759   else
760     {
761       g_warning ("fb setup not supported (%x)", status);
762     }
763 }
764 
765 static gboolean
create_context_accumulator(GSignalInvocationHint * ihint,GValue * return_accu,const GValue * handler_return,gpointer data)766 create_context_accumulator (GSignalInvocationHint *ihint,
767                             GValue *return_accu,
768                             const GValue *handler_return,
769                             gpointer data)
770 {
771   g_value_copy (handler_return, return_accu);
772 
773   /* stop after the first handler returning a valid object */
774   return g_value_get_object (handler_return) == NULL;
775 }
776 
777 static void
gtk_gl_area_class_init(GtkGLAreaClass * klass)778 gtk_gl_area_class_init (GtkGLAreaClass *klass)
779 {
780   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
781   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
782 
783   klass->resize = gtk_gl_area_resize;
784   klass->create_context = gtk_gl_area_real_create_context;
785 
786   widget_class->realize = gtk_gl_area_realize;
787   widget_class->unrealize = gtk_gl_area_unrealize;
788   widget_class->size_allocate = gtk_gl_area_size_allocate;
789   widget_class->snapshot = gtk_gl_area_snapshot;
790 
791   /**
792    * GtkGLArea:context:
793    *
794    * The `GdkGLContext` used by the `GtkGLArea` widget.
795    *
796    * The `GtkGLArea` widget is responsible for creating the `GdkGLContext`
797    * instance. If you need to render with other kinds of buffers (stencil,
798    * depth, etc), use render buffers.
799    */
800   obj_props[PROP_CONTEXT] =
801     g_param_spec_object ("context",
802                          P_("Context"),
803                          P_("The GL context"),
804                          GDK_TYPE_GL_CONTEXT,
805                          G_PARAM_READABLE |
806                          G_PARAM_STATIC_STRINGS);
807 
808   /**
809    * GtkGLArea:auto-render: (attributes org.gtk.Property.get=gtk_gl_area_get_auto_render org.gtk.Property.set=gtk_gl_area_set_auto_render)
810    *
811    * If set to %TRUE the ::render signal will be emitted every time
812    * the widget draws.
813    *
814    * This is the default and is useful if drawing the widget is faster.
815    *
816    * If set to %FALSE the data from previous rendering is kept around and will
817    * be used for drawing the widget the next time, unless the window is resized.
818    * In order to force a rendering [method@Gtk.GLArea.queue_render] must be called.
819    * This mode is useful when the scene changes seldom, but takes a long time
820    * to redraw.
821    */
822   obj_props[PROP_AUTO_RENDER] =
823     g_param_spec_boolean ("auto-render",
824                           P_("Auto render"),
825                           P_("Whether the GtkGLArea renders on each redraw"),
826                           TRUE,
827                           GTK_PARAM_READWRITE |
828                           G_PARAM_STATIC_STRINGS |
829                           G_PARAM_EXPLICIT_NOTIFY);
830 
831   /**
832    * GtkGLArea:has-depth-buffer: (attributes org.gtk.Property.get=gtk_gl_area_get_has_depth_buffer org.gtk.Property.set=gtk_gl_area_set_has_depth_buffer)
833    *
834    * If set to %TRUE the widget will allocate and enable a depth buffer for the
835    * target framebuffer.
836    */
837   obj_props[PROP_HAS_DEPTH_BUFFER] =
838     g_param_spec_boolean ("has-depth-buffer",
839                           P_("Has depth buffer"),
840                           P_("Whether a depth buffer is allocated"),
841                           FALSE,
842                           GTK_PARAM_READWRITE |
843                           G_PARAM_STATIC_STRINGS |
844                           G_PARAM_EXPLICIT_NOTIFY);
845 
846   /**
847    * GtkGLArea:has-stencil-buffer: (attributes org.gtk.Property.get=gtk_gl_area_get_has_stencil_buffer org.gtk.Property.set=gtk_gl_area_set_has_stencil_buffer)
848    *
849    * If set to %TRUE the widget will allocate and enable a stencil buffer for the
850    * target framebuffer.
851    */
852   obj_props[PROP_HAS_STENCIL_BUFFER] =
853     g_param_spec_boolean ("has-stencil-buffer",
854                           P_("Has stencil buffer"),
855                           P_("Whether a stencil buffer is allocated"),
856                           FALSE,
857                           GTK_PARAM_READWRITE |
858                           G_PARAM_STATIC_STRINGS |
859                           G_PARAM_EXPLICIT_NOTIFY);
860 
861   /**
862    * GtkGLArea:use-es: (attributes org.gtk.Property.get=gtk_gl_area_get_use_es org.gtk.Property.set=gtk_gl_area_set_use_es)
863    *
864    * If set to %TRUE the widget will try to create a `GdkGLContext` using
865    * OpenGL ES instead of OpenGL.
866    */
867   obj_props[PROP_USE_ES] =
868     g_param_spec_boolean ("use-es",
869                           P_("Use OpenGL ES"),
870                           P_("Whether the context uses OpenGL or OpenGL ES"),
871                           FALSE,
872                           GTK_PARAM_READWRITE |
873                           G_PARAM_STATIC_STRINGS |
874                           G_PARAM_EXPLICIT_NOTIFY);
875 
876   gobject_class->set_property = gtk_gl_area_set_property;
877   gobject_class->get_property = gtk_gl_area_get_property;
878   gobject_class->notify = gtk_gl_area_notify;
879 
880   g_object_class_install_properties (gobject_class, LAST_PROP, obj_props);
881 
882   /**
883    * GtkGLArea::render:
884    * @area: the `GtkGLArea` that emitted the signal
885    * @context: the `GdkGLContext` used by @area
886    *
887    * Emitted every time the contents of the `GtkGLArea` should be redrawn.
888    *
889    * The @context is bound to the @area prior to emitting this function,
890    * and the buffers are painted to the window once the emission terminates.
891    *
892    * Returns: %TRUE to stop other handlers from being invoked for the event.
893    *   %FALSE to propagate the event further.
894    */
895   area_signals[RENDER] =
896     g_signal_new (I_("render"),
897                   G_TYPE_FROM_CLASS (gobject_class),
898                   G_SIGNAL_RUN_LAST,
899                   G_STRUCT_OFFSET (GtkGLAreaClass, render),
900                   _gtk_boolean_handled_accumulator, NULL,
901                   _gtk_marshal_BOOLEAN__OBJECT,
902                   G_TYPE_BOOLEAN, 1,
903                   GDK_TYPE_GL_CONTEXT);
904   g_signal_set_va_marshaller (area_signals[RENDER],
905                               G_TYPE_FROM_CLASS (klass),
906                               _gtk_marshal_BOOLEAN__OBJECTv);
907 
908   /**
909    * GtkGLArea::resize:
910    * @area: the `GtkGLArea` that emitted the signal
911    * @width: the width of the viewport
912    * @height: the height of the viewport
913    *
914    * Emitted once when the widget is realized, and then each time the widget
915    * is changed while realized.
916    *
917    * This is useful in order to keep GL state up to date with the widget size,
918    * like for instance camera properties which may depend on the width/height
919    * ratio.
920    *
921    * The GL context for the area is guaranteed to be current when this signal
922    * is emitted.
923    *
924    * The default handler sets up the GL viewport.
925    */
926   area_signals[RESIZE] =
927     g_signal_new (I_("resize"),
928                   G_TYPE_FROM_CLASS (klass),
929                   G_SIGNAL_RUN_LAST,
930                   G_STRUCT_OFFSET (GtkGLAreaClass, resize),
931                   NULL, NULL,
932                   _gtk_marshal_VOID__INT_INT,
933                   G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
934   g_signal_set_va_marshaller (area_signals[RESIZE],
935                               G_TYPE_FROM_CLASS (klass),
936                               _gtk_marshal_VOID__INT_INTv);
937 
938   /**
939    * GtkGLArea::create-context:
940    * @area: the `GtkGLArea` that emitted the signal
941    * @error: (nullable): location to store error information on failure
942    *
943    * Emitted when the widget is being realized.
944    *
945    * This allows you to override how the GL context is created.
946    * This is useful when you want to reuse an existing GL context,
947    * or if you want to try creating different kinds of GL options.
948    *
949    * If context creation fails then the signal handler can use
950    * [method@Gtk.GLArea.set_error] to register a more detailed error
951    * of how the construction failed.
952    *
953    * Returns: (transfer full): a newly created `GdkGLContext`;
954    *     the `GtkGLArea` widget will take ownership of the returned value.
955    */
956   area_signals[CREATE_CONTEXT] =
957     g_signal_new (I_("create-context"),
958                   G_TYPE_FROM_CLASS (klass),
959                   G_SIGNAL_RUN_LAST,
960                   G_STRUCT_OFFSET (GtkGLAreaClass, create_context),
961                   create_context_accumulator, NULL,
962                   _gtk_marshal_OBJECT__VOID,
963                   GDK_TYPE_GL_CONTEXT, 0);
964   g_signal_set_va_marshaller (area_signals[CREATE_CONTEXT],
965                               G_TYPE_FROM_CLASS (klass),
966                               _gtk_marshal_OBJECT__VOIDv);
967 }
968 
969 static void
gtk_gl_area_init(GtkGLArea * area)970 gtk_gl_area_init (GtkGLArea *area)
971 {
972   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
973 
974   priv->auto_render = TRUE;
975   priv->needs_render = TRUE;
976   priv->required_gl_version = 0;
977 }
978 
979 /**
980  * gtk_gl_area_new:
981  *
982  * Creates a new `GtkGLArea` widget.
983  *
984  * Returns: a new `GtkGLArea`
985  */
986 GtkWidget *
gtk_gl_area_new(void)987 gtk_gl_area_new (void)
988 {
989   return g_object_new (GTK_TYPE_GL_AREA, NULL);
990 }
991 
992 /**
993  * gtk_gl_area_set_error:
994  * @area: a `GtkGLArea`
995  * @error: (nullable): a new `GError`, or %NULL to unset the error
996  *
997  * Sets an error on the area which will be shown instead of the
998  * GL rendering.
999  *
1000  * This is useful in the [signal@Gtk.GLArea::create-context]
1001  * signal if GL context creation fails.
1002  */
1003 void
gtk_gl_area_set_error(GtkGLArea * area,const GError * error)1004 gtk_gl_area_set_error (GtkGLArea    *area,
1005                        const GError *error)
1006 {
1007   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
1008 
1009   g_return_if_fail (GTK_IS_GL_AREA (area));
1010 
1011   g_clear_error (&priv->error);
1012   if (error)
1013     priv->error = g_error_copy (error);
1014 }
1015 
1016 /**
1017  * gtk_gl_area_get_error:
1018  * @area: a `GtkGLArea`
1019  *
1020  * Gets the current error set on the @area.
1021  *
1022  * Returns: (nullable) (transfer none): the `GError`
1023  */
1024 GError *
gtk_gl_area_get_error(GtkGLArea * area)1025 gtk_gl_area_get_error (GtkGLArea *area)
1026 {
1027   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
1028 
1029   g_return_val_if_fail (GTK_IS_GL_AREA (area), NULL);
1030 
1031   return priv->error;
1032 }
1033 
1034 /**
1035  * gtk_gl_area_set_use_es: (attributes org.gtk.Method.set_property=use-es)
1036  * @area: a `GtkGLArea`
1037  * @use_es: whether to use OpenGL or OpenGL ES
1038  *
1039  * Sets whether the @area should create an OpenGL or an OpenGL ES context.
1040  *
1041  * You should check the capabilities of the `GdkGLContext` before drawing
1042  * with either API.
1043  */
1044 void
gtk_gl_area_set_use_es(GtkGLArea * area,gboolean use_es)1045 gtk_gl_area_set_use_es (GtkGLArea *area,
1046                         gboolean   use_es)
1047 {
1048   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
1049 
1050   g_return_if_fail (GTK_IS_GL_AREA (area));
1051   g_return_if_fail (!gtk_widget_get_realized (GTK_WIDGET (area)));
1052 
1053   use_es = !!use_es;
1054 
1055   if (priv->use_es != use_es)
1056     {
1057       priv->use_es = use_es;
1058 
1059       g_object_notify_by_pspec (G_OBJECT (area), obj_props[PROP_USE_ES]);
1060     }
1061 }
1062 
1063 /**
1064  * gtk_gl_area_get_use_es: (attributes org.gtk.Method.get_property=use-es)
1065  * @area: a `GtkGLArea`
1066  *
1067  * Returns whether the `GtkGLArea` should use OpenGL ES.
1068  *
1069  * See [method@Gtk.GLArea.set_use_es].
1070  *
1071  * Returns: %TRUE if the `GtkGLArea` should create an OpenGL ES context
1072  *   and %FALSE otherwise
1073  */
1074 gboolean
gtk_gl_area_get_use_es(GtkGLArea * area)1075 gtk_gl_area_get_use_es (GtkGLArea *area)
1076 {
1077   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
1078 
1079   g_return_val_if_fail (GTK_IS_GL_AREA (area), FALSE);
1080 
1081   return priv->use_es;
1082 }
1083 
1084 /**
1085  * gtk_gl_area_set_required_version:
1086  * @area: a `GtkGLArea`
1087  * @major: the major version
1088  * @minor: the minor version
1089  *
1090  * Sets the required version of OpenGL to be used when creating
1091  * the context for the widget.
1092  *
1093  * This function must be called before the area has been realized.
1094  */
1095 void
gtk_gl_area_set_required_version(GtkGLArea * area,int major,int minor)1096 gtk_gl_area_set_required_version (GtkGLArea *area,
1097                                   int        major,
1098                                   int        minor)
1099 {
1100   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
1101 
1102   g_return_if_fail (GTK_IS_GL_AREA (area));
1103   g_return_if_fail (!gtk_widget_get_realized (GTK_WIDGET (area)));
1104 
1105   priv->required_gl_version = major * 10 + minor;
1106 }
1107 
1108 /**
1109  * gtk_gl_area_get_required_version:
1110  * @area: a `GtkGLArea`
1111  * @major: (out): return location for the required major version
1112  * @minor: (out): return location for the required minor version
1113  *
1114  * Retrieves the required version of OpenGL.
1115  *
1116  * See [method@Gtk.GLArea.set_required_version].
1117  */
1118 void
gtk_gl_area_get_required_version(GtkGLArea * area,int * major,int * minor)1119 gtk_gl_area_get_required_version (GtkGLArea *area,
1120                                   int       *major,
1121                                   int       *minor)
1122 {
1123   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
1124 
1125   g_return_if_fail (GTK_IS_GL_AREA (area));
1126 
1127   if (major != NULL)
1128     *major = priv->required_gl_version / 10;
1129   if (minor != NULL)
1130     *minor = priv->required_gl_version % 10;
1131 }
1132 
1133 /**
1134  * gtk_gl_area_get_has_depth_buffer: (attributes org.gtk.Method.get_property=has-depth-buffer)
1135  * @area: a `GtkGLArea`
1136  *
1137  * Returns whether the area has a depth buffer.
1138  *
1139  * Returns: %TRUE if the @area has a depth buffer, %FALSE otherwise
1140  */
1141 gboolean
gtk_gl_area_get_has_depth_buffer(GtkGLArea * area)1142 gtk_gl_area_get_has_depth_buffer (GtkGLArea *area)
1143 {
1144   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
1145 
1146   g_return_val_if_fail (GTK_IS_GL_AREA (area), FALSE);
1147 
1148   return priv->has_depth_buffer;
1149 }
1150 
1151 /**
1152  * gtk_gl_area_set_has_depth_buffer: (attributes org.gtk.Method.set_property=has-depth-buffer)
1153  * @area: a `GtkGLArea`
1154  * @has_depth_buffer: %TRUE to add a depth buffer
1155  *
1156  * Sets whether the `GtkGLArea` should use a depth buffer.
1157  *
1158  * If @has_depth_buffer is %TRUE the widget will allocate and
1159  * enable a depth buffer for the target framebuffer. Otherwise
1160  * there will be none.
1161  */
1162 void
gtk_gl_area_set_has_depth_buffer(GtkGLArea * area,gboolean has_depth_buffer)1163 gtk_gl_area_set_has_depth_buffer (GtkGLArea *area,
1164                                   gboolean   has_depth_buffer)
1165 {
1166   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
1167 
1168   g_return_if_fail (GTK_IS_GL_AREA (area));
1169 
1170   has_depth_buffer = !!has_depth_buffer;
1171 
1172   if (priv->has_depth_buffer != has_depth_buffer)
1173     {
1174       priv->has_depth_buffer = has_depth_buffer;
1175 
1176       g_object_notify (G_OBJECT (area), "has-depth-buffer");
1177 
1178       priv->have_buffers = FALSE;
1179     }
1180 }
1181 
1182 /**
1183  * gtk_gl_area_get_has_stencil_buffer: (attributes org.gtk.Method.get_property=has-stencil-buffer)
1184  * @area: a `GtkGLArea`
1185  *
1186  * Returns whether the area has a stencil buffer.
1187  *
1188  * Returns: %TRUE if the @area has a stencil buffer, %FALSE otherwise
1189  */
1190 gboolean
gtk_gl_area_get_has_stencil_buffer(GtkGLArea * area)1191 gtk_gl_area_get_has_stencil_buffer (GtkGLArea *area)
1192 {
1193   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
1194 
1195   g_return_val_if_fail (GTK_IS_GL_AREA (area), FALSE);
1196 
1197   return priv->has_stencil_buffer;
1198 }
1199 
1200 /**
1201  * gtk_gl_area_set_has_stencil_buffer: (attributes org.gtk.Method.set_property=has-stencil-buffer)
1202  * @area: a `GtkGLArea`
1203  * @has_stencil_buffer: %TRUE to add a stencil buffer
1204  *
1205  * Sets whether the `GtkGLArea` should use a stencil buffer.
1206  *
1207  * If @has_stencil_buffer is %TRUE the widget will allocate and
1208  * enable a stencil buffer for the target framebuffer. Otherwise
1209  * there will be none.
1210  */
1211 void
gtk_gl_area_set_has_stencil_buffer(GtkGLArea * area,gboolean has_stencil_buffer)1212 gtk_gl_area_set_has_stencil_buffer (GtkGLArea *area,
1213                                     gboolean   has_stencil_buffer)
1214 {
1215   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
1216 
1217   g_return_if_fail (GTK_IS_GL_AREA (area));
1218 
1219   has_stencil_buffer = !!has_stencil_buffer;
1220 
1221   if (priv->has_stencil_buffer != has_stencil_buffer)
1222     {
1223       priv->has_stencil_buffer = has_stencil_buffer;
1224 
1225       g_object_notify (G_OBJECT (area), "has-stencil-buffer");
1226 
1227       priv->have_buffers = FALSE;
1228     }
1229 }
1230 
1231 /**
1232  * gtk_gl_area_queue_render:
1233  * @area: a `GtkGLArea`
1234  *
1235  * Marks the currently rendered data (if any) as invalid, and queues
1236  * a redraw of the widget.
1237  *
1238  * This ensures that the [signal@Gtk.GLArea::render] signal
1239  * is emitted during the draw.
1240  *
1241  * This is only needed when [method@Gtk.GLArea.set_auto_render] has
1242  * been called with a %FALSE value. The default behaviour is to
1243  * emit [signal@Gtk.GLArea::render] on each draw.
1244  */
1245 void
gtk_gl_area_queue_render(GtkGLArea * area)1246 gtk_gl_area_queue_render (GtkGLArea *area)
1247 {
1248   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
1249 
1250   g_return_if_fail (GTK_IS_GL_AREA (area));
1251 
1252   priv->needs_render = TRUE;
1253 
1254   gtk_widget_queue_draw (GTK_WIDGET (area));
1255 }
1256 
1257 
1258 /**
1259  * gtk_gl_area_get_auto_render: (attributes org.gtk.Method.get_property=auto-render)
1260  * @area: a `GtkGLArea`
1261  *
1262  * Returns whether the area is in auto render mode or not.
1263  *
1264  * Returns: %TRUE if the @area is auto rendering, %FALSE otherwise
1265  */
1266 gboolean
gtk_gl_area_get_auto_render(GtkGLArea * area)1267 gtk_gl_area_get_auto_render (GtkGLArea *area)
1268 {
1269   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
1270 
1271   g_return_val_if_fail (GTK_IS_GL_AREA (area), FALSE);
1272 
1273   return priv->auto_render;
1274 }
1275 
1276 /**
1277  * gtk_gl_area_set_auto_render: (attributes org.gtk.Method.set_property=auto-render)
1278  * @area: a `GtkGLArea`
1279  * @auto_render: a boolean
1280  *
1281  * Sets whether the `GtkGLArea` is in auto render mode.
1282  *
1283  * If @auto_render is %TRUE the [signal@Gtk.GLArea::render] signal will
1284  * be emitted every time the widget draws. This is the default and is
1285  * useful if drawing the widget is faster.
1286  *
1287  * If @auto_render is %FALSE the data from previous rendering is kept
1288  * around and will be used for drawing the widget the next time,
1289  * unless the window is resized. In order to force a rendering
1290  * [method@Gtk.GLArea.queue_render] must be called. This mode is
1291  * useful when the scene changes seldom, but takes a long time to redraw.
1292  */
1293 void
gtk_gl_area_set_auto_render(GtkGLArea * area,gboolean auto_render)1294 gtk_gl_area_set_auto_render (GtkGLArea *area,
1295                              gboolean   auto_render)
1296 {
1297   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
1298 
1299   g_return_if_fail (GTK_IS_GL_AREA (area));
1300 
1301   auto_render = !!auto_render;
1302 
1303   if (priv->auto_render != auto_render)
1304     {
1305       priv->auto_render = auto_render;
1306 
1307       g_object_notify (G_OBJECT (area), "auto-render");
1308 
1309       if (auto_render)
1310         gtk_widget_queue_draw (GTK_WIDGET (area));
1311     }
1312 }
1313 
1314 /**
1315  * gtk_gl_area_get_context:
1316  * @area: a `GtkGLArea`
1317  *
1318  * Retrieves the `GdkGLContext` used by @area.
1319  *
1320  * Returns: (transfer none): the `GdkGLContext`
1321  */
1322 GdkGLContext *
gtk_gl_area_get_context(GtkGLArea * area)1323 gtk_gl_area_get_context (GtkGLArea *area)
1324 {
1325   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
1326 
1327   g_return_val_if_fail (GTK_IS_GL_AREA (area), NULL);
1328 
1329   return priv->context;
1330 }
1331 
1332 /**
1333  * gtk_gl_area_make_current:
1334  * @area: a `GtkGLArea`
1335  *
1336  * Ensures that the `GdkGLContext` used by @area is associated with
1337  * the `GtkGLArea`.
1338  *
1339  * This function is automatically called before emitting the
1340  * [signal@Gtk.GLArea::render] signal, and doesn't normally need
1341  * to be called by application code.
1342  */
1343 void
gtk_gl_area_make_current(GtkGLArea * area)1344 gtk_gl_area_make_current (GtkGLArea *area)
1345 {
1346   GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
1347 
1348   g_return_if_fail (GTK_IS_GL_AREA (area));
1349   g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (area)));
1350 
1351   if (priv->context != NULL)
1352     gdk_gl_context_make_current (priv->context);
1353 }
1354