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