gtkglarea.c 36.1 KB
Newer Older
Alexander Larsson's avatar
Alexander Larsson committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/* GTK - The GIMP Toolkit
 *
 * gtkglarea.c: A GL drawing area
 *
 * Copyright © 2014  Emmanuele Bassi
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 */

#include "config.h"

#include "config.h"
#include "gtkglarea.h"
#include "gtkintl.h"
#include "gtkstylecontext.h"
#include "gtkmarshalers.h"
#include "gtkprivate.h"
29 30
#include "gtkrender.h"

Alexander Larsson's avatar
Alexander Larsson committed
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
#include <epoxy/gl.h>

/**
 * SECTION:gtkglarea
 * @Title: GtkGLArea
 * @Short_description: A widget for custom drawing with OpenGL
 *
 * #GtkGLArea is a widget that allows drawing with OpenGL.
 *
 * #GtkGLArea sets up its own #GdkGLContext for the window it creates, and
 * creates a custom GL framebuffer that the widget will do GL rendering onto.
 * It also ensures that this framebuffer is the default GL rendering target
 * when rendering.
 *
 * In order to draw, you have to connect to the #GtkGLArea::render signal,
 * or subclass #GtkGLArea and override the @GtkGLAreaClass.render() virtual
 * function.
 *
 * The #GtkGLArea widget ensures that the #GdkGLContext is associated with
 * the widget's drawing area, and it is kept updated when the size and
 * position of the drawing area changes.
 *
 * ## Drawing with GtkGLArea ##
 *
 * The simplest way to draw using OpenGL commands in a #GtkGLArea is to
 * create a widget instance and connect to the #GtkGLArea::render signal:
 *
 * |[<!-- language="C" -->
 *   // create a GtkGLArea instance
 *   GtkWidget *gl_area = gtk_gl_area_new ();
 *
 *   // connect to the "render" signal
 *   g_signal_connect (gl_area, "render", G_CALLBACK (render), NULL);
 * ]|
 *
 * The `render()` function will be called when the #GtkGLArea is ready
 * for you to draw its content:
 *
 * |[<!-- language="C" -->
 *   static gboolean
 *   render (GtkGLArea *area, GdkGLContext *context)
 *   {
 *     // inside this function it's safe to use GL; the given
 *     // #GdkGLContext has been made current to the drawable
 *     // surface used by the #GtkGLArea and the viewport has
 *     // already been set to be the size of the allocation
 *
 *     // we can start by clearing the buffer
 *     glClearColor (0, 0, 0, 0);
 *     glClear (GL_COLOR_BUFFER_BIT);
 *
 *     // draw your object
 *     draw_an_object ();
 *
 *     // we completed our drawing; the draw commands will be
 *     // flushed at the end of the signal emission chain, and
 *     // the buffers will be drawn on the window
 *     return TRUE;
 *   }
 * ]|
 *
92
 * If you need to initialize OpenGL state, e.g. buffer objects or
93
 * shaders, you should use the #GtkWidget::realize signal; you
94
 * can use the #GtkWidget::unrealize signal to clean up.
Alexander Larsson's avatar
Alexander Larsson committed
95
 *
96 97
 * If you need to change the options for creating the #GdkGLContext
 * you should use the #GtkGLArea::create-context signal.
Alexander Larsson's avatar
Alexander Larsson committed
98 99 100 101
 */

typedef struct {
  GdkGLContext *context;
102
  GdkWindow *event_window;
103
  GError *error;
104 105 106

  gboolean have_buffers;

107 108
  int required_gl_version;

109 110 111 112 113
  guint frame_buffer;
  guint render_buffer;
  guint texture;
  guint depth_stencil_buffer;

Alexander Larsson's avatar
Alexander Larsson committed
114 115
  gboolean has_alpha;
  gboolean has_depth_buffer;
116 117
  gboolean has_stencil_buffer;

118
  gboolean needs_resize;
119 120
  gboolean needs_render;
  gboolean auto_render;
Alexander Larsson's avatar
Alexander Larsson committed
121 122 123 124 125 126 127 128
} GtkGLAreaPrivate;

enum {
  PROP_0,

  PROP_CONTEXT,
  PROP_HAS_ALPHA,
  PROP_HAS_DEPTH_BUFFER,
129
  PROP_HAS_STENCIL_BUFFER,
Alexander Larsson's avatar
Alexander Larsson committed
130

131
  PROP_AUTO_RENDER,
132

Alexander Larsson's avatar
Alexander Larsson committed
133 134 135 136 137 138 139
  LAST_PROP
};

static GParamSpec *obj_props[LAST_PROP] = { NULL, };

enum {
  RENDER,
140
  RESIZE,
141
  CREATE_CONTEXT,
Alexander Larsson's avatar
Alexander Larsson committed
142 143 144 145

  LAST_SIGNAL
};

146
static void gtk_gl_area_allocate_buffers (GtkGLArea *area);
147

Alexander Larsson's avatar
Alexander Larsson committed
148 149 150 151 152 153 154
static guint area_signals[LAST_SIGNAL] = { 0, };

G_DEFINE_TYPE_WITH_PRIVATE (GtkGLArea, gtk_gl_area, GTK_TYPE_WIDGET)

static void
gtk_gl_area_dispose (GObject *gobject)
{
155 156
  GtkGLArea *area = GTK_GL_AREA (gobject);
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
Alexander Larsson's avatar
Alexander Larsson committed
157 158 159 160 161 162 163 164 165 166 167 168

  g_clear_object (&priv->context);

  G_OBJECT_CLASS (gtk_gl_area_parent_class)->dispose (gobject);
}

