clutter-stage.c 146 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
 * Clutter.
 *
 * An OpenGL based 'interactive canvas' library.
 *
 * Authored By Matthew Allum  <mallum@openedhand.com>
 *
 * Copyright (C) 2006 OpenedHand
 *
 * 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
21
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
22 23
 */

24 25 26
/**
 * SECTION:clutter-stage
 * @short_description: Top level visual element to which actors are placed.
27
 *
28 29
 * #ClutterStage is a top level 'window' on which child actors are placed
 * and manipulated.
30 31 32 33 34 35 36 37 38 39 40 41 42
 *
 * Backends might provide support for multiple stages. The support for this
 * feature can be checked at run-time using the clutter_feature_available()
 * function and the %CLUTTER_FEATURE_STAGE_MULTIPLE flag. If the backend used
 * supports multiple stages, new #ClutterStage instances can be created
 * using clutter_stage_new(). These stages must be managed by the developer
 * using clutter_actor_destroy(), which will take care of destroying all the
 * actors contained inside them.
 *
 * #ClutterStage is a proxy actor, wrapping the backend-specific
 * implementation of the windowing system. It is possible to subclass
 * #ClutterStage, as long as every overridden virtual function chains up to
 * the parent class corresponding function.
43 44
 */

45
#include "clutter-build-config.h"
46

47
#include <math.h>
48
#include <cairo.h>
49

50
#define CLUTTER_DISABLE_DEPRECATION_WARNINGS
51
#define CLUTTER_ENABLE_EXPERIMENTAL_API
52

53
#include "clutter-stage.h"
54
#include "deprecated/clutter-stage.h"
55
#include "deprecated/clutter-container.h"
56

57
#include "clutter-actor-private.h"
58
#include "clutter-backend-private.h"
59
#include "clutter-cairo.h"
60
#include "clutter-color.h"
61 62 63 64
#include "clutter-container.h"
#include "clutter-debug.h"
#include "clutter-device-manager-private.h"
#include "clutter-enum-types.h"
65
#include "clutter-event-private.h"
66
#include "clutter-id-pool.h"
67
#include "clutter-main.h"
68
#include "clutter-marshal.h"
69
#include "clutter-master-clock.h"
70
#include "clutter-mutter.h"
71
#include "clutter-paint-volume-private.h"
72
#include "clutter-private.h"
73
#include "clutter-stage-manager-private.h"
74
#include "clutter-stage-private.h"
75
#include "clutter-private.h"
76

77
#include "cogl/cogl.h"
78

79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
/* <private>
 * ClutterStageHint:
 * @CLUTTER_STAGE_NONE: No hint set
 * @CLUTTER_STAGE_NO_CLEAR_ON_PAINT: When this hint is set, the stage
 *   should not clear the viewport; this flag is useful when painting
 *   fully opaque actors covering the whole visible area of the stage,
 *   i.e. when no blending with the stage color happens over the whole
 *   stage viewport
 *
 * A series of hints that enable or disable behaviours on the stage
 */
typedef enum { /*< prefix=CLUTTER_STAGE >*/
  CLUTTER_STAGE_HINT_NONE = 0,

  CLUTTER_STAGE_NO_CLEAR_ON_PAINT = 1 << 0
} ClutterStageHint;

#define STAGE_NO_CLEAR_ON_PAINT(s)      ((((ClutterStage *) (s))->priv->stage_hints & CLUTTER_STAGE_NO_CLEAR_ON_PAINT) != 0)

98 99 100 101 102 103 104
struct _ClutterStageQueueRedrawEntry
{
  ClutterActor *actor;
  gboolean has_clip;
  ClutterPaintVolume clip;
};

105
struct _ClutterStagePrivate
106
{
107
  /* the stage implementation */
108
  ClutterStageWindow *impl;
109

110 111 112 113 114
  ClutterPerspective perspective;
  CoglMatrix projection;
  CoglMatrix inverse_projection;
  CoglMatrix view;
  float viewport[4];
115

116
  ClutterFog fog;
117

118 119
  gchar *title;
  ClutterActor *key_focused_actor;
120

121
  GQueue *event_queue;
122

123
  ClutterStageHint stage_hints;
124

125
  GArray *paint_volume_stack;
126

127
  ClutterPlane current_clip_planes[4];
128

129
  GList *pending_queue_redraws;
130

131
  CoglFramebuffer *active_framebuffer;
132

133 134
  gint sync_delay;

135 136 137
  GTimer *fps_timer;
  gint32 timer_n_frames;

138 139
  ClutterIDPool *pick_id_pool;

140 141 142 143
#ifdef CLUTTER_ENABLE_DEBUG
  gulong redraw_count;
#endif /* CLUTTER_ENABLE_DEBUG */

144 145
  ClutterStageState current_state;

146 147 148
  gpointer paint_data;
  GDestroyNotify paint_notify;

149 150
  int update_freeze_count;

151
  guint relayout_pending       : 1;
152 153 154 155 156 157
  guint redraw_pending         : 1;
  guint is_fullscreen          : 1;
  guint is_cursor_visible      : 1;
  guint is_user_resizable      : 1;
  guint use_fog                : 1;
  guint throttle_motion_events : 1;
158
  guint use_alpha              : 1;
159
  guint min_size_changed       : 1;
160
  guint accept_focus           : 1;
161
  guint motion_events_enabled  : 1;
162
  guint has_custom_perspective : 1;
163
  guint stage_was_relayout     : 1;
164 165 166 167 168
};

enum
{
  PROP_0,
169

170
  PROP_COLOR,
171
  PROP_FULLSCREEN_SET,
172
  PROP_OFFSCREEN,
173 174
  PROP_CURSOR_VISIBLE,
  PROP_PERSPECTIVE,
175
  PROP_TITLE,
176
  PROP_USER_RESIZABLE,
177
  PROP_USE_FOG,
178
  PROP_FOG,
179
  PROP_USE_ALPHA,
180
  PROP_KEY_FOCUS,
181 182
  PROP_NO_CLEAR_HINT,
  PROP_ACCEPT_FOCUS
183 184 185 186
};

enum
{
187 188 189 190
  FULLSCREEN,
  UNFULLSCREEN,
  ACTIVATE,
  DEACTIVATE,
191
  DELETE_EVENT,
192
  AFTER_PAINT,
193
  PRESENTED,
194

195 196 197
  LAST_SIGNAL
};

198
static guint stage_signals[LAST_SIGNAL] = { 0, };
199

