1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Authored By Matthew Allum  <mallum@openedhand.com>
7  *
8  * Copyright (C) 2006 OpenedHand
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * 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  * SECTION:clutter-stage
26  * @short_description: Top level visual element to which actors are placed.
27  *
28  * #ClutterStage is a top level 'window' on which child actors are placed
29  * and manipulated.
30  *
31  * Backends might provide support for multiple stages. The support for this
32  * feature can be checked at run-time using the clutter_feature_available()
33  * function and the %CLUTTER_FEATURE_STAGE_MULTIPLE flag. If the backend used
34  * supports multiple stages, new #ClutterStage instances can be created
35  * using clutter_stage_new(). These stages must be managed by the developer
36  * using clutter_actor_destroy(), which will take care of destroying all the
37  * actors contained inside them.
38  *
39  * #ClutterStage is a proxy actor, wrapping the backend-specific implementation
40  * (a #StageWindow) of the windowing system. It is possible to subclass
41  * #ClutterStage, as long as every overridden virtual function chains up to the
42  * parent class corresponding function.
43  */
44 
45 #include "clutter-build-config.h"
46 
47 #include <math.h>
48 #include <cairo-gobject.h>
49 
50 #define CLUTTER_DISABLE_DEPRECATION_WARNINGS
51 
52 #include "clutter-stage.h"
53 #include "deprecated/clutter-container.h"
54 
55 #include "clutter-actor-private.h"
56 #include "clutter-backend-private.h"
57 #include "clutter-cairo.h"
58 #include "clutter-container.h"
59 #include "clutter-debug.h"
60 #include "clutter-enum-types.h"
61 #include "clutter-event-private.h"
62 #include "clutter-frame-clock.h"
63 #include "clutter-id-pool.h"
64 #include "clutter-input-device-private.h"
65 #include "clutter-main.h"
66 #include "clutter-marshal.h"
67 #include "clutter-mutter.h"
68 #include "clutter-paint-context-private.h"
69 #include "clutter-paint-volume-private.h"
70 #include "clutter-pick-context-private.h"
71 #include "clutter-private.h"
72 #include "clutter-stage-manager-private.h"
73 #include "clutter-stage-private.h"
74 #include "clutter-stage-view-private.h"
75 #include "clutter-private.h"
76 
77 #include "cogl/cogl.h"
78 
79 #define MAX_FRUSTA 64
80 
81 typedef struct _QueueRedrawEntry
82 {
83   gboolean has_clip;
84   ClutterPaintVolume clip;
85 } QueueRedrawEntry;
86 
87 typedef struct _PickRecord
88 {
89   graphene_point_t vertex[4];
90   ClutterActor *actor;
91   int clip_stack_top;
92 } PickRecord;
93 
94 typedef struct _PickClipRecord
95 {
96   int prev;
97   graphene_point_t vertex[4];
98 } PickClipRecord;
99 
100 typedef struct _PointerDeviceEntry
101 {
102   ClutterStage *stage;
103   ClutterInputDevice *device;
104   ClutterEventSequence *sequence;
105   graphene_point_t coords;
106   ClutterActor *current_actor;
107 } PointerDeviceEntry;
108 
109 struct _ClutterStagePrivate
110 {
111   /* the stage implementation */
112   ClutterStageWindow *impl;
113 
114   ClutterPerspective perspective;
115   graphene_matrix_t projection;
116   graphene_matrix_t inverse_projection;
117   graphene_matrix_t view;
118   float viewport[4];
119 
120   gchar *title;
121   ClutterActor *key_focused_actor;
122 
123   GQueue *event_queue;
124 
125   GArray *paint_volume_stack;
126 
127   GSList *pending_relayouts;
128   GHashTable *pending_queue_redraws;
129 
130   int update_freeze_count;
131 
132   gboolean needs_update_devices;
133   gboolean pending_finish_queue_redraws;
134 
135   GHashTable *pointer_devices;
136   GHashTable *touch_sequences;
137 
138   guint throttle_motion_events : 1;
139   guint min_size_changed       : 1;
140   guint motion_events_enabled  : 1;
141   guint actor_needs_immediate_relayout : 1;
142 };
143 
144 enum
145 {
146   PROP_0,
147 
148   PROP_PERSPECTIVE,
149   PROP_TITLE,
150   PROP_KEY_FOCUS,
151   PROP_LAST
152 };
153 
154 static GParamSpec *obj_props[PROP_LAST] = { NULL, };
155 
156 enum
157 {
158   ACTIVATE,
159   DEACTIVATE,
160   DELETE_EVENT,
161   BEFORE_UPDATE,
162   BEFORE_PAINT,
163   AFTER_PAINT,
164   AFTER_UPDATE,
165   PAINT_VIEW,
166   PRESENTED,
167   GL_VIDEO_MEMORY_PURGED,
168 
169   LAST_SIGNAL
170 };
171 
172 static guint stage_signals[LAST_SIGNAL] = { 0, };
173 
174 static const ClutterColor default_stage_color = { 255, 255, 255, 255 };
175 
176 static void free_queue_redraw_entry (QueueRedrawEntry *entry);
177 static void free_pointer_device_entry (PointerDeviceEntry *entry);
178 static void clutter_stage_update_view_perspective (ClutterStage *stage);
179 static void clutter_stage_set_viewport (ClutterStage *stage,
180                                         float         width,
181                                         float         height);
182 
G_DEFINE_TYPE_WITH_PRIVATE(ClutterStage,clutter_stage,CLUTTER_TYPE_ACTOR)183 G_DEFINE_TYPE_WITH_PRIVATE (ClutterStage, clutter_stage, CLUTTER_TYPE_ACTOR)
184 
185 static void
186 clutter_stage_get_preferred_width (ClutterActor *self,
187                                    gfloat        for_height,
188                                    gfloat       *min_width_p,
189                                    gfloat       *natural_width_p)
190 {
191   ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
192   cairo_rectangle_int_t geom;
193 
194   if (priv->impl == NULL)
195     return;
196 
197   _clutter_stage_window_get_geometry (priv->impl, &geom);
198 
199   if (min_width_p)
200     *min_width_p = geom.width;
201 
202   if (natural_width_p)
203     *natural_width_p = geom.width;
204 }
205 
206 static void
clutter_stage_get_preferred_height(ClutterActor * self,gfloat for_width,gfloat * min_height_p,gfloat * natural_height_p)207 clutter_stage_get_preferred_height (ClutterActor *self,
208                                     gfloat        for_width,
209                                     gfloat       *min_height_p,
210                                     gfloat       *natural_height_p)
211 {
212   ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
213   cairo_rectangle_int_t geom;
214 
215   if (priv->impl == NULL)
216     return;
217 
218   _clutter_stage_window_get_geometry (priv->impl, &geom);
219 
220   if (min_height_p)
221     *min_height_p = geom.height;
222 
223   if (natural_height_p)
224     *natural_height_p = geom.height;
225 }
226 
227 static void
clutter_stage_add_redraw_clip(ClutterStage * stage,cairo_rectangle_int_t * clip)228 clutter_stage_add_redraw_clip (ClutterStage          *stage,
229                                cairo_rectangle_int_t *clip)
230 {
231   GList *l;
232 
233   for (l = clutter_stage_peek_stage_views (stage); l; l = l->next)
234     {
235       ClutterStageView *view = l->data;
236 
237       if (!clip)
238         {
239           clutter_stage_view_add_redraw_clip (view, NULL);
240         }
241       else
242         {
243           cairo_rectangle_int_t view_layout;
244           cairo_rectangle_int_t intersection;
245 
246           clutter_stage_view_get_layout (view, &view_layout);
247           if (_clutter_util_rectangle_intersection (&view_layout, clip,
248                                                     &intersection))
249             clutter_stage_view_add_redraw_clip (view, &intersection);
250         }
251     }
252 }
253 
254 static inline void
queue_full_redraw(ClutterStage * stage)255 queue_full_redraw (ClutterStage *stage)
256 {
257   ClutterStageWindow *stage_window;
258 
259   if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
260     return;
261 
262   clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
263 
264   /* Just calling clutter_actor_queue_redraw will typically only
265    * redraw the bounding box of the children parented on the stage but
266    * in this case we really need to ensure that the full stage is
267    * redrawn so we add a NULL redraw clip to the stage window. */
268   stage_window = _clutter_stage_get_window (stage);
269   if (stage_window == NULL)
270     return;
271 
272   clutter_stage_add_redraw_clip (stage, NULL);
273 }
274 
275 static void
clutter_stage_allocate(ClutterActor * self,const ClutterActorBox * box)276 clutter_stage_allocate (ClutterActor           *self,
277                         const ClutterActorBox  *box)
278 {
279   ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
280   ClutterActorBox alloc = CLUTTER_ACTOR_BOX_INIT_ZERO;
281   float new_width, new_height;
282   float width, height;
283   cairo_rectangle_int_t window_size;
284   ClutterLayoutManager *layout_manager = clutter_actor_get_layout_manager (self);
285 
286   if (priv->impl == NULL)
287     return;
288 
289   /* the current allocation */
290   clutter_actor_box_get_size (box, &width, &height);
291 
292   /* the current Stage implementation size */
293   _clutter_stage_window_get_geometry (priv->impl, &window_size);
294 
295   /* if the stage is fixed size (for instance, it's using a EGL framebuffer)
296    * then we simply ignore any allocation request and override the
297    * allocation chain - because we cannot forcibly change the size of the
298    * stage window.
299    */
300   if (!clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC))
301     {
302       ClutterActorBox children_box;
303 
304       children_box.x1 = children_box.y1 = 0.f;
305       children_box.x2 = box->x2 - box->x1;
306       children_box.y2 = box->y2 - box->y1;
307 
308       CLUTTER_NOTE (LAYOUT,
309                     "Following allocation to %.2fx%.2f",
310                     width, height);
311 
312       clutter_actor_set_allocation (self, box);
313 
314       clutter_layout_manager_allocate (layout_manager,
315                                        CLUTTER_CONTAINER (self),
316                                        &children_box);
317 
318       /* Ensure the window is sized correctly */
319       if (priv->min_size_changed)
320         {
321           gfloat min_width, min_height;
322           gboolean min_width_set, min_height_set;
323 
324           g_object_get (G_OBJECT (self),
325                         "min-width", &min_width,
326                         "min-width-set", &min_width_set,
327                         "min-height", &min_height,
328                         "min-height-set", &min_height_set,
329                         NULL);
330 
331           if (!min_width_set)
332             min_width = 1;
333           if (!min_height_set)
334             min_height = 1;
335 
336           if (width < min_width)
337             width = min_width;
338           if (height < min_height)
339             height = min_height;
340 
341           priv->min_size_changed = FALSE;
342         }
343 
344       if (window_size.width != CLUTTER_NEARBYINT (width) ||
345           window_size.height != CLUTTER_NEARBYINT (height))
346         {
347           _clutter_stage_window_resize (priv->impl,
348                                         CLUTTER_NEARBYINT (width),
349                                         CLUTTER_NEARBYINT (height));
350         }
351     }
352   else
353     {
354       ClutterActorBox override = { 0, };
355 
356       /* override the passed allocation */
357       override.x1 = 0;
358       override.y1 = 0;
359       override.x2 = window_size.width;
360       override.y2 = window_size.height;
361 
362       CLUTTER_NOTE (LAYOUT,
363                     "Overriding original allocation of %.2fx%.2f "
364                     "with %.2fx%.2f",
365                     width, height,
366                     override.x2, override.y2);
367 
368       /* and store the overridden allocation */
369       clutter_actor_set_allocation (self, &override);
370 
371       clutter_layout_manager_allocate (layout_manager,
372                                        CLUTTER_CONTAINER (self),
373                                        &override);
374     }
375 
376   /* set the viewport to the new allocation */
377   clutter_actor_get_allocation_box (self, &alloc);
378   clutter_actor_box_get_size (&alloc, &new_width, &new_height);
379 
380   clutter_stage_set_viewport (CLUTTER_STAGE (self), new_width, new_height);
381 }
382 
383 static void
setup_clip_frustum(ClutterStage * stage,const cairo_rectangle_int_t * clip,graphene_frustum_t * frustum)384 setup_clip_frustum (ClutterStage                *stage,
385                     const cairo_rectangle_int_t *clip,
386                     graphene_frustum_t          *frustum)
387 {
388   ClutterStagePrivate *priv = stage->priv;
389   cairo_rectangle_int_t geom;
390   graphene_point3d_t camera_position;
391   graphene_point3d_t p[4];
392   graphene_plane_t planes[6];
393   graphene_vec4_t v;
394   int i;
395 
396   _clutter_stage_window_get_geometry (priv->impl, &geom);
397 
398   CLUTTER_NOTE (CLIPPING, "Creating stage clip frustum for "
399                 "x=%d, y=%d, width=%d, height=%d",
400                 clip->x, clip->y, clip->width, clip->height);
401 
402   camera_position = GRAPHENE_POINT3D_INIT_ZERO;
403 
404   p[0] = GRAPHENE_POINT3D_INIT (MAX (clip->x, 0), MAX (clip->y, 0), 0.f);
405   p[2] = GRAPHENE_POINT3D_INIT (MIN (clip->x + clip->width, geom.width),
406                                 MIN (clip->y + clip->height, geom.height),
407                                 0.f);
408 
409   for (i = 0; i < 2; i++)
410     {
411       float w = 1.0;
412       cogl_graphene_matrix_project_point (&priv->view,
413                                           &p[2 * i].x,
414                                           &p[2 * i].y,
415                                           &p[2 * i].z,
416                                           &w);
417     }
418 
419   graphene_point3d_init (&p[1], p[2].x, p[0].y, p[0].z);
420   graphene_point3d_init (&p[3], p[0].x, p[2].y, p[0].z);
421 
422   for (i = 0; i < 4; i++)
423     {
424       graphene_plane_init_from_points (&planes[i],
425                                        &camera_position,
426                                        &p[i],
427                                        &p[(i + 1) % 4]);
428     }
429 
430   graphene_vec4_init (&v, 0.f, 0.f, -1.f, priv->perspective.z_near);
431   graphene_plane_init_from_vec4 (&planes[4], &v);
432 
433   graphene_vec4_init (&v, 0.f, 0.f, 1.f, priv->perspective.z_far);
434   graphene_plane_init_from_vec4 (&planes[5], &v);
435 
436   graphene_frustum_init (frustum,
437                          &planes[0], &planes[1],
438                          &planes[2], &planes[3],
439                          &planes[4], &planes[5]);
440 }
441 
442 static void
clutter_stage_do_paint_view(ClutterStage * stage,ClutterStageView * view,const cairo_region_t * redraw_clip)443 clutter_stage_do_paint_view (ClutterStage         *stage,
444                              ClutterStageView     *view,
445                              const cairo_region_t *redraw_clip)
446 {
447   ClutterPaintContext *paint_context;
448   cairo_rectangle_int_t clip_rect;
449   g_autoptr (GArray) clip_frusta = NULL;
450   graphene_frustum_t clip_frustum;
451   int n_rectangles;
452 
453   n_rectangles = redraw_clip ? cairo_region_num_rectangles (redraw_clip) : 0;
454   if (redraw_clip && n_rectangles < MAX_FRUSTA)
455     {
456       int i;
457 
458       clip_frusta = g_array_sized_new (FALSE, FALSE,
459                                        sizeof (graphene_frustum_t),
460                                        n_rectangles);
461 
462       for (i = 0; i < n_rectangles; i++)
463         {
464           cairo_region_get_rectangle (redraw_clip, i, &clip_rect);
465           setup_clip_frustum (stage, &clip_rect, &clip_frustum);
466           g_array_append_val (clip_frusta, clip_frustum);
467         }
468     }
469   else
470     {
471       clip_frusta = g_array_sized_new (FALSE, FALSE,
472                                        sizeof (graphene_frustum_t),
473                                        1);
474       if (redraw_clip)
475         cairo_region_get_extents (redraw_clip, &clip_rect);
476       else
477         clutter_stage_view_get_layout (view, &clip_rect);
478 
479       setup_clip_frustum (stage, &clip_rect, &clip_frustum);
480       g_array_append_val (clip_frusta, clip_frustum);
481     }
482 
483   _clutter_stage_paint_volume_stack_free_all (stage);
484 
485   paint_context = clutter_paint_context_new_for_view (view,
486                                                       redraw_clip,
487                                                       clip_frusta,
488                                                       CLUTTER_PAINT_FLAG_NONE);
489 
490   clutter_actor_paint (CLUTTER_ACTOR (stage), paint_context);
491   clutter_paint_context_destroy (paint_context);
492 }
493 
494 /* This provides a common point of entry for painting the scenegraph
495  * for picking or painting...
496  */
497 void
clutter_stage_paint_view(ClutterStage * stage,ClutterStageView * view,const cairo_region_t * redraw_clip)498 clutter_stage_paint_view (ClutterStage         *stage,
499                           ClutterStageView     *view,
500                           const cairo_region_t *redraw_clip)
501 {
502   ClutterStagePrivate *priv = stage->priv;
503 
504   if (!priv->impl)
505     return;
506 
507   COGL_TRACE_BEGIN_SCOPED (ClutterStagePaintView, "Paint (view)");
508 
509   if (g_signal_has_handler_pending (stage, stage_signals[PAINT_VIEW],
510                                     0, TRUE))
511     g_signal_emit (stage, stage_signals[PAINT_VIEW], 0, view, redraw_clip);
512   else
513     CLUTTER_STAGE_GET_CLASS (stage)->paint_view (stage, view, redraw_clip);
514 }
515 
516 void
clutter_stage_emit_before_update(ClutterStage * stage,ClutterStageView * view)517 clutter_stage_emit_before_update (ClutterStage     *stage,
518                                   ClutterStageView *view)
519 {
520   g_signal_emit (stage, stage_signals[BEFORE_UPDATE], 0, view);
521 }
522 
523 void
clutter_stage_emit_before_paint(ClutterStage * stage,ClutterStageView * view)524 clutter_stage_emit_before_paint (ClutterStage     *stage,
525                                  ClutterStageView *view)
526 {
527   g_signal_emit (stage, stage_signals[BEFORE_PAINT], 0, view);
528 }
529 
530 void
clutter_stage_emit_after_paint(ClutterStage * stage,ClutterStageView * view)531 clutter_stage_emit_after_paint (ClutterStage     *stage,
532                                 ClutterStageView *view)
533 {
534   g_signal_emit (stage, stage_signals[AFTER_PAINT], 0, view);
535 }
536 
537 void
clutter_stage_emit_after_update(ClutterStage * stage,ClutterStageView * view)538 clutter_stage_emit_after_update (ClutterStage     *stage,
539                                  ClutterStageView *view)
540 {
541   g_signal_emit (stage, stage_signals[AFTER_UPDATE], 0, view);
542 }
543 
544 static gboolean
clutter_stage_get_paint_volume(ClutterActor * self,ClutterPaintVolume * volume)545 clutter_stage_get_paint_volume (ClutterActor *self,
546                                 ClutterPaintVolume *volume)
547 {
548   /* Returning False effectively means Clutter has to assume it covers
549    * everything... */
550   return FALSE;
551 }
552 
553 static void
clutter_stage_realize(ClutterActor * self)554 clutter_stage_realize (ClutterActor *self)
555 {
556   ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
557   gboolean is_realized;
558 
559   g_assert (priv->impl != NULL);
560   is_realized = _clutter_stage_window_realize (priv->impl);
561 
562   if (!is_realized)
563     CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
564 }
565 
566 static void
clutter_stage_unrealize(ClutterActor * self)567 clutter_stage_unrealize (ClutterActor *self)
568 {
569   ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
570 
571   /* and then unrealize the implementation */
572   g_assert (priv->impl != NULL);
573   _clutter_stage_window_unrealize (priv->impl);
574 
575   CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
576 }
577 
578 static void
clutter_stage_show(ClutterActor * self)579 clutter_stage_show (ClutterActor *self)
580 {
581   ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
582 
583   CLUTTER_ACTOR_CLASS (clutter_stage_parent_class)->show (self);
584 
585   /* Possibly do an allocation run so that the stage will have the
586      right size before we map it */
587   clutter_stage_maybe_relayout (self);
588 
589   g_assert (priv->impl != NULL);
590   _clutter_stage_window_show (priv->impl, TRUE);
591 }
592 
593 static void
clutter_stage_hide_all(ClutterActor * self)594 clutter_stage_hide_all (ClutterActor *self)
595 {
596   ClutterActorIter iter;
597   ClutterActor *child;
598 
599   clutter_actor_hide (self);
600 
601   /* we don't do a recursive hide_all(), to maintain the old invariants
602    * from ClutterGroup
603    */
604   clutter_actor_iter_init (&iter, self);
605   while (clutter_actor_iter_next (&iter, &child))
606     clutter_actor_hide (child);
607 }
608 
609 static void
clutter_stage_hide(ClutterActor * self)610 clutter_stage_hide (ClutterActor *self)
611 {
612   ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
613 
614   g_assert (priv->impl != NULL);
615   _clutter_stage_window_hide (priv->impl);
616 
617   CLUTTER_ACTOR_CLASS (clutter_stage_parent_class)->hide (self);
618 }
619 
620 static void
clutter_stage_emit_key_focus_event(ClutterStage * stage,gboolean focus_in)621 clutter_stage_emit_key_focus_event (ClutterStage *stage,
622                                     gboolean      focus_in)
623 {
624   ClutterStagePrivate *priv = stage->priv;
625 
626   if (priv->key_focused_actor == NULL)
627     return;
628 
629   _clutter_actor_set_has_key_focus (CLUTTER_ACTOR (stage), focus_in);
630 
631   g_object_notify_by_pspec (G_OBJECT (stage), obj_props[PROP_KEY_FOCUS]);
632 }
633 
634 static void
clutter_stage_real_activate(ClutterStage * stage)635 clutter_stage_real_activate (ClutterStage *stage)
636 {
637   clutter_stage_emit_key_focus_event (stage, TRUE);
638 }
639 
640 static void
clutter_stage_real_deactivate(ClutterStage * stage)641 clutter_stage_real_deactivate (ClutterStage *stage)
642 {
643   clutter_stage_emit_key_focus_event (stage, FALSE);
644 }
645 
646 void
_clutter_stage_queue_event(ClutterStage * stage,ClutterEvent * event,gboolean copy_event)647 _clutter_stage_queue_event (ClutterStage *stage,
648                             ClutterEvent *event,
649                             gboolean      copy_event)
650 {
651   ClutterStagePrivate *priv;
652   gboolean first_event;
653 
654   g_return_if_fail (CLUTTER_IS_STAGE (stage));
655 
656   priv = stage->priv;
657 
658   first_event = priv->event_queue->length == 0;
659 
660   if (copy_event)
661     event = clutter_event_copy (event);
662 
663   if (first_event)
664     {
665       gboolean compressible = event->type == CLUTTER_MOTION ||
666                               event->type == CLUTTER_TOUCH_UPDATE;
667 
668       if (!compressible)
669         {
670           _clutter_process_event (event);
671           clutter_event_free (event);
672           return;
673         }
674     }
675 
676   g_queue_push_tail (priv->event_queue, event);
677 
678   if (first_event)
679     clutter_stage_schedule_update (stage);
680 }
681 
682 gboolean
_clutter_stage_has_queued_events(ClutterStage * stage)683 _clutter_stage_has_queued_events (ClutterStage *stage)
684 {
685   ClutterStagePrivate *priv;
686 
687   g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
688 
689   priv = stage->priv;
690 
691   return priv->event_queue->length > 0;
692 }
693 
694 static void
clutter_stage_compress_motion(ClutterStage * stage,ClutterEvent * event,const ClutterEvent * to_discard)695 clutter_stage_compress_motion (ClutterStage       *stage,
696                                ClutterEvent       *event,
697                                const ClutterEvent *to_discard)
698 {
699   double dx, dy;
700   double dx_unaccel, dy_unaccel;
701   double dst_dx = 0.0, dst_dy = 0.0;
702   double dst_dx_unaccel = 0.0, dst_dy_unaccel = 0.0;
703 
704   if (!clutter_event_get_relative_motion (to_discard,
705                                           &dx, &dy,
706                                           &dx_unaccel, &dy_unaccel))
707     return;
708 
709   clutter_event_get_relative_motion (event,
710                                      &dst_dx, &dst_dy,
711                                      &dst_dx_unaccel, &dst_dy_unaccel);
712 
713   event->motion.flags |= CLUTTER_EVENT_FLAG_RELATIVE_MOTION;
714   event->motion.dx = dx + dst_dx;
715   event->motion.dy = dy + dst_dy;
716   event->motion.dx_unaccel = dx_unaccel + dst_dx_unaccel;
717   event->motion.dy_unaccel = dy_unaccel + dst_dy_unaccel;
718 }
719 
720 void
_clutter_stage_process_queued_events(ClutterStage * stage)721 _clutter_stage_process_queued_events (ClutterStage *stage)
722 {
723   ClutterStagePrivate *priv;
724   GList *events, *l;
725 
726   g_return_if_fail (CLUTTER_IS_STAGE (stage));
727 
728   priv = stage->priv;
729 
730   if (priv->event_queue->length == 0)
731     return;
732 
733   /* In case the stage gets destroyed during event processing */
734   g_object_ref (stage);
735 
736   /* Steal events before starting processing to avoid reentrancy
737    * issues */
738   events = priv->event_queue->head;
739   priv->event_queue->head =  NULL;
740   priv->event_queue->tail = NULL;
741   priv->event_queue->length = 0;
742 
743   for (l = events; l != NULL; l = l->next)
744     {
745       ClutterEvent *event;
746       ClutterEvent *next_event;
747       ClutterInputDevice *device;
748       ClutterInputDevice *next_device;
749       ClutterInputDeviceType device_type;
750       gboolean check_device = FALSE;
751 
752       event = l->data;
753       next_event = l->next ? l->next->data : NULL;
754 
755       device = clutter_event_get_device (event);
756 
757       if (next_event != NULL)
758         next_device = clutter_event_get_device (next_event);
759       else
760         next_device = NULL;
761 
762       if (device != NULL && next_device != NULL)
763         check_device = TRUE;
764 
765       device_type = clutter_input_device_get_device_type (device);
766 
767       /* Skip consecutive motion events coming from the same device,
768        * except those of tablet tools, since users of these events
769        * want no precision loss.
770        */
771       if (priv->throttle_motion_events && next_event != NULL &&
772           device_type != CLUTTER_TABLET_DEVICE &&
773           device_type != CLUTTER_PEN_DEVICE &&
774           device_type != CLUTTER_ERASER_DEVICE)
775         {
776           if (event->type == CLUTTER_MOTION &&
777               (next_event->type == CLUTTER_MOTION ||
778                next_event->type == CLUTTER_LEAVE) &&
779               (!check_device || (device == next_device)))
780             {
781               CLUTTER_NOTE (EVENT,
782                             "Omitting motion event at %d, %d",
783                             (int) event->motion.x,
784                             (int) event->motion.y);
785 
786               if (next_event->type == CLUTTER_MOTION)
787                 clutter_stage_compress_motion (stage, next_event, event);
788 
789               goto next_event;
790             }
791           else if (event->type == CLUTTER_TOUCH_UPDATE &&
792                    next_event->type == CLUTTER_TOUCH_UPDATE &&
793                    event->touch.sequence == next_event->touch.sequence &&
794                    (!check_device || (device == next_device)))
795             {
796               CLUTTER_NOTE (EVENT,
797                             "Omitting touch update event at %d, %d",
798                             (int) event->touch.x,
799                             (int) event->touch.y);
800               goto next_event;
801             }
802         }
803 
804       _clutter_process_event (event);
805 
806     next_event:
807       clutter_event_free (event);
808     }
809 
810   g_list_free (events);
811 
812   g_object_unref (stage);
813 }
814 
815 void
clutter_stage_queue_actor_relayout(ClutterStage * stage,ClutterActor * actor)816 clutter_stage_queue_actor_relayout (ClutterStage *stage,
817                                     ClutterActor *actor)
818 {
819   ClutterStagePrivate *priv = stage->priv;
820 
821   if (priv->pending_relayouts == NULL)
822     clutter_stage_schedule_update (stage);
823 
824   priv->pending_relayouts = g_slist_prepend (priv->pending_relayouts,
825                                              g_object_ref (actor));
826 }
827 
828 void
clutter_stage_dequeue_actor_relayout(ClutterStage * stage,ClutterActor * actor)829 clutter_stage_dequeue_actor_relayout (ClutterStage *stage,
830                                       ClutterActor *actor)
831 {
832   ClutterStagePrivate *priv = stage->priv;
833   GSList *l;
834 
835   for (l = priv->pending_relayouts; l; l = l->next)
836     {
837       ClutterActor *relayout_actor = l->data;
838 
839       if (relayout_actor == actor)
840         {
841           g_object_unref (relayout_actor);
842           priv->pending_relayouts =
843             g_slist_delete_link (priv->pending_relayouts, l);
844 
845           return;
846         }
847     }
848 }
849 
850 void
clutter_stage_maybe_relayout(ClutterActor * actor)851 clutter_stage_maybe_relayout (ClutterActor *actor)
852 {
853   ClutterStage *stage = CLUTTER_STAGE (actor);
854   ClutterStagePrivate *priv = stage->priv;
855   g_autoptr (GSList) stolen_list = NULL;
856   GSList *l;
857   int count = 0;
858 
859   /* No work to do? Avoid the extraneous debug log messages too. */
860   if (priv->pending_relayouts == NULL)
861     return;
862 
863   COGL_TRACE_BEGIN_SCOPED (ClutterStageRelayout, "Layout");
864 
865   CLUTTER_NOTE (ACTOR, ">>> Recomputing layout");
866 
867   stolen_list = g_steal_pointer (&priv->pending_relayouts);
868   for (l = stolen_list; l; l = l->next)
869     {
870       g_autoptr (ClutterActor) queued_actor = l->data;
871       float x = 0.f;
872       float y = 0.f;
873 
874       if (CLUTTER_ACTOR_IN_RELAYOUT (queued_actor))  /* avoid reentrancy */
875         continue;
876 
877       if (queued_actor == actor)
878         CLUTTER_NOTE (ACTOR, "    Deep relayout of stage %s",
879                       _clutter_actor_get_debug_name (queued_actor));
880       else
881         CLUTTER_NOTE (ACTOR, "    Shallow relayout of actor %s",
882                       _clutter_actor_get_debug_name (queued_actor));
883 
884       CLUTTER_SET_PRIVATE_FLAGS (queued_actor, CLUTTER_IN_RELAYOUT);
885 
886       clutter_actor_get_fixed_position (queued_actor, &x, &y);
887       clutter_actor_allocate_preferred_size (queued_actor, x, y);
888 
889       CLUTTER_UNSET_PRIVATE_FLAGS (queued_actor, CLUTTER_IN_RELAYOUT);
890 
891       count++;
892     }
893 
894   CLUTTER_NOTE (ACTOR, "<<< Completed recomputing layout of %d subtrees", count);
895 
896   if (count)
897     priv->needs_update_devices = TRUE;
898 }
899 
900 GSList *
clutter_stage_find_updated_devices(ClutterStage * stage)901 clutter_stage_find_updated_devices (ClutterStage *stage)
902 {
903   ClutterStagePrivate *priv = stage->priv;
904   GSList *updating = NULL;
905   GHashTableIter iter;
906   gpointer value;
907 
908   if (!priv->needs_update_devices)
909     return NULL;
910 
911   priv->needs_update_devices = FALSE;
912 
913   g_hash_table_iter_init (&iter, priv->pointer_devices);
914   while (g_hash_table_iter_next (&iter, NULL, &value))
915     {
916       PointerDeviceEntry *entry = value;
917       ClutterStageView *view;
918       const cairo_region_t *clip;
919 
920       view = clutter_stage_get_view_at (stage, entry->coords.x, entry->coords.y);
921       if (!view)
922         continue;
923 
924       clip = clutter_stage_view_peek_redraw_clip (view);
925       if (!clip || cairo_region_contains_point (clip, entry->coords.x, entry->coords.y))
926         updating = g_slist_prepend (updating, entry->device);
927     }
928 
929   return updating;
930 }
931 
932 void
clutter_stage_finish_layout(ClutterStage * stage)933 clutter_stage_finish_layout (ClutterStage *stage)
934 {
935   ClutterActor *actor = CLUTTER_ACTOR (stage);
936   ClutterStagePrivate *priv = stage->priv;
937   int phase;
938 
939   COGL_TRACE_BEGIN_SCOPED (ClutterStageUpdateActorStageViews,
940                            "Actor stage-views");
941 
942   /* If an actor needs an immediate relayout because its resource scale
943    * changed, we give it another chance to allocate correctly before
944    * the paint.
945    *
946    * We're doing the whole thing twice and pass the phase to
947    * clutter_actor_finish_layout() to allow actors to detect loops:
948    * If the resource scale changes again after the relayout, the new
949    * allocation of an actor probably moved the actor onto another stage
950    * view, so if an actor sees phase == 1, it can choose a "final" scale.
951    */
952   for (phase = 0; phase < 2; phase++)
953     {
954       clutter_actor_finish_layout (actor, phase);
955 
956       if (!priv->actor_needs_immediate_relayout)
957         break;
958 
959       priv->actor_needs_immediate_relayout = FALSE;
960       clutter_stage_maybe_relayout (actor);
961       clutter_stage_maybe_finish_queue_redraws (stage);
962     }
963 
964   g_warn_if_fail (!priv->actor_needs_immediate_relayout);
965 }
966 
967 void
clutter_stage_update_devices(ClutterStage * stage,GSList * devices)968 clutter_stage_update_devices (ClutterStage *stage,
969                               GSList       *devices)
970 {
971   ClutterStagePrivate *priv = stage->priv;
972   GSList *l;
973 
974   COGL_TRACE_BEGIN (ClutterStageUpdateDevices, "UpdateDevices");
975 
976   for (l = devices; l; l = l->next)
977     {
978       ClutterInputDevice *device = l->data;
979       PointerDeviceEntry *entry = NULL;
980       ClutterActor *new_actor;
981 
982       entry = g_hash_table_lookup (priv->pointer_devices, device);
983       g_assert (entry != NULL);
984 
985       new_actor = _clutter_stage_do_pick (stage,
986                                           entry->coords.x,
987                                           entry->coords.y,
988                                           CLUTTER_PICK_REACTIVE);
989 
990       clutter_stage_update_device (stage,
991                                    device, NULL,
992                                    entry->coords,
993                                    CLUTTER_CURRENT_TIME,
994                                    new_actor,
995                                    TRUE);
996     }
997 }
998 
999 static void
clutter_stage_real_queue_relayout(ClutterActor * self)1000 clutter_stage_real_queue_relayout (ClutterActor *self)
1001 {
1002   ClutterStage *stage = CLUTTER_STAGE (self);
1003   ClutterActorClass *parent_class;
1004 
1005   clutter_stage_queue_actor_relayout (stage, self);
1006 
1007   /* chain up */
1008   parent_class = CLUTTER_ACTOR_CLASS (clutter_stage_parent_class);
1009   parent_class->queue_relayout (self);
1010 }
1011 
1012 static gboolean
is_full_stage_redraw_queued(ClutterStage * stage)1013 is_full_stage_redraw_queued (ClutterStage *stage)
1014 {
1015   GList *l;
1016 
1017   for (l = clutter_stage_peek_stage_views (stage); l; l = l->next)
1018     {
1019       ClutterStageView *view = l->data;
1020 
1021       if (!clutter_stage_view_has_full_redraw_clip (view))
1022         return FALSE;
1023     }
1024 
1025   return TRUE;
1026 }
1027 
1028 gboolean
_clutter_stage_has_full_redraw_queued(ClutterStage * stage)1029 _clutter_stage_has_full_redraw_queued (ClutterStage *stage)
1030 {
1031   if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
1032     return FALSE;
1033 
1034   return is_full_stage_redraw_queued (stage);
1035 }
1036 
1037 static void
setup_ray_for_coordinates(ClutterStage * stage,float x,float y,graphene_point3d_t * point,graphene_ray_t * ray)1038 setup_ray_for_coordinates (ClutterStage       *stage,
1039                            float               x,
1040                            float               y,
1041                            graphene_point3d_t *point,
1042                            graphene_ray_t     *ray)
1043 {
1044   ClutterStagePrivate *priv = stage->priv;
1045   graphene_point3d_t camera_position;
1046   graphene_point3d_t p;
1047   graphene_vec3_t direction;
1048   graphene_vec3_t cv;
1049   graphene_vec3_t v;
1050 
1051   camera_position = GRAPHENE_POINT3D_INIT_ZERO;
1052   graphene_vec3_init (&cv,
1053                       camera_position.x,
1054                       camera_position.y,
1055                       camera_position.z);
1056 
1057   p = GRAPHENE_POINT3D_INIT (x, y, 0.f);
1058   graphene_matrix_transform_point3d (&priv->view, &p, &p);
1059 
1060   graphene_vec3_init (&v, p.x, p.y, p.z);
1061   graphene_vec3_subtract (&v, &cv, &direction);
1062   graphene_vec3_normalize (&direction, &direction);
1063 
1064   graphene_ray_init (ray, &camera_position, &direction);
1065   graphene_point3d_init_from_point (point, &p);
1066 }
1067 
1068 static ClutterActor *
_clutter_stage_do_pick_on_view(ClutterStage * stage,float x,float y,ClutterPickMode mode,ClutterStageView * view)1069 _clutter_stage_do_pick_on_view (ClutterStage     *stage,
1070                                 float             x,
1071                                 float             y,
1072                                 ClutterPickMode   mode,
1073                                 ClutterStageView *view)
1074 {
1075   g_autoptr (ClutterPickStack) pick_stack = NULL;
1076   ClutterPickContext *pick_context;
1077   graphene_point3d_t p;
1078   graphene_ray_t ray;
1079   ClutterActor *actor;
1080 
1081   COGL_TRACE_BEGIN_SCOPED (ClutterStagePickView, "Pick (view)");
1082 
1083   setup_ray_for_coordinates (stage, x, y, &p, &ray);
1084 
1085   pick_context = clutter_pick_context_new_for_view (view, mode, &p, &ray);
1086 
1087   clutter_actor_pick (CLUTTER_ACTOR (stage), pick_context);
1088   pick_stack = clutter_pick_context_steal_stack (pick_context);
1089   clutter_pick_context_destroy (pick_context);
1090 
1091   actor = clutter_pick_stack_search_actor (pick_stack, &p, &ray);
1092   return actor ? actor : CLUTTER_ACTOR (stage);
1093 }
1094 
1095 /**
1096  * clutter_stage_get_view_at: (skip)
1097  */
1098 ClutterStageView *
clutter_stage_get_view_at(ClutterStage * stage,float x,float y)1099 clutter_stage_get_view_at (ClutterStage *stage,
1100                            float         x,
1101                            float         y)
1102 {
1103   ClutterStagePrivate *priv = stage->priv;
1104   GList *l;
1105 
1106   for (l = _clutter_stage_window_get_views (priv->impl); l; l = l->next)
1107     {
1108       ClutterStageView *view = l->data;
1109       cairo_rectangle_int_t view_layout;
1110 
1111       clutter_stage_view_get_layout (view, &view_layout);
1112       if (x >= view_layout.x &&
1113           x < view_layout.x + view_layout.width &&
1114           y >= view_layout.y &&
1115           y < view_layout.y + view_layout.height)
1116         return view;
1117     }
1118 
1119   return NULL;
1120 }
1121 
1122 ClutterActor *
_clutter_stage_do_pick(ClutterStage * stage,float x,float y,ClutterPickMode mode)1123 _clutter_stage_do_pick (ClutterStage   *stage,
1124                         float           x,
1125                         float           y,
1126                         ClutterPickMode mode)
1127 {
1128   ClutterActor *actor = CLUTTER_ACTOR (stage);
1129   ClutterStagePrivate *priv = stage->priv;
1130   float stage_width, stage_height;
1131   ClutterStageView *view = NULL;
1132 
1133   priv = stage->priv;
1134 
1135   if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
1136     return actor;
1137 
1138   if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_NOP_PICKING))
1139     return actor;
1140 
1141   if (G_UNLIKELY (priv->impl == NULL))
1142     return actor;
1143 
1144   clutter_actor_get_size (CLUTTER_ACTOR (stage), &stage_width, &stage_height);
1145   if (x < 0 || x >= stage_width || y < 0 || y >= stage_height)
1146     return actor;
1147 
1148   view = clutter_stage_get_view_at (stage, x, y);
1149   if (view)
1150     return _clutter_stage_do_pick_on_view (stage, x, y, mode, view);
1151 
1152   return actor;
1153 }
1154 
1155 static void
clutter_stage_real_apply_transform(ClutterActor * stage,graphene_matrix_t * matrix)1156 clutter_stage_real_apply_transform (ClutterActor      *stage,
1157                                     graphene_matrix_t *matrix)
1158 {
1159   ClutterStagePrivate *priv = CLUTTER_STAGE (stage)->priv;
1160 
1161   /* FIXME: we probably shouldn't be explicitly resetting the matrix
1162    * here... */
1163   graphene_matrix_init_from_matrix (matrix, &priv->view);
1164 }
1165 
1166 static void
clutter_stage_constructed(GObject * gobject)1167 clutter_stage_constructed (GObject *gobject)
1168 {
1169   ClutterStage *self = CLUTTER_STAGE (gobject);
1170   ClutterStageManager *stage_manager;
1171 
1172   stage_manager = clutter_stage_manager_get_default ();
1173 
1174   /* this will take care to sinking the floating reference */
1175   _clutter_stage_manager_add_stage (stage_manager, self);
1176 
1177   /* if this stage has been created on a backend that does not
1178    * support multiple stages then it becomes the default stage
1179    * as well; any other attempt at creating a ClutterStage will
1180    * fail.
1181    */
1182   if (!clutter_feature_available (CLUTTER_FEATURE_STAGE_MULTIPLE))
1183     {
1184       if (G_UNLIKELY (clutter_stage_manager_get_default_stage (stage_manager) != NULL))
1185         {
1186           g_error ("Unable to create another stage: the backend of "
1187                    "type '%s' does not support multiple stages. Use "
1188                    "clutter_stage_manager_get_default_stage() instead "
1189                    "to access the stage singleton.",
1190                    G_OBJECT_TYPE_NAME (clutter_get_default_backend ()));
1191         }
1192 
1193       _clutter_stage_manager_set_default_stage (stage_manager, self);
1194     }
1195 
1196   G_OBJECT_CLASS (clutter_stage_parent_class)->constructed (gobject);
1197 }
1198 
1199 static void
clutter_stage_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1200 clutter_stage_set_property (GObject      *object,
1201 			    guint         prop_id,
1202 			    const GValue *value,
1203 			    GParamSpec   *pspec)
1204 {
1205   ClutterStage *stage = CLUTTER_STAGE (object);
1206 
1207   switch (prop_id)
1208     {
1209     case PROP_TITLE:
1210       clutter_stage_set_title (stage, g_value_get_string (value));
1211       break;
1212 
1213     case PROP_KEY_FOCUS:
1214       clutter_stage_set_key_focus (stage, g_value_get_object (value));
1215       break;
1216 
1217     default:
1218       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1219       break;
1220   }
1221 }
1222 
1223 static void
clutter_stage_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * pspec)1224 clutter_stage_get_property (GObject    *gobject,
1225 			    guint       prop_id,
1226 			    GValue     *value,
1227 			    GParamSpec *pspec)
1228 {
1229   ClutterStagePrivate *priv = CLUTTER_STAGE (gobject)->priv;
1230 
1231   switch (prop_id)
1232     {
1233     case PROP_PERSPECTIVE:
1234       g_value_set_boxed (value, &priv->perspective);
1235       break;
1236 
1237     case PROP_TITLE:
1238       g_value_set_string (value, priv->title);
1239       break;
1240 
1241     case PROP_KEY_FOCUS:
1242       g_value_set_object (value, priv->key_focused_actor);
1243       break;
1244 
1245     default:
1246       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
1247       break;
1248     }
1249 }
1250 
1251 static void
clutter_stage_dispose(GObject * object)1252 clutter_stage_dispose (GObject *object)
1253 {
1254   ClutterStage        *stage = CLUTTER_STAGE (object);
1255   ClutterStagePrivate *priv = stage->priv;
1256   ClutterStageManager *stage_manager;
1257 
1258   clutter_actor_hide (CLUTTER_ACTOR (object));
1259 
1260   _clutter_clear_events_queue ();
1261 
1262   if (priv->impl != NULL)
1263     {
1264       CLUTTER_NOTE (BACKEND, "Disposing of the stage implementation");
1265 
1266       if (CLUTTER_ACTOR_IS_REALIZED (object))
1267         _clutter_stage_window_unrealize (priv->impl);
1268 
1269       g_object_unref (priv->impl);
1270       priv->impl = NULL;
1271     }
1272 
1273   clutter_actor_destroy_all_children (CLUTTER_ACTOR (object));
1274 
1275   g_hash_table_remove_all (priv->pending_queue_redraws);
1276 
1277   g_slist_free_full (priv->pending_relayouts,
1278                      (GDestroyNotify) g_object_unref);
1279   priv->pending_relayouts = NULL;
1280 
1281   /* this will release the reference on the stage */
1282   stage_manager = clutter_stage_manager_get_default ();
1283   _clutter_stage_manager_remove_stage (stage_manager, stage);
1284 
1285   G_OBJECT_CLASS (clutter_stage_parent_class)->dispose (object);
1286 }
1287 
1288 static void
clutter_stage_finalize(GObject * object)1289 clutter_stage_finalize (GObject *object)
1290 {
1291   ClutterStage *stage = CLUTTER_STAGE (object);
1292   ClutterStagePrivate *priv = stage->priv;
1293 
1294   g_queue_foreach (priv->event_queue, (GFunc) clutter_event_free, NULL);
1295   g_queue_free (priv->event_queue);
1296 
1297   g_hash_table_destroy (priv->pointer_devices);
1298   g_hash_table_destroy (priv->touch_sequences);
1299 
1300   g_free (priv->title);
1301 
1302   g_array_free (priv->paint_volume_stack, TRUE);
1303 
1304   G_OBJECT_CLASS (clutter_stage_parent_class)->finalize (object);
1305 }
1306 
1307 static void
clutter_stage_real_paint_view(ClutterStage * stage,ClutterStageView * view,const cairo_region_t * redraw_clip)1308 clutter_stage_real_paint_view (ClutterStage         *stage,
1309                                ClutterStageView     *view,
1310                                const cairo_region_t *redraw_clip)
1311 {
1312   clutter_stage_do_paint_view (stage, view, redraw_clip);
1313 }
1314 
1315 static void
clutter_stage_paint(ClutterActor * actor,ClutterPaintContext * paint_context)1316 clutter_stage_paint (ClutterActor        *actor,
1317                      ClutterPaintContext *paint_context)
1318 {
1319   ClutterStageView *view;
1320 
1321   CLUTTER_ACTOR_CLASS (clutter_stage_parent_class)->paint (actor, paint_context);
1322 
1323   view = clutter_paint_context_get_stage_view (paint_context);
1324   if (view &&
1325       G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_MAX_RENDER_TIME))
1326     {
1327       cairo_rectangle_int_t view_layout;
1328       ClutterFrameClock *frame_clock;
1329       g_autoptr (GString) string = NULL;
1330       PangoLayout *layout;
1331       PangoRectangle logical;
1332       ClutterColor color;
1333       g_autoptr (ClutterPaintNode) node = NULL;
1334       ClutterActorBox box;
1335 
1336       clutter_stage_view_get_layout (view, &view_layout);
1337       frame_clock = clutter_stage_view_get_frame_clock (view);
1338 
1339       string = clutter_frame_clock_get_max_render_time_debug_info (frame_clock);
1340 
1341       layout = clutter_actor_create_pango_layout (actor, string->str);
1342       pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT);
1343       pango_layout_get_pixel_extents (layout, NULL, &logical);
1344 
1345       clutter_color_init (&color, 255, 255, 255, 255);
1346       node = clutter_text_node_new (layout, &color);
1347 
1348       box.x1 = view_layout.x;
1349       box.y1 = view_layout.y + 30;
1350       box.x2 = box.x1 + logical.width;
1351       box.y2 = box.y1 + logical.height;
1352       clutter_paint_node_add_rectangle (node, &box);
1353 
1354       clutter_paint_node_paint (node, paint_context);
1355 
1356       g_object_unref (layout);
1357     }
1358 }
1359 
1360 static void
clutter_stage_class_init(ClutterStageClass * klass)1361 clutter_stage_class_init (ClutterStageClass *klass)
1362 {
1363   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1364   ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
1365 
1366   gobject_class->constructed = clutter_stage_constructed;
1367   gobject_class->set_property = clutter_stage_set_property;
1368   gobject_class->get_property = clutter_stage_get_property;
1369   gobject_class->dispose = clutter_stage_dispose;
1370   gobject_class->finalize = clutter_stage_finalize;
1371 
1372   actor_class->allocate = clutter_stage_allocate;
1373   actor_class->get_preferred_width = clutter_stage_get_preferred_width;
1374   actor_class->get_preferred_height = clutter_stage_get_preferred_height;
1375   actor_class->get_paint_volume = clutter_stage_get_paint_volume;
1376   actor_class->realize = clutter_stage_realize;
1377   actor_class->unrealize = clutter_stage_unrealize;
1378   actor_class->show = clutter_stage_show;
1379   actor_class->hide = clutter_stage_hide;
1380   actor_class->hide_all = clutter_stage_hide_all;
1381   actor_class->queue_relayout = clutter_stage_real_queue_relayout;
1382   actor_class->apply_transform = clutter_stage_real_apply_transform;
1383   actor_class->paint = clutter_stage_paint;
1384 
1385   klass->paint_view = clutter_stage_real_paint_view;
1386 
1387   /**
1388    * ClutterStage:perspective:
1389    *
1390    * The parameters used for the perspective projection from 3D
1391    * coordinates to 2D
1392    *
1393    * Since: 0.8
1394    */
1395   obj_props[PROP_PERSPECTIVE] =
1396       g_param_spec_boxed ("perspective",
1397                           P_("Perspective"),
1398                           P_("Perspective projection parameters"),
1399                           CLUTTER_TYPE_PERSPECTIVE,
1400                           CLUTTER_PARAM_READABLE |
1401                           G_PARAM_EXPLICIT_NOTIFY);
1402 
1403   /**
1404    * ClutterStage:title:
1405    *
1406    * The stage's title - usually displayed in stage windows title decorations.
1407    *
1408    * Since: 0.4
1409    */
1410   obj_props[PROP_TITLE] =
1411       g_param_spec_string ("title",
1412                            P_("Title"),
1413                            P_("Stage Title"),
1414                            NULL,
1415                            CLUTTER_PARAM_READWRITE |
1416                            G_PARAM_EXPLICIT_NOTIFY);
1417 
1418   /**
1419    * ClutterStage:key-focus:
1420    *
1421    * The #ClutterActor that will receive key events from the underlying
1422    * windowing system.
1423    *
1424    * If %NULL, the #ClutterStage will receive the events.
1425    *
1426    * Since: 1.2
1427    */
1428   obj_props[PROP_KEY_FOCUS] =
1429       g_param_spec_object ("key-focus",
1430                            P_("Key Focus"),
1431                            P_("The currently key focused actor"),
1432                            CLUTTER_TYPE_ACTOR,
1433                            CLUTTER_PARAM_READWRITE |
1434                            G_PARAM_EXPLICIT_NOTIFY);
1435 
1436   g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
1437 
1438   /**
1439    * ClutterStage::activate:
1440    * @stage: the stage which was activated
1441    *
1442    * The ::activate signal is emitted when the stage receives key focus
1443    * from the underlying window system.
1444    *
1445    * Since: 0.6
1446    */
1447   stage_signals[ACTIVATE] =
1448     g_signal_new (I_("activate"),
1449 		  G_TYPE_FROM_CLASS (gobject_class),
1450 		  G_SIGNAL_RUN_LAST,
1451 		  G_STRUCT_OFFSET (ClutterStageClass, activate),
1452 		  NULL, NULL, NULL,
1453 		  G_TYPE_NONE, 0);
1454   /**
1455    * ClutterStage::deactivate:
1456    * @stage: the stage which was deactivated
1457    *
1458    * The ::deactivate signal is emitted when the stage loses key focus
1459    * from the underlying window system.
1460    *
1461    * Since: 0.6
1462    */
1463   stage_signals[DEACTIVATE] =
1464     g_signal_new (I_("deactivate"),
1465 		  G_TYPE_FROM_CLASS (gobject_class),
1466 		  G_SIGNAL_RUN_LAST,
1467 		  G_STRUCT_OFFSET (ClutterStageClass, deactivate),
1468 		  NULL, NULL, NULL,
1469 		  G_TYPE_NONE, 0);
1470 
1471   /**
1472    * ClutterStage::before-update:
1473    * @stage: the #ClutterStage
1474    * @view: a #ClutterStageView
1475    */
1476   stage_signals[BEFORE_UPDATE] =
1477     g_signal_new (I_("before-update"),
1478                   G_TYPE_FROM_CLASS (gobject_class),
1479                   G_SIGNAL_RUN_LAST,
1480                   0,
1481                   NULL, NULL, NULL,
1482                   G_TYPE_NONE, 1,
1483                   CLUTTER_TYPE_STAGE_VIEW);
1484 
1485   /**
1486    * ClutterStage::before-paint:
1487    * @stage: the stage that received the event
1488    * @view: a #ClutterStageView
1489    *
1490    * The ::before-paint signal is emitted before the stage is painted.
1491    */
1492   stage_signals[BEFORE_PAINT] =
1493     g_signal_new (I_("before-paint"),
1494                   G_TYPE_FROM_CLASS (gobject_class),
1495                   G_SIGNAL_RUN_LAST,
1496                   G_STRUCT_OFFSET (ClutterStageClass, before_paint),
1497                   NULL, NULL, NULL,
1498                   G_TYPE_NONE, 1,
1499                   CLUTTER_TYPE_STAGE_VIEW);
1500   /**
1501    * ClutterStage::after-paint:
1502    * @stage: the stage that received the event
1503    * @view: a #ClutterStageView
1504    *
1505    * The ::after-paint signal is emitted after the stage is painted,
1506    * but before the results are displayed on the screen.
1507    *
1508    * Since: 1.20
1509    */
1510   stage_signals[AFTER_PAINT] =
1511     g_signal_new (I_("after-paint"),
1512                   G_TYPE_FROM_CLASS (gobject_class),
1513                   G_SIGNAL_RUN_LAST,
1514                   0, /* no corresponding vfunc */
1515                   NULL, NULL, NULL,
1516                   G_TYPE_NONE, 1,
1517                   CLUTTER_TYPE_STAGE_VIEW);
1518 
1519   /**
1520    * ClutterStage::after-update:
1521    * @stage: the #ClutterStage
1522    * @view: a #ClutterStageView
1523    */
1524   stage_signals[AFTER_UPDATE] =
1525     g_signal_new (I_("after-update"),
1526                   G_TYPE_FROM_CLASS (gobject_class),
1527                   G_SIGNAL_RUN_LAST,
1528                   0,
1529                   NULL, NULL, NULL,
1530                   G_TYPE_NONE, 1,
1531                   CLUTTER_TYPE_STAGE_VIEW);
1532 
1533   /**
1534    * ClutterStage::paint-view:
1535    * @stage: the stage that received the event
1536    * @view: a #ClutterStageView
1537    * @redraw_clip: a #cairo_region_t with the redraw clip
1538    *
1539    * The ::paint-view signal is emitted before a #ClutterStageView is being
1540    * painted.
1541    *
1542    * The view is painted in the default handler. Hence, if you want to perform
1543    * some action after the view is painted, like reading the contents of the
1544    * framebuffer, use g_signal_connect_after() or pass %G_CONNECT_AFTER.
1545    */
1546   stage_signals[PAINT_VIEW] =
1547     g_signal_new (I_("paint-view"),
1548                   G_TYPE_FROM_CLASS (gobject_class),
1549                   G_SIGNAL_RUN_LAST,
1550                   G_STRUCT_OFFSET (ClutterStageClass, paint_view),
1551                   NULL, NULL, NULL,
1552                   G_TYPE_NONE, 2,
1553                   CLUTTER_TYPE_STAGE_VIEW,
1554                   CAIRO_GOBJECT_TYPE_REGION);
1555 
1556   /**
1557    * ClutterStage::presented: (skip)
1558    * @stage: the stage that received the event
1559    * @view: the #ClutterStageView presented
1560    * @frame_info: a #ClutterFrameInfo
1561    *
1562    * Signals that the #ClutterStage was presented on the screen to the user.
1563    */
1564   stage_signals[PRESENTED] =
1565     g_signal_new (I_("presented"),
1566                   G_TYPE_FROM_CLASS (gobject_class),
1567                   G_SIGNAL_RUN_LAST,
1568                   0,
1569                   NULL, NULL, NULL,
1570                   G_TYPE_NONE, 2,
1571                   CLUTTER_TYPE_STAGE_VIEW,
1572                   G_TYPE_POINTER);
1573 
1574  /**
1575    * ClutterStage::gl-video-memory-purged: (skip)
1576    * @stage: the stage that received the event
1577    *
1578    * Signals that the underlying GL driver has had its texture memory purged
1579    * so anything presently held in texture memory is now invalidated, and
1580    * likely corrupt. It needs redrawing.
1581    */
1582   stage_signals[GL_VIDEO_MEMORY_PURGED] =
1583     g_signal_new (I_("gl-video-memory-purged"),
1584                   G_TYPE_FROM_CLASS (gobject_class),
1585                   G_SIGNAL_RUN_LAST,
1586                   0,
1587                   NULL, NULL, NULL,
1588                   G_TYPE_NONE, 0);
1589 
1590   klass->activate = clutter_stage_real_activate;
1591   klass->deactivate = clutter_stage_real_deactivate;
1592 }
1593 
1594 static void
clutter_stage_notify_min_size(ClutterStage * self)1595 clutter_stage_notify_min_size (ClutterStage *self)
1596 {
1597   self->priv->min_size_changed = TRUE;
1598 }
1599 
1600 static void
clutter_stage_init(ClutterStage * self)1601 clutter_stage_init (ClutterStage *self)
1602 {
1603   cairo_rectangle_int_t geom = { 0, };
1604   ClutterStagePrivate *priv;
1605   ClutterStageWindow *impl;
1606   ClutterBackend *backend;
1607   GError *error;
1608 
1609   /* a stage is a top-level object */
1610   CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IS_TOPLEVEL);
1611 
1612   self->priv = priv = clutter_stage_get_instance_private (self);
1613 
1614   CLUTTER_NOTE (BACKEND, "Creating stage from the default backend");
1615   backend = clutter_get_default_backend ();
1616 
1617   error = NULL;
1618   impl = _clutter_backend_create_stage (backend, self, &error);
1619 
1620   if (G_LIKELY (impl != NULL))
1621     {
1622       _clutter_stage_set_window (self, impl);
1623       _clutter_stage_window_get_geometry (priv->impl, &geom);
1624     }
1625   else
1626     {
1627       if (error != NULL)
1628         {
1629           g_critical ("Unable to create a new stage implementation: %s",
1630                       error->message);
1631           g_error_free (error);
1632         }
1633       else
1634         g_critical ("Unable to create a new stage implementation.");
1635     }
1636 
1637   priv->event_queue = g_queue_new ();
1638 
1639   priv->throttle_motion_events = TRUE;
1640   priv->min_size_changed = FALSE;
1641   priv->motion_events_enabled = TRUE;
1642 
1643   priv->pointer_devices =
1644     g_hash_table_new_full (NULL, NULL,
1645                            NULL, (GDestroyNotify) free_pointer_device_entry);
1646   priv->touch_sequences =
1647     g_hash_table_new_full (NULL, NULL,
1648                            NULL, (GDestroyNotify) free_pointer_device_entry);
1649 
1650   clutter_actor_set_background_color (CLUTTER_ACTOR (self),
1651                                       &default_stage_color);
1652 
1653   clutter_stage_queue_actor_relayout (self, CLUTTER_ACTOR (self));
1654 
1655   clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE);
1656   clutter_stage_set_title (self, g_get_prgname ());
1657   clutter_stage_set_key_focus (self, NULL);
1658 
1659   g_signal_connect (self, "notify::min-width",
1660                     G_CALLBACK (clutter_stage_notify_min_size), NULL);
1661   g_signal_connect (self, "notify::min-height",
1662                     G_CALLBACK (clutter_stage_notify_min_size), NULL);
1663 
1664   clutter_stage_set_viewport (self, geom.width, geom.height);
1665 
1666   priv->pending_queue_redraws =
1667     g_hash_table_new_full (NULL, NULL,
1668                            g_object_unref,
1669                            (GDestroyNotify) free_queue_redraw_entry);
1670 
1671   priv->paint_volume_stack =
1672     g_array_new (FALSE, FALSE, sizeof (ClutterPaintVolume));
1673 }
1674 
1675 static void
clutter_stage_set_perspective(ClutterStage * stage,ClutterPerspective * perspective)1676 clutter_stage_set_perspective (ClutterStage       *stage,
1677                                ClutterPerspective *perspective)
1678 {
1679   ClutterStagePrivate *priv = stage->priv;
1680 
1681   if (priv->perspective.fovy == perspective->fovy &&
1682       priv->perspective.aspect == perspective->aspect &&
1683       priv->perspective.z_near == perspective->z_near &&
1684       priv->perspective.z_far == perspective->z_far)
1685     return;
1686 
1687   priv->perspective = *perspective;
1688 
1689   graphene_matrix_init_perspective (&priv->projection,
1690                                     priv->perspective.fovy,
1691                                     priv->perspective.aspect,
1692                                     priv->perspective.z_near,
1693                                     priv->perspective.z_far);
1694   graphene_matrix_inverse (&priv->projection,
1695                            &priv->inverse_projection);
1696 
1697   _clutter_stage_dirty_projection (stage);
1698   clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
1699 }
1700 
1701 /**
1702  * clutter_stage_get_perspective:
1703  * @stage: A #ClutterStage
1704  * @perspective: (out caller-allocates) (allow-none): return location for a
1705  *   #ClutterPerspective
1706  *
1707  * Retrieves the stage perspective.
1708  */
1709 void
clutter_stage_get_perspective(ClutterStage * stage,ClutterPerspective * perspective)1710 clutter_stage_get_perspective (ClutterStage       *stage,
1711                                ClutterPerspective *perspective)
1712 {
1713   g_return_if_fail (CLUTTER_IS_STAGE (stage));
1714   g_return_if_fail (perspective != NULL);
1715 
1716   *perspective = stage->priv->perspective;
1717 }
1718 
1719 /*
1720  * clutter_stage_get_projection_matrix:
1721  * @stage: A #ClutterStage
1722  * @projection: return location for a #graphene_matrix_t representing the
1723  *              perspective projection applied to actors on the given
1724  *              @stage.
1725  *
1726  * Retrieves the @stage's projection matrix. This is derived from the
1727  * current perspective.
1728  *
1729  * Since: 1.6
1730  */
1731 void
_clutter_stage_get_projection_matrix(ClutterStage * stage,graphene_matrix_t * projection)1732 _clutter_stage_get_projection_matrix (ClutterStage      *stage,
1733                                       graphene_matrix_t *projection)
1734 {
1735   g_return_if_fail (CLUTTER_IS_STAGE (stage));
1736   g_return_if_fail (projection != NULL);
1737 
1738   *projection = stage->priv->projection;
1739 }
1740 
1741 /* This simply provides a simple mechanism for us to ensure that
1742  * the projection matrix gets re-asserted before painting.
1743  *
1744  * This is used when switching between multiple stages */
1745 void
_clutter_stage_dirty_projection(ClutterStage * stage)1746 _clutter_stage_dirty_projection (ClutterStage *stage)
1747 {
1748   ClutterStagePrivate *priv;
1749   GList *l;
1750 
1751   g_return_if_fail (CLUTTER_IS_STAGE (stage));
1752 
1753   priv = stage->priv;
1754 
1755   for (l = _clutter_stage_window_get_views (priv->impl); l; l = l->next)
1756     {
1757       ClutterStageView *view = l->data;
1758 
1759       clutter_stage_view_invalidate_projection (view);
1760     }
1761 }
1762 
1763 /*
1764  * clutter_stage_set_viewport:
1765  * @stage: A #ClutterStage
1766  * @width: The width to render the stage at, in window coordinates
1767  * @height: The height to render the stage at, in window coordinates
1768  *
1769  * Sets the stage viewport. The viewport defines a final scale and
1770  * translation of your rendered stage and actors. This lets you render
1771  * your stage into a subregion of the stage window or you could use it to
1772  * pan a subregion of the stage if your stage window is smaller then
1773  * the stage. (XXX: currently this isn't possible)
1774  *
1775  * Unlike a scale and translation done using the modelview matrix this
1776  * is done after everything has had perspective projection applied, so
1777  * for example if you were to pan across a subregion of the stage using
1778  * the viewport then you would not see a change in perspective for the
1779  * actors on the stage.
1780  *
1781  * Normally the stage viewport will automatically track the size of the
1782  * stage window with no offset so the stage will fill your window. This
1783  * behaviour can be changed with the "viewport-mimics-window" property
1784  * which will automatically be set to FALSE if you use this API. If
1785  * you want to revert to the original behaviour then you should set
1786  * this property back to %TRUE using
1787  * clutter_stage_set_viewport_mimics_window().
1788  * (XXX: If we were to make this API public then we might want to do
1789  *  add that property.)
1790  *
1791  * Note: currently this interface only support integer precision
1792  * offsets and sizes for viewports but the interface takes floats because
1793  * OpenGL 4.0 has introduced floating point viewports which we might
1794  * want to expose via this API eventually.
1795  *
1796  * Since: 1.6
1797  */
1798 static void
clutter_stage_set_viewport(ClutterStage * stage,float width,float height)1799 clutter_stage_set_viewport (ClutterStage *stage,
1800                             float         width,
1801                             float         height)
1802 {
1803   ClutterStagePrivate *priv;
1804   float x, y;
1805 
1806   g_return_if_fail (CLUTTER_IS_STAGE (stage));
1807 
1808   priv = stage->priv;
1809 
1810   x = 0.f;
1811   y = 0.f;
1812   width = roundf (width);
1813   height = roundf (height);
1814 
1815   if (x == priv->viewport[0] &&
1816       y == priv->viewport[1] &&
1817       width == priv->viewport[2] &&
1818       height == priv->viewport[3])
1819     return;
1820 
1821   priv->viewport[0] = x;
1822   priv->viewport[1] = y;
1823   priv->viewport[2] = width;
1824   priv->viewport[3] = height;
1825 
1826   clutter_stage_update_view_perspective (stage);
1827   _clutter_stage_dirty_viewport (stage);
1828 
1829   queue_full_redraw (stage);
1830 }
1831 
1832 /* This simply provides a simple mechanism for us to ensure that
1833  * the viewport gets re-asserted before next painting.
1834  *
1835  * This is used when switching between multiple stages */
1836 void
_clutter_stage_dirty_viewport(ClutterStage * stage)1837 _clutter_stage_dirty_viewport (ClutterStage *stage)
1838 {
1839   ClutterStagePrivate *priv;
1840   GList *l;
1841 
1842   g_return_if_fail (CLUTTER_IS_STAGE (stage));
1843 
1844   priv = stage->priv;
1845 
1846   for (l = _clutter_stage_window_get_views (priv->impl); l; l = l->next)
1847     {
1848       ClutterStageView *view = l->data;
1849 
1850       clutter_stage_view_invalidate_viewport (view);
1851     }
1852 }
1853 
1854 /*
1855  * clutter_stage_get_viewport:
1856  * @stage: A #ClutterStage
1857  * @x: A location for the X position where the stage is rendered,
1858  *     in window coordinates.
1859  * @y: A location for the Y position where the stage is rendered,
1860  *     in window coordinates.
1861  * @width: A location for the width the stage is rendered at,
1862  *         in window coordinates.
1863  * @height: A location for the height the stage is rendered at,
1864  *          in window coordinates.
1865  *
1866  * Returns the viewport offset and size set using
1867  * clutter_stage_set_viewport() or if the "viewport-mimics-window" property
1868  * is TRUE then @x and @y will be set to 0 and @width and @height will equal
1869  * the width if the stage window.
1870  *
1871  * Since: 1.6
1872  */
1873 void
_clutter_stage_get_viewport(ClutterStage * stage,float * x,float * y,float * width,float * height)1874 _clutter_stage_get_viewport (ClutterStage *stage,
1875                              float        *x,
1876                              float        *y,
1877                              float        *width,
1878                              float        *height)
1879 {
1880   ClutterStagePrivate *priv;
1881 
1882   g_return_if_fail (CLUTTER_IS_STAGE (stage));
1883 
1884   priv = stage->priv;
1885 
1886   *x = priv->viewport[0];
1887   *y = priv->viewport[1];
1888   *width = priv->viewport[2];
1889   *height = priv->viewport[3];
1890 }
1891 
1892 /**
1893  * clutter_stage_read_pixels:
1894  * @stage: A #ClutterStage
1895  * @x: x coordinate of the first pixel that is read from stage
1896  * @y: y coordinate of the first pixel that is read from stage
1897  * @width: Width dimension of pixels to be read, or -1 for the
1898  *   entire stage width
1899  * @height: Height dimension of pixels to be read, or -1 for the
1900  *   entire stage height
1901  *
1902  * Makes a screenshot of the stage in RGBA 8bit data, returns a
1903  * linear buffer with @width * 4 as rowstride.
1904  *
1905  * The alpha data contained in the returned buffer is driver-dependent,
1906  * and not guaranteed to hold any sensible value.
1907  *
1908  * Return value: (transfer full) (array): a pointer to newly allocated memory with the buffer
1909  *   or %NULL if the read failed. Use g_free() on the returned data
1910  *   to release the resources it has allocated.
1911  */
1912 guchar *
clutter_stage_read_pixels(ClutterStage * stage,gint x,gint y,gint width,gint height)1913 clutter_stage_read_pixels (ClutterStage *stage,
1914                            gint          x,
1915                            gint          y,
1916                            gint          width,
1917                            gint          height)
1918 {
1919   ClutterStagePrivate *priv;
1920   ClutterActorBox box;
1921   GList *l;
1922   ClutterStageView *view;
1923   cairo_region_t *clip;
1924   cairo_rectangle_int_t clip_rect;
1925   CoglFramebuffer *framebuffer;
1926   float view_scale;
1927   float pixel_width;
1928   float pixel_height;
1929   uint8_t *pixels;
1930 
1931   COGL_TRACE_BEGIN_SCOPED (ClutterStageReadPixels, "Read Pixels");
1932 
1933   g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
1934 
1935   priv = stage->priv;
1936 
1937   clutter_actor_get_allocation_box (CLUTTER_ACTOR (stage), &box);
1938 
1939   if (width < 0)
1940     width = ceilf (box.x2 - box.x1);
1941 
1942   if (height < 0)
1943     height = ceilf (box.y2 - box.y1);
1944 
1945   l = _clutter_stage_window_get_views (priv->impl);
1946 
1947   if (!l)
1948     return NULL;
1949 
1950   /* XXX: We only read the first view. Needs different API for multi view screen
1951    * capture. */
1952   view = l->data;
1953 
1954   clutter_stage_view_get_layout (view, &clip_rect);
1955   clip = cairo_region_create_rectangle (&clip_rect);
1956   cairo_region_intersect_rectangle (clip,
1957                                     &(cairo_rectangle_int_t) {
1958                                       .x = x,
1959                                       .y = y,
1960                                       .width = width,
1961                                       .height = height,
1962                                     });
1963   cairo_region_get_extents (clip, &clip_rect);
1964 
1965   if (clip_rect.width == 0 || clip_rect.height == 0)
1966     {
1967       cairo_region_destroy (clip);
1968       return NULL;
1969     }
1970 
1971   framebuffer = clutter_stage_view_get_framebuffer (view);
1972   clutter_stage_do_paint_view (stage, view, clip);
1973 
1974   cairo_region_destroy (clip);
1975 
1976   view_scale = clutter_stage_view_get_scale (view);
1977   pixel_width = roundf (clip_rect.width * view_scale);
1978   pixel_height = roundf (clip_rect.height * view_scale);
1979 
1980   pixels = g_malloc0 (pixel_width * pixel_height * 4);
1981   cogl_framebuffer_read_pixels (framebuffer,
1982                                 clip_rect.x * view_scale,
1983                                 clip_rect.y * view_scale,
1984                                 pixel_width, pixel_height,
1985                                 COGL_PIXEL_FORMAT_RGBA_8888,
1986                                 pixels);
1987 
1988   return pixels;
1989 }
1990 
1991 /**
1992  * clutter_stage_get_actor_at_pos:
1993  * @stage: a #ClutterStage
1994  * @pick_mode: how the scene graph should be painted
1995  * @x: X coordinate to check
1996  * @y: Y coordinate to check
1997  *
1998  * Checks the scene at the coordinates @x and @y and returns a pointer
1999  * to the #ClutterActor at those coordinates. The result is the actor which
2000  * would be at the specified location on the next redraw, and is not
2001  * necessarily that which was there on the previous redraw. This allows the
2002  * function to perform chronologically correctly after any queued changes to
2003  * the scene, and even if nothing has been drawn.
2004  *
2005  * By using @pick_mode it is possible to control which actors will be
2006  * painted and thus available.
2007  *
2008  * Return value: (transfer none): the actor at the specified coordinates,
2009  *   if any
2010  */
2011 ClutterActor *
clutter_stage_get_actor_at_pos(ClutterStage * stage,ClutterPickMode pick_mode,float x,float y)2012 clutter_stage_get_actor_at_pos (ClutterStage    *stage,
2013                                 ClutterPickMode  pick_mode,
2014                                 float            x,
2015                                 float            y)
2016 {
2017   g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
2018 
2019   return _clutter_stage_do_pick (stage, x, y, pick_mode);
2020 }
2021 
2022 /**
2023  * clutter_stage_set_title:
2024  * @stage: A #ClutterStage
2025  * @title: A utf8 string for the stage windows title.
2026  *
2027  * Sets the stage title.
2028  *
2029  * Since: 0.4
2030  **/
2031 void
clutter_stage_set_title(ClutterStage * stage,const gchar * title)2032 clutter_stage_set_title (ClutterStage       *stage,
2033 			 const gchar        *title)
2034 {
2035   ClutterStagePrivate *priv;
2036   ClutterStageWindow *impl;
2037 
2038   g_return_if_fail (CLUTTER_IS_STAGE (stage));
2039 
2040   priv = stage->priv;
2041 
2042   g_free (priv->title);
2043   priv->title = g_strdup (title);
2044 
2045   impl = CLUTTER_STAGE_WINDOW (priv->impl);
2046   if (CLUTTER_STAGE_WINDOW_GET_IFACE(impl)->set_title != NULL)
2047     CLUTTER_STAGE_WINDOW_GET_IFACE (impl)->set_title (impl, priv->title);
2048 
2049   g_object_notify_by_pspec (G_OBJECT (stage), obj_props[PROP_TITLE]);
2050 }
2051 
2052 /**
2053  * clutter_stage_get_title:
2054  * @stage: A #ClutterStage
2055  *
2056  * Gets the stage title.
2057  *
2058  * Return value: pointer to the title string for the stage. The
2059  * returned string is owned by the actor and should not
2060  * be modified or freed.
2061  *
2062  * Since: 0.4
2063  **/
2064 const gchar *
clutter_stage_get_title(ClutterStage * stage)2065 clutter_stage_get_title (ClutterStage       *stage)
2066 {
2067   g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
2068 
2069   return stage->priv->title;
2070 }
2071 
2072 /**
2073  * clutter_stage_set_key_focus:
2074  * @stage: the #ClutterStage
2075  * @actor: (allow-none): the actor to set key focus to, or %NULL
2076  *
2077  * Sets the key focus on @actor. An actor with key focus will receive
2078  * all the key events. If @actor is %NULL, the stage will receive
2079  * focus.
2080  *
2081  * Since: 0.6
2082  */
2083 void
clutter_stage_set_key_focus(ClutterStage * stage,ClutterActor * actor)2084 clutter_stage_set_key_focus (ClutterStage *stage,
2085 			     ClutterActor *actor)
2086 {
2087   ClutterStagePrivate *priv;
2088 
2089   g_return_if_fail (CLUTTER_IS_STAGE (stage));
2090   g_return_if_fail (actor == NULL || CLUTTER_IS_ACTOR (actor));
2091 
2092   priv = stage->priv;
2093 
2094   /* normalize the key focus. NULL == stage */
2095   if (actor == CLUTTER_ACTOR (stage))
2096     actor = NULL;
2097 
2098   /* avoid emitting signals and notifications if we're setting the same
2099    * actor as the key focus
2100    */
2101   if (priv->key_focused_actor == actor)
2102     return;
2103 
2104   if (priv->key_focused_actor != NULL)
2105     {
2106       ClutterActor *old_focused_actor;
2107 
2108       old_focused_actor = priv->key_focused_actor;
2109 
2110       /* set key_focused_actor to NULL before emitting the signal or someone
2111        * might hide the previously focused actor in the signal handler
2112        */
2113       priv->key_focused_actor = NULL;
2114 
2115       _clutter_actor_set_has_key_focus (old_focused_actor, FALSE);
2116     }
2117   else
2118     _clutter_actor_set_has_key_focus (CLUTTER_ACTOR (stage), FALSE);
2119 
2120   /* Note, if someone changes key focus in focus-out signal handler we'd be
2121    * overriding the latter call below moving the focus where it was originally
2122    * intended. The order of events would be:
2123    *   1st focus-out, 2nd focus-out (on stage), 2nd focus-in, 1st focus-in
2124    */
2125   if (actor != NULL)
2126     {
2127       priv->key_focused_actor = actor;
2128       _clutter_actor_set_has_key_focus (actor, TRUE);
2129     }
2130   else
2131     _clutter_actor_set_has_key_focus (CLUTTER_ACTOR (stage), TRUE);
2132 
2133   g_object_notify_by_pspec (G_OBJECT (stage), obj_props[PROP_KEY_FOCUS]);
2134 }
2135 
2136 /**
2137  * clutter_stage_get_key_focus:
2138  * @stage: the #ClutterStage
2139  *
2140  * Retrieves the actor that is currently under key focus.
2141  *
2142  * Return value: (transfer none): the actor with key focus, or the stage
2143  *
2144  * Since: 0.6
2145  */
2146 ClutterActor *
clutter_stage_get_key_focus(ClutterStage * stage)2147 clutter_stage_get_key_focus (ClutterStage *stage)
2148 {
2149   g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
2150 
2151   if (stage->priv->key_focused_actor)
2152     return stage->priv->key_focused_actor;
2153 
2154   return CLUTTER_ACTOR (stage);
2155 }
2156 
2157 /*** Perspective boxed type ******/
2158 
2159 static gpointer
clutter_perspective_copy(gpointer data)2160 clutter_perspective_copy (gpointer data)
2161 {
2162   if (G_LIKELY (data))
2163     return g_memdup2 (data, sizeof (ClutterPerspective));
2164 
2165   return NULL;
2166 }
2167 
2168 static void
clutter_perspective_free(gpointer data)2169 clutter_perspective_free (gpointer data)
2170 {
2171   if (G_LIKELY (data))
2172     g_free (data);
2173 }
2174 
2175 G_DEFINE_BOXED_TYPE (ClutterPerspective, clutter_perspective,
2176                      clutter_perspective_copy,
2177                      clutter_perspective_free);
2178 
2179 /**
2180  * clutter_stage_ensure_viewport:
2181  * @stage: a #ClutterStage
2182  *
2183  * Ensures that the GL viewport is updated with the current
2184  * stage window size.
2185  *
2186  * This function will queue a redraw of @stage.
2187  *
2188  * This function should not be called by applications; it is used
2189  * when embedding a #ClutterStage into a toolkit with another
2190  * windowing system, like GTK+.
2191  *
2192  * Since: 1.0
2193  */
2194 void
clutter_stage_ensure_viewport(ClutterStage * stage)2195 clutter_stage_ensure_viewport (ClutterStage *stage)
2196 {
2197   g_return_if_fail (CLUTTER_IS_STAGE (stage));
2198 
2199   _clutter_stage_dirty_viewport (stage);
2200 
2201   clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
2202 }
2203 
2204 # define _DEG_TO_RAD(d)         ((d) * ((float) G_PI / 180.0f))
2205 
2206 /* This calculates a distance into the view frustum to position the
2207  * stage so there is a decent amount of space to position geometry
2208  * between the stage and the near clipping plane.
2209  *
2210  * Some awkward issues with this problem are:
2211  * - It's not possible to have a gap as large as the stage size with
2212  *   a fov > 53° which is basically always the case since the default
2213  *   fov is 60°.
2214  *    - This can be deduced if you consider that this requires a
2215  *      triangle as wide as it is deep to fit in the frustum in front
2216  *      of the z_near plane. That triangle will always have an angle
2217  *      of 53.13° at the point sitting on the z_near plane, but if the
2218  *      frustum has a wider fov angle the left/right clipping planes
2219  *      can never converge with the two corners of our triangle no
2220  *      matter what size the triangle has.
2221  * - With a fov > 53° there is a trade off between maximizing the gap
2222  *   size relative to the stage size but not losing depth precision.
2223  * - Perhaps ideally we wouldn't just consider the fov on the y-axis
2224  *   that is usually used to define a perspective, we would consider
2225  *   the fov of the axis with the largest stage size so the gap would
2226  *   accommodate that size best.
2227  *
2228  * After going around in circles a few times with how to handle these
2229  * issues, we decided in the end to go for the simplest solution to
2230  * start with instead of an elaborate function that handles arbitrary
2231  * fov angles that we currently have no use-case for.
2232  *
2233  * The solution assumes a fovy of 60° and for that case gives a gap
2234  * that's 85% of the stage height. We can consider more elaborate
2235  * functions if necessary later.
2236  *
2237  * One guide we had to steer the gap size we support is the
2238  * interactive test, test-texture-quality which expects to animate an
2239  * actor to +400 on the z axis with a stage size of 640x480. A gap
2240  * that's 85% of the stage height gives a gap of 408 in that case.
2241  */
2242 static float
calculate_z_translation(float z_near)2243 calculate_z_translation (float z_near)
2244 {
2245   /* This solution uses fairly basic trigonometry, but is seems worth
2246    * clarifying the particular geometry we are looking at in-case
2247    * anyone wants to develop this further later. Not sure how well an
2248    * ascii diagram is going to work :-)
2249    *
2250    *    |--- stage_height ---|
2251    *    |     stage line     |
2252    *   ╲━━━━━━━━━━━━━━━━━━━━━╱------------
2253    *    ╲.  (2)   │        .╱       |   |
2254    *   C ╲ .      │      . ╱     gap|   |
2255    * =0.5°╲  . a  │    .  ╱         |   |
2256    *      b╲(1). D│  .   ╱          |   |
2257    *        ╲   B.│.    ╱near plane |   |
2258    *      A= ╲━━━━━━━━━╱-------------   |
2259    *     120° ╲ c │   ╱  |            z_2d
2260    *           ╲  │  ╱  z_near          |
2261    *       left ╲ │ ╱    |              |
2262    *       clip  60°fovy |              |
2263    *       plane  ╳----------------------
2264    *              |
2265    *              |
2266    *         origin line
2267    *
2268    * The area of interest is the triangle labeled (1) at the top left
2269    * marked with the ... line (a) from where the origin line crosses
2270    * the near plane to the top left where the stage line cross the
2271    * left clip plane.
2272    *
2273    * The sides of the triangle are a, b and c and the corresponding
2274    * angles opposite those sides are A, B and C.
2275    *
2276    * The angle of C is what trades off the gap size we have relative
2277    * to the stage size vs the depth precision we have.
2278    *
2279    * As mentioned above we arove at the angle for C is by working
2280    * backwards from how much space we want for test-texture-quality.
2281    * With a stage_height of 480 we want a gap > 400, ideally we also
2282    * wanted a somewhat round number as a percentage of the height for
2283    * documentation purposes. ~87% or a gap of ~416 is the limit
2284    * because that's where we approach a C angle of 0° and effectively
2285    * loose all depth precision.
2286    *
2287    * So for our test app with a stage_height of 480 if we aim for a
2288    * gap of 408 (85% of 480) we can get the angle D as
2289    * atan (stage_height/2/408) = 30.5°.
2290    *
2291    * That gives us the angle for B as 90° - 30.5° = 59.5°
2292    *
2293    * We can already determine that A has an angle of (fovy/2 + 90°) =
2294    * 120°
2295    *
2296    * Therefore C = 180 - A - B = 0.5°
2297    *
2298    * The length of c = z_near * tan (30°)
2299    *
2300    * Now we can use the rule a/SinA = c/SinC to calculate the
2301    * length of a. After some rearranging that gives us:
2302    *
2303    *      a              c
2304    *  ----------  =  ----------
2305    *  sin (120°)     sin (0.5°)
2306    *
2307    *      c * sin (120°)
2308    *  a = --------------
2309    *        sin (0.5°)
2310    *
2311    * And with that we can determine z_2d = cos (D) * a =
2312    * cos (30.5°) * a + z_near:
2313    *
2314    *         c * sin (120°) * cos (30.5°)
2315    *  z_2d = --------------------------- + z_near
2316    *                 sin (0.5°)
2317    */
2318 
2319    /* We expect the compiler should boil this down to z_near * CONSTANT
2320     * already, but just in case we use precomputed constants
2321     */
2322 #if 0
2323 # define A      tanf (_DEG_TO_RAD (30.f))
2324 # define B      sinf (_DEG_TO_RAD (120.f))
2325 # define C      cosf (_DEG_TO_RAD (30.5f))
2326 # define D      sinf (_DEG_TO_RAD (.5f))
2327 #else
2328 # define A      0.57735025882720947265625f
2329 # define B      0.866025388240814208984375f
2330 # define C      0.86162912845611572265625f
2331 # define D      0.00872653536498546600341796875f
2332 #endif
2333 
2334   return z_near
2335        * A * B * C
2336        / D
2337        + z_near;
2338 }
2339 
2340 static void
view_2d_in_perspective(graphene_matrix_t * matrix,float fov_y,float aspect,float z_near,float z_2d,float width_2d,float height_2d)2341 view_2d_in_perspective (graphene_matrix_t *matrix,
2342                         float              fov_y,
2343                         float              aspect,
2344                         float              z_near,
2345                         float              z_2d,
2346                         float              width_2d,
2347                         float              height_2d)
2348 {
2349   float top = z_near * tan (fov_y * G_PI / 360.0);
2350   float left = -top * aspect;
2351   float right = top * aspect;
2352   float bottom = -top;
2353 
2354   float left_2d_plane = left / z_near * z_2d;
2355   float right_2d_plane = right / z_near * z_2d;
2356   float bottom_2d_plane = bottom / z_near * z_2d;
2357   float top_2d_plane = top / z_near * z_2d;
2358 
2359   float width_2d_start = right_2d_plane - left_2d_plane;
2360   float height_2d_start = top_2d_plane - bottom_2d_plane;
2361 
2362   /* Factors to scale from framebuffer geometry to frustum
2363    * cross-section geometry. */
2364   float width_scale = width_2d_start / width_2d;
2365   float height_scale = height_2d_start / height_2d;
2366 
2367   graphene_matrix_init_scale (matrix, width_scale, -height_scale, width_scale);
2368   graphene_matrix_translate (matrix,
2369                              &GRAPHENE_POINT3D_INIT (left_2d_plane,
2370                                                      top_2d_plane,
2371                                                      -z_2d));
2372 }
2373 
2374 static void
clutter_stage_update_view_perspective(ClutterStage * stage)2375 clutter_stage_update_view_perspective (ClutterStage *stage)
2376 {
2377   ClutterStagePrivate *priv = stage->priv;
2378   ClutterPerspective perspective;
2379   float z_2d;
2380 
2381   perspective = priv->perspective;
2382 
2383   perspective.fovy = 60.0; /* 60 Degrees */
2384   perspective.z_near = 1.0;
2385   perspective.aspect = priv->viewport[2] / priv->viewport[3];
2386   z_2d = calculate_z_translation (perspective.z_near);
2387 
2388   /* NB: z_2d is only enough room for 85% of the stage_height between
2389    * the stage and the z_near plane. For behind the stage plane we
2390    * want a more consistent gap of 10 times the stage_height before
2391    * hitting the far plane so we calculate that relative to the final
2392    * height of the stage plane at the z_2d_distance we got... */
2393   perspective.z_far = z_2d +
2394     tanf (_DEG_TO_RAD (perspective.fovy / 2.0f)) * z_2d * 20.0f;
2395 
2396   clutter_stage_set_perspective (stage, &perspective);
2397 
2398   view_2d_in_perspective (&priv->view,
2399                           perspective.fovy,
2400                           perspective.aspect,
2401                           perspective.z_near,
2402                           z_2d,
2403                           priv->viewport[2],
2404                           priv->viewport[3]);
2405 
2406   clutter_actor_invalidate_transform (CLUTTER_ACTOR (stage));
2407 }
2408 
2409 void
_clutter_stage_maybe_setup_viewport(ClutterStage * stage,ClutterStageView * view)2410 _clutter_stage_maybe_setup_viewport (ClutterStage     *stage,
2411                                      ClutterStageView *view)
2412 {
2413   ClutterStagePrivate *priv = stage->priv;
2414 
2415   if (clutter_stage_view_is_dirty_viewport (view))
2416     {
2417       cairo_rectangle_int_t view_layout;
2418       float fb_scale;
2419       float viewport_offset_x;
2420       float viewport_offset_y;
2421       float viewport_x;
2422       float viewport_y;
2423       float viewport_width;
2424       float viewport_height;
2425 
2426       CLUTTER_NOTE (PAINT,
2427                     "Setting up the viewport { w:%f, h:%f }",
2428                     priv->viewport[2],
2429                     priv->viewport[3]);
2430 
2431       fb_scale = clutter_stage_view_get_scale (view);
2432       clutter_stage_view_get_layout (view, &view_layout);
2433 
2434       viewport_offset_x = view_layout.x * fb_scale;
2435       viewport_offset_y = view_layout.y * fb_scale;
2436       viewport_x = roundf (priv->viewport[0] * fb_scale - viewport_offset_x);
2437       viewport_y = roundf (priv->viewport[1] * fb_scale - viewport_offset_y);
2438       viewport_width = roundf (priv->viewport[2] * fb_scale);
2439       viewport_height = roundf (priv->viewport[3] * fb_scale);
2440 
2441       clutter_stage_view_set_viewport (view,
2442                                        viewport_x, viewport_y,
2443                                        viewport_width, viewport_height);
2444     }
2445 
2446   if (clutter_stage_view_is_dirty_projection (view))
2447     clutter_stage_view_set_projection (view, &priv->projection);
2448 }
2449 
2450 #undef _DEG_TO_RAD
2451 
2452 /**
2453  * clutter_stage_is_redraw_queued_on_view: (skip)
2454  */
2455 gboolean
clutter_stage_is_redraw_queued_on_view(ClutterStage * stage,ClutterStageView * view)2456 clutter_stage_is_redraw_queued_on_view (ClutterStage     *stage,
2457                                         ClutterStageView *view)
2458 {
2459   clutter_stage_maybe_finish_queue_redraws (stage);
2460 
2461   return clutter_stage_view_has_redraw_clip (view);
2462 }
2463 
2464 void
_clutter_stage_set_window(ClutterStage * stage,ClutterStageWindow * stage_window)2465 _clutter_stage_set_window (ClutterStage       *stage,
2466                            ClutterStageWindow *stage_window)
2467 {
2468   g_return_if_fail (CLUTTER_IS_STAGE (stage));
2469   g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (stage_window));
2470 
2471   if (stage->priv->impl != NULL)
2472     g_object_unref (stage->priv->impl);
2473 
2474   stage->priv->impl = stage_window;
2475 }
2476 
2477 ClutterStageWindow *
_clutter_stage_get_window(ClutterStage * stage)2478 _clutter_stage_get_window (ClutterStage *stage)
2479 {
2480   g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
2481 
2482   return CLUTTER_STAGE_WINDOW (stage->priv->impl);
2483 }
2484 
2485 ClutterStageWindow *
_clutter_stage_get_default_window(void)2486 _clutter_stage_get_default_window (void)
2487 {
2488   ClutterStageManager *manager = clutter_stage_manager_get_default ();
2489   ClutterStage *stage;
2490 
2491   stage = clutter_stage_manager_get_default_stage (manager);
2492   if (stage == NULL)
2493     return NULL;
2494 
2495   return _clutter_stage_get_window (stage);
2496 }
2497 
2498 /**
2499  * clutter_stage_set_throttle_motion_events:
2500  * @stage: a #ClutterStage
2501  * @throttle: %TRUE to throttle motion events
2502  *
2503  * Sets whether motion events received between redraws should
2504  * be throttled or not. If motion events are throttled, those
2505  * events received by the windowing system between redraws will
2506  * be compressed so that only the last event will be propagated
2507  * to the @stage and its actors.
2508  *
2509  * This function should only be used if you want to have all
2510  * the motion events delivered to your application code.
2511  *
2512  * Since: 1.0
2513  */
2514 void
clutter_stage_set_throttle_motion_events(ClutterStage * stage,gboolean throttle)2515 clutter_stage_set_throttle_motion_events (ClutterStage *stage,
2516                                           gboolean      throttle)
2517 {
2518   ClutterStagePrivate *priv;
2519 
2520   g_return_if_fail (CLUTTER_IS_STAGE (stage));
2521 
2522   priv = stage->priv;
2523 
2524   if (priv->throttle_motion_events != throttle)
2525     priv->throttle_motion_events = throttle;
2526 }
2527 
2528 /**
2529  * clutter_stage_get_throttle_motion_events:
2530  * @stage: a #ClutterStage
2531  *
2532  * Retrieves the value set with clutter_stage_set_throttle_motion_events()
2533  *
2534  * Return value: %TRUE if the motion events are being throttled,
2535  *   and %FALSE otherwise
2536  *
2537  * Since: 1.0
2538  */
2539 gboolean
clutter_stage_get_throttle_motion_events(ClutterStage * stage)2540 clutter_stage_get_throttle_motion_events (ClutterStage *stage)
2541 {
2542   g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
2543 
2544   return stage->priv->throttle_motion_events;
2545 }
2546 
2547 /**
2548  * clutter_stage_set_minimum_size:
2549  * @stage: a #ClutterStage
2550  * @width: width, in pixels
2551  * @height: height, in pixels
2552  *
2553  * Sets the minimum size for a stage window, if the default backend
2554  * uses #ClutterStage inside a window
2555  *
2556  * This is a convenience function, and it is equivalent to setting the
2557  * #ClutterActor:min-width and #ClutterActor:min-height on @stage
2558  *
2559  * If the current size of @stage is smaller than the minimum size, the
2560  * @stage will be resized to the new @width and @height
2561  *
2562  * Since: 1.2
2563  */
2564 void
clutter_stage_set_minimum_size(ClutterStage * stage,guint width,guint height)2565 clutter_stage_set_minimum_size (ClutterStage *stage,
2566                                 guint         width,
2567                                 guint         height)
2568 {
2569   g_return_if_fail (CLUTTER_IS_STAGE (stage));
2570   g_return_if_fail ((width > 0) && (height > 0));
2571 
2572   g_object_set (G_OBJECT (stage),
2573                 "min-width", (gfloat) width,
2574                 "min-height", (gfloat )height,
2575                 NULL);
2576 }
2577 
2578 /**
2579  * clutter_stage_get_minimum_size:
2580  * @stage: a #ClutterStage
2581  * @width: (out): return location for the minimum width, in pixels,
2582  *   or %NULL
2583  * @height: (out): return location for the minimum height, in pixels,
2584  *   or %NULL
2585  *
2586  * Retrieves the minimum size for a stage window as set using
2587  * clutter_stage_set_minimum_size().
2588  *
2589  * The returned size may not correspond to the actual minimum size and
2590  * it is specific to the #ClutterStage implementation inside the
2591  * Clutter backend
2592  *
2593  * Since: 1.2
2594  */
2595 void
clutter_stage_get_minimum_size(ClutterStage * stage,guint * width_p,guint * height_p)2596 clutter_stage_get_minimum_size (ClutterStage *stage,
2597                                 guint        *width_p,
2598                                 guint        *height_p)
2599 {
2600   gfloat width, height;
2601   gboolean width_set, height_set;
2602 
2603   g_return_if_fail (CLUTTER_IS_STAGE (stage));
2604 
2605   g_object_get (G_OBJECT (stage),
2606                 "min-width", &width,
2607                 "min-width-set", &width_set,
2608                 "min-height", &height,
2609                 "min-height-set", &height_set,
2610                 NULL);
2611 
2612   /* if not width or height have been set, then the Stage
2613    * minimum size is defined to be 1x1
2614    */
2615   if (!width_set)
2616     width = 1;
2617 
2618   if (!height_set)
2619     height = 1;
2620 
2621   if (width_p)
2622     *width_p = (guint) width;
2623 
2624   if (height_p)
2625     *height_p = (guint) height;
2626 }
2627 
2628 /**
2629  * clutter_stage_schedule_update:
2630  * @stage: a #ClutterStage actor
2631  *
2632  * Schedules a redraw of the #ClutterStage at the next optimal timestamp.
2633  */
2634 void
clutter_stage_schedule_update(ClutterStage * stage)2635 clutter_stage_schedule_update (ClutterStage *stage)
2636 {
2637   ClutterStageWindow *stage_window;
2638   GList *l;
2639 
2640   if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
2641     return;
2642 
2643   stage_window = _clutter_stage_get_window (stage);
2644   if (stage_window == NULL)
2645     return;
2646 
2647   for (l = clutter_stage_peek_stage_views (stage); l; l = l->next)
2648     {
2649       ClutterStageView *view = l->data;
2650 
2651       clutter_stage_view_schedule_update (view);
2652     }
2653 }
2654 
2655 ClutterPaintVolume *
_clutter_stage_paint_volume_stack_allocate(ClutterStage * stage)2656 _clutter_stage_paint_volume_stack_allocate (ClutterStage *stage)
2657 {
2658   GArray *paint_volume_stack = stage->priv->paint_volume_stack;
2659 
2660   g_array_set_size (paint_volume_stack,
2661                     paint_volume_stack->len+1);
2662 
2663   return &g_array_index (paint_volume_stack,
2664                          ClutterPaintVolume,
2665                          paint_volume_stack->len - 1);
2666 }
2667 
2668 void
_clutter_stage_paint_volume_stack_free_all(ClutterStage * stage)2669 _clutter_stage_paint_volume_stack_free_all (ClutterStage *stage)
2670 {
2671   GArray *paint_volume_stack = stage->priv->paint_volume_stack;
2672   int i;
2673 
2674   for (i = 0; i < paint_volume_stack->len; i++)
2675     {
2676       ClutterPaintVolume *pv =
2677         &g_array_index (paint_volume_stack, ClutterPaintVolume, i);
2678       clutter_paint_volume_free (pv);
2679     }
2680 
2681   g_array_set_size (paint_volume_stack, 0);
2682 }
2683 
2684 /* When an actor queues a redraw we add it to a list on the stage that
2685  * gets processed once all updates to the stage have been finished.
2686  *
2687  * This deferred approach to processing queue_redraw requests means
2688  * that we can avoid redundant transformations of clip volumes if
2689  * something later triggers a full stage redraw anyway. It also means
2690  * we can be more sure that all the referenced actors will have valid
2691  * allocations improving the chance that we can determine the actors
2692  * paint volume so we can clip the redraw request even if the user
2693  * didn't explicitly do so.
2694  */
2695 void
clutter_stage_queue_actor_redraw(ClutterStage * stage,ClutterActor * actor,const ClutterPaintVolume * clip)2696 clutter_stage_queue_actor_redraw (ClutterStage             *stage,
2697                                   ClutterActor             *actor,
2698                                   const ClutterPaintVolume *clip)
2699 {
2700   ClutterStagePrivate *priv = stage->priv;
2701   QueueRedrawEntry *entry = NULL;
2702 
2703   CLUTTER_NOTE (CLIPPING, "stage_queue_actor_redraw (actor=%s, clip=%p): ",
2704                 _clutter_actor_get_debug_name (actor), clip);
2705 
2706   if (!priv->pending_finish_queue_redraws)
2707     {
2708       GList *l;
2709 
2710       for (l = clutter_stage_peek_stage_views (stage); l; l = l->next)
2711         {
2712           ClutterStageView *view = l->data;
2713 
2714           clutter_stage_view_schedule_update (view);
2715         }
2716 
2717       priv->pending_finish_queue_redraws = TRUE;
2718     }
2719 
2720   entry = g_hash_table_lookup (priv->pending_queue_redraws, actor);
2721 
2722   if (entry)
2723     {
2724       /* Ignore all requests to queue a redraw for an actor if a full
2725        * (non-clipped) redraw of the actor has already been queued. */
2726       if (!entry->has_clip)
2727         {
2728           CLUTTER_NOTE (CLIPPING, "Bail from stage_queue_actor_redraw (%s): "
2729                         "Unclipped redraw of actor already queued",
2730                         _clutter_actor_get_debug_name (actor));
2731           return;
2732         }
2733 
2734       /* If queuing a clipped redraw and a clipped redraw has
2735        * previously been queued for this actor then combine the latest
2736        * clip together with the existing clip */
2737       if (clip)
2738         clutter_paint_volume_union (&entry->clip, clip);
2739       else
2740         {
2741           clutter_paint_volume_free (&entry->clip);
2742           entry->has_clip = FALSE;
2743         }
2744     }
2745   else
2746     {
2747       entry = g_new0 (QueueRedrawEntry, 1);
2748 
2749       if (clip)
2750         {
2751           entry->has_clip = TRUE;
2752           _clutter_paint_volume_init_static (&entry->clip, actor);
2753           _clutter_paint_volume_set_from_volume (&entry->clip, clip);
2754         }
2755       else
2756         entry->has_clip = FALSE;
2757 
2758       g_hash_table_insert (priv->pending_queue_redraws,
2759                            g_object_ref (actor), entry);
2760     }
2761 }
2762 
2763 static void
free_queue_redraw_entry(QueueRedrawEntry * entry)2764 free_queue_redraw_entry (QueueRedrawEntry *entry)
2765 {
2766   if (entry->has_clip)
2767     clutter_paint_volume_free (&entry->clip);
2768   g_free (entry);
2769 }
2770 
2771 void
clutter_stage_dequeue_actor_redraw(ClutterStage * self,ClutterActor * actor)2772 clutter_stage_dequeue_actor_redraw (ClutterStage *self,
2773                                     ClutterActor *actor)
2774 {
2775   g_hash_table_remove (self->priv->pending_queue_redraws, actor);
2776 }
2777 
2778 static void
add_to_stage_clip(ClutterStage * stage,ClutterPaintVolume * redraw_clip)2779 add_to_stage_clip (ClutterStage       *stage,
2780                    ClutterPaintVolume *redraw_clip)
2781 {
2782   ClutterStageWindow *stage_window;
2783   ClutterActorBox bounding_box;
2784   ClutterActorBox intersection_box;
2785   cairo_rectangle_int_t geom, stage_clip;
2786 
2787   if (CLUTTER_ACTOR_IN_DESTRUCTION (CLUTTER_ACTOR (stage)))
2788     return;
2789 
2790   stage_window = _clutter_stage_get_window (stage);
2791   if (stage_window == NULL)
2792     return;
2793 
2794   if (is_full_stage_redraw_queued (stage))
2795     return;
2796 
2797   if (redraw_clip == NULL)
2798     {
2799       clutter_stage_add_redraw_clip (stage, NULL);
2800       return;
2801     }
2802 
2803   if (redraw_clip->is_empty)
2804     return;
2805 
2806   /* Now transform and project the clip volume to view coordinates and get
2807    * the axis aligned bounding box that's aligned to the pixel grid.
2808    */
2809   _clutter_paint_volume_get_stage_paint_box (redraw_clip,
2810                                              stage,
2811                                              &bounding_box);
2812 
2813   _clutter_stage_window_get_geometry (stage_window, &geom);
2814 
2815   intersection_box.x1 = MAX (bounding_box.x1, 0);
2816   intersection_box.y1 = MAX (bounding_box.y1, 0);
2817   intersection_box.x2 = MIN (bounding_box.x2, geom.width);
2818   intersection_box.y2 = MIN (bounding_box.y2, geom.height);
2819 
2820   /* There is no need to track degenerate/empty redraw clips */
2821   if (intersection_box.x2 <= intersection_box.x1 ||
2822       intersection_box.y2 <= intersection_box.y1)
2823     return;
2824 
2825   stage_clip.x = intersection_box.x1;
2826   stage_clip.y = intersection_box.y1;
2827   stage_clip.width = intersection_box.x2 - stage_clip.x;
2828   stage_clip.height = intersection_box.y2 - stage_clip.y;
2829 
2830   clutter_stage_add_redraw_clip (stage, &stage_clip);
2831 }
2832 
2833 void
clutter_stage_maybe_finish_queue_redraws(ClutterStage * stage)2834 clutter_stage_maybe_finish_queue_redraws (ClutterStage *stage)
2835 {
2836   ClutterStagePrivate *priv = stage->priv;
2837   GHashTableIter iter;
2838   gpointer key, value;
2839 
2840   COGL_TRACE_BEGIN_SCOPED (ClutterStageFinishQueueRedraws, "FinishQueueRedraws");
2841 
2842   if (!priv->pending_finish_queue_redraws)
2843     return;
2844 
2845   priv->pending_finish_queue_redraws = FALSE;
2846 
2847   g_hash_table_iter_init (&iter, priv->pending_queue_redraws);
2848   while (g_hash_table_iter_next (&iter, &key, &value))
2849     {
2850       ClutterActor *redraw_actor = key;
2851       QueueRedrawEntry *entry = value;
2852 
2853       g_hash_table_iter_steal (&iter);
2854 
2855       if (clutter_actor_is_mapped (redraw_actor))
2856         {
2857           ClutterPaintVolume old_actor_pv, new_actor_pv;
2858 
2859           _clutter_paint_volume_init_static (&old_actor_pv, NULL);
2860           _clutter_paint_volume_init_static (&new_actor_pv, NULL);
2861 
2862           if (entry->has_clip)
2863             {
2864               add_to_stage_clip (stage, &entry->clip);
2865             }
2866           else if (clutter_actor_get_redraw_clip (redraw_actor,
2867                                                   &old_actor_pv,
2868                                                   &new_actor_pv))
2869             {
2870               /* Add both the old paint volume of the actor (which is
2871                * currently visible on the screen) and the new paint volume
2872                * (which will be visible on the screen after this redraw)
2873                * to the redraw clip.
2874                * The former we do to ensure the old texture on the screen
2875                * will be fully painted over in case the actor was moved.
2876                */
2877               add_to_stage_clip (stage, &old_actor_pv);
2878               add_to_stage_clip (stage, &new_actor_pv);
2879             }
2880           else
2881             {
2882               /* If there's no clip we can use, we have to trigger an
2883                * unclipped full stage redraw.
2884                */
2885               add_to_stage_clip (stage, NULL);
2886             }
2887         }
2888 
2889       g_object_unref (redraw_actor);
2890       free_queue_redraw_entry (entry);
2891 
2892       /* get_paint_volume() vfuncs might queue redraws and can cause our
2893        * iterator to now be invalidated. So start over. This isn't wasting
2894        * any time since we already stole (removed) the elements previously
2895        * visited.
2896        */
2897       g_hash_table_iter_init (&iter, priv->pending_queue_redraws);
2898     }
2899 }
2900 
2901 /**
2902  * clutter_stage_set_motion_events_enabled:
2903  * @stage: a #ClutterStage
2904  * @enabled: %TRUE to enable the motion events delivery, and %FALSE
2905  *   otherwise
2906  *
2907  * Sets whether per-actor motion events (and relative crossing
2908  * events) should be disabled or not.
2909  *
2910  * The default is %TRUE.
2911  *
2912  * If @enable is %FALSE the following signals will not be emitted
2913  * by the actors children of @stage:
2914  *
2915  *  - #ClutterActor::motion-event
2916  *  - #ClutterActor::enter-event
2917  *  - #ClutterActor::leave-event
2918  *
2919  * The events will still be delivered to the #ClutterStage.
2920  *
2921  * The main side effect of this function is that disabling the motion
2922  * events will disable picking to detect the #ClutterActor underneath
2923  * the pointer for each motion event. This is useful, for instance,
2924  * when dragging a #ClutterActor across the @stage: the actor underneath
2925  * the pointer is not going to change, so it's meaningless to perform
2926  * a pick.
2927  *
2928  * Since: 1.8
2929  */
2930 void
clutter_stage_set_motion_events_enabled(ClutterStage * stage,gboolean enabled)2931 clutter_stage_set_motion_events_enabled (ClutterStage *stage,
2932                                          gboolean      enabled)
2933 {
2934   ClutterStagePrivate *priv;
2935 
2936   g_return_if_fail (CLUTTER_IS_STAGE (stage));
2937 
2938   priv = stage->priv;
2939 
2940   enabled = !!enabled;
2941 
2942   if (priv->motion_events_enabled != enabled)
2943     priv->motion_events_enabled = enabled;
2944 }
2945 
2946 /**
2947  * clutter_stage_get_motion_events_enabled:
2948  * @stage: a #ClutterStage
2949  *
2950  * Retrieves the value set using clutter_stage_set_motion_events_enabled().
2951  *
2952  * Return value: %TRUE if the per-actor motion event delivery is enabled
2953  *   and %FALSE otherwise
2954  *
2955  * Since: 1.8
2956  */
2957 gboolean
clutter_stage_get_motion_events_enabled(ClutterStage * stage)2958 clutter_stage_get_motion_events_enabled (ClutterStage *stage)
2959 {
2960   g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
2961 
2962   return stage->priv->motion_events_enabled;
2963 }
2964 
2965 void
_clutter_stage_add_pointer_drag_actor(ClutterStage * stage,ClutterInputDevice * device,ClutterActor * actor)2966 _clutter_stage_add_pointer_drag_actor (ClutterStage       *stage,
2967                                        ClutterInputDevice *device,
2968                                        ClutterActor       *actor)
2969 {
2970   GHashTable *drag_actors;
2971 
2972   drag_actors = g_object_get_data (G_OBJECT (stage),
2973                                    "__clutter_stage_pointer_drag_actors");
2974   if (drag_actors == NULL)
2975     {
2976       drag_actors = g_hash_table_new (NULL, NULL);
2977       g_object_set_data_full (G_OBJECT (stage),
2978                               "__clutter_stage_pointer_drag_actors",
2979                               drag_actors,
2980                               (GDestroyNotify) g_hash_table_destroy);
2981     }
2982 
2983   g_hash_table_replace (drag_actors, device, actor);
2984 }
2985 
2986 ClutterActor *
_clutter_stage_get_pointer_drag_actor(ClutterStage * stage,ClutterInputDevice * device)2987 _clutter_stage_get_pointer_drag_actor (ClutterStage       *stage,
2988                                        ClutterInputDevice *device)
2989 {
2990   GHashTable *drag_actors;
2991 
2992   drag_actors = g_object_get_data (G_OBJECT (stage),
2993                                    "__clutter_stage_pointer_drag_actors");
2994   if (drag_actors == NULL)
2995     return NULL;
2996 
2997   return g_hash_table_lookup (drag_actors, device);
2998 }
2999 
3000 void
_clutter_stage_remove_pointer_drag_actor(ClutterStage * stage,ClutterInputDevice * device)3001 _clutter_stage_remove_pointer_drag_actor (ClutterStage       *stage,
3002                                           ClutterInputDevice *device)
3003 {
3004   GHashTable *drag_actors;
3005 
3006   drag_actors = g_object_get_data (G_OBJECT (stage),
3007                                    "__clutter_stage_pointer_drag_actors");
3008   if (drag_actors == NULL)
3009     return;
3010 
3011   g_hash_table_remove (drag_actors, device);
3012 
3013   if (g_hash_table_size (drag_actors) == 0)
3014     g_object_set_data (G_OBJECT (stage),
3015                        "__clutter_stage_pointer_drag_actors",
3016                        NULL);
3017 }
3018 
3019 void
_clutter_stage_add_touch_drag_actor(ClutterStage * stage,ClutterEventSequence * sequence,ClutterActor * actor)3020 _clutter_stage_add_touch_drag_actor (ClutterStage         *stage,
3021                                      ClutterEventSequence *sequence,
3022                                      ClutterActor         *actor)
3023 {
3024   GHashTable *drag_actors;
3025 
3026   drag_actors = g_object_get_data (G_OBJECT (stage),
3027                                    "__clutter_stage_touch_drag_actors");
3028   if (drag_actors == NULL)
3029     {
3030       drag_actors = g_hash_table_new (NULL, NULL);
3031       g_object_set_data_full (G_OBJECT (stage),
3032                               "__clutter_stage_touch_drag_actors",
3033                               drag_actors,
3034                               (GDestroyNotify) g_hash_table_destroy);
3035     }
3036 
3037   g_hash_table_replace (drag_actors, sequence, actor);
3038 }
3039 
3040 ClutterActor *
_clutter_stage_get_touch_drag_actor(ClutterStage * stage,ClutterEventSequence * sequence)3041 _clutter_stage_get_touch_drag_actor (ClutterStage         *stage,
3042                                      ClutterEventSequence *sequence)
3043 {
3044   GHashTable *drag_actors;
3045 
3046   drag_actors = g_object_get_data (G_OBJECT (stage),
3047                                    "__clutter_stage_touch_drag_actors");
3048   if (drag_actors == NULL)
3049     return NULL;
3050 
3051   return g_hash_table_lookup (drag_actors, sequence);
3052 }
3053 
3054 void
_clutter_stage_remove_touch_drag_actor(ClutterStage * stage,ClutterEventSequence * sequence)3055 _clutter_stage_remove_touch_drag_actor (ClutterStage         *stage,
3056                                         ClutterEventSequence *sequence)
3057 {
3058   GHashTable *drag_actors;
3059 
3060   drag_actors = g_object_get_data (G_OBJECT (stage),
3061                                    "__clutter_stage_touch_drag_actors");
3062   if (drag_actors == NULL)
3063     return;
3064 
3065   g_hash_table_remove (drag_actors, sequence);
3066 
3067   if (g_hash_table_size (drag_actors) == 0)
3068     g_object_set_data (G_OBJECT (stage),
3069                        "__clutter_stage_touch_drag_actors",
3070                        NULL);
3071 }
3072 
3073 int64_t
clutter_stage_get_frame_counter(ClutterStage * stage)3074 clutter_stage_get_frame_counter (ClutterStage          *stage)
3075 {
3076   ClutterStageWindow *stage_window;
3077 
3078   stage_window = _clutter_stage_get_window (stage);
3079   return _clutter_stage_window_get_frame_counter (stage_window);
3080 }
3081 
3082 void
clutter_stage_presented(ClutterStage * stage,ClutterStageView * view,ClutterFrameInfo * frame_info)3083 clutter_stage_presented (ClutterStage     *stage,
3084                          ClutterStageView *view,
3085                          ClutterFrameInfo *frame_info)
3086 {
3087   g_signal_emit (stage, stage_signals[PRESENTED], 0, view, frame_info);
3088 }
3089 
3090 /**
3091  * clutter_stage_get_capture_final_size:
3092  * @stage: a #ClutterStage actor
3093  * @rect: a #cairo_rectangle_int_t
3094  * @out_width: (out) (optional): the final width
3095  * @out_height: (out) (optional): the final height
3096  * @out_scale: (out) (optional): the final scale factor
3097  *
3098  * Get the size of the framebuffer one must pass to
3099  * clutter_stage_paint_to_buffer() or clutter_stage_paint_to_framebuffer()
3100  * would be used with the same @rect.
3101  *
3102  * Returns: %TRUE if the size has been retrieved, %FALSE otherwise.
3103  */
3104 gboolean
clutter_stage_get_capture_final_size(ClutterStage * stage,cairo_rectangle_int_t * rect,int * out_width,int * out_height,float * out_scale)3105 clutter_stage_get_capture_final_size (ClutterStage          *stage,
3106                                       cairo_rectangle_int_t *rect,
3107                                       int                   *out_width,
3108                                       int                   *out_height,
3109                                       float                 *out_scale)
3110 {
3111   float max_scale = 1.0;
3112 
3113   g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
3114 
3115   if (rect)
3116     {
3117       graphene_rect_t capture_rect;
3118       g_autoptr (GList) views = NULL;
3119       GList *l;
3120 
3121       _clutter_util_rect_from_rectangle (rect, &capture_rect);
3122       views = clutter_stage_get_views_for_rect (stage, &capture_rect);
3123 
3124       if (!views)
3125         return FALSE;
3126 
3127       for (l = views; l; l = l->next)
3128         {
3129           ClutterStageView *view = l->data;
3130 
3131           max_scale = MAX (clutter_stage_view_get_scale (view), max_scale);
3132         }
3133 
3134       if (out_width)
3135         *out_width = (gint) roundf (rect->width * max_scale);
3136 
3137       if (out_height)
3138         *out_height = (gint) roundf (rect->height * max_scale);
3139     }
3140   else
3141     {
3142       ClutterActorBox alloc;
3143       float stage_width, stage_height;
3144 
3145       clutter_actor_get_allocation_box (CLUTTER_ACTOR (stage), &alloc);
3146       clutter_actor_box_get_size (&alloc, &stage_width, &stage_height);
3147       max_scale = clutter_actor_get_real_resource_scale (CLUTTER_ACTOR (stage));
3148 
3149       if (out_width)
3150         *out_width = (gint) roundf (stage_width * max_scale);
3151 
3152       if (out_height)
3153         *out_height = (gint) roundf (stage_height * max_scale);
3154     }
3155 
3156   if (out_scale)
3157     *out_scale = max_scale;
3158 
3159   return TRUE;
3160 }
3161 
3162 void
clutter_stage_paint_to_framebuffer(ClutterStage * stage,CoglFramebuffer * framebuffer,const cairo_rectangle_int_t * rect,float scale,ClutterPaintFlag paint_flags)3163 clutter_stage_paint_to_framebuffer (ClutterStage                *stage,
3164                                     CoglFramebuffer             *framebuffer,
3165                                     const cairo_rectangle_int_t *rect,
3166                                     float                        scale,
3167                                     ClutterPaintFlag             paint_flags)
3168 {
3169   ClutterStagePrivate *priv = stage->priv;
3170   ClutterPaintContext *paint_context;
3171   cairo_region_t *redraw_clip;
3172 
3173   if (paint_flags & CLUTTER_PAINT_FLAG_CLEAR)
3174     {
3175       CoglColor clear_color;
3176 
3177       cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 0);
3178       cogl_framebuffer_clear (framebuffer, COGL_BUFFER_BIT_COLOR, &clear_color);
3179     }
3180 
3181   redraw_clip = cairo_region_create_rectangle (rect);
3182   paint_context =
3183     clutter_paint_context_new_for_framebuffer (framebuffer,
3184                                                redraw_clip,
3185                                                paint_flags);
3186   cairo_region_destroy (redraw_clip);
3187 
3188   cogl_framebuffer_push_matrix (framebuffer);
3189   cogl_framebuffer_set_projection_matrix (framebuffer, &priv->projection);
3190   cogl_framebuffer_set_viewport (framebuffer,
3191                                  -(rect->x * scale),
3192                                  -(rect->y * scale),
3193                                  priv->viewport[2] * scale,
3194                                  priv->viewport[3] * scale);
3195   clutter_actor_paint (CLUTTER_ACTOR (stage), paint_context);
3196   cogl_framebuffer_pop_matrix (framebuffer);
3197 
3198   clutter_paint_context_destroy (paint_context);
3199 }
3200 
3201 /**
3202  * clutter_stage_paint_to_buffer:
3203  * @stage: a #ClutterStage actor
3204  * @rect: a #cairo_rectangle_int_t
3205  * @scale: the scale
3206  * @data: (array) (element-type guint8): a pointer to the data
3207  * @stride: stride of the image surface
3208  * @format: the pixel format
3209  * @paint_flags: the #ClutterPaintFlag
3210  * @error: the error
3211  *
3212  * Take a snapshot of the stage to a provided buffer.
3213  *
3214  * Returns: %TRUE is the buffer has been paint successfully, %FALSE otherwise.
3215  */
3216 gboolean
clutter_stage_paint_to_buffer(ClutterStage * stage,const cairo_rectangle_int_t * rect,float scale,uint8_t * data,int stride,CoglPixelFormat format,ClutterPaintFlag paint_flags,GError ** error)3217 clutter_stage_paint_to_buffer (ClutterStage                 *stage,
3218                                const cairo_rectangle_int_t  *rect,
3219                                float                         scale,
3220                                uint8_t                      *data,
3221                                int                           stride,
3222                                CoglPixelFormat               format,
3223                                ClutterPaintFlag              paint_flags,
3224                                GError                      **error)
3225 {
3226   ClutterBackend *clutter_backend = clutter_get_default_backend ();
3227   CoglContext *cogl_context =
3228     clutter_backend_get_cogl_context (clutter_backend);
3229   int texture_width, texture_height;
3230   CoglTexture2D *texture;
3231   CoglOffscreen *offscreen;
3232   CoglFramebuffer *framebuffer;
3233   CoglBitmap *bitmap;
3234 
3235   texture_width = (int) roundf (rect->width * scale);
3236   texture_height = (int) roundf (rect->height * scale);
3237   texture = cogl_texture_2d_new_with_size (cogl_context,
3238                                            texture_width,
3239                                            texture_height);
3240   if (!texture)
3241     {
3242       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
3243                    "Failed to create %dx%d texture",
3244                    texture_width, texture_height);
3245       return FALSE;
3246     }
3247 
3248   offscreen = cogl_offscreen_new_with_texture (COGL_TEXTURE (texture));
3249   framebuffer = COGL_FRAMEBUFFER (offscreen);
3250 
3251   cogl_object_unref (texture);
3252 
3253   if (!cogl_framebuffer_allocate (framebuffer, error))
3254     return FALSE;
3255 
3256   clutter_stage_paint_to_framebuffer (stage, framebuffer,
3257                                       rect, scale, paint_flags);
3258 
3259   bitmap = cogl_bitmap_new_for_data (cogl_context,
3260                                      texture_width, texture_height,
3261                                      format,
3262                                      stride,
3263                                      data);
3264 
3265   cogl_framebuffer_read_pixels_into_bitmap (framebuffer,
3266                                             0, 0,
3267                                             COGL_READ_PIXELS_COLOR_BUFFER,
3268                                             bitmap);
3269 
3270   cogl_object_unref (bitmap);
3271   g_object_unref (framebuffer);
3272 
3273   return TRUE;
3274 }
3275 
3276 /**
3277  * clutter_stage_paint_to_content:
3278  * @stage: a #ClutterStage actor
3279  * @rect: a #cairo_rectangle_int_t
3280  * @scale: the scale
3281  * @paint_flags: the #ClutterPaintFlag
3282  * @error: the error
3283  *
3284  * Take a snapshot of the stage to a #ClutterContent.
3285  *
3286  * Returns: (transfer full): the #ClutterContent or %NULL on error.
3287  */
3288 ClutterContent *
clutter_stage_paint_to_content(ClutterStage * stage,const cairo_rectangle_int_t * rect,float scale,ClutterPaintFlag paint_flags,GError ** error)3289 clutter_stage_paint_to_content (ClutterStage                 *stage,
3290                                 const cairo_rectangle_int_t  *rect,
3291                                 float                         scale,
3292                                 ClutterPaintFlag              paint_flags,
3293                                 GError                      **error)
3294 {
3295   ClutterBackend *clutter_backend = clutter_get_default_backend ();
3296   CoglContext *cogl_context =
3297     clutter_backend_get_cogl_context (clutter_backend);
3298   int texture_width, texture_height;
3299   CoglTexture2D *texture;
3300   CoglOffscreen *offscreen;
3301   g_autoptr (CoglFramebuffer) framebuffer = NULL;
3302 
3303   texture_width = (int) roundf (rect->width * scale);
3304   texture_height = (int) roundf (rect->height * scale);
3305   texture = cogl_texture_2d_new_with_size (cogl_context,
3306                                            texture_width,
3307                                            texture_height);
3308   if (!texture)
3309     {
3310       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
3311                    "Failed to create %dx%d texture",
3312                    texture_width, texture_height);
3313       return NULL;
3314     }
3315 
3316   offscreen = cogl_offscreen_new_with_texture (COGL_TEXTURE (texture));
3317   framebuffer = COGL_FRAMEBUFFER (offscreen);
3318 
3319   cogl_object_unref (texture);
3320 
3321   if (!cogl_framebuffer_allocate (framebuffer, error))
3322     return NULL;
3323 
3324   clutter_stage_paint_to_framebuffer (stage, framebuffer,
3325                                       rect, scale, paint_flags);
3326 
3327   return clutter_texture_content_new_from_texture (cogl_offscreen_get_texture (offscreen),
3328                                                    NULL);
3329 }
3330 
3331 void
clutter_stage_capture_view_into(ClutterStage * stage,ClutterStageView * view,cairo_rectangle_int_t * rect,uint8_t * data,int stride)3332 clutter_stage_capture_view_into (ClutterStage          *stage,
3333                                  ClutterStageView      *view,
3334                                  cairo_rectangle_int_t *rect,
3335                                  uint8_t               *data,
3336                                  int                    stride)
3337 {
3338   CoglFramebuffer *framebuffer;
3339   ClutterBackend *backend;
3340   CoglContext *context;
3341   CoglBitmap *bitmap;
3342   cairo_rectangle_int_t view_layout;
3343   float view_scale;
3344   float texture_width;
3345   float texture_height;
3346 
3347   g_return_if_fail (CLUTTER_IS_STAGE (stage));
3348 
3349   framebuffer = clutter_stage_view_get_framebuffer (view);
3350 
3351   clutter_stage_view_get_layout (view, &view_layout);
3352 
3353   if (!rect)
3354     rect = &view_layout;
3355 
3356   view_scale = clutter_stage_view_get_scale (view);
3357   texture_width = roundf (rect->width * view_scale);
3358   texture_height = roundf (rect->height * view_scale);
3359 
3360   backend = clutter_get_default_backend ();
3361   context = clutter_backend_get_cogl_context (backend);
3362   bitmap = cogl_bitmap_new_for_data (context,
3363                                      texture_width, texture_height,
3364                                      CLUTTER_CAIRO_FORMAT_ARGB32,
3365                                      stride,
3366                                      data);
3367 
3368   cogl_framebuffer_read_pixels_into_bitmap (framebuffer,
3369                                             roundf ((rect->x - view_layout.x) * view_scale),
3370                                             roundf ((rect->y - view_layout.y) * view_scale),
3371                                             COGL_READ_PIXELS_COLOR_BUFFER,
3372                                             bitmap);
3373 
3374   cogl_object_unref (bitmap);
3375 }
3376 
3377 /**
3378  * clutter_stage_peek_stage_views: (skip)
3379  */
3380 GList *
clutter_stage_peek_stage_views(ClutterStage * stage)3381 clutter_stage_peek_stage_views (ClutterStage *stage)
3382 {
3383   ClutterStagePrivate *priv = stage->priv;
3384 
3385   return _clutter_stage_window_get_views (priv->impl);
3386 }
3387 
3388 void
clutter_stage_clear_stage_views(ClutterStage * stage)3389 clutter_stage_clear_stage_views (ClutterStage *stage)
3390 {
3391   clutter_actor_clear_stage_views_recursive (CLUTTER_ACTOR (stage));
3392 }
3393 
3394 GList *
clutter_stage_get_views_for_rect(ClutterStage * stage,const graphene_rect_t * rect)3395 clutter_stage_get_views_for_rect (ClutterStage          *stage,
3396                                   const graphene_rect_t *rect)
3397 {
3398   ClutterStagePrivate *priv = stage->priv;
3399   GList *views_for_rect = NULL;
3400   GList *l;
3401 
3402   for (l = _clutter_stage_window_get_views (priv->impl); l; l = l->next)
3403     {
3404       ClutterStageView *view = l->data;
3405       cairo_rectangle_int_t view_layout;
3406       graphene_rect_t view_rect;
3407 
3408       clutter_stage_view_get_layout (view, &view_layout);
3409       _clutter_util_rect_from_rectangle (&view_layout, &view_rect);
3410 
3411       if (graphene_rect_intersection (&view_rect, rect, NULL))
3412         views_for_rect = g_list_prepend (views_for_rect, view);
3413     }
3414 
3415   return views_for_rect;
3416 }
3417 
3418 void
clutter_stage_set_actor_needs_immediate_relayout(ClutterStage * stage)3419 clutter_stage_set_actor_needs_immediate_relayout (ClutterStage *stage)
3420 {
3421   ClutterStagePrivate *priv = stage->priv;
3422 
3423   priv->actor_needs_immediate_relayout = TRUE;
3424 }
3425 
3426 static void
on_device_actor_reactive_changed(ClutterActor * actor,GParamSpec * pspec,PointerDeviceEntry * entry)3427 on_device_actor_reactive_changed (ClutterActor       *actor,
3428                                   GParamSpec         *pspec,
3429                                   PointerDeviceEntry *entry)
3430 {
3431   ClutterStage *self = entry->stage;
3432   ClutterActor *new_device_actor;
3433 
3434   g_assert (!clutter_actor_get_reactive (actor));
3435 
3436   new_device_actor =
3437     _clutter_stage_do_pick (self,
3438                             entry->coords.x,
3439                             entry->coords.y,
3440                             CLUTTER_PICK_REACTIVE);
3441 
3442   clutter_stage_update_device (self,
3443                                entry->device, entry->sequence,
3444                                entry->coords,
3445                                CLUTTER_CURRENT_TIME,
3446                                new_device_actor,
3447                                TRUE);
3448 }
3449 
3450 static void
on_device_actor_destroyed(ClutterActor * actor,PointerDeviceEntry * entry)3451 on_device_actor_destroyed (ClutterActor       *actor,
3452                            PointerDeviceEntry *entry)
3453 {
3454   /* Simply unset the current_actor pointer here, there's no need to
3455    * unset has_pointer or to disconnect any signals because the actor
3456    * is gone anyway.
3457    * Also, as soon as the next repaint happens, a repick should be triggered
3458    * and the PointerDeviceEntry will get updated again, so no need to
3459    * trigger a repick here.
3460    */
3461   entry->current_actor = NULL;
3462 }
3463 
3464 static void
free_pointer_device_entry(PointerDeviceEntry * entry)3465 free_pointer_device_entry (PointerDeviceEntry *entry)
3466 {
3467   if (entry->current_actor)
3468     {
3469       ClutterActor *actor = entry->current_actor;
3470 
3471       g_signal_handlers_disconnect_by_func (actor,
3472                                             G_CALLBACK (on_device_actor_reactive_changed),
3473                                             entry);
3474       g_signal_handlers_disconnect_by_func (actor,
3475                                             G_CALLBACK (on_device_actor_destroyed),
3476                                             entry);
3477 
3478       _clutter_actor_set_has_pointer (actor, FALSE);
3479    }
3480 
3481   g_free (entry);
3482 }
3483 
3484 void
clutter_stage_update_device_entry(ClutterStage * self,ClutterInputDevice * device,ClutterEventSequence * sequence,graphene_point_t coords,ClutterActor * actor)3485 clutter_stage_update_device_entry (ClutterStage         *self,
3486                                    ClutterInputDevice   *device,
3487                                    ClutterEventSequence *sequence,
3488                                    graphene_point_t      coords,
3489                                    ClutterActor         *actor)
3490 {
3491   ClutterStagePrivate *priv = self->priv;
3492   PointerDeviceEntry *entry = NULL;
3493 
3494   g_assert (device != NULL);
3495 
3496   if (sequence != NULL)
3497     entry = g_hash_table_lookup (priv->touch_sequences, sequence);
3498   else
3499     entry = g_hash_table_lookup (priv->pointer_devices, device);
3500 
3501   if (!entry)
3502     {
3503       entry = g_new0 (PointerDeviceEntry, 1);
3504 
3505       if (sequence != NULL)
3506         g_hash_table_insert (priv->touch_sequences, sequence, entry);
3507       else
3508         g_hash_table_insert (priv->pointer_devices, device, entry);
3509 
3510       entry->stage = self;
3511       entry->device = device;
3512       entry->sequence = sequence;
3513     }
3514 
3515   entry->coords = coords;
3516 
3517   if (entry->current_actor != actor)
3518     {
3519       if (entry->current_actor)
3520         {
3521           ClutterActor *old_actor = entry->current_actor;
3522 
3523           g_signal_handlers_disconnect_by_func (old_actor,
3524                                                 G_CALLBACK (on_device_actor_reactive_changed),
3525                                                 entry);
3526           g_signal_handlers_disconnect_by_func (old_actor,
3527                                                 G_CALLBACK (on_device_actor_destroyed),
3528                                                 entry);
3529 
3530           _clutter_actor_set_has_pointer (old_actor, FALSE);
3531         }
3532 
3533       entry->current_actor = actor;
3534 
3535       if (actor)
3536         {
3537           g_signal_connect (actor, "notify::reactive",
3538                             G_CALLBACK (on_device_actor_reactive_changed), entry);
3539           g_signal_connect (actor, "destroy",
3540                             G_CALLBACK (on_device_actor_destroyed), entry);
3541 
3542           _clutter_actor_set_has_pointer (actor, TRUE);
3543         }
3544     }
3545 }
3546 
3547 void
clutter_stage_remove_device_entry(ClutterStage * self,ClutterInputDevice * device,ClutterEventSequence * sequence)3548 clutter_stage_remove_device_entry (ClutterStage         *self,
3549                                    ClutterInputDevice   *device,
3550                                    ClutterEventSequence *sequence)
3551 {
3552   ClutterStagePrivate *priv = self->priv;
3553   gboolean removed;
3554 
3555   g_assert (device != NULL);
3556 
3557   if (sequence != NULL)
3558     removed = g_hash_table_remove (priv->touch_sequences, sequence);
3559   else
3560     removed = g_hash_table_remove (priv->pointer_devices, device);
3561 
3562   g_assert (removed);
3563 }
3564 
3565 /**
3566  * clutter_stage_get_device_actor:
3567  * @stage: a #ClutterStage
3568  * @device: a #ClutterInputDevice
3569  * @sequence: (allow-none): an optional #ClutterEventSequence
3570  *
3571  * Retrieves the #ClutterActor underneath the pointer or touch point
3572  * of @device and @sequence.
3573  *
3574  * Return value: (transfer none): a pointer to the #ClutterActor or %NULL
3575  */
3576 ClutterActor *
clutter_stage_get_device_actor(ClutterStage * stage,ClutterInputDevice * device,ClutterEventSequence * sequence)3577 clutter_stage_get_device_actor (ClutterStage         *stage,
3578                                 ClutterInputDevice   *device,
3579                                 ClutterEventSequence *sequence)
3580 {
3581   ClutterStagePrivate *priv = stage->priv;
3582   PointerDeviceEntry *entry = NULL;
3583 
3584   g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
3585   g_return_val_if_fail (device != NULL, NULL);
3586 
3587   if (sequence != NULL)
3588     entry = g_hash_table_lookup (priv->touch_sequences, sequence);
3589   else
3590     entry = g_hash_table_lookup (priv->pointer_devices, device);
3591 
3592   if (entry)
3593     return entry->current_actor;
3594 
3595   return NULL;
3596 }
3597 
3598 /**
3599  * clutter_stage_get_device_coords: (skip):
3600  */
3601 void
clutter_stage_get_device_coords(ClutterStage * stage,ClutterInputDevice * device,ClutterEventSequence * sequence,graphene_point_t * coords)3602 clutter_stage_get_device_coords (ClutterStage         *stage,
3603                                  ClutterInputDevice   *device,
3604                                  ClutterEventSequence *sequence,
3605                                  graphene_point_t     *coords)
3606 {
3607   ClutterStagePrivate *priv = stage->priv;
3608   PointerDeviceEntry *entry = NULL;
3609 
3610   g_return_if_fail (CLUTTER_IS_STAGE (stage));
3611   g_return_if_fail (device != NULL);
3612 
3613   if (sequence != NULL)
3614     entry = g_hash_table_lookup (priv->touch_sequences, sequence);
3615   else
3616     entry = g_hash_table_lookup (priv->pointer_devices, device);
3617 
3618   if (entry && coords)
3619     *coords = entry->coords;
3620 }
3621