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