200 201
static const ClutterColor default_stage_color = { 255, 255, 255, 255 };

202
static void clutter_stage_maybe_finish_queue_redraws (ClutterStage *stage);
203
static void free_queue_redraw_entry (ClutterStageQueueRedrawEntry *entry);
204

205 206 207 208 209 210 211
static void clutter_container_iface_init (ClutterContainerIface *iface);

G_DEFINE_TYPE_WITH_CODE (ClutterStage, clutter_stage, CLUTTER_TYPE_GROUP,
                         G_ADD_PRIVATE (ClutterStage)
                         G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
                                                clutter_container_iface_init))

212
static void
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
clutter_stage_real_add (ClutterContainer *container,
                        ClutterActor     *child)
{
  clutter_actor_add_child (CLUTTER_ACTOR (container), child);
}

static void
clutter_stage_real_remove (ClutterContainer *container,
                           ClutterActor     *child)
{
  clutter_actor_remove_child (CLUTTER_ACTOR (container), child);
}

static void
clutter_stage_real_foreach (ClutterContainer *container,
                            ClutterCallback   callback,
                            gpointer          user_data)
{
231 232
  ClutterActorIter iter;
  ClutterActor *child;
233

234
  clutter_actor_iter_init (&iter, CLUTTER_ACTOR (container));
235

236 237
  while (clutter_actor_iter_next (&iter, &child))
    callback (child, user_data);
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
}

static void
clutter_stage_real_raise (ClutterContainer *container,
                          ClutterActor     *child,
                          ClutterActor     *sibling)
{
  clutter_actor_set_child_above_sibling (CLUTTER_ACTOR (container),
                                         child,
                                         sibling);
}

static void
clutter_stage_real_lower (ClutterContainer *container,
                          ClutterActor     *child,
                          ClutterActor     *sibling)
{
  clutter_actor_set_child_below_sibling (CLUTTER_ACTOR (container),
                                         child,
                                         sibling);
}

static void
clutter_stage_real_sort_depth_order (ClutterContainer *container)
{
}

static void
clutter_container_iface_init (ClutterContainerIface *iface)
{
  iface->add = clutter_stage_real_add;
  iface->remove = clutter_stage_real_remove;
  iface->foreach = clutter_stage_real_foreach;
  iface->raise = clutter_stage_real_raise;
  iface->lower = clutter_stage_real_lower;
  iface->sort_depth_order = clutter_stage_real_sort_depth_order;
}
275

276
static void
277
clutter_stage_get_preferred_width (ClutterActor *self,
278 279 280
                                   gfloat        for_height,
                                   gfloat       *min_width_p,
                                   gfloat       *natural_width_p)
281 282
{
  ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
283
  cairo_rectangle_int_t geom;
284

285 286
  if (priv->impl == NULL)
    return;
287

288 289 290 291 292 293 294
  _clutter_stage_window_get_geometry (priv->impl, &geom);

  if (min_width_p)
    *min_width_p = geom.width;

  if (natural_width_p)
    *natural_width_p = geom.width;
295 296 297
}

static void
298
clutter_stage_get_preferred_height (ClutterActor *self,
299 300 301
                                    gfloat        for_width,
                                    gfloat       *min_height_p,
                                    gfloat       *natural_height_p)
302 303
{
  ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
304
  cairo_rectangle_int_t geom;
305

306 307
  if (priv->impl == NULL)
    return;
308

309 310 311 312 313 314 315
  _clutter_stage_window_get_geometry (priv->impl, &geom);

  if (min_height_p)
    *min_height_p = geom.height;

  if (natural_height_p)
    *natural_height_p = geom.height;
316
}
317

318
static inline void
319 320 321 322
queue_full_redraw (ClutterStage *stage)
{
  ClutterStageWindow *stage_window;

323 324 325
  if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
    return;

326 327 328 329 330 331 332
  clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));

  /* Just calling clutter_actor_queue_redraw will typically only
   * redraw the bounding box of the children parented on the stage but
   * in this case we really need to ensure that the full stage is
   * redrawn so we add a NULL redraw clip to the stage window. */
  stage_window = _clutter_stage_get_window (stage);
333 334 335
  if (stage_window == NULL)
    return;

336 337 338
  _clutter_stage_window_add_redraw_clip (stage_window, NULL);
}

339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
static gboolean
stage_is_default (ClutterStage *stage)
{
  ClutterStageManager *stage_manager;
  ClutterStageWindow *impl;

  stage_manager = clutter_stage_manager_get_default ();
  if (stage != clutter_stage_manager_get_default_stage (stage_manager))
    return FALSE;

  impl = _clutter_stage_get_window (stage);
  if (impl != _clutter_stage_get_default_window ())
    return FALSE;

  return TRUE;
}

356
static void
357 358 359
clutter_stage_allocate (ClutterActor           *self,
                        const ClutterActorBox  *box,
                        ClutterAllocationFlags  flags)
360 361
{
  ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
362 363 364 365
  ClutterActorBox alloc = CLUTTER_ACTOR_BOX_INIT_ZERO;
  float old_width, old_height;
  float new_width, new_height;
  float width, height;
366
  cairo_rectangle_int_t window_size;
367

368 369
  if (priv->impl == NULL)
    return;
370

371
  /* our old allocation */
372 373
  clutter_actor_get_allocation_box (self, &alloc);
  clutter_actor_box_get_size (&alloc, &old_width, &old_height);
374

375
  /* the current allocation */
376
  clutter_actor_box_get_size (box, &width, &height);
377

378 379 380 381
  /* the current Stage implementation size */
  _clutter_stage_window_get_geometry (priv->impl, &window_size);

  /* if the stage is fixed size (for instance, it's using a EGL framebuffer)
382
   * then we simply ignore any allocation request and override the
383 384
   * allocation chain - because we cannot forcibly change the size of the
   * stage window.
385
   */
386
  if (!clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC))