static void
gtk_gl_area_set_property (GObject      *gobject,
                          guint         prop_id,
                          const GValue *value,
                          GParamSpec   *pspec)
{
169 170
  GtkGLArea *self = GTK_GL_AREA (gobject);

Alexander Larsson's avatar
Alexander Larsson committed
171 172
  switch (prop_id)
    {
173
    case PROP_AUTO_RENDER:
174
      gtk_gl_area_set_auto_render (self, g_value_get_boolean (value));
175 176
      break;

Alexander Larsson's avatar
Alexander Larsson committed
177
    case PROP_HAS_ALPHA:
178
      gtk_gl_area_set_has_alpha (self, g_value_get_boolean (value));
Alexander Larsson's avatar
Alexander Larsson committed
179 180 181
      break;

    case PROP_HAS_DEPTH_BUFFER:
182
      gtk_gl_area_set_has_depth_buffer (self, g_value_get_boolean (value));
Alexander Larsson's avatar
Alexander Larsson committed
183 184
      break;

185
    case PROP_HAS_STENCIL_BUFFER:
186
      gtk_gl_area_set_has_stencil_buffer (self, g_value_get_boolean (value));
187 188
      break;

Alexander Larsson's avatar
Alexander Larsson committed
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
    }
}

static void
gtk_gl_area_get_property (GObject    *gobject,
                          guint       prop_id,
                          GValue     *value,
                          GParamSpec *pspec)
{
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (GTK_GL_AREA (gobject));

  switch (prop_id)
    {
204 205 206 207
    case PROP_AUTO_RENDER:
      g_value_set_boolean (value, priv->auto_render);
      break;

Alexander Larsson's avatar
Alexander Larsson committed
208 209 210 211 212 213 214 215
    case PROP_HAS_ALPHA:
      g_value_set_boolean (value, priv->has_alpha);
      break;

    case PROP_HAS_DEPTH_BUFFER:
      g_value_set_boolean (value, priv->has_depth_buffer);
      break;

216 217 218 219
    case PROP_HAS_STENCIL_BUFFER:
      g_value_set_boolean (value, priv->has_stencil_buffer);
      break;

Alexander Larsson's avatar
Alexander Larsson committed
220 221 222 223 224 225 226 227 228 229 230 231
    case PROP_CONTEXT:
      g_value_set_object (value, priv->context);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
    }
}

static void
gtk_gl_area_realize (GtkWidget *widget)
{
232 233
  GtkGLArea *area = GTK_GL_AREA (widget);
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
234 235 236
  GtkAllocation allocation;
  GdkWindowAttr attributes;
  gint attributes_mask;
Alexander Larsson's avatar
Alexander Larsson committed
237 238 239

  GTK_WIDGET_CLASS (gtk_gl_area_parent_class)->realize (widget);

240 241
  gtk_widget_get_allocation (widget, &allocation);

242 243 244 245 246 247 248 249 250 251 252
  attributes.window_type = GDK_WINDOW_CHILD;
  attributes.x = allocation.x;
  attributes.y = allocation.y;
  attributes.width = allocation.width;
  attributes.height = allocation.height;
  attributes.wclass = GDK_INPUT_ONLY;
  attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;

  attributes_mask = GDK_WA_X | GDK_WA_Y;

  priv->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
253
                                       &attributes, attributes_mask);
254 255
  gtk_widget_register_window (widget, priv->event_window);

256 257 258 259 260 261 262 263 264
  g_clear_error (&priv->error);
  priv->context = NULL;
  g_signal_emit (area, area_signals[CREATE_CONTEXT], 0, &priv->context);

  /* In case the signal failed, but did not set an error */
  if (priv->context == NULL && priv->error == NULL)
    g_set_error_literal (&priv->error, GDK_GL_ERROR,
                         GDK_GL_ERROR_NOT_AVAILABLE,
                         _("OpenGL context creation failed"));
265 266 267 268

  priv->needs_resize = TRUE;
}

269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
static void
gtk_gl_area_notify (GObject    *object,
                    GParamSpec *pspec)
{
  if (strcmp (pspec->name, "scale-factor") == 0)
    {
      GtkGLArea *area = GTK_GL_AREA (object);
      GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);

      priv->needs_resize = TRUE;
    }

  if (G_OBJECT_CLASS (gtk_gl_area_parent_class)->notify)
    G_OBJECT_CLASS (gtk_gl_area_parent_class)->notify (object, pspec);
}

285 286 287 288 289 290 291 292
static GdkGLContext *
gtk_gl_area_real_create_context (GtkGLArea *area)
{
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
  GtkWidget *widget = GTK_WIDGET (area);
  GError *error = NULL;
  GdkGLContext *context;

293
  context = gdk_window_create_gl_context (gtk_widget_get_window (widget), &error);
294
  if (error != NULL)
295 296 297
    {
      gtk_gl_area_set_error (area, error);
      g_clear_object (&context);
298
      g_clear_error (&error);
299 300 301
      return NULL;
    }

302 303 304
  gdk_gl_context_set_required_version (context,
                                       priv->required_gl_version / 10,
                                       priv->required_gl_version % 10);
305

306
  gdk_gl_context_realize (context, &error);
307
  if (error != NULL)
308 309 310
    {
      gtk_gl_area_set_error (area, error);
      g_clear_object (&context);
311
      g_clear_error (&error);
312 313
      return NULL;
    }
314 315 316 317

  return context;
}

318 319 320 321
static void
gtk_gl_area_resize (GtkGLArea *area, int width, int height)
{
  glViewport (0, 0, width, height);
322 323 324 325 326 327
}

/*
 * Creates all the buffer objects needed for rendering the scene
 */
