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