387
    {
388
      CLUTTER_NOTE (LAYOUT,
389
                    "Following allocation to %.2fx%.2f (absolute origin %s)",
390
                    width, height,
391 392 393
                    (flags & CLUTTER_ABSOLUTE_ORIGIN_CHANGED)
                      ? "changed"
                      : "not changed");
394

395 396
      clutter_actor_set_allocation (self, box,
                                    flags | CLUTTER_DELEGATE_LAYOUT);
397

398
      /* Ensure the window is sized correctly */
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
      if (!priv->is_fullscreen)
        {
          if (priv->min_size_changed)
            {
              gfloat min_width, min_height;
              gboolean min_width_set, min_height_set;

              g_object_get (G_OBJECT (self),
                            "min-width", &min_width,
                            "min-width-set", &min_width_set,
                            "min-height", &min_height,
                            "min-height-set", &min_height_set,
                            NULL);

              if (!min_width_set)
                min_width = 1;
              if (!min_height_set)
                min_height = 1;

              if (width < min_width)
                width = min_width;
              if (height < min_height)
                height = min_height;

              priv->min_size_changed = FALSE;
            }

426 427
          if (window_size.width != CLUTTER_NEARBYINT (width) ||
              window_size.height != CLUTTER_NEARBYINT (height))
428
            {
429 430 431
              _clutter_stage_window_resize (priv->impl,
                                            CLUTTER_NEARBYINT (width),
                                            CLUTTER_NEARBYINT (height));
432
            }
433
        }
434
    }
435 436 437
  else
    {
      ClutterActorBox override = { 0, };
438

439
      /* override the passed allocation */
440 441
      override.x1 = 0;
      override.y1 = 0;
442 443
      override.x2 = window_size.width;
      override.y2 = window_size.height;
444

445
      CLUTTER_NOTE (LAYOUT,
446
                    "Overriding original allocation of %.2fx%.2f "
447
                    "with %.2fx%.2f (absolute origin %s)",
448
                    width, height,
449
                    override.x2, override.y2,
450 451 452
                    (flags & CLUTTER_ABSOLUTE_ORIGIN_CHANGED)
                      ? "changed"
                      : "not changed");
453

454
      /* and store the overridden allocation */
455 456
      clutter_actor_set_allocation (self, &override,
                                    flags | CLUTTER_DELEGATE_LAYOUT);
457
    }
458

459 460 461 462
  /* XXX: Until Cogl becomes fully responsible for backend windows
   * Clutter need to manually keep it informed of the current window
   * size. We do this after the allocation above so that the stage
   * window has a chance to update the window size based on the
463 464 465
   * allocation.
   */
  _clutter_stage_window_get_geometry (priv->impl, &window_size);
466

467 468
  cogl_onscreen_clutter_backend_set_size (window_size.width,
                                          window_size.height);
469

470
  /* reset the viewport if the allocation effectively changed */
471 472 473 474 475
  clutter_actor_get_allocation_box (self, &alloc);
  clutter_actor_box_get_size (&alloc, &new_width, &new_height);

  if (CLUTTER_NEARBYINT (old_width) != CLUTTER_NEARBYINT (new_width) ||
      CLUTTER_NEARBYINT (old_height) != CLUTTER_NEARBYINT (new_height))
476
    {
477 478 479
      int real_width = CLUTTER_NEARBYINT (new_width);
      int real_height = CLUTTER_NEARBYINT (new_height);

480
      _clutter_stage_set_viewport (CLUTTER_STAGE (self),
481
                                   0, 0,
482 483
                                   real_width,
                                   real_height);
484

485 486
      /* Note: we don't assume that set_viewport will queue a full redraw
       * since it may bail-out early if something preemptively set the
487 488
       * viewport before the stage was really allocated its new size.
       */
489 490
      queue_full_redraw (CLUTTER_STAGE (self));
    }
491 492
}

493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
typedef struct _Vector4
{
  float x, y, z, w;
} Vector4;

static void
_cogl_util_get_eye_planes_for_screen_poly (float *polygon,
                                           int n_vertices,
                                           float *viewport,
                                           const CoglMatrix *projection,
                                           const CoglMatrix *inverse_project,
                                           ClutterPlane *planes)
{
  float Wc;
  Vector4 *tmp_poly;
  ClutterPlane *plane;
  int i;
Neil Roberts's avatar
Neil Roberts committed
510 511
  float b[3];
  float c[3];
512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
  int count;

  tmp_poly = g_alloca (sizeof (Vector4) * n_vertices * 2);

#define DEPTH -50

  /* Determine W in clip-space (Wc) for a point (0, 0, DEPTH, 1)
   *
   * Note: the depth could be anything except 0.
   *
   * We will transform the polygon into clip coordinates using this
   * depth and then into eye coordinates. Our clip planes will be
   * defined by triangles that extend between points of the polygon at
   * DEPTH and corresponding points of the same polygon at DEPTH * 2.
   *
   * NB: Wc defines the position of the clip planes in clip
   * coordinates. Given a screen aligned cross section through the
   * frustum; coordinates range from [-Wc,Wc] left to right on the
   * x-axis and [Wc,-Wc] top to bottom on the y-axis.
   */
  Wc = DEPTH * projection->wz + projection->ww;

#define CLIP_X(X) ((((float)X - viewport[0]) * (2.0 / viewport[2])) - 1) * Wc
#define CLIP_Y(Y) ((((float)Y - viewport[1]) * (2.0 / viewport[3])) - 1) * -Wc

  for (i = 0; i < n_vertices; i++)
    {
      tmp_poly[i].x = CLIP_X (polygon[i * 2]);
      tmp_poly[i].y = CLIP_Y (polygon[i * 2 + 1]);
      tmp_poly[i].z = DEPTH;
      tmp_poly[i].w = Wc;
    }

  Wc = DEPTH * 2 * projection->wz + projection->ww;

  /* FIXME: technically we don't need to project all of the points
   * twice, it would be enough project every other point since
   * we can share points in this set to define the plane vectors. */
  for (i = 0; i < n_vertices; i++)
    {
      tmp_poly[n_vertices + i].x = CLIP_X (polygon[i * 2]);
      tmp_poly[n_vertices + i].y = CLIP_Y (polygon[i * 2 + 1]);
      tmp_poly[n_vertices + i].z = DEPTH * 2;
      tmp_poly[n_vertices + i].w = Wc;
    }

#undef CLIP_X
#undef CLIP_Y

  cogl_matrix_project_points (inverse_project,
                              4,
                              sizeof (Vector4),
                              tmp_poly,
                              sizeof (Vector4),
                              tmp_poly,
                              n_vertices * 2);

  /* XXX: It's quite ugly that we end up with these casts between
   * Vector4 types and CoglVector3s, it might be better if the
   * cogl_vector APIs just took pointers to floats.
   */

  count = n_vertices - 1;
  for (i = 0; i < count; i++)
    {
      plane = &planes[i];
Neil Roberts's avatar
Neil Roberts committed
578 579 580 581 582 583 584
      memcpy (plane->v0, tmp_poly + i, sizeof (float) * 3);
      memcpy (b, tmp_poly + n_vertices + i, sizeof (float) * 3);
      memcpy (c, tmp_poly + n_vertices + i + 1, sizeof (float) * 3);
      cogl_vector3_subtract (b, b, plane->v0);
      cogl_vector3_subtract (c, c, plane->v0);
      cogl_vector3_cross_product (plane->n, b, c);
      cogl_vector3_normalize (plane->n);
585 586 587
    }

  plane = &planes[n_vertices - 1];
Neil Roberts's avatar
Neil Roberts committed
588 589 590 591 592 593 594
  memcpy (plane->v0, tmp_poly + 0, sizeof (float) * 3);
  memcpy (b, tmp_poly + (2 * n_vertices - 1), sizeof (float) * 3);
  memcpy (c, tmp_poly + n_vertices, sizeof (float) * 3);
  cogl_vector3_subtract (b, b, plane->v0);
  cogl_vector3_subtract (c, c, plane->v0);
  cogl_vector3_cross_product (plane->n, b, c);
  cogl_vector3_normalize (plane->n);
595 596
}