static void
328
gtk_gl_area_ensure_buffers (GtkGLArea *area)
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
{
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
  GtkWidget *widget = GTK_WIDGET (area);

  gtk_widget_realize (widget);

  if (priv->context == NULL)
    return;

  if (priv->have_buffers)
    return;

  priv->have_buffers = TRUE;

  glGenFramebuffersEXT (1, &priv->frame_buffer);

  if (priv->has_alpha)
346 347 348 349 350 351 352 353 354 355 356 357
    {
      /* For alpha we use textures as that is required for blending to work */
      if (priv->texture == 0)
        glGenTextures (1, &priv->texture);

      /* Delete old render buffer if any */
      if (priv->render_buffer != 0)
        {
          glDeleteRenderbuffersEXT(1, &priv->render_buffer);
          priv->render_buffer = 0;
        }
    }
358
  else
359
    {
360
    /* For non-alpha we use render buffers so we can blit instead of texture the result */
361 362
      if (priv->render_buffer == 0)
        glGenRenderbuffersEXT (1, &priv->render_buffer);
363

364 365 366 367 368 369 370
      /* Delete old texture if any */
      if (priv->texture != 0)
        {
          glDeleteTextures(1, &priv->texture);
          priv->texture = 0;
        }
    }
371

372 373 374 375 376 377 378 379 380 381 382 383 384
  if ((priv->has_depth_buffer || priv->has_stencil_buffer))
    {
      if (priv->depth_stencil_buffer == 0)
        glGenRenderbuffersEXT (1, &priv->depth_stencil_buffer);
    }
  else if (priv->depth_stencil_buffer != 0)
    {
      /* Delete old depth/stencil buffer */
      glDeleteRenderbuffersEXT (1, &priv->depth_stencil_buffer);
      priv->depth_stencil_buffer = 0;
    }

  gtk_gl_area_allocate_buffers (area);
385 386 387 388 389 390
}

/*
 * Allocates space of the right type and size for all the buffers
 */
static void
391
gtk_gl_area_allocate_buffers (GtkGLArea *area)
392 393
{
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
394 395
  GtkWidget *widget = GTK_WIDGET (area);
  int scale, width, height;
396 397 398 399

  if (priv->context == NULL)
    return;

400 401 402 403
  scale = gtk_widget_get_scale_factor (widget);
  width = gtk_widget_get_allocated_width (widget) * scale;
  height = gtk_widget_get_allocated_height (widget) * scale;

404 405 406 407 408 409 410 411 412 413 414 415
  if (priv->texture)
    {
      glBindTexture (GL_TEXTURE_2D, priv->texture);
      glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
      glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
      glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
      glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
      glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
    }

  if (priv->render_buffer)
    {
416 417
      glBindRenderbuffer (GL_RENDERBUFFER, priv->render_buffer);
      glRenderbufferStorage (GL_RENDERBUFFER, GL_RGB8, width, height);
418 419 420
    }

  if (priv->has_depth_buffer || priv->has_stencil_buffer)
Alexander Larsson's avatar
Alexander Larsson committed
421
    {
422
      glBindRenderbuffer (GL_RENDERBUFFER, priv->depth_stencil_buffer);
423
      if (priv->has_stencil_buffer)
424
        glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
425
      else
426
        glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
Alexander Larsson's avatar
Alexander Larsson committed
427
    }
428 429

  priv->needs_render = TRUE;
Alexander Larsson's avatar
Alexander Larsson committed
430 431
}

432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
/**
 * gtk_gl_area_attach_buffers:
 * @area: a #GtkGLArea
 *
 * Ensures that the @area framebuffer object is made the current draw
 * and read target, and that all the required buffers for the @area
 * are created and bound to the frambuffer.
 *
 * This function is automatically called before emitting the
 * #GtkGLArea::render signal, and doesn't normally need to be called
 * by application code.
 *
 * Since: 3.16
 */
void
gtk_gl_area_attach_buffers (GtkGLArea *area)
{
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);

  gtk_gl_area_make_current (area);

  if (!priv->have_buffers)
454 455 456
    gtk_gl_area_ensure_buffers (area);
  else if (priv->needs_resize)
    gtk_gl_area_allocate_buffers (area);
457 458 459 460 461

  glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, priv->frame_buffer);

  if (priv->texture)
    glFramebufferTexture2D (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
462
                            GL_TEXTURE_2D, priv->texture, 0);
463 464
  else if (priv->render_buffer)
    glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
465
                                  GL_RENDERBUFFER_EXT, priv->render_buffer);
466 467 468 469

  if (priv->depth_stencil_buffer)
    {
      if (priv->has_depth_buffer)
470 471
        glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
                                      GL_RENDERBUFFER_EXT, priv->depth_stencil_buffer);
472
      if (priv->has_stencil_buffer)
473 474
        glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
                                      GL_RENDERBUFFER_EXT, priv->depth_stencil_buffer);
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
    }
}

static void
gtk_gl_area_delete_buffers (GtkGLArea *area)
{
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);

  if (priv->context == NULL)
    return;

  priv->have_buffers = FALSE;

  if (priv->render_buffer != 0)
    {
490
      glDeleteRenderbuffersEXT (1, &priv->render_buffer);
491 492 493 494 495 496 497 498 499 500 501
      priv->render_buffer = 0;
    }

  if (priv->texture != 0)
    {
      glDeleteTextures(1, &priv->texture);
      priv->texture = 0;
    }

  if (priv->depth_stencil_buffer != 0)
    {
502 503 504
      glDeleteRenderbuffersEXT (1, &priv->depth_stencil_buffer);
      priv->depth_stencil_buffer = 0;
    }
505

506 507
  if (priv->frame_buffer != 0)
    {
508 509 510 511 512 513
      glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
      glDeleteFramebuffersEXT (1, &priv->frame_buffer);
      priv->frame_buffer = 0;
    }
}

Alexander Larsson's avatar
Alexander Larsson committed
514 515 516
static void
gtk_gl_area_unrealize (GtkWidget *widget)
{
517 518
  GtkGLArea *area = GTK_GL_AREA (widget);
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
Alexander Larsson's avatar
Alexander Larsson committed
519 520 521

  if (priv->context != NULL)
    {
522
      if (priv->have_buffers)
523 524 525 526
        {
          gtk_gl_area_make_current (area);
          gtk_gl_area_delete_buffers (area);
        }
Alexander Larsson's avatar
Alexander Larsson committed
527

528
      /* Make sure to unset the context if current */
529 530
      if (priv->context == gdk_gl_context_get_current ())
        gdk_gl_context_clear_current ();
Alexander Larsson's avatar
Alexander Larsson committed
531 532
    }

533
  g_clear_object (&priv->context);
534 535
  g_clear_error (&priv->error);

536 537 538 539 540 541 542
  if (priv->event_window != NULL)
    {
      gtk_widget_unregister_window (widget, priv->event_window);
      gdk_window_destroy (priv->event_window);
      priv->event_window = NULL;
    }

Alexander Larsson's avatar
Alexander Larsson committed
543 544 545
  GTK_WIDGET_CLASS (gtk_gl_area_parent_class)->unrealize (widget);
}

546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569
static void
gtk_gl_area_map (GtkWidget *widget)
{
  GtkGLArea *area = GTK_GL_AREA (widget);
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);

  if (priv->event_window != NULL)
    gdk_window_show (priv->event_window);

  GTK_WIDGET_CLASS (gtk_gl_area_parent_class)->map (widget);
}

static void
gtk_gl_area_unmap (GtkWidget *widget)
{
  GtkGLArea *area = GTK_GL_AREA (widget);
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);

  if (priv->event_window != NULL)
    gdk_window_hide (priv->event_window);

  GTK_WIDGET_CLASS (gtk_gl_area_parent_class)->unmap (widget);
}

Alexander Larsson's avatar
Alexander Larsson committed
570 571 572 573
static void
gtk_gl_area_size_allocate (GtkWidget     *widget,
                           GtkAllocation *allocation)
{
574 575 576
  GtkGLArea *area = GTK_GL_AREA (widget);
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);

Alexander Larsson's avatar
Alexander Larsson committed
577
  GTK_WIDGET_CLASS (gtk_gl_area_parent_class)->size_allocate (widget, allocation);
578

579 580 581
  if (gtk_widget_get_realized (widget))
    {
      if (priv->event_window != NULL)
582 583 584 585 586
        gdk_window_move_resize (priv->event_window,
                                allocation->x,
                                allocation->y,
                                allocation->width,
                                allocation->height);
587 588 589

      priv->needs_resize = TRUE;
    }
Alexander Larsson's avatar
Alexander Larsson committed
590 591
}

592
static void
593
gtk_gl_area_draw_error_screen (GtkGLArea *area,
594 595 596 597
                               cairo_t   *cr,
                               gint       width,
                               gint       height)
{
598
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
599 600 601
  PangoLayout *layout;
  int layout_height;

602
  layout = gtk_widget_create_pango_layout (GTK_WIDGET (area),
603 604 605 606
                                           priv->error->message);
  pango_layout_set_width (layout, width * PANGO_SCALE);
  pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
  pango_layout_get_pixel_size (layout, NULL, &layout_height);
607
  gtk_render_layout (gtk_widget_get_style_context (GTK_WIDGET (area)),
608 609 610 611 612 613 614
                     cr,
                     0, (height - layout_height) / 2,
                     layout);

  g_object_unref (layout);
}

Alexander Larsson's avatar
Alexander Larsson committed
615 616 617 618
static gboolean
gtk_gl_area_draw (GtkWidget *widget,
                  cairo_t   *cr)
{
619 620
  GtkGLArea *area = GTK_GL_AREA (widget);
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
Alexander Larsson's avatar
Alexander Larsson committed
621 622 623 624
  gboolean unused;
  int w, h, scale;
  GLenum status;

625
  if (priv->error != NULL)
626
    {
627
      gtk_gl_area_draw_error_screen (area,
628 629 630 631 632
                                     cr,
                                     gtk_widget_get_allocated_width (widget),
                                     gtk_widget_get_allocated_height (widget));
      return FALSE;
    }
Alexander Larsson's avatar
Alexander Larsson committed
633

634 635 636 637 638 639 640 641
  gtk_gl_area_make_current (area);

  gtk_gl_area_attach_buffers (area);

 if (priv->has_depth_buffer)
   glEnable (GL_DEPTH_TEST);
 else
   glDisable (GL_DEPTH_TEST);
Alexander Larsson's avatar
Alexander Larsson committed
642 643 644 645

  scale = gtk_widget_get_scale_factor (widget);
  w = gtk_widget_get_allocated_width (widget) * scale;
  h = gtk_widget_get_allocated_height (widget) * scale;
646

Alexander Larsson's avatar
Alexander Larsson committed
647 648 649
  status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
  if (status ==  GL_FRAMEBUFFER_COMPLETE_EXT)
    {
650
      if (priv->needs_render || priv->auto_render)
651 652 653 654 655 656
        {
          if (priv->needs_resize)
            {
              g_signal_emit (area, area_signals[RESIZE], 0, w, h, NULL);
              priv->needs_resize = FALSE;
            }
657

658 659
          g_signal_emit (area, area_signals[RENDER], 0, priv->context, &unused);
        }
660

661
      priv->needs_render = FALSE;
Alexander Larsson's avatar
Alexander Larsson committed
662 663 664

      gdk_cairo_draw_from_gl (cr,
                              gtk_widget_get_window (widget),
665 666
                              priv->texture ? priv->texture : priv->render_buffer,
                              priv->texture ? GL_TEXTURE : GL_RENDERBUFFER,
Alexander Larsson's avatar
Alexander Larsson committed
667
                              scale, 0, 0, w, h);
668
      gtk_gl_area_make_current (area);
Alexander Larsson's avatar
Alexander Larsson committed
669 670 671 672 673 674 675 676 677
    }
  else
    {
      g_print ("fb setup not supported\n");
    }

  return TRUE;
}