597
static void
598 599
_clutter_stage_update_active_framebuffer (ClutterStage    *stage,
                                          CoglFramebuffer *framebuffer)
600 601 602 603 604 605 606 607
{
  ClutterStagePrivate *priv = stage->priv;

  /* We track the CoglFramebuffer that corresponds to the stage itself
   * so, for example, we can disable culling when rendering to an
   * offscreen framebuffer.
   */

608
  priv->active_framebuffer = framebuffer;
609 610
}

611
/* XXX: Instead of having a toplevel 2D clip region, it might be
612 613 614
 * better to have a clip volume within the view frustum. This could
 * allow us to avoid projecting actors into window coordinates to
 * be able to cull them.
615
 */
616 617 618 619
static void
clutter_stage_do_paint_view (ClutterStage                *stage,
                             ClutterStageView            *view,
                             const cairo_rectangle_int_t *clip)
620
{
621
  ClutterStagePrivate *priv = stage->priv;
622 623
  CoglFramebuffer *framebuffer = clutter_stage_view_get_framebuffer (view);
  cairo_rectangle_int_t view_layout;
624
  float clip_poly[8];
625
  float viewport[4];
626
  cairo_rectangle_int_t geom;
627 628

  _clutter_stage_window_get_geometry (priv->impl, &geom);
629

630 631 632 633
  viewport[0] = priv->viewport[0];
  viewport[1] = priv->viewport[1];
  viewport[2] = priv->viewport[2];
  viewport[3] = priv->viewport[3];
634

635
  if (!clip)
636
    {
637 638
      clutter_stage_view_get_layout (view, &view_layout);
      clip = &view_layout;
639 640
    }

641 642
  clip_poly[0] = MAX (clip->x, 0);
  clip_poly[1] = MAX (clip->y, 0);
643

644
  clip_poly[2] = MIN (clip->x + clip->width, geom.width);
645 646 647
  clip_poly[3] = clip_poly[1];

  clip_poly[4] = clip_poly[2];
648
  clip_poly[5] = MIN (clip->y + clip->height, geom.height);
649 650 651 652

  clip_poly[6] = clip_poly[0];
  clip_poly[7] = clip_poly[5];

653 654 655 656 657 658
  CLUTTER_NOTE (CLIPPING, "Setting stage clip too: "
                "x=%f, y=%f, width=%f, height=%f",
                clip_poly[0], clip_poly[1],
                clip_poly[2] - clip_poly[0],
                clip_poly[5] - clip_poly[1]);

659 660
  _cogl_util_get_eye_planes_for_screen_poly (clip_poly,
                                             4,
661
                                             viewport,
662 663 664 665
                                             &priv->projection,
                                             &priv->inverse_projection,
                                             priv->current_clip_planes);

666
  _clutter_stage_paint_volume_stack_free_all (stage);
667
  _clutter_stage_update_active_framebuffer (stage, framebuffer);
668
  clutter_actor_paint (CLUTTER_ACTOR (stage));
669
}
670

671 672 673 674 675 676 677 678 679 680 681 682 683 684
/* This provides a common point of entry for painting the scenegraph
 * for picking or painting...
 */
void
_clutter_stage_paint_view (ClutterStage                *stage,
                           ClutterStageView            *view,
                           const cairo_rectangle_int_t *clip)
{
  ClutterStagePrivate *priv = stage->priv;

  if (!priv->impl)
    return;

  clutter_stage_do_paint_view (stage, view, clip);
685
  g_signal_emit (stage, stage_signals[AFTER_PAINT], 0);
686 687
}

688 689 690 691 692 693 694 695 696 697 698 699 700 701 702
/* If we don't implement this here, we get the paint function
 * from the deprecated clutter-group class, which doesn't
 * respect the Z order as it uses our empty sort_depth_order.
 */
static void
clutter_stage_paint (ClutterActor *self)
{
  ClutterActorIter iter;
  ClutterActor *child;

  clutter_actor_iter_init (&iter, self);
  while (clutter_actor_iter_next (&iter, &child))
    clutter_actor_paint (child);
}

703 704 705 706
static void
clutter_stage_pick (ClutterActor       *self,
		    const ClutterColor *color)
{
707
  ClutterActorIter iter;
708 709
  ClutterActor *child;

710 711 712
  /* Note: we don't chain up to our parent as we don't want any geometry
   * emitted for the stage itself. The stage's pick id is effectively handled
   * by the call to cogl_clear done in clutter-main.c:_clutter_do_pick_async()
713
   */
714 715 716
  clutter_actor_iter_init (&iter, self);
  while (clutter_actor_iter_next (&iter, &child))
    clutter_actor_paint (child);
717 718
}

719 720 721 722 723 724 725 726 727
static gboolean
clutter_stage_get_paint_volume (ClutterActor *self,
                                ClutterPaintVolume *volume)
{
  /* Returning False effectively means Clutter has to assume it covers
   * everything... */
  return FALSE;
}

728 729 730 731
static void
clutter_stage_realize (ClutterActor *self)
{
  ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
732
  gboolean is_realized;
733

734
  g_assert (priv->impl != NULL);
735
  is_realized = _clutter_stage_window_realize (priv->impl);
736

737
  if (!is_realized)
738
    CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
739 740 741 742 743 744 745 746
}