678 679 680 681 682 683 684 685 686 687 688 689
static gboolean
create_context_accumulator (GSignalInvocationHint *ihint,
                            GValue *return_accu,
                            const GValue *handler_return,
                            gpointer data)
{
  g_value_copy (handler_return, return_accu);

  /* stop after the first handler returning a valid object */
  return g_value_get_object (handler_return) == NULL;
}

Alexander Larsson's avatar
Alexander Larsson committed
690 691 692 693 694 695
static void
gtk_gl_area_class_init (GtkGLAreaClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);

696
  klass->resize = gtk_gl_area_resize;
697
  klass->create_context = gtk_gl_area_real_create_context;
698

Alexander Larsson's avatar
Alexander Larsson committed
699 700
  widget_class->realize = gtk_gl_area_realize;
  widget_class->unrealize = gtk_gl_area_unrealize;
701 702
  widget_class->map = gtk_gl_area_map;
  widget_class->unmap = gtk_gl_area_unmap;
Alexander Larsson's avatar
Alexander Larsson committed
703 704 705 706 707 708 709 710 711 712 713
  widget_class->size_allocate = gtk_gl_area_size_allocate;
  widget_class->draw = gtk_gl_area_draw;

  gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_DRAWING_AREA);

  /**
   * GtkGLArea:context:
   *
   * The #GdkGLContext used by the #GtkGLArea widget.
   *
   * The #GtkGLArea widget is responsible for creating the #GdkGLContext
714 715
   * instance. If you need to render with other kinds of buffers (stencil,
   * depth, etc), use render buffers.
Alexander Larsson's avatar
Alexander Larsson committed
716 717 718 719 720 721 722 723 724 725 726
   *
   * Since: 3.16
   */
  obj_props[PROP_CONTEXT] =
    g_param_spec_object ("context",
                         P_("Context"),
                         P_("The GL context"),
                         GDK_TYPE_GL_CONTEXT,
                         G_PARAM_READABLE |
                         G_PARAM_STATIC_STRINGS);

727 728 729
  /**
   * GtkGLArea:auto-render:
   *
730 731 732
   * If set to %TRUE the #GtkGLArea::render signal will be emitted every time
   * the widget draws. This is the default and is useful if drawing the widget
   * is faster.
733
   *
734 735 736 737 738
   * If set to %FALSE the data from previous rendering is kept around and will
   * be used for drawing the widget the next time, unless the window is resized.
   * In order to force a rendering gtk_gl_area_queue_render() must be called.
   * This mode is useful when the scene changes seldomly, but takes a long time
   * to redraw.
739 740 741 742 743 744
   *
   * Since: 3.16
   */
  obj_props[PROP_AUTO_RENDER] =
    g_param_spec_boolean ("auto-render",
                          P_("Auto render"),
Matthias Clasen's avatar
Matthias Clasen committed
745
                          P_("Whether the GtkGLArea renders on each redraw"),
746
                          TRUE,
747 748 749
                          GTK_PARAM_READWRITE |
                          G_PARAM_STATIC_STRINGS |
                          G_PARAM_EXPLICIT_NOTIFY);
750

Alexander Larsson's avatar
Alexander Larsson committed
751 752 753
  /**
   * GtkGLArea:has-alpha:
   *
754 755 756
   * If set to %TRUE the buffer allocated by the widget will have an alpha channel
   * component, and when rendering to the window the result will be composited over
   * whatever is below the widget.
Alexander Larsson's avatar
Alexander Larsson committed
757
   *
758 759
   * If set to %FALSE there will be no alpha channel, and the buffer will fully
   * replace anything below the widget.
Alexander Larsson's avatar
Alexander Larsson committed
760 761 762 763 764 765
   *
   * Since: 3.16
   */
  obj_props[PROP_HAS_ALPHA] =
    g_param_spec_boolean ("has-alpha",
                          P_("Has alpha"),
Matthias Clasen's avatar
Matthias Clasen committed
766
                          P_("Whether the color buffer has an alpha component"),
Alexander Larsson's avatar
Alexander Larsson committed
767
                          FALSE,
768 769 770
                          GTK_PARAM_READWRITE |
                          G_PARAM_STATIC_STRINGS |
                          G_PARAM_EXPLICIT_NOTIFY);
Alexander Larsson's avatar
Alexander Larsson committed
771 772 773 774

  /**
   * GtkGLArea:has-depth-buffer:
   *
775 776
   * If set to %TRUE the widget will allocate and enable a depth buffer for the
   * target framebuffer.
Alexander Larsson's avatar
Alexander Larsson committed
777 778 779 780 781 782 783 784
   *
   * Since: 3.16
   */
  obj_props[PROP_HAS_DEPTH_BUFFER] =
    g_param_spec_boolean ("has-depth-buffer",
                          P_("Has depth buffer"),
                          P_("Whether a depth buffer is allocated"),
                          FALSE,
785 786 787
                          GTK_PARAM_READWRITE |
                          G_PARAM_STATIC_STRINGS |
                          G_PARAM_EXPLICIT_NOTIFY);
Alexander Larsson's avatar
Alexander Larsson committed
788

789 790 791
  /**
   * GtkGLArea:has-stencil-buffer:
   *
792 793
   * If set to %TRUE the widget will allocate and enable a stencil buffer for the
   * target framebuffer.
794 795 796 797 798 799 800 801
   *
   * Since: 3.16
   */
  obj_props[PROP_HAS_STENCIL_BUFFER] =
    g_param_spec_boolean ("has-stencil-buffer",
                          P_("Has stencil buffer"),
                          P_("Whether a stencil buffer is allocated"),
                          FALSE,
802 803 804
                          GTK_PARAM_READWRITE |
                          G_PARAM_STATIC_STRINGS |
                          G_PARAM_EXPLICIT_NOTIFY);
805

Alexander Larsson's avatar
Alexander Larsson committed
806 807 808
  gobject_class->set_property = gtk_gl_area_set_property;
  gobject_class->get_property = gtk_gl_area_get_property;
  gobject_class->dispose = gtk_gl_area_dispose;
809
  gobject_class->notify = gtk_gl_area_notify;
Alexander Larsson's avatar
Alexander Larsson committed
810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830

  g_object_class_install_properties (gobject_class, LAST_PROP, obj_props);

  /**
   * GtkGLArea::render:
   * @area: the #GtkGLArea that emitted the signal
   * @context: the #GdkGLContext used by @area
   *
   * The ::render signal is emitted every time the contents
   * of the #GtkGLArea should be redrawn.
   *
   * The @context is bound to the @area prior to emitting this function,
   * and the buffers are painted to the window once the emission terminates.
   *
   * Returns: %TRUE to stop other handlers from being invoked for the event.
   *   %FALSE to propagate the event further.
   *
   * Since: 3.16
   */
  area_signals[RENDER] =
    g_signal_new (I_("render"),
831 832 833 834 835 836 837
                  G_TYPE_FROM_CLASS (gobject_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkGLAreaClass, render),
                  _gtk_boolean_handled_accumulator, NULL,
                  NULL,
                  G_TYPE_BOOLEAN, 1,
                  GDK_TYPE_GL_CONTEXT);
838 839 840 841 842 843 844 845 846 847

  /**
   * GtkGLArea::resized:
   * @area: the #GtkGLArea that emitted the signal
   *
   * The ::resized signal is emitted once when the widget is realized, and
   * then each time the widget is changed while realized. This is useful
   * in order to keep GL state up to date with the widget size, like for
   * instance camera properties which may depend on the width/height ratio.
   *
848 849
   * The GL context for the area is guaranteed to be current when this signal
   * is emitted.
850
   *
851
   * The default handler sets up the GL viewport.
852 853 854 855 856 857 858 859 860 861 862 863
   *
   * Since: 3.16
   */
  area_signals[RESIZE] =
    g_signal_new ("resize",
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkGLAreaClass, resize),
                  NULL, NULL,
                  _gtk_marshal_VOID__INT_INT,
                  G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);

864 865 866 867 868 869 870 871 872
  /**
   * GtkGLArea::create-context:
   * @area: the #GtkGLArea that emitted the signal
   * @error: (allow-none): location to store error information on failure
   *
   * The ::create-context signal is emitted when the widget is being
   * realized, and allows you to override how the GL context is
   * created. This is useful when you want to reuse an existing GL
   * context, or if you want to try creating different kinds of GL
Emmanuele Bassi's avatar
Emmanuele Bassi committed
873
   * options.
874 875 876 877 878
   *
   * If context creation fails then the signal handler can use
   * gtk_gl_area_set_error() to register a more detailed error
   * of how the construction failed.
   *
879 880
   * Returns: (transfer full): a newly created #GdkGLContext;
   *     the #GtkGLArea widget will take ownership of the returned value.
881 882 883 884 885 886 887 888 889 890 891
   *
   * Since: 3.16
   */
  area_signals[CREATE_CONTEXT] =
    g_signal_new ("create-context",
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkGLAreaClass, create_context),
                  create_context_accumulator, NULL,
                  _gtk_marshal_OBJECT__VOID,
                  GDK_TYPE_GL_CONTEXT, 0);
Alexander Larsson's avatar
Alexander Larsson committed
892 893 894
}

static void
895
gtk_gl_area_init (GtkGLArea *area)
Alexander Larsson's avatar
Alexander Larsson committed
896
{
897 898 899 900 901 902 903
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);

  gtk_widget_set_has_window (GTK_WIDGET (area), FALSE);
  gtk_widget_set_app_paintable (GTK_WIDGET (area), TRUE);

  priv->auto_render = TRUE;
  priv->needs_render = TRUE;
904
  priv->required_gl_version = 0;
Alexander Larsson's avatar
Alexander Larsson committed
905 906 907 908 909 910 911 912 913 914 915 916 917 918
}

/**
 * gtk_gl_area_new:
 *
 * Creates a new #GtkGLArea widget.
 *
 * Returns: (transfer full): the newly created #GtkGLArea
 *
 * Since: 3.16
 */
GtkWidget *
gtk_gl_area_new (void)
{
919
  return g_object_new (GTK_TYPE_GL_AREA, NULL);
Alexander Larsson's avatar
Alexander Larsson committed
920 921
}

922 923 924 925 926 927
/**
 * gtk_gl_area_set_error:
 * @area: a #GtkGLArea
 * @error: (allow-none): a new #GError, or %NULL to unset the error
 *
 * Sets an error on the area which will be shown instead of the
928 929
 * GL rendering. This is useful in the #GtkGLArea::create-context
 * signal if GL context creation fails.
930 931 932 933
 *
 * Since: 3.16
 */
void
934
gtk_gl_area_set_error (GtkGLArea    *area,
935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965
                       const GError *error)
{
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);

  g_return_if_fail (GTK_IS_GL_AREA (area));

  g_clear_error (&priv->error);
  if (error)
    priv->error = g_error_copy (error);
}

/**
 * gtk_gl_area_get_error:
 * @area: a #GtkGLArea
 *
 * Gets the current error set on the @area.
 *
 * Returns: (transfer none): the #GError or %NULL
 *
 * Since: 3.16
 */
GError *
gtk_gl_area_get_error (GtkGLArea *area)
{
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);

  g_return_val_if_fail (GTK_IS_GL_AREA (area), NULL);

  return priv->error;
}

966 967 968
/**
 * gtk_gl_area_set_required_version:
 * @area: a #GtkGLArea
969 970
 * @major: the major version
 * @minor: the minor version
971 972 973 974 975 976 977 978 979 980
 *
 * Sets the required version of OpenGL to be used when creating the context
 * for the widget.
 *
 * This function must be called before the area has been realized.
 *
 * Since: 3.16
 */
void
gtk_gl_area_set_required_version (GtkGLArea *area,
981 982
                                  gint       major,
                                  gint       minor)
983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004
{
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);

  g_return_if_fail (GTK_IS_GL_AREA (area));
  g_return_if_fail (!gtk_widget_get_realized (GTK_WIDGET (area)));

  priv->required_gl_version = major * 10 + minor;
}

/**
 * gtk_gl_area_get_required_version:
 * @area: a #GtkGLArea
 * @major: (out): return location for the required major version
 * @minor: (out): return location for the required minor version
 *
 * Retrieves the required version of OpenGL set
 * using gtk_gl_area_set_required_version().
 *
 * Since: 3.16
 */
void
gtk_gl_area_get_required_version (GtkGLArea *area,
1005 1006
                                  gint      *major,
                                  gint      *minor)
1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017
{
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);

  g_return_if_fail (GTK_IS_GL_AREA (area));

  if (major != NULL)
    *major = priv->required_gl_version / 10;
  if (minor != NULL)
    *minor = priv->required_gl_version % 10;
}

Alexander Larsson's avatar
Alexander Larsson committed
1018 1019 1020 1021
/**
 * gtk_gl_area_get_has_alpha:
 * @area: a #GtkGLArea
 *
1022 1023 1024
 * Returns whether the area has an alpha component.
 *
 * Returns: %TRUE if the @area has an alpha component, %FALSE otherwise
Alexander Larsson's avatar
Alexander Larsson committed
1025 1026 1027 1028
 *
 * Since: 3.16
 */
gboolean
1029
gtk_gl_area_get_has_alpha (GtkGLArea *area)
Alexander Larsson's avatar
Alexander Larsson committed
1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040
{
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);

  g_return_val_if_fail (GTK_IS_GL_AREA (area), FALSE);

  return priv->has_alpha;
}

/**
 * gtk_gl_area_set_has_alpha:
 * @area: a #GtkGLArea
1041
 * @has_alpha: %TRUE to add an alpha component
Alexander Larsson's avatar
Alexander Larsson committed
1042
 *
1043 1044 1045
 * If @has_alpha is %TRUE the buffer allocated by the widget will have
 * an alpha channel component, and when rendering to the window the
 * result will be composited over whatever is below the widget.
Alexander Larsson's avatar
Alexander Larsson committed
1046
 *
1047 1048
 * If @has_alpha is %FALSE there will be no alpha channel, and the
 * buffer will fully replace anything below the widget.
Alexander Larsson's avatar
Alexander Larsson committed
1049 1050 1051 1052
 *
 * Since: 3.16
 */
void
1053 1054
gtk_gl_area_set_has_alpha (GtkGLArea *area,
                           gboolean   has_alpha)
Alexander Larsson's avatar
Alexander Larsson committed
1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066
{
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);

  g_return_if_fail (GTK_IS_GL_AREA (area));

  has_alpha = !!has_alpha;

  if (priv->has_alpha != has_alpha)
    {
      priv->has_alpha = has_alpha;

      g_object_notify (G_OBJECT (area), "has-alpha");
1067

1068
      gtk_gl_area_delete_buffers (area);
Alexander Larsson's avatar
Alexander Larsson committed
1069 1070 1071 1072 1073 1074 1075
    }
}

/**
 * gtk_gl_area_get_has_depth_buffer:
 * @area: a #GtkGLArea
 *
1076 1077 1078
 * Returns whether the area has a depth buffer.
 *
 * Returns: %TRUE if the @area has a depth buffer, %FALSE otherwise
Alexander Larsson's avatar
Alexander Larsson committed
1079 1080 1081 1082
 *
 * Since: 3.16
 */
gboolean
1083
gtk_gl_area_get_has_depth_buffer (GtkGLArea *area)
Alexander Larsson's avatar
Alexander Larsson committed
1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094
{
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);

  g_return_val_if_fail (GTK_IS_GL_AREA (area), FALSE);

  return priv->has_depth_buffer;
}

/**
 * gtk_gl_area_set_has_depth_buffer:
 * @area: a #GtkGLArea
1095
 * @has_depth_buffer: %TRUE to add a depth buffer
Alexander Larsson's avatar
Alexander Larsson committed
1096
 *
1097 1098 1099
 * If @has_depth_buffer is %TRUE the widget will allocate and
 * enable a depth buffer for the target framebuffer. Otherwise
 * there will be none.
Alexander Larsson's avatar
Alexander Larsson committed
1100 1101 1102 1103
 *
 * Since: 3.16
 */
void
1104 1105
gtk_gl_area_set_has_depth_buffer (GtkGLArea *area,
                                  gboolean   has_depth_buffer)
Alexander Larsson's avatar
Alexander Larsson committed
1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117
{
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);

  g_return_if_fail (GTK_IS_GL_AREA (area));

  has_depth_buffer = !!has_depth_buffer;

  if (priv->has_depth_buffer != has_depth_buffer)
    {
      priv->has_depth_buffer = has_depth_buffer;

      g_object_notify (G_OBJECT (area), "has-depth-buffer");
1118

1119
      priv->have_buffers = FALSE;
1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155
    }
}

/**
 * gtk_gl_area_get_has_stencil_buffer:
 * @area: a #GtkGLArea
 *
 * Returns whether the area has a stencil buffer.
 *
 * Returns: %TRUE if the @area has a stencil buffer, %FALSE otherwise
 *
 * Since: 3.16
 */
gboolean
gtk_gl_area_get_has_stencil_buffer (GtkGLArea *area)
{
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);

  g_return_val_if_fail (GTK_IS_GL_AREA (area), FALSE);

  return priv->has_stencil_buffer;
}

/**
 * gtk_gl_area_set_has_stencil_buffer:
 * @area: a #GtkGLArea
 * @has_stencil_buffer: %TRUE to add a stencil buffer
 *
 * If @has_stencil_buffer is %TRUE the widget will allocate and
 * enable a stencil buffer for the target framebuffer. Otherwise
 * there will be none.
 *
 * Since: 3.16
 */
void
gtk_gl_area_set_has_stencil_buffer (GtkGLArea *area,
1156
                                    gboolean   has_stencil_buffer)
1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169
{
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);

  g_return_if_fail (GTK_IS_GL_AREA (area));

  has_stencil_buffer = !!has_stencil_buffer;

  if (priv->has_stencil_buffer != has_stencil_buffer)
    {
      priv->has_stencil_buffer = has_stencil_buffer;

      g_object_notify (G_OBJECT (area), "has-stencil-buffer");

1170
      priv->have_buffers = FALSE;
1171 1172 1173 1174 1175 1176 1177
    }
}

/**
 * gtk_gl_area_queue_render:
 * @area: a #GtkGLArea
 *
1178 1179
 * Marks the currently rendered data (if any) as invalid, and queues
 * a redraw of the widget, ensuring that the #GtkGLArea::render signal
1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227
 * is emitted during the draw.
 *
 * This is only needed when the gtk_gl_area_set_auto_render() has
 * been called with a %FALSE value. The default behaviour is to
 * emit #GtkGLArea::render on each draw.
 *
 * Since: 3.16
 */
void
gtk_gl_area_queue_render (GtkGLArea *area)
{
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);

  g_return_if_fail (GTK_IS_GL_AREA (area));

  priv->needs_render = TRUE;

  gtk_widget_queue_draw (GTK_WIDGET (area));
}


/**
 * gtk_gl_area_get_auto_render:
 * @area: a #GtkGLArea
 *
 * Returns whether the area is in auto render mode or not.
 *
 * Returns: %TRUE if the @area is auto rendering, %FALSE otherwise
 *
 * Since: 3.16
 */
gboolean
gtk_gl_area_get_auto_render (GtkGLArea *area)
{
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);

  g_return_val_if_fail (GTK_IS_GL_AREA (area), FALSE);

  return priv->auto_render;
}

/**
 * gtk_gl_area_set_auto_render:
 * @area: a #GtkGLArea
 * @auto_render: a boolean
 *
 * If @auto_render is %TRUE the #GtkGLArea::render signal will be
 * emitted every time the widget draws. This is the default and is
1228
 * useful if drawing the widget is faster.
1229 1230 1231 1232 1233
 *
 * If @auto_render is %FALSE the data from previous rendering is kept
 * around and will be used for drawing the widget the next time,
 * unless the window is resized. In order to force a rendering
 * gtk_gl_area_queue_render() must be called. This mode is useful when
1234
 * the scene changes seldomly, but takes a long time to redraw.
1235 1236 1237 1238 1239
 *
 * Since: 3.16
 */
void
gtk_gl_area_set_auto_render (GtkGLArea *area,
1240
                             gboolean   auto_render)
1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254
{
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);

  g_return_if_fail (GTK_IS_GL_AREA (area));

  auto_render = !!auto_render;

  if (priv->auto_render != auto_render)
    {
      priv->auto_render = auto_render;

      g_object_notify (G_OBJECT (area), "auto-render");

      if (auto_render)
1255
        gtk_widget_queue_draw (GTK_WIDGET (area));
Alexander Larsson's avatar
Alexander Larsson committed
1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286
    }
}

/**
 * gtk_gl_area_get_context:
 * @area: a #GtkGLArea
 *
 * Retrieves the #GdkGLContext used by @area.
 *
 * Returns: (transfer none): the #GdkGLContext
 *
 * Since: 3.16
 */
GdkGLContext *
gtk_gl_area_get_context (GtkGLArea *area)
{
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);

  g_return_val_if_fail (GTK_IS_GL_AREA (area), NULL);

  return priv->context;
}

/**
 * gtk_gl_area_make_current:
 * @area: a #GtkGLArea
 *
 * Ensures that the #GdkGLContext used by @area is associated with
 * the #GtkGLArea.
 *
 * This function is automatically called before emitting the
1287 1288
 * #GtkGLArea::render signal, and doesn't normally need to be called
 * by application code.
Alexander Larsson's avatar
Alexander Larsson committed
1289 1290 1291
 *
 * Since: 3.16
 */
1292
void
Alexander Larsson's avatar
Alexander Larsson committed
1293 1294 1295 1296 1297
gtk_gl_area_make_current (GtkGLArea *area)
{
  GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
  GtkWidget *widget;

1298
  g_return_if_fail (GTK_IS_GL_AREA (area));
Alexander Larsson's avatar
Alexander Larsson committed
1299 1300 1301

  widget = GTK_WIDGET (area);

1302
  g_return_if_fail (gtk_widget_get_realized (widget));
Alexander Larsson's avatar
Alexander Larsson committed
1303

1304 1305
  if (priv->context)
    gdk_gl_context_make_current (priv->context);
Alexander Larsson's avatar
Alexander Larsson committed
1306
}