static void
clutter_stage_unrealize (ClutterActor *self)
{
  ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;

  /* and then unrealize the implementation */
747
  g_assert (priv->impl != NULL);
748 749 750
  _clutter_stage_window_unrealize (priv->impl);

  CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
751 752 753
}

static void
754
clutter_stage_show_all (ClutterActor *self)
755
{
756
  ClutterActorIter iter;
757 758
  ClutterActor *child;

759 760 761
  /* we don't do a recursive show_all(), to maintain the old
   * invariants from ClutterGroup
   */
762 763 764
  clutter_actor_iter_init (&iter, self);
  while (clutter_actor_iter_next (&iter, &child))
    clutter_actor_show (child);
765

766 767 768 769 770 771 772 773
  clutter_actor_show (self);
}

static void
clutter_stage_show (ClutterActor *self)
{
  ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;

774 775
  CLUTTER_ACTOR_CLASS (clutter_stage_parent_class)->show (self);

776 777 778 779
  /* Possibly do an allocation run so that the stage will have the
     right size before we map it */
  _clutter_stage_maybe_relayout (self);

780
  g_assert (priv->impl != NULL);
781
  _clutter_stage_window_show (priv->impl, TRUE);
782 783 784
}

static void
785
clutter_stage_hide_all (ClutterActor *self)
786
{
787
  ClutterActorIter iter;
788
  ClutterActor *child;
789

790
  clutter_actor_hide (self);
791

792 793 794
  /* we don't do a recursive hide_all(), to maintain the old invariants
   * from ClutterGroup
   */
795 796
  clutter_actor_iter_init (&iter, self);
  while (clutter_actor_iter_next (&iter, &child))
797 798 799 800 801 802 803 804 805 806
    clutter_actor_hide (child);
}

static void
clutter_stage_hide (ClutterActor *self)
{
  ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;

  g_assert (priv->impl != NULL);
  _clutter_stage_window_hide (priv->impl);
807

808
  CLUTTER_ACTOR_CLASS (clutter_stage_parent_class)->hide (self);
809 810
}

811 812 813 814 815 816 817 818 819 820 821 822 823
static void
clutter_stage_emit_key_focus_event (ClutterStage *stage,
                                    gboolean      focus_in)
{
  ClutterStagePrivate *priv = stage->priv;

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

  if (focus_in)
    g_signal_emit_by_name (priv->key_focused_actor, "key-focus-in");
  else
    g_signal_emit_by_name (priv->key_focused_actor, "key-focus-out");
824 825

  g_object_notify (G_OBJECT (stage), "key-focus");
826 827 828 829 830 831 832 833 834 835 836 837 838 839
}

static void
clutter_stage_real_activate (ClutterStage *stage)
{
  clutter_stage_emit_key_focus_event (stage, TRUE);
}

static void
clutter_stage_real_deactivate (ClutterStage *stage)
{
  clutter_stage_emit_key_focus_event (stage, FALSE);
}

840 841 842 843
static void
clutter_stage_real_fullscreen (ClutterStage *stage)
{
  ClutterStagePrivate *priv = stage->priv;
844
  cairo_rectangle_int_t geom;
845 846 847 848 849 850 851 852 853
  ClutterActorBox box;

  /* we need to force an allocation here because the size
   * of the stage might have been changed by the backend
   *
   * this is a really bad solution to the issues caused by
   * the fact that fullscreening the stage on the X11 backends
   * is really an asynchronous operation
   */
854
  _clutter_stage_window_get_geometry (priv->impl, &geom);
855 856 857

  box.x1 = 0;
  box.y1 = 0;
858 859
  box.x2 = geom.width;
  box.y2 = geom.height;
860

861 862 863 864 865 866 867 868
  /* we need to blow the caching on the Stage size, given that
   * we're about to force an allocation, because if anything
   * ends up querying the size of the stage during the allocate()
   * call, like constraints or signal handlers, we'll get into an
   * inconsistent state: the stage will report the old cached size,
   * but the allocation will be updated anyway.
   */
  clutter_actor_set_size (CLUTTER_ACTOR (stage), -1.0, -1.0);
869 870 871
  clutter_actor_allocate (CLUTTER_ACTOR (stage),
                          &box,
                          CLUTTER_ALLOCATION_NONE);
872 873
}

874 875
void
_clutter_stage_queue_event (ClutterStage *stage,
876 877
                            ClutterEvent *event,
                            gboolean      copy_event)
878
{
879
  ClutterStagePrivate *priv;
880
  gboolean first_event;
881
  ClutterInputDevice *device;
882

883
  g_return_if_fail (CLUTTER_IS_STAGE (stage));
884

885 886
  priv = stage->priv;

887 888
  first_event = priv->event_queue->length == 0;

889 890 891 892
  if (copy_event)
    event = clutter_event_copy (event);

  g_queue_push_tail (priv->event_queue, event);
893 894 895 896 897

  if (first_event)
    {
      ClutterMasterClock *master_clock = _clutter_master_clock_get_default ();
      _clutter_master_clock_start_running (master_clock);
898
      _clutter_stage_schedule_update (stage);
899
    }
900 901 902 903 904 905

  /* if needed, update the state of the input device of the event.
   * we do it here to avoid calling the same code from every backend
   * event processing function
   */
  device = clutter_event_get_device (event);
906 907 908
  if (device != NULL &&
      event->type != CLUTTER_PROXIMITY_IN &&
      event->type != CLUTTER_PROXIMITY_OUT)
909 910
    {
      ClutterModifierType event_state = clutter_event_get_state (event);
911
      ClutterEventSequence *sequence = clutter_event_get_event_sequence (event);
912 913 914 915 916
      guint32 event_time = clutter_event_get_time (event);
      gfloat event_x, event_y;

      clutter_event_get_coords (event, &event_x, &event_y);

917
      _clutter_input_device_set_coords (device, sequence, event_x, event_y, stage);
918 919 920
      _clutter_input_device_set_state (device, event_state);
      _clutter_input_device_set_time (device, event_time);
    }
921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938
}

gboolean
_clutter_stage_has_queued_events (ClutterStage *stage)
{
  ClutterStagePrivate *priv;

  g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);

  priv = stage->priv;

  return priv->event_queue->length > 0;
}

void
_clutter_stage_process_queued_events (ClutterStage *stage)
{
  ClutterStagePrivate *priv;
939
  GList *events, *l;
940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957

  g_return_if_fail (CLUTTER_IS_STAGE (stage));

  priv = stage->priv;

  if (priv->event_queue->length == 0)
    return;

  /* In case the stage gets destroyed during event processing */
  g_object_ref (stage);

  /* Steal events before starting processing to avoid reentrancy
   * issues */
  events = priv->event_queue->head;
  priv->event_queue->head =  NULL;
  priv->event_queue->tail = NULL;
  priv->event_queue->length = 0;

958
  for (l = events; l != NULL; l = l->next)
959 960 961
    {
      ClutterEvent *event;
      ClutterEvent *next_event;
962 963
      ClutterInputDevice *device;
      ClutterInputDevice *next_device;
964
      ClutterInputDeviceType device_type;
965
      gboolean check_device = FALSE;
966 967 968 969

      event = l->data;
      next_event = l->next ? l->next->data : NULL;

970 971 972 973 974 975 976 977 978 979
      device = clutter_event_get_device (event);

      if (next_event != NULL)
        next_device = clutter_event_get_device (next_event);
      else
        next_device = NULL;

      if (device != NULL && next_device != NULL)
        check_device = TRUE;

980 981 982 983 984 985 986 987 988 989
      device_type = clutter_input_device_get_device_type (device);

      /* Skip consecutive motion events coming from the same device,
       * except those of tablet tools, since users of these events
       * want no precision loss.
       */
      if (priv->throttle_motion_events && next_event != NULL &&
          device_type != CLUTTER_TABLET_DEVICE &&
          device_type != CLUTTER_PEN_DEVICE &&
          device_type != CLUTTER_ERASER_DEVICE)
990 991 992 993 994 995 996 997 998 999
        {
          if (event->type == CLUTTER_MOTION &&
              (next_event->type == CLUTTER_MOTION ||
               next_event->type == CLUTTER_LEAVE) &&
              (!check_device || (device == next_device)))
            {
              CLUTTER_NOTE (EVENT,
                            "Omitting motion event at %d, %d",
                            (int) event->motion.x,
                            (int) event->motion.y);
1000 1001 1002 1003 1004 1005 1006 1007 1008 1009

              if (next_event->type == CLUTTER_MOTION)
                {
                  ClutterDeviceManager *device_manager =
                    clutter_device_manager_get_default ();

                  _clutter_device_manager_compress_motion (device_manager,
                                                           next_event, event);
                }

1010 1011 1012
              goto next_event;
            }
          else if (event->type == CLUTTER_TOUCH_UPDATE &&
1013 1014
                   next_event->type == CLUTTER_TOUCH_UPDATE &&
                   event->touch.sequence == next_event->touch.sequence &&
1015 1016 1017 1018 1019 1020 1021 1022 1023
                   (!check_device || (device == next_device)))
            {
              CLUTTER_NOTE (EVENT,
                            "Omitting touch update event at %d, %d",
                            (int) event->touch.x,
                            (int) event->touch.y);
              goto next_event;
            }
        }
1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035

      _clutter_process_event (event);

    next_event:
      clutter_event_free (event);
    }

  g_list_free (events);

  g_object_unref (stage);
}

1036 1037 1038 1039 1040 1041
/**
 * _clutter_stage_needs_update:
 * @stage: A #ClutterStage
 *
 * Determines if _clutter_stage_do_update() needs to be called.
 *
1042
 * Return value: %TRUE if the stage need layout or painting
1043 1044 1045
 */
gboolean
_clutter_stage_needs_update (ClutterStage *stage)
1046
{
1047
  ClutterStagePrivate *priv;
1048

1049 1050 1051 1052
  g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);

  priv = stage->priv;

1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069
  return priv->relayout_pending || priv->redraw_pending;
}

void
_clutter_stage_maybe_relayout (ClutterActor *actor)
{
  ClutterStage *stage = CLUTTER_STAGE (actor);
  ClutterStagePrivate *priv = stage->priv;
  gfloat natural_width, natural_height;
  ClutterActorBox box = { 0, };

  if (!priv->relayout_pending)
    return;

  /* avoid reentrancy */
  if (!CLUTTER_ACTOR_IN_RELAYOUT (stage))
    {
1070
      priv->relayout_pending = FALSE;
1071
      priv->stage_was_relayout = TRUE;
1072

1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095
      CLUTTER_NOTE (ACTOR, "Recomputing layout");

      CLUTTER_SET_PRIVATE_FLAGS (stage, CLUTTER_IN_RELAYOUT);

      natural_width = natural_height = 0;
      clutter_actor_get_preferred_size (CLUTTER_ACTOR (stage),
                                        NULL, NULL,
                                        &natural_width, &natural_height);

      box.x1 = 0;
      box.y1 = 0;
      box.x2 = natural_width;
      box.y2 = natural_height;

      CLUTTER_NOTE (ACTOR, "Allocating (0, 0 - %d, %d) for the stage",
                    (int) natural_width,
                    (int) natural_height);

      clutter_actor_allocate (CLUTTER_ACTOR (stage),
                              &box, CLUTTER_ALLOCATION_NONE);

      CLUTTER_UNSET_PRIVATE_FLAGS (stage, CLUTTER_IN_RELAYOUT);
    }
1096 1097
}

1098 1099 1100 1101 1102 1103
static void
clutter_stage_do_redraw (ClutterStage *stage)
{
  ClutterActor *actor = CLUTTER_ACTOR (stage);
  ClutterStagePrivate *priv = stage->priv;

1104 1105 1106 1107 1108 1109
  if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
    return;

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

1110 1111 1112
  CLUTTER_NOTE (PAINT, "Redraw started for stage '%s'[%p]",
                _clutter_actor_get_debug_name (actor),
                stage);
1113

1114
  if (_clutter_context_get_show_fps ())
1115 1116 1117 1118 1119
    {
      if (priv->fps_timer == NULL)
        priv->fps_timer = g_timer_new ();
    }

1120 1121
  _clutter_stage_window_redraw (priv->impl);

1122
  if (_clutter_context_get_show_fps ())
1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136
    {
      priv->timer_n_frames += 1;

      if (g_timer_elapsed (priv->fps_timer, NULL) >= 1.0)
        {
          g_print ("*** FPS for %s: %i ***\n",
                   _clutter_actor_get_debug_name (actor),
                   priv->timer_n_frames);

          priv->timer_n_frames = 0;
          g_timer_start (priv->fps_timer);
        }
    }

1137 1138 1139
  CLUTTER_NOTE (PAINT, "Redraw finished for stage '%s'[%p]",
                _clutter_actor_get_debug_name (actor),
                stage);
1140 1141
}

1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193
static GSList *
_clutter_stage_check_updated_pointers (ClutterStage *stage)
{
  ClutterStagePrivate *priv = stage->priv;
  ClutterDeviceManager *device_manager;
  GSList *updating = NULL;
  const GSList *devices;
  cairo_rectangle_int_t clip;
  ClutterPoint point;
  gboolean has_clip;

  has_clip = _clutter_stage_window_get_redraw_clip_bounds (priv->impl, &clip);

  device_manager = clutter_device_manager_get_default ();
  devices = clutter_device_manager_peek_devices (device_manager);

  for (; devices != NULL; devices = devices->next)
    {
      ClutterInputDevice *dev = devices->data;

      if (clutter_input_device_get_device_mode (dev) !=
          CLUTTER_INPUT_MODE_MASTER)
        continue;

      switch (clutter_input_device_get_device_type (dev))
        {
        case CLUTTER_POINTER_DEVICE:
        case CLUTTER_TABLET_DEVICE:
        case CLUTTER_PEN_DEVICE:
        case CLUTTER_ERASER_DEVICE:
        case CLUTTER_CURSOR_DEVICE:
          if (!clutter_input_device_get_coords (dev, NULL, &point))
            continue;

          if (!has_clip ||
              (point.x >= clip.x && point.x < clip.x + clip.width &&
               point.y >= clip.y && point.y < clip.y + clip.height))
            updating = g_slist_prepend (updating, dev);
          break;
        default:
          /* Any other devices don't need checking, either because they
           * don't have x/y coordinates, or because they're implicitly
           * grabbed on an actor by default as it's the case of
           * touch(screens).
           */
          break;
        }
    }

  return updating;
}

1194 1195 1196 1197 1198
/**
 * _clutter_stage_do_update:
 * @stage: A #ClutterStage
 *
 * Handles per-frame layout and repaint for the stage.
1199 1200
 *
 * Return value: %TRUE if the stage was updated
1201
 */
1202
gboolean
1203 1204
_clutter_stage_do_update (ClutterStage *stage)
{
1205
  ClutterStagePrivate *priv = stage->priv;
1206 1207 1208 1209
  gboolean stage_was_relayout = priv->stage_was_relayout;
  GSList *pointers = NULL;

  priv->stage_was_relayout = FALSE;
1210

1211 1212 1213 1214 1215
  /* if the stage is being destroyed, or if the destruction already
   * happened and we don't have an StageWindow any more, then we
   * should bail out
   */
  if (CLUTTER_ACTOR_IN_DESTRUCTION (stage) || priv->impl == NULL)
1216 1217
    return FALSE;

1218 1219 1220
  if (!CLUTTER_ACTOR_IS_REALIZED (stage))
    return FALSE;

1221 1222 1223
  /* NB: We need to ensure we have an up to date layout *before* we
   * check or clear the pending redraws flag since a relayout may
   * queue a redraw.
1224 1225 1226
   */
  _clutter_stage_maybe_relayout (CLUTTER_ACTOR (stage));

1227 1228 1229
  if (!priv->redraw_pending)
    return FALSE;

1230 1231 1232
  if (stage_was_relayout)
    pointers = _clutter_stage_check_updated_pointers (stage);

1233
  clutter_stage_maybe_finish_queue_redraws (stage);
1234

1235
  clutter_stage_do_redraw (stage);
1236

1237
  /* reset the guard, so that new redraws are possible */
1238
  priv->redraw_pending = FALSE;
1239

1240 1241
#ifdef CLUTTER_ENABLE_DEBUG
  if (priv->redraw_count > 0)
1242
    {
1243
      CLUTTER_NOTE (SCHEDULER, "Queued %lu redraws during the last cycle",
1244
                    priv->redraw_count);
1245

1246
      priv->redraw_count = 0;
1247
    }
1248
#endif /* CLUTTER_ENABLE_DEBUG */
1249

1250 1251 1252 1253 1254 1255
  while (pointers)
    {
      _clutter_input_device_update (pointers->data, NULL, TRUE);
      pointers = g_slist_delete_link (pointers, pointers);
    }

1256
  return TRUE;
1257 1258
}

1259 1260 1261 1262 1263 1264 1265
static void
clutter_stage_real_queue_relayout (ClutterActor *self)
{
  ClutterStage *stage = CLUTTER_STAGE (self);
  ClutterStagePrivate *priv = stage->priv;
  ClutterActorClass *parent_class;

1266 1267 1268 1269 1270
  if (!priv->relayout_pending)
    {
      _clutter_stage_schedule_update (stage);
      priv->relayout_pending = TRUE;
    }
1271 1272 1273 1274 1275 1276

  /* chain up */
  parent_class = CLUTTER_ACTOR_CLASS (clutter_stage_parent_class);
  parent_class->queue_relayout (self);
}

1277
static void
1278 1279
clutter_stage_real_queue_redraw (ClutterActor *actor,
                                 ClutterActor *leaf)
1280
{
1281
  ClutterStage *stage = CLUTTER_STAGE (actor);
1282
  ClutterStageWindow *stage_window;
1283
  ClutterPaintVolume *redraw_clip;
1284
  ClutterActorBox bounding_box;
1285
  ClutterActorBox intersection_box;
1286
  cairo_rectangle_int_t geom, stage_clip;
1287

1288 1289 1290
  if (CLUTTER_ACTOR_IN_DESTRUCTION (actor))
    return;

1291 1292
  /* If the backend can't do anything with redraw clips (e.g. it already knows
   * it needs to redraw everything anyway) then don't spend time transforming
1293
   * any clip volume into stage coordinates... */
1294
  stage_window = _clutter_stage_get_window (stage);
1295 1296 1297
  if (stage_window == NULL)
    return;

1298
  if (_clutter_stage_window_ignoring_redraw_clips (stage_window))
1299 1300 1301 1302
    {
      _clutter_stage_window_add_redraw_clip (stage_window, NULL);
      return;
    }
1303

1304 1305
  /* Convert the clip volume into stage coordinates and then into an
   * axis aligned stage coordinates bounding box...
1306
   */
1307 1308
  redraw_clip = _clutter_actor_get_queue_redraw_clip (leaf);
  if (redraw_clip == NULL)
1309 1310 1311 1312 1313
    {
      _clutter_stage_window_add_redraw_clip (stage_window, NULL);
      return;
    }

1314 1315 1316
  if (redraw_clip->is_empty)
    return;

1317 1318 1319
  _clutter_paint_volume_get_stage_paint_box (redraw_clip,
                                             stage,
                                             &bounding_box);
1320

1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332
  _clutter_stage_window_get_geometry (stage_window, &geom);

  intersection_box.x1 = MAX (bounding_box.x1, 0);
  intersection_box.y1 = MAX (bounding_box.y1, 0);
  intersection_box.x2 = MIN (bounding_box.x2, geom.width);
  intersection_box.y2 = MIN (bounding_box.y2, geom.height);

  /* There is no need to track degenerate/empty redraw clips */
  if (intersection_box.x2 <= intersection_box.x1 ||
      intersection_box.y2 <= intersection_box.y1)
    return;

1333 1334
  /* when converting to integer coordinates make sure we round the edges of the
   * clip rectangle outwards... */
1335 1336 1337 1338
  stage_clip.x = intersection_box.x1;
  stage_clip.y = intersection_box.y1;
  stage_clip.width = intersection_box.x2 - stage_clip.x;
  stage_clip.height = intersection_box.y2 - stage_clip.y;
1339 1340 1341 1342 1343 1344 1345 1346

  _clutter_stage_window_add_redraw_clip (stage_window, &stage_clip);
}

gboolean
_clutter_stage_has_full_redraw_queued (ClutterStage *stage)
{
  ClutterStageWindow *stage_window = _clutter_stage_get_window (stage);
1347

1348
  if (CLUTTER_ACTOR_IN_DESTRUCTION (stage) || stage_window == NULL)
1349
    return FALSE;
1350 1351 1352 1353 1354 1355

  if (stage->priv->redraw_pending &&
      !_clutter_stage_window_has_redraw_clips (stage_window))
    return TRUE;
  else
    return FALSE;
1356 1357
}

1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388
/**
 * clutter_stage_get_redraw_clip_bounds:
 * @stage: A #ClutterStage
 * @clip: (out caller-allocates): Return location for the clip bounds
 *
 * Gets the bounds of the current redraw for @stage in stage pixel
 * coordinates. E.g., if only a single actor has queued a redraw then
 * Clutter may redraw the stage with a clip so that it doesn't have to
 * paint every pixel in the stage. This function would then return the
 * bounds of that clip. An application can use this information to
 * avoid some extra work if it knows that some regions of the stage
 * aren't going to be painted. This should only be called while the
 * stage is being painted. If there is no current redraw clip then
 * this function will set @clip to the full extents of the stage.
 *
 * Since: 1.8
 */
void
clutter_stage_get_redraw_clip_bounds (ClutterStage          *stage,
                                      cairo_rectangle_int_t *clip)
{
  ClutterStagePrivate *priv;

  g_return_if_fail (CLUTTER_IS_STAGE (stage));
  g_return_if_fail (clip != NULL);

  priv = stage->priv;

  if (!_clutter_stage_window_get_redraw_clip_bounds (priv->impl, clip))
    {
      /* Set clip to the full extents of the stage */
1389
      _clutter_stage_window_get_geometry (priv->impl, clip);
1390 1391 1392
    }
}

1393
static void
1394 1395 1396 1397 1398 1399
read_pixels_to_file (CoglFramebuffer *fb,
                     char            *filename_stem,
                     int              x,
                     int              y,
                     int              width,
                     int              height)
1400
{
1401
  guint8 *data;
1402
  cairo_surface_t *surface;
1403
  static int read_count = 0;
1404 1405 1406
  char *filename = g_strdup_printf ("%s-%05d.png",
                                    filename_stem,
                                    read_count);
1407 1408

  data = g_malloc (4 * width * height);
1409 1410 1411 1412
  cogl_framebuffer_read_pixels (fb,
                                x, y, width, height,
                                CLUTTER_CAIRO_FORMAT_ARGB32,
                                data);
1413

1414 1415 1416
  surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_RGB24,
                                                 width, height,
                                                 width * 4);
1417

1418 1419 1420 1421 1422 1423 1424
  cairo_surface_write_to_png (surface, filename);
  cairo_surface_destroy (surface);

  g_free (data);
  g_free (filename);

  read_count++;
1425 1426
}

1427 1428 1429 1430 1431 1432
static ClutterActor *
_clutter_stage_do_pick_on_view (ClutterStage     *stage,
                                gint              x,
                                gint              y,
                                ClutterPickMode   mode,
                                ClutterStageView *view)
1433
{
1434 1435
  ClutterActor *actor = CLUTTER_ACTOR (stage);
  ClutterStagePrivate *priv = stage->priv;
1436 1437
  CoglFramebuffer *fb = clutter_stage_view_get_framebuffer (view);
  cairo_rectangle_int_t view_layout;
1438 1439 1440
  ClutterMainContext *context;
  guchar pixel[4] = { 0xff, 0xff, 0xff, 0xff };
  CoglColor stage_pick_id;
1441
  gboolean dither_enabled_save;
1442
  ClutterActor *retval;
1443 1444
  gint dirty_x;
  gint dirty_y;
1445 1446
  gint read_x;
  gint read_y;
1447
  float fb_width, fb_height;
1448
  float fb_scale;
1449 1450
  float viewport_offset_x;
  float viewport_offset_y;
1451

1452 1453 1454
  priv = stage->priv;

  context = _clutter_context_get_default ();
1455
  fb_scale = clutter_stage_view_get_scale (view);
1456
  clutter_stage_view_get_layout (view, &view_layout);
1457

1458 1459
  fb_width = view_layout.width * fb_scale;
  fb_height = view_layout.height * fb_scale;
1460
  cogl_push_framebuffer (fb);
1461 1462

  /* needed for when a context switch happens */
1463 1464 1465 1466 1467 1468
  _clutter_stage_maybe_setup_viewport (stage, view);

  /* FIXME: For some reason leaving the cogl clip stack empty causes the
   * picking to not work at all, so setting it the whole framebuffer content
   * for now. */
  cogl_framebuffer_push_scissor_clip (fb, 0, 0,
1469 1470
                                      view_layout.width * fb_scale,
                                      view_layout.height * fb_scale);
1471

1472
  _clutter_stage_window_get_dirty_pixel (priv->impl, view, &dirty_x, &dirty_y);
1473

1474
  if (G_LIKELY (!(clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)))
1475 1476
    {
      CLUTTER_NOTE (PICK, "Pushing pick scissor clip x: %d, y: %d, 1x1",
1477 1478
                    (int) dirty_x * fb_scale,
                    (int) dirty_y * fb_scale);
Jonas Ådahl's avatar