4 * An OpenGL based 'interactive canvas' library.
6 * Authored By Matthew Allum <mallum@openedhand.com>
8 * Copyright (C) 2006 OpenedHand
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.
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.
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/>.
27 * SECTION:clutter-stage
28 * @short_description: Top level visual element to which actors are placed.
30 * #ClutterStage is a top level 'window' on which child actors are placed
33 * Backends might provide support for multiple stages. The support for this
34 * feature can be checked at run-time using the clutter_feature_available()
35 * function and the %CLUTTER_FEATURE_STAGE_MULTIPLE flag. If the backend used
36 * supports multiple stages, new #ClutterStage instances can be created
37 * using clutter_stage_new(). These stages must be managed by the developer
38 * using clutter_actor_destroy(), which will take care of destroying all the
39 * actors contained inside them.
41 * #ClutterStage is a proxy actor, wrapping the backend-specific
42 * implementation of the windowing system. It is possible to subclass
43 * #ClutterStage, as long as every overridden virtual function chains up to
44 * the parent class corresponding function.
54 #define CLUTTER_DISABLE_DEPRECATION_WARNINGS
56 #include "clutter-stage.h"
57 #include "deprecated/clutter-stage.h"
58 #include "deprecated/clutter-container.h"
60 #include "clutter-actor-private.h"
61 #include "clutter-backend-private.h"
62 #include "clutter-cairo-texture.h"
63 #include "clutter-color.h"
64 #include "clutter-container.h"
65 #include "clutter-debug.h"
66 #include "clutter-device-manager-private.h"
67 #include "clutter-enum-types.h"
68 #include "clutter-event-private.h"
69 #include "clutter-id-pool.h"
70 #include "clutter-main.h"
71 #include "clutter-marshal.h"
72 #include "clutter-master-clock.h"
73 #include "clutter-paint-volume-private.h"
74 #include "clutter-private.h"
75 #include "clutter-profile.h"
76 #include "clutter-stage-manager-private.h"
77 #include "clutter-stage-private.h"
78 #include "clutter-util.h"
79 #include "clutter-version.h" /* For flavour */
80 #include "clutter-private.h"
82 #include "cogl/cogl.h"
84 static void clutter_container_iface_init (ClutterContainerIface *iface);
86 G_DEFINE_TYPE_WITH_CODE (ClutterStage, clutter_stage, CLUTTER_TYPE_GROUP,
87 G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
88 clutter_container_iface_init))
90 #define CLUTTER_STAGE_GET_PRIVATE(obj) \
91 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_STAGE, ClutterStagePrivate))
95 * @CLUTTER_STAGE_NONE: No hint set
96 * @CLUTTER_STAGE_NO_CLEAR_ON_PAINT: When this hint is set, the stage
97 * should not clear the viewport; this flag is useful when painting
98 * fully opaque actors covering the whole visible area of the stage,
99 * i.e. when no blending with the stage color happens over the whole
102 * A series of hints that enable or disable behaviours on the stage
104 typedef enum { /*< prefix=CLUTTER_STAGE >*/
105 CLUTTER_STAGE_HINT_NONE = 0,
107 CLUTTER_STAGE_NO_CLEAR_ON_PAINT = 1 << 0
110 #define STAGE_NO_CLEAR_ON_PAINT(s) ((((ClutterStage *) (s))->priv->stage_hints & CLUTTER_STAGE_NO_CLEAR_ON_PAINT) != 0)
112 struct _ClutterStageQueueRedrawEntry
116 ClutterPaintVolume clip;
119 struct _ClutterStagePrivate
121 /* the stage implementation */
122 ClutterStageWindow *impl;
124 ClutterPerspective perspective;
125 CoglMatrix projection;
126 CoglMatrix inverse_projection;
133 ClutterActor *key_focused_actor;
137 ClutterStageHint stage_hints;
139 gint picks_per_frame;
141 GArray *paint_volume_stack;
143 ClutterPlane current_clip_planes[4];
145 GList *pending_queue_redraws;
147 ClutterPickMode pick_buffer_mode;
149 CoglFramebuffer *active_framebuffer;
154 gint32 timer_n_frames;
156 ClutterIDPool *pick_id_pool;
158 #ifdef CLUTTER_ENABLE_DEBUG
160 #endif /* CLUTTER_ENABLE_DEBUG */
162 ClutterStageState current_state;
164 guint relayout_pending : 1;
165 guint redraw_pending : 1;
166 guint is_fullscreen : 1;
167 guint is_cursor_visible : 1;
168 guint is_user_resizable : 1;
170 guint throttle_motion_events : 1;
172 guint min_size_changed : 1;
173 guint dirty_viewport : 1;
174 guint dirty_projection : 1;
175 guint have_valid_pick_buffer : 1;
176 guint accept_focus : 1;
177 guint motion_events_enabled : 1;
178 guint has_custom_perspective : 1;
211 static guint stage_signals[LAST_SIGNAL] = { 0, };
213 static const ClutterColor default_stage_color = { 255, 255, 255, 255 };
215 static void _clutter_stage_maybe_finish_queue_redraws (ClutterStage *stage);
218 clutter_stage_real_add (ClutterContainer *container,
221 clutter_actor_add_child (CLUTTER_ACTOR (container), child);
225 clutter_stage_real_remove (ClutterContainer *container,
228 clutter_actor_remove_child (CLUTTER_ACTOR (container), child);
232 clutter_stage_real_foreach (ClutterContainer *container,
233 ClutterCallback callback,
236 ClutterActorIter iter;
239 clutter_actor_iter_init (&iter, CLUTTER_ACTOR (container));
241 while (clutter_actor_iter_next (&iter, &child))
242 callback (child, user_data);
246 clutter_stage_real_raise (ClutterContainer *container,
248 ClutterActor *sibling)
250 clutter_actor_set_child_above_sibling (CLUTTER_ACTOR (container),
256 clutter_stage_real_lower (ClutterContainer *container,
258 ClutterActor *sibling)
260 clutter_actor_set_child_below_sibling (CLUTTER_ACTOR (container),
266 clutter_stage_real_sort_depth_order (ClutterContainer *container)
271 clutter_container_iface_init (ClutterContainerIface *iface)
273 iface->add = clutter_stage_real_add;
274 iface->remove = clutter_stage_real_remove;
275 iface->foreach = clutter_stage_real_foreach;
276 iface->raise = clutter_stage_real_raise;
277 iface->lower = clutter_stage_real_lower;
278 iface->sort_depth_order = clutter_stage_real_sort_depth_order;
282 clutter_stage_get_preferred_width (ClutterActor *self,
285 gfloat *natural_width_p)
287 ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
288 cairo_rectangle_int_t geom;
290 if (priv->impl == NULL)
293 _clutter_stage_window_get_geometry (priv->impl, &geom);
296 *min_width_p = geom.width;
299 *natural_width_p = geom.width;
303 clutter_stage_get_preferred_height (ClutterActor *self,
305 gfloat *min_height_p,
306 gfloat *natural_height_p)
308 ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
309 cairo_rectangle_int_t geom;
311 if (priv->impl == NULL)
314 _clutter_stage_window_get_geometry (priv->impl, &geom);
317 *min_height_p = geom.height;
319 if (natural_height_p)
320 *natural_height_p = geom.height;
324 queue_full_redraw (ClutterStage *stage)
326 ClutterStageWindow *stage_window;
328 if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
331 clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
333 /* Just calling clutter_actor_queue_redraw will typically only
334 * redraw the bounding box of the children parented on the stage but
335 * in this case we really need to ensure that the full stage is
336 * redrawn so we add a NULL redraw clip to the stage window. */
337 stage_window = _clutter_stage_get_window (stage);
338 if (stage_window == NULL)
341 _clutter_stage_window_add_redraw_clip (stage_window, NULL);
345 stage_is_default (ClutterStage *stage)
347 ClutterStageManager *stage_manager;
348 ClutterStageWindow *impl;
350 stage_manager = clutter_stage_manager_get_default ();
351 if (stage != clutter_stage_manager_get_default_stage (stage_manager))
354 impl = _clutter_stage_get_window (stage);
355 if (impl != _clutter_stage_get_default_window ())
362 clutter_stage_allocate (ClutterActor *self,
363 const ClutterActorBox *box,
364 ClutterAllocationFlags flags)
366 ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
367 ClutterGeometry prev_geom, geom;
368 cairo_rectangle_int_t window_size;
369 gboolean origin_changed;
372 origin_changed = (flags & CLUTTER_ABSOLUTE_ORIGIN_CHANGED)
376 if (priv->impl == NULL)
379 /* our old allocation */
380 clutter_actor_get_allocation_geometry (self, &prev_geom);
382 /* the current allocation */
383 width = clutter_actor_box_get_width (box);
384 height = clutter_actor_box_get_height (box);
386 /* the current Stage implementation size */
387 _clutter_stage_window_get_geometry (priv->impl, &window_size);
389 /* if the stage is fixed size (for instance, it's using a EGL framebuffer)
390 * then we simply ignore any allocation request and override the
391 * allocation chain - because we cannot forcibly change the size of the
394 if ((!clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC)))
396 CLUTTER_NOTE (LAYOUT,
397 "Following allocation to %dx%d (origin %s)",
399 origin_changed ? "changed" : "not changed");
401 clutter_actor_set_allocation (self, box,
402 flags | CLUTTER_DELEGATE_LAYOUT);
404 /* Ensure the window is sized correctly */
405 if (!priv->is_fullscreen)
407 if (priv->min_size_changed)
409 gfloat min_width, min_height;
410 gboolean min_width_set, min_height_set;
412 g_object_get (G_OBJECT (self),
413 "min-width", &min_width,
414 "min-width-set", &min_width_set,
415 "min-height", &min_height,
416 "min-height-set", &min_height_set,
424 if (width < min_width)
426 if (height < min_height)
429 priv->min_size_changed = FALSE;
432 if (window_size.width != width ||
433 window_size.height != height)
435 _clutter_stage_window_resize (priv->impl, width, height);
441 ClutterActorBox override = { 0, };
443 /* override the passed allocation */
446 override.x2 = window_size.width;
447 override.y2 = window_size.height;
449 CLUTTER_NOTE (LAYOUT,
450 "Overrigin original allocation of %dx%d "
451 "with %dx%d (origin %s)",
455 origin_changed ? "changed" : "not changed");
457 /* and store the overridden allocation */
458 clutter_actor_set_allocation (self, &override,
459 flags | CLUTTER_DELEGATE_LAYOUT);
462 /* XXX: Until Cogl becomes fully responsible for backend windows
463 * Clutter need to manually keep it informed of the current window
464 * size. We do this after the allocation above so that the stage
465 * window has a chance to update the window size based on the
468 _clutter_stage_window_get_geometry (priv->impl, &window_size);
469 cogl_onscreen_clutter_backend_set_size (window_size.width,
472 /* reset the viewport if the allocation effectively changed */
473 clutter_actor_get_allocation_geometry (self, &geom);
474 if (geom.width != prev_geom.width ||
475 geom.height != prev_geom.height)
477 _clutter_stage_set_viewport (CLUTTER_STAGE (self),
482 /* Note: we don't assume that set_viewport will queue a full redraw
483 * since it may bail-out early if something preemptively set the
484 * viewport before the stage was really allocated its new size.
486 queue_full_redraw (CLUTTER_STAGE (self));
490 typedef struct _Vector4
496 _cogl_util_get_eye_planes_for_screen_poly (float *polygon,
499 const CoglMatrix *projection,
500 const CoglMatrix *inverse_project,
501 ClutterPlane *planes)
511 tmp_poly = g_alloca (sizeof (Vector4) * n_vertices * 2);
515 /* Determine W in clip-space (Wc) for a point (0, 0, DEPTH, 1)
517 * Note: the depth could be anything except 0.
519 * We will transform the polygon into clip coordinates using this
520 * depth and then into eye coordinates. Our clip planes will be
521 * defined by triangles that extend between points of the polygon at
522 * DEPTH and corresponding points of the same polygon at DEPTH * 2.
524 * NB: Wc defines the position of the clip planes in clip
525 * coordinates. Given a screen aligned cross section through the
526 * frustum; coordinates range from [-Wc,Wc] left to right on the
527 * x-axis and [Wc,-Wc] top to bottom on the y-axis.
529 Wc = DEPTH * projection->wz + projection->ww;
531 #define CLIP_X(X) ((((float)X - viewport[0]) * (2.0 / viewport[2])) - 1) * Wc
532 #define CLIP_Y(Y) ((((float)Y - viewport[1]) * (2.0 / viewport[3])) - 1) * -Wc
534 for (i = 0; i < n_vertices; i++)
536 tmp_poly[i].x = CLIP_X (polygon[i * 2]);
537 tmp_poly[i].y = CLIP_Y (polygon[i * 2 + 1]);
538 tmp_poly[i].z = DEPTH;
542 Wc = DEPTH * 2 * projection->wz + projection->ww;
544 /* FIXME: technically we don't need to project all of the points
545 * twice, it would be enough project every other point since
546 * we can share points in this set to define the plane vectors. */
547 for (i = 0; i < n_vertices; i++)
549 tmp_poly[n_vertices + i].x = CLIP_X (polygon[i * 2]);
550 tmp_poly[n_vertices + i].y = CLIP_Y (polygon[i * 2 + 1]);
551 tmp_poly[n_vertices + i].z = DEPTH * 2;
552 tmp_poly[n_vertices + i].w = Wc;
558 cogl_matrix_project_points (inverse_project,
566 /* XXX: It's quite ugly that we end up with these casts between
567 * Vector4 types and CoglVector3s, it might be better if the
568 * cogl_vector APIs just took pointers to floats.
571 count = n_vertices - 1;
572 for (i = 0; i < count; i++)
575 memcpy (plane->v0, tmp_poly + i, sizeof (float) * 3);
576 memcpy (b, tmp_poly + n_vertices + i, sizeof (float) * 3);
577 memcpy (c, tmp_poly + n_vertices + i + 1, sizeof (float) * 3);
578 cogl_vector3_subtract (b, b, plane->v0);
579 cogl_vector3_subtract (c, c, plane->v0);
580 cogl_vector3_cross_product (plane->n, b, c);
581 cogl_vector3_normalize (plane->n);
584 plane = &planes[n_vertices - 1];
585 memcpy (plane->v0, tmp_poly + 0, sizeof (float) * 3);
586 memcpy (b, tmp_poly + (2 * n_vertices - 1), sizeof (float) * 3);
587 memcpy (c, tmp_poly + n_vertices, sizeof (float) * 3);
588 cogl_vector3_subtract (b, b, plane->v0);
589 cogl_vector3_subtract (c, c, plane->v0);
590 cogl_vector3_cross_product (plane->n, b, c);
591 cogl_vector3_normalize (plane->n);
595 _clutter_stage_update_active_framebuffer (ClutterStage *stage)
597 ClutterStagePrivate *priv = stage->priv;
599 /* We track the CoglFramebuffer that corresponds to the stage itself
600 * so, for example, we can disable culling when rendering to an
601 * offscreen framebuffer.
604 priv->active_framebuffer =
605 _clutter_stage_window_get_active_framebuffer (priv->impl);
607 if (!priv->active_framebuffer)
608 priv->active_framebuffer = cogl_get_draw_framebuffer ();
611 /* This provides a common point of entry for painting the scenegraph
612 * for picking or painting...
614 * XXX: Instead of having a toplevel 2D clip region, it might be
615 * better to have a clip volume within the view frustum. This could
616 * allow us to avoid projecting actors into window coordinates to
617 * be able to cull them.
620 _clutter_stage_do_paint (ClutterStage *stage,
621 const cairo_rectangle_int_t *clip)
623 ClutterStagePrivate *priv = stage->priv;
625 cairo_rectangle_int_t geom;
627 _clutter_stage_window_get_geometry (priv->impl, &geom);
631 clip_poly[0] = MAX (clip->x, 0);
632 clip_poly[1] = MAX (clip->y, 0);
633 clip_poly[2] = MIN (clip->x + clip->width, geom.width);
634 clip_poly[3] = clip_poly[1];
635 clip_poly[4] = clip_poly[2];
636 clip_poly[5] = MIN (clip->y + clip->height, geom.height);
637 clip_poly[6] = clip_poly[0];
638 clip_poly[7] = clip_poly[5];
644 clip_poly[2] = geom.width;
646 clip_poly[4] = geom.width;
647 clip_poly[5] = geom.height;
649 clip_poly[7] = geom.height;
652 CLUTTER_NOTE (CLIPPING, "Setting stage clip too: "
653 "x=%f, y=%f, width=%f, height=%f",
654 clip_poly[0], clip_poly[1],
655 clip_poly[2] - clip_poly[0],
656 clip_poly[5] - clip_poly[1]);
658 _cogl_util_get_eye_planes_for_screen_poly (clip_poly,
662 &priv->inverse_projection,
663 priv->current_clip_planes);
665 _clutter_stage_paint_volume_stack_free_all (stage);
666 _clutter_stage_update_active_framebuffer (stage);
667 clutter_actor_paint (CLUTTER_ACTOR (stage));
671 clutter_stage_paint (ClutterActor *self)
673 ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
674 CoglBufferBit clear_flags;
675 ClutterColor bg_color;
676 CoglColor stage_color;
677 ClutterActorIter iter;
681 CLUTTER_STATIC_TIMER (stage_clear_timer,
682 "Painting actors", /* parent */
684 "The time spent clearing the stage",
685 0 /* no application private data */);
687 CLUTTER_NOTE (PAINT, "Initializing stage paint");
689 /* composite the opacity to the stage color */
690 clutter_actor_get_background_color (self, &bg_color);
691 real_alpha = clutter_actor_get_opacity (self)
695 clear_flags = COGL_BUFFER_BIT_DEPTH;
696 if (!STAGE_NO_CLEAR_ON_PAINT (self))
697 clear_flags |= COGL_BUFFER_BIT_COLOR;
701 CLUTTER_TIMER_START (_clutter_uprof_context, stage_clear_timer);
702 /* we use the real alpha to clear the stage if :use-alpha is
703 * set; the effect depends entirely on the Clutter backend
705 cogl_color_init_from_4ub (&stage_color,
709 priv->use_alpha ? real_alpha : 255);
710 cogl_color_premultiply (&stage_color);
711 cogl_clear (&stage_color, clear_flags);
712 CLUTTER_TIMER_STOP (_clutter_uprof_context, stage_clear_timer);
714 clutter_actor_iter_init (&iter, self);
715 while (clutter_actor_iter_next (&iter, &child))
716 clutter_actor_paint (child);
720 clutter_stage_pick (ClutterActor *self,
721 const ClutterColor *color)
723 ClutterActorIter iter;
726 /* Note: we don't chain up to our parent as we don't want any geometry
727 * emitted for the stage itself. The stage's pick id is effectively handled
728 * by the call to cogl_clear done in clutter-main.c:_clutter_do_pick_async()
730 clutter_actor_iter_init (&iter, self);
731 while (clutter_actor_iter_next (&iter, &child))
732 clutter_actor_paint (child);
736 clutter_stage_get_paint_volume (ClutterActor *self,
737 ClutterPaintVolume *volume)
739 /* Returning False effectively means Clutter has to assume it covers
745 clutter_stage_realize (ClutterActor *self)
747 ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
748 gboolean is_realized;
750 /* Make sure the viewport and projection matrix are valid for the
751 * first paint (which will likely occur before the ConfigureNotify
754 priv->dirty_viewport = TRUE;
755 priv->dirty_projection = TRUE;
757 g_assert (priv->impl != NULL);
758 is_realized = _clutter_stage_window_realize (priv->impl);
760 /* ensure that the stage is using the context if the
761 * realization sequence was successful
765 ClutterBackend *backend = clutter_get_default_backend ();
767 /* We want to select the context without calling
768 clutter_backend_ensure_context so that it doesn't call any
769 Cogl functions. Otherwise it would create the Cogl context
770 before we get a chance to check whether the GL version is
772 _clutter_backend_ensure_context_internal (backend, CLUTTER_STAGE (self));
775 CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
779 clutter_stage_unrealize (ClutterActor *self)
781 ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
783 /* and then unrealize the implementation */
784 g_assert (priv->impl != NULL);
785 _clutter_stage_window_unrealize (priv->impl);
787 CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
789 clutter_stage_ensure_current (CLUTTER_STAGE (self));
793 clutter_stage_show_all (ClutterActor *self)
795 ClutterActorIter iter;
798 /* we don't do a recursive show_all(), to maintain the old
799 * invariants from ClutterGroup
801 clutter_actor_iter_init (&iter, self);
802 while (clutter_actor_iter_next (&iter, &child))
803 clutter_actor_show (child);
805 clutter_actor_show (self);
809 clutter_stage_show (ClutterActor *self)
811 ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
813 CLUTTER_ACTOR_CLASS (clutter_stage_parent_class)->show (self);
815 /* Possibly do an allocation run so that the stage will have the
816 right size before we map it */
817 _clutter_stage_maybe_relayout (self);
819 g_assert (priv->impl != NULL);
820 _clutter_stage_window_show (priv->impl, TRUE);
824 clutter_stage_hide_all (ClutterActor *self)
826 ClutterActorIter iter;
829 clutter_actor_hide (self);
831 /* we don't do a recursive hide_all(), to maintain the old invariants
834 clutter_actor_iter_init (&iter, self);
835 while (clutter_actor_iter_next (&iter, &child))
836 clutter_actor_hide (child);
840 clutter_stage_hide (ClutterActor *self)
842 ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
844 g_assert (priv->impl != NULL);
845 _clutter_stage_window_hide (priv->impl);
847 CLUTTER_ACTOR_CLASS (clutter_stage_parent_class)->hide (self);
851 clutter_stage_emit_key_focus_event (ClutterStage *stage,
854 ClutterStagePrivate *priv = stage->priv;
856 if (priv->key_focused_actor == NULL)
860 g_signal_emit_by_name (priv->key_focused_actor, "key-focus-in");
862 g_signal_emit_by_name (priv->key_focused_actor, "key-focus-out");
864 g_object_notify (G_OBJECT (stage), "key-focus");
868 clutter_stage_real_activate (ClutterStage *stage)
870 clutter_stage_emit_key_focus_event (stage, TRUE);
874 clutter_stage_real_deactivate (ClutterStage *stage)
876 clutter_stage_emit_key_focus_event (stage, FALSE);
880 clutter_stage_real_fullscreen (ClutterStage *stage)
882 ClutterStagePrivate *priv = stage->priv;
883 cairo_rectangle_int_t geom;
886 /* we need to force an allocation here because the size
887 * of the stage might have been changed by the backend
889 * this is a really bad solution to the issues caused by
890 * the fact that fullscreening the stage on the X11 backends
891 * is really an asynchronous operation
893 _clutter_stage_window_get_geometry (priv->impl, &geom);
898 box.y2 = geom.height;
900 /* we need to blow the caching on the Stage size, given that
901 * we're about to force an allocation, because if anything
902 * ends up querying the size of the stage during the allocate()
903 * call, like constraints or signal handlers, we'll get into an
904 * inconsistent state: the stage will report the old cached size,
905 * but the allocation will be updated anyway.
907 clutter_actor_set_size (CLUTTER_ACTOR (stage), -1.0, -1.0);
908 clutter_actor_allocate (CLUTTER_ACTOR (stage),
910 CLUTTER_ALLOCATION_NONE);
914 _clutter_stage_queue_event (ClutterStage *stage,
917 ClutterStagePrivate *priv;
918 gboolean first_event;
919 ClutterInputDevice *device;
921 g_return_if_fail (CLUTTER_IS_STAGE (stage));
925 first_event = priv->event_queue->length == 0;
927 g_queue_push_tail (priv->event_queue, clutter_event_copy (event));
931 ClutterMasterClock *master_clock = _clutter_master_clock_get_default ();
932 _clutter_master_clock_start_running (master_clock);
935 /* if needed, update the state of the input device of the event.
936 * we do it here to avoid calling the same code from every backend
937 * event processing function
939 device = clutter_event_get_device (event);
942 ClutterModifierType event_state = clutter_event_get_state (event);
943 guint32 event_time = clutter_event_get_time (event);
944 gfloat event_x, event_y;
946 clutter_event_get_coords (event, &event_x, &event_y);
948 _clutter_input_device_set_coords (device, event_x, event_y);
949 _clutter_input_device_set_state (device, event_state);
950 _clutter_input_device_set_time (device, event_time);
955 _clutter_stage_has_queued_events (ClutterStage *stage)
957 ClutterStagePrivate *priv;
959 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
963 return priv->event_queue->length > 0;
967 _clutter_stage_process_queued_events (ClutterStage *stage)
969 ClutterStagePrivate *priv;
972 g_return_if_fail (CLUTTER_IS_STAGE (stage));
976 if (priv->event_queue->length == 0)
979 /* In case the stage gets destroyed during event processing */
980 g_object_ref (stage);
982 /* Steal events before starting processing to avoid reentrancy
984 events = priv->event_queue->head;
985 priv->event_queue->head = NULL;
986 priv->event_queue->tail = NULL;
987 priv->event_queue->length = 0;
989 for (l = events; l != NULL; l = l->next)
992 ClutterEvent *next_event;
993 ClutterInputDevice *device;
994 ClutterInputDevice *next_device;
995 gboolean check_device = FALSE;
998 next_event = l->next ? l->next->data : NULL;
1000 device = clutter_event_get_device (event);
1002 if (next_event != NULL)
1003 next_device = clutter_event_get_device (next_event);
1007 if (device != NULL && next_device != NULL)
1008 check_device = TRUE;
1010 /* Skip consecutive motion events coming from the same device */
1011 if (priv->throttle_motion_events &&
1012 next_event != NULL &&
1013 event->type == CLUTTER_MOTION &&
1014 (next_event->type == CLUTTER_MOTION ||
1015 next_event->type == CLUTTER_LEAVE) &&
1016 (!check_device || (device == next_device)))
1018 CLUTTER_NOTE (EVENT,
1019 "Omitting motion event at %d, %d",
1020 (int) event->motion.x,
1021 (int) event->motion.y);
1025 _clutter_process_event (event);
1028 clutter_event_free (event);
1031 g_list_free (events);
1033 g_object_unref (stage);
1037 * _clutter_stage_needs_update:
1038 * @stage: A #ClutterStage
1040 * Determines if _clutter_stage_do_update() needs to be called.
1042 * Return value: %TRUE if the stage need layout or painting
1045 _clutter_stage_needs_update (ClutterStage *stage)
1047 ClutterStagePrivate *priv;
1049 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
1053 return priv->relayout_pending || priv->redraw_pending;
1057 _clutter_stage_maybe_relayout (ClutterActor *actor)
1059 ClutterStage *stage = CLUTTER_STAGE (actor);
1060 ClutterStagePrivate *priv = stage->priv;
1061 gfloat natural_width, natural_height;
1062 ClutterActorBox box = { 0, };
1063 CLUTTER_STATIC_TIMER (relayout_timer,
1064 "Mainloop", /* no parent */
1066 "The time spent reallocating the stage",
1067 0 /* no application private data */);
1069 if (!priv->relayout_pending)
1072 /* avoid reentrancy */
1073 if (!CLUTTER_ACTOR_IN_RELAYOUT (stage))
1075 priv->relayout_pending = FALSE;
1077 CLUTTER_TIMER_START (_clutter_uprof_context, relayout_timer);
1078 CLUTTER_NOTE (ACTOR, "Recomputing layout");
1080 CLUTTER_SET_PRIVATE_FLAGS (stage, CLUTTER_IN_RELAYOUT);
1082 natural_width = natural_height = 0;
1083 clutter_actor_get_preferred_size (CLUTTER_ACTOR (stage),
1085 &natural_width, &natural_height);
1089 box.x2 = natural_width;
1090 box.y2 = natural_height;
1092 CLUTTER_NOTE (ACTOR, "Allocating (0, 0 - %d, %d) for the stage",
1093 (int) natural_width,
1094 (int) natural_height);
1096 clutter_actor_allocate (CLUTTER_ACTOR (stage),
1097 &box, CLUTTER_ALLOCATION_NONE);
1099 CLUTTER_UNSET_PRIVATE_FLAGS (stage, CLUTTER_IN_RELAYOUT);
1100 CLUTTER_TIMER_STOP (_clutter_uprof_context, relayout_timer);
1105 _clutter_stage_get_pick_buffer_valid (ClutterStage *stage, ClutterPickMode mode)
1107 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
1109 if (stage->priv->pick_buffer_mode != mode)
1112 return stage->priv->have_valid_pick_buffer;
1116 _clutter_stage_set_pick_buffer_valid (ClutterStage *stage,
1118 ClutterPickMode mode)
1120 g_return_if_fail (CLUTTER_IS_STAGE (stage));
1122 stage->priv->have_valid_pick_buffer = !!valid;
1123 stage->priv->pick_buffer_mode = mode;
1127 clutter_stage_do_redraw (ClutterStage *stage)
1129 ClutterBackend *backend = clutter_get_default_backend ();
1130 ClutterActor *actor = CLUTTER_ACTOR (stage);
1131 ClutterStagePrivate *priv = stage->priv;
1133 CLUTTER_STATIC_COUNTER (redraw_counter,
1134 "clutter_stage_do_redraw counter",
1135 "Increments for each Stage redraw",
1136 0 /* no application private data */);
1137 CLUTTER_STATIC_TIMER (redraw_timer,
1138 "Master Clock", /* parent */
1140 "The time spent redrawing everything",
1141 0 /* no application private data */);
1143 if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
1146 if (priv->impl == NULL)
1149 CLUTTER_NOTE (PAINT, "Redraw started for stage '%s'[%p]",
1150 _clutter_actor_get_debug_name (actor),
1153 _clutter_stage_set_pick_buffer_valid (stage, FALSE, -1);
1154 priv->picks_per_frame = 0;
1156 _clutter_backend_ensure_context (backend, stage);
1158 if (_clutter_context_get_show_fps ())
1160 if (priv->fps_timer == NULL)
1161 priv->fps_timer = g_timer_new ();
1164 _clutter_stage_maybe_setup_viewport (stage);
1166 CLUTTER_COUNTER_INC (_clutter_uprof_context, redraw_counter);
1167 CLUTTER_TIMER_START (_clutter_uprof_context, redraw_timer);
1169 _clutter_stage_window_redraw (priv->impl);
1171 CLUTTER_TIMER_STOP (_clutter_uprof_context, redraw_timer);
1173 if (_clutter_context_get_show_fps ())
1175 priv->timer_n_frames += 1;
1177 if (g_timer_elapsed (priv->fps_timer, NULL) >= 1.0)
1179 g_print ("*** FPS for %s: %i ***\n",
1180 _clutter_actor_get_debug_name (actor),
1181 priv->timer_n_frames);
1183 priv->timer_n_frames = 0;
1184 g_timer_start (priv->fps_timer);
1188 CLUTTER_NOTE (PAINT, "Redraw finished for stage '%s'[%p]",
1189 _clutter_actor_get_debug_name (actor),
1194 * _clutter_stage_do_update:
1195 * @stage: A #ClutterStage
1197 * Handles per-frame layout and repaint for the stage.
1199 * Return value: %TRUE if the stage was updated
1202 _clutter_stage_do_update (ClutterStage *stage)
1204 ClutterStagePrivate *priv = stage->priv;
1206 /* if the stage is being destroyed, or if the destruction already
1207 * happened and we don't have an StageWindow any more, then we
1210 if (CLUTTER_ACTOR_IN_DESTRUCTION (stage) || priv->impl == NULL)
1213 if (!CLUTTER_ACTOR_IS_REALIZED (stage))
1216 /* NB: We need to ensure we have an up to date layout *before* we
1217 * check or clear the pending redraws flag since a relayout may
1220 _clutter_stage_maybe_relayout (CLUTTER_ACTOR (stage));
1222 if (!priv->redraw_pending)
1225 _clutter_stage_maybe_finish_queue_redraws (stage);
1227 clutter_stage_do_redraw (stage);
1229 /* reset the guard, so that new redraws are possible */
1230 priv->redraw_pending = FALSE;
1232 #ifdef CLUTTER_ENABLE_DEBUG
1233 if (priv->redraw_count > 0)
1235 CLUTTER_NOTE (SCHEDULER, "Queued %lu redraws during the last cycle",
1236 priv->redraw_count);
1238 priv->redraw_count = 0;
1240 #endif /* CLUTTER_ENABLE_DEBUG */
1246 clutter_stage_real_queue_relayout (ClutterActor *self)
1248 ClutterStage *stage = CLUTTER_STAGE (self);
1249 ClutterStagePrivate *priv = stage->priv;
1250 ClutterActorClass *parent_class;
1252 priv->relayout_pending = TRUE;
1255 parent_class = CLUTTER_ACTOR_CLASS (clutter_stage_parent_class);
1256 parent_class->queue_relayout (self);
1260 clutter_stage_real_queue_redraw (ClutterActor *actor,
1263 ClutterStage *stage = CLUTTER_STAGE (actor);
1264 ClutterStageWindow *stage_window;
1265 ClutterPaintVolume *redraw_clip;
1266 ClutterActorBox bounding_box;
1267 ClutterActorBox intersection_box;
1268 cairo_rectangle_int_t geom, stage_clip;
1270 if (CLUTTER_ACTOR_IN_DESTRUCTION (actor))
1273 /* If the backend can't do anything with redraw clips (e.g. it already knows
1274 * it needs to redraw everything anyway) then don't spend time transforming
1275 * any clip volume into stage coordinates... */
1276 stage_window = _clutter_stage_get_window (stage);
1277 if (stage_window == NULL)
1280 if (_clutter_stage_window_ignoring_redraw_clips (stage_window))
1282 _clutter_stage_window_add_redraw_clip (stage_window, NULL);
1286 /* Convert the clip volume into stage coordinates and then into an
1287 * axis aligned stage coordinates bounding box...
1289 redraw_clip = _clutter_actor_get_queue_redraw_clip (leaf);
1290 if (redraw_clip == NULL)
1292 _clutter_stage_window_add_redraw_clip (stage_window, NULL);
1296 _clutter_paint_volume_get_stage_paint_box (redraw_clip,
1300 _clutter_stage_window_get_geometry (stage_window, &geom);
1302 intersection_box.x1 = MAX (bounding_box.x1, 0);
1303 intersection_box.y1 = MAX (bounding_box.y1, 0);
1304 intersection_box.x2 = MIN (bounding_box.x2, geom.width);
1305 intersection_box.y2 = MIN (bounding_box.y2, geom.height);
1307 /* There is no need to track degenerate/empty redraw clips */
1308 if (intersection_box.x2 <= intersection_box.x1 ||
1309 intersection_box.y2 <= intersection_box.y1)
1312 /* when converting to integer coordinates make sure we round the edges of the
1313 * clip rectangle outwards... */
1314 stage_clip.x = intersection_box.x1;
1315 stage_clip.y = intersection_box.y1;
1316 stage_clip.width = intersection_box.x2 - stage_clip.x;
1317 stage_clip.height = intersection_box.y2 - stage_clip.y;
1319 _clutter_stage_window_add_redraw_clip (stage_window, &stage_clip);
1323 _clutter_stage_has_full_redraw_queued (ClutterStage *stage)
1325 ClutterStageWindow *stage_window = _clutter_stage_get_window (stage);
1327 if (CLUTTER_ACTOR_IN_DESTRUCTION (stage) || stage_window == NULL)
1330 if (stage->priv->redraw_pending &&
1331 !_clutter_stage_window_has_redraw_clips (stage_window))
1338 * clutter_stage_get_redraw_clip_bounds:
1339 * @stage: A #ClutterStage
1340 * @clip: (out caller-allocates): Return location for the clip bounds
1342 * Gets the bounds of the current redraw for @stage in stage pixel
1343 * coordinates. E.g., if only a single actor has queued a redraw then
1344 * Clutter may redraw the stage with a clip so that it doesn't have to
1345 * paint every pixel in the stage. This function would then return the
1346 * bounds of that clip. An application can use this information to
1347 * avoid some extra work if it knows that some regions of the stage
1348 * aren't going to be painted. This should only be called while the
1349 * stage is being painted. If there is no current redraw clip then
1350 * this function will set @clip to the full extents of the stage.
1355 clutter_stage_get_redraw_clip_bounds (ClutterStage *stage,
1356 cairo_rectangle_int_t *clip)
1358 ClutterStagePrivate *priv;
1360 g_return_if_fail (CLUTTER_IS_STAGE (stage));
1361 g_return_if_fail (clip != NULL);
1365 if (!_clutter_stage_window_get_redraw_clip_bounds (priv->impl, clip))
1367 /* Set clip to the full extents of the stage */
1368 _clutter_stage_window_get_geometry (priv->impl, clip);
1373 read_pixels_to_file (char *filename_stem,
1380 cairo_surface_t *surface;
1381 static int read_count = 0;
1382 char *filename = g_strdup_printf ("%s-%05d.png",
1386 data = g_malloc (4 * width * height);
1387 cogl_read_pixels (x, y, width, height,
1388 COGL_READ_PIXELS_COLOR_BUFFER,
1389 CLUTTER_CAIRO_FORMAT_ARGB32,
1392 surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_RGB24,
1396 cairo_surface_write_to_png (surface, filename);
1397 cairo_surface_destroy (surface);
1406 _clutter_stage_do_pick (ClutterStage *stage,
1409 ClutterPickMode mode)
1411 ClutterStagePrivate *priv;
1412 ClutterMainContext *context;
1413 guchar pixel[4] = { 0xff, 0xff, 0xff, 0xff };
1414 CoglColor stage_pick_id;
1415 gboolean dither_enabled_save;
1416 CoglFramebuffer *fb;
1417 ClutterActor *actor;
1418 gboolean is_clipped;
1419 CLUTTER_STATIC_COUNTER (do_pick_counter,
1420 "_clutter_stage_do_pick counter",
1421 "Increments for each full pick run",
1422 0 /* no application private data */);
1423 CLUTTER_STATIC_TIMER (pick_timer,
1424 "Mainloop", /* parent */
1426 "The time spent picking",
1427 0 /* no application private data */);
1428 CLUTTER_STATIC_TIMER (pick_clear,
1429 "Picking", /* parent */
1430 "Stage clear (pick)",
1431 "The time spent clearing stage for picking",
1432 0 /* no application private data */);
1433 CLUTTER_STATIC_TIMER (pick_paint,
1434 "Picking", /* parent */
1435 "Painting actors (pick mode)",
1436 "The time spent painting actors in pick mode",
1437 0 /* no application private data */);
1438 CLUTTER_STATIC_TIMER (pick_read,
1439 "Picking", /* parent */
1441 "The time spent issuing a read pixels",
1442 0 /* no application private data */);
1444 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
1448 if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_NOP_PICKING))
1449 return CLUTTER_ACTOR (stage);
1451 #ifdef CLUTTER_ENABLE_PROFILE
1452 if (clutter_profile_flags & CLUTTER_PROFILE_PICKING_ONLY)
1453 _clutter_profile_resume ();
1454 #endif /* CLUTTER_ENABLE_PROFILE */
1456 CLUTTER_COUNTER_INC (_clutter_uprof_context, do_pick_counter);
1457 CLUTTER_TIMER_START (_clutter_uprof_context, pick_timer);
1459 context = _clutter_context_get_default ();
1460 clutter_stage_ensure_current (stage);
1462 /* It's possible that we currently have a static scene and have renderered a
1463 * full, unclipped pick buffer. If so we can simply continue to read from
1464 * this cached buffer until the scene next changes. */
1465 if (_clutter_stage_get_pick_buffer_valid (stage, mode))
1467 CLUTTER_TIMER_START (_clutter_uprof_context, pick_read);
1468 cogl_read_pixels (x, y, 1, 1,
1469 COGL_READ_PIXELS_COLOR_BUFFER,
1470 COGL_PIXEL_FORMAT_RGBA_8888_PRE,
1472 CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_read);
1474 CLUTTER_NOTE (PICK, "Reusing pick buffer from previous render to fetch "
1475 "actor at %i,%i", x, y);
1480 priv->picks_per_frame++;
1482 _clutter_backend_ensure_context (context->backend, stage);
1484 /* needed for when a context switch happens */
1485 _clutter_stage_maybe_setup_viewport (stage);
1487 /* If we are seeing multiple picks per frame that means the scene is static
1488 * so we promote to doing a non-scissored pick render so that all subsequent
1489 * picks for the same static scene won't require additional renders */
1490 if (priv->picks_per_frame < 2)
1492 if (G_LIKELY (!(clutter_pick_debug_flags &
1493 CLUTTER_DEBUG_DUMP_PICK_BUFFERS)))
1494 cogl_clip_push_window_rectangle (x, y, 1, 1);
1500 CLUTTER_NOTE (PICK, "Performing %s pick at %i,%i",
1501 is_clipped ? "clippped" : "full", x, y);
1503 cogl_disable_fog ();
1504 cogl_color_init_from_4ub (&stage_pick_id, 255, 255, 255, 255);
1505 CLUTTER_TIMER_START (_clutter_uprof_context, pick_clear);
1506 cogl_clear (&stage_pick_id,
1507 COGL_BUFFER_BIT_COLOR |
1508 COGL_BUFFER_BIT_DEPTH);
1509 CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_clear);
1511 /* Disable dithering (if any) when doing the painting in pick mode */
1512 fb = cogl_get_draw_framebuffer ();
1513 dither_enabled_save = cogl_framebuffer_get_dither_enabled (fb);
1514 cogl_framebuffer_set_dither_enabled (fb, FALSE);
1516 /* Render the entire scence in pick mode - just single colored silhouette's
1517 * are drawn offscreen (as we never swap buffers)
1519 CLUTTER_TIMER_START (_clutter_uprof_context, pick_paint);
1520 context->pick_mode = mode;
1521 _clutter_stage_do_paint (stage, NULL);
1522 context->pick_mode = CLUTTER_PICK_NONE;
1523 CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_paint);
1527 if (G_LIKELY (!(clutter_pick_debug_flags &
1528 CLUTTER_DEBUG_DUMP_PICK_BUFFERS)))
1531 _clutter_stage_set_pick_buffer_valid (stage, FALSE, -1);
1534 _clutter_stage_set_pick_buffer_valid (stage, TRUE, mode);
1536 /* Read the color of the screen co-ords pixel. RGBA_8888_PRE is used
1537 even though we don't care about the alpha component because under
1538 GLES this is the only format that is guaranteed to work so Cogl
1539 will end up having to do a conversion if any other format is
1540 used. The format is requested as pre-multiplied because Cogl
1541 assumes that all pixels in the framebuffer are premultiplied so
1542 it avoids a conversion. */
1543 CLUTTER_TIMER_START (_clutter_uprof_context, pick_read);
1544 cogl_read_pixels (x, y, 1, 1,
1545 COGL_READ_PIXELS_COLOR_BUFFER,
1546 COGL_PIXEL_FORMAT_RGBA_8888_PRE,
1548 CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_read);
1550 if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))
1553 g_strconcat ("pick-buffer-",
1554 _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage)),
1557 read_pixels_to_file (file_name, 0, 0,
1558 clutter_actor_get_width (CLUTTER_ACTOR (stage)),
1559 clutter_actor_get_height (CLUTTER_ACTOR (stage)));
1564 /* Restore whether GL_DITHER was enabled */
1565 cogl_framebuffer_set_dither_enabled (fb, dither_enabled_save);
1568 if (pixel[0] == 0xff && pixel[1] == 0xff && pixel[2] == 0xff)
1570 actor = CLUTTER_ACTOR (stage);
1574 guint32 id_ = _clutter_pixel_to_id (pixel);
1576 actor = _clutter_get_actor_by_id (stage, id_);
1579 CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_timer);
1581 #ifdef CLUTTER_ENABLE_PROFILE
1582 if (clutter_profile_flags & CLUTTER_PROFILE_PICKING_ONLY)
1583 _clutter_profile_suspend ();
1592 clutter_stage_real_delete_event (ClutterStage *stage,
1593 ClutterEvent *event)
1595 if (stage_is_default (stage))
1596 clutter_main_quit ();
1598 clutter_actor_destroy (CLUTTER_ACTOR (stage));
1604 clutter_stage_real_apply_transform (ClutterActor *stage,
1607 ClutterStagePrivate *priv = CLUTTER_STAGE (stage)->priv;
1609 /* FIXME: we probably shouldn't be explicitly reseting the matrix
1611 cogl_matrix_init_identity (matrix);
1612 cogl_matrix_multiply (matrix, matrix, &priv->view);
1616 clutter_stage_constructed (GObject *gobject)
1618 ClutterStage *self = CLUTTER_STAGE (gobject);
1619 ClutterStageManager *stage_manager;
1621 stage_manager = clutter_stage_manager_get_default ();
1623 /* this will take care to sinking the floating reference */
1624 _clutter_stage_manager_add_stage (stage_manager, self);
1626 /* if this stage has been created on a backend that does not
1627 * support multiple stages then it becomes the default stage
1628 * as well; any other attempt at creating a ClutterStage will
1631 if (!clutter_feature_available (CLUTTER_FEATURE_STAGE_MULTIPLE))
1633 if (G_UNLIKELY (clutter_stage_manager_get_default_stage (stage_manager) != NULL))
1635 g_error ("Unable to create another stage: the backend of "
1636 "type '%s' does not support multiple stages. Use "
1637 "clutter_stage_manager_get_default_stage() instead "
1638 "to access the stage singleton.",
1639 G_OBJECT_TYPE_NAME (clutter_get_default_backend ()));
1642 _clutter_stage_manager_set_default_stage (stage_manager, self);
1645 G_OBJECT_CLASS (clutter_stage_parent_class)->constructed (gobject);
1649 clutter_stage_set_property (GObject *object,
1651 const GValue *value,
1654 ClutterStage *stage = CLUTTER_STAGE (object);
1659 clutter_actor_set_background_color (CLUTTER_ACTOR (stage),
1660 clutter_value_get_color (value));
1663 case PROP_OFFSCREEN:
1664 if (g_value_get_boolean (value))
1665 g_warning ("Offscreen stages are currently not supported\n");
1668 case PROP_CURSOR_VISIBLE:
1669 if (g_value_get_boolean (value))
1670 clutter_stage_show_cursor (stage);
1672 clutter_stage_hide_cursor (stage);
1675 case PROP_PERSPECTIVE:
1676 clutter_stage_set_perspective (stage, g_value_get_boxed (value));
1680 clutter_stage_set_title (stage, g_value_get_string (value));
1683 case PROP_USER_RESIZABLE:
1684 clutter_stage_set_user_resizable (stage, g_value_get_boolean (value));
1688 clutter_stage_set_use_fog (stage, g_value_get_boolean (value));
1692 clutter_stage_set_fog (stage, g_value_get_boxed (value));
1695 case PROP_USE_ALPHA:
1696 clutter_stage_set_use_alpha (stage, g_value_get_boolean (value));
1699 case PROP_KEY_FOCUS:
1700 clutter_stage_set_key_focus (stage, g_value_get_object (value));
1703 case PROP_NO_CLEAR_HINT:
1704 clutter_stage_set_no_clear_hint (stage, g_value_get_boolean (value));
1707 case PROP_ACCEPT_FOCUS:
1708 clutter_stage_set_accept_focus (stage, g_value_get_boolean (value));
1712 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1718 clutter_stage_get_property (GObject *gobject,
1723 ClutterStagePrivate *priv = CLUTTER_STAGE (gobject)->priv;
1729 ClutterColor bg_color;
1731 clutter_actor_get_background_color (CLUTTER_ACTOR (gobject),
1733 clutter_value_set_color (value, &bg_color);
1737 case PROP_OFFSCREEN:
1738 g_value_set_boolean (value, FALSE);
1741 case PROP_FULLSCREEN_SET:
1742 g_value_set_boolean (value, priv->is_fullscreen);
1745 case PROP_CURSOR_VISIBLE:
1746 g_value_set_boolean (value, priv->is_cursor_visible);
1749 case PROP_PERSPECTIVE:
1750 g_value_set_boxed (value, &priv->perspective);
1754 g_value_set_string (value, priv->title);
1757 case PROP_USER_RESIZABLE:
1758 g_value_set_boolean (value, priv->is_user_resizable);
1762 g_value_set_boolean (value, priv->use_fog);
1766 g_value_set_boxed (value, &priv->fog);
1769 case PROP_USE_ALPHA:
1770 g_value_set_boolean (value, priv->use_alpha);
1773 case PROP_KEY_FOCUS:
1774 g_value_set_object (value, priv->key_focused_actor);
1777 case PROP_NO_CLEAR_HINT:
1780 (priv->stage_hints & CLUTTER_STAGE_NO_CLEAR_ON_PAINT) != 0;
1782 g_value_set_boolean (value, hint);
1786 case PROP_ACCEPT_FOCUS:
1787 g_value_set_boolean (value, priv->accept_focus);
1791 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
1797 clutter_stage_dispose (GObject *object)
1799 ClutterStage *stage = CLUTTER_STAGE (object);
1800 ClutterStagePrivate *priv = stage->priv;
1801 ClutterStageManager *stage_manager;
1803 clutter_actor_hide (CLUTTER_ACTOR (object));
1805 /* remove_stage() will unref() the stage instance, so we need to
1806 * add a reference here to keep it temporarily alive
1808 g_object_ref (object);
1809 stage_manager = clutter_stage_manager_get_default ();
1810 _clutter_stage_manager_remove_stage (stage_manager, stage);
1812 _clutter_clear_events_queue_for_stage (stage);
1814 if (priv->impl != NULL)
1816 CLUTTER_NOTE (BACKEND, "Disposing of the stage implementation");
1818 if (CLUTTER_ACTOR_IS_REALIZED (object))
1819 _clutter_stage_window_unrealize (priv->impl);
1821 g_object_unref (priv->impl);
1825 clutter_actor_remove_all_children (CLUTTER_ACTOR (object));
1827 G_OBJECT_CLASS (clutter_stage_parent_class)->dispose (object);
1831 clutter_stage_finalize (GObject *object)
1833 ClutterStage *stage = CLUTTER_STAGE (object);
1834 ClutterStagePrivate *priv = stage->priv;
1836 g_queue_foreach (priv->event_queue, (GFunc) clutter_event_free, NULL);
1837 g_queue_free (priv->event_queue);
1839 g_free (priv->title);
1841 g_array_free (priv->paint_volume_stack, TRUE);
1843 g_hash_table_destroy (priv->devices);
1845 _clutter_id_pool_free (priv->pick_id_pool);
1847 if (priv->fps_timer != NULL)
1848 g_timer_destroy (priv->fps_timer);
1850 G_OBJECT_CLASS (clutter_stage_parent_class)->finalize (object);
1854 clutter_stage_class_init (ClutterStageClass *klass)
1856 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1857 ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
1860 gobject_class->constructed = clutter_stage_constructed;
1861 gobject_class->set_property = clutter_stage_set_property;
1862 gobject_class->get_property = clutter_stage_get_property;
1863 gobject_class->dispose = clutter_stage_dispose;
1864 gobject_class->finalize = clutter_stage_finalize;
1866 actor_class->allocate = clutter_stage_allocate;
1867 actor_class->get_preferred_width = clutter_stage_get_preferred_width;
1868 actor_class->get_preferred_height = clutter_stage_get_preferred_height;
1869 actor_class->paint = clutter_stage_paint;
1870 actor_class->pick = clutter_stage_pick;
1871 actor_class->get_paint_volume = clutter_stage_get_paint_volume;
1872 actor_class->realize = clutter_stage_realize;
1873 actor_class->unrealize = clutter_stage_unrealize;
1874 actor_class->show = clutter_stage_show;
1875 actor_class->show_all = clutter_stage_show_all;
1876 actor_class->hide = clutter_stage_hide;
1877 actor_class->hide_all = clutter_stage_hide_all;
1878 actor_class->queue_relayout = clutter_stage_real_queue_relayout;
1879 actor_class->queue_redraw = clutter_stage_real_queue_redraw;
1880 actor_class->apply_transform = clutter_stage_real_apply_transform;
1883 * ClutterStage:fullscreen:
1885 * Whether the stage should be fullscreen or not.
1887 * This property is set by calling clutter_stage_set_fullscreen()
1888 * but since the actual implementation is delegated to the backend
1889 * you should connect to the notify::fullscreen-set signal in order
1890 * to get notification if the fullscreen state has been successfully
1895 pspec = g_param_spec_boolean ("fullscreen-set",
1896 P_("Fullscreen Set"),
1897 P_("Whether the main stage is fullscreen"),
1899 CLUTTER_PARAM_READABLE);
1900 g_object_class_install_property (gobject_class,
1901 PROP_FULLSCREEN_SET,
1904 * ClutterStage:offscreen:
1906 * Whether the stage should be rendered in an offscreen buffer.
1908 * Deprecated: 1.10: This property does not do anything.
1910 pspec = g_param_spec_boolean ("offscreen",
1912 P_("Whether the main stage should be rendered offscreen"),
1914 CLUTTER_PARAM_READWRITE | G_PARAM_DEPRECATED);
1915 g_object_class_install_property (gobject_class,
1919 * ClutterStage:cursor-visible:
1921 * Whether the mouse pointer should be visible
1923 pspec = g_param_spec_boolean ("cursor-visible",
1924 P_("Cursor Visible"),
1925 P_("Whether the mouse pointer is visible on the main stage"),
1927 CLUTTER_PARAM_READWRITE);
1928 g_object_class_install_property (gobject_class,
1929 PROP_CURSOR_VISIBLE,
1932 * ClutterStage:user-resizable:
1934 * Whether the stage is resizable via user interaction.
1938 pspec = g_param_spec_boolean ("user-resizable",
1939 P_("User Resizable"),
1940 P_("Whether the stage is able to be resized via user interaction"),
1942 CLUTTER_PARAM_READWRITE);
1943 g_object_class_install_property (gobject_class,
1944 PROP_USER_RESIZABLE,
1947 * ClutterStage:color:
1949 * The background color of the main stage.
1951 * Deprecated: 1.10: Use the #ClutterActor:background-color property of
1952 * #ClutterActor instead.
1954 pspec = clutter_param_spec_color ("color",
1956 P_("The color of the stage"),
1957 &default_stage_color,
1958 CLUTTER_PARAM_READWRITE |
1959 G_PARAM_DEPRECATED);
1960 g_object_class_install_property (gobject_class, PROP_COLOR, pspec);
1963 * ClutterStage:perspective:
1965 * The parameters used for the perspective projection from 3D
1970 pspec = g_param_spec_boxed ("perspective",
1972 P_("Perspective projection parameters"),
1973 CLUTTER_TYPE_PERSPECTIVE,
1974 CLUTTER_PARAM_READWRITE);
1975 g_object_class_install_property (gobject_class,
1980 * ClutterStage:title:
1982 * The stage's title - usually displayed in stage windows title decorations.
1986 pspec = g_param_spec_string ("title",
1990 CLUTTER_PARAM_READWRITE);
1991 g_object_class_install_property (gobject_class, PROP_TITLE, pspec);
1994 * ClutterStage:use-fog:
1996 * Whether the stage should use a linear GL "fog" in creating the
1997 * depth-cueing effect, to enhance the perception of depth by fading
1998 * actors farther from the viewpoint.
2002 * Deprecated: 1.10: This property does not do anything.
2004 pspec = g_param_spec_boolean ("use-fog",
2006 P_("Whether to enable depth cueing"),
2008 CLUTTER_PARAM_READWRITE | G_PARAM_DEPRECATED);
2009 g_object_class_install_property (gobject_class, PROP_USE_FOG, pspec);
2014 * The settings for the GL "fog", used only if #ClutterStage:use-fog
2019 * Deprecated: 1.10: This property does not do anything.
2021 pspec = g_param_spec_boxed ("fog",
2023 P_("Settings for the depth cueing"),
2025 CLUTTER_PARAM_READWRITE | G_PARAM_DEPRECATED);
2026 g_object_class_install_property (gobject_class, PROP_FOG, pspec);
2029 * ClutterStage:use-alpha:
2031 * Whether the #ClutterStage should honour the alpha component of the
2032 * #ClutterStage:color property when painting. If Clutter is run under
2033 * a compositing manager this will result in the stage being blended
2034 * with the underlying window(s)
2038 pspec = g_param_spec_boolean ("use-alpha",
2040 P_("Whether to honour the alpha component of the stage color"),
2042 CLUTTER_PARAM_READWRITE);
2043 g_object_class_install_property (gobject_class, PROP_USE_ALPHA, pspec);
2046 * ClutterStage:key-focus:
2048 * The #ClutterActor that will receive key events from the underlying
2051 * If %NULL, the #ClutterStage will receive the events.
2055 pspec = g_param_spec_object ("key-focus",
2057 P_("The currently key focused actor"),
2059 CLUTTER_PARAM_READWRITE);
2060 g_object_class_install_property (gobject_class, PROP_KEY_FOCUS, pspec);
2063 * ClutterStage:no-clear-hint:
2065 * Whether or not the #ClutterStage should clear its contents
2066 * before each paint cycle.
2068 * See clutter_stage_set_no_clear_hint() for further information.
2072 pspec = g_param_spec_boolean ("no-clear-hint",
2073 P_("No Clear Hint"),
2074 P_("Whether the stage should clear its contents"),
2076 CLUTTER_PARAM_READWRITE);
2077 g_object_class_install_property (gobject_class, PROP_NO_CLEAR_HINT, pspec);
2080 * ClutterStage:accept-focus:
2082 * Whether the #ClutterStage should accept key focus when shown.
2086 pspec = g_param_spec_boolean ("accept-focus",
2088 P_("Whether the stage should accept focus on show"),
2090 CLUTTER_PARAM_READWRITE);
2091 g_object_class_install_property (gobject_class, PROP_ACCEPT_FOCUS, pspec);
2094 * ClutterStage::fullscreen:
2095 * @stage: the stage which was fullscreened
2097 * The ::fullscreen signal is emitted when the stage is made fullscreen.
2101 stage_signals[FULLSCREEN] =
2102 g_signal_new (I_("fullscreen"),
2103 G_TYPE_FROM_CLASS (gobject_class),
2105 G_STRUCT_OFFSET (ClutterStageClass, fullscreen),
2107 _clutter_marshal_VOID__VOID,
2110 * ClutterStage::unfullscreen:
2111 * @stage: the stage which has left a fullscreen state.
2113 * The ::unfullscreen signal is emitted when the stage leaves a fullscreen
2118 stage_signals[UNFULLSCREEN] =
2119 g_signal_new (I_("unfullscreen"),
2120 G_TYPE_FROM_CLASS (gobject_class),
2122 G_STRUCT_OFFSET (ClutterStageClass, unfullscreen),
2124 _clutter_marshal_VOID__VOID,
2127 * ClutterStage::activate:
2128 * @stage: the stage which was activated
2130 * The ::activate signal is emitted when the stage receives key focus
2131 * from the underlying window system.
2135 stage_signals[ACTIVATE] =
2136 g_signal_new (I_("activate"),
2137 G_TYPE_FROM_CLASS (gobject_class),
2139 G_STRUCT_OFFSET (ClutterStageClass, activate),
2141 _clutter_marshal_VOID__VOID,
2144 * ClutterStage::deactivate:
2145 * @stage: the stage which was deactivated
2147 * The ::activate signal is emitted when the stage loses key focus
2148 * from the underlying window system.
2152 stage_signals[DEACTIVATE] =
2153 g_signal_new (I_("deactivate"),
2154 G_TYPE_FROM_CLASS (gobject_class),
2156 G_STRUCT_OFFSET (ClutterStageClass, deactivate),
2158 _clutter_marshal_VOID__VOID,
2162 * ClutterStage::delete-event:
2163 * @stage: the stage that received the event
2164 * @event: a #ClutterEvent of type %CLUTTER_DELETE
2166 * The ::delete-event signal is emitted when the user closes a
2167 * #ClutterStage window using the window controls.
2169 * Clutter by default will call clutter_main_quit() if @stage is
2170 * the default stage, and clutter_actor_destroy() for any other
2173 * It is possible to override the default behaviour by connecting
2174 * a new handler and returning %TRUE there.
2176 * <note>This signal is emitted only on Clutter backends that
2177 * embed #ClutterStage in native windows. It is not emitted for
2178 * backends that use a static frame buffer.</note>
2182 stage_signals[DELETE_EVENT] =
2183 g_signal_new (I_("delete-event"),
2184 G_TYPE_FROM_CLASS (gobject_class),
2186 G_STRUCT_OFFSET (ClutterStageClass, delete_event),
2187 _clutter_boolean_handled_accumulator, NULL,
2188 _clutter_marshal_BOOLEAN__BOXED,
2190 CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
2192 klass->fullscreen = clutter_stage_real_fullscreen;
2193 klass->activate = clutter_stage_real_activate;
2194 klass->deactivate = clutter_stage_real_deactivate;
2195 klass->delete_event = clutter_stage_real_delete_event;
2197 g_type_class_add_private (gobject_class, sizeof (ClutterStagePrivate));
2201 clutter_stage_notify_min_size (ClutterStage *self)
2203 self->priv->min_size_changed = TRUE;
2207 clutter_stage_init (ClutterStage *self)
2209 cairo_rectangle_int_t geom = { 0, };
2210 ClutterStagePrivate *priv;
2211 ClutterStageWindow *impl;
2212 ClutterBackend *backend;
2215 /* a stage is a top-level object */
2216 CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IS_TOPLEVEL);
2218 self->priv = priv = CLUTTER_STAGE_GET_PRIVATE (self);
2220 CLUTTER_NOTE (BACKEND, "Creating stage from the default backend");
2221 backend = clutter_get_default_backend ();
2224 impl = _clutter_backend_create_stage (backend, self, &error);
2226 if (G_LIKELY (impl != NULL))
2228 _clutter_stage_set_window (self, impl);
2229 _clutter_stage_window_get_geometry (priv->impl, &geom);
2235 g_critical ("Unable to create a new stage implementation: %s",
2237 g_error_free (error);
2240 g_critical ("Unable to create a new stage implementation.");
2243 priv->event_queue = g_queue_new ();
2245 priv->is_fullscreen = FALSE;
2246 priv->is_user_resizable = FALSE;
2247 priv->is_cursor_visible = TRUE;
2248 priv->use_fog = FALSE;
2249 priv->throttle_motion_events = TRUE;
2250 priv->min_size_changed = FALSE;
2252 /* XXX - we need to keep the invariant that calling
2253 * clutter_set_motion_event_enabled() before the stage creation
2254 * will cause motion event delivery to be disabled on any newly
2255 * created stage. this can go away when we break API and remove
2256 * deprecated functions.
2258 priv->motion_events_enabled = _clutter_context_get_motion_events_enabled ();
2260 clutter_actor_set_background_color (CLUTTER_ACTOR (self),
2261 &default_stage_color);
2263 priv->perspective.fovy = 60.0; /* 60 Degrees */
2264 priv->perspective.aspect = (float) geom.width / (float) geom.height;
2265 priv->perspective.z_near = 0.1;
2266 priv->perspective.z_far = 100.0;
2268 cogl_matrix_init_identity (&priv->projection);
2269 cogl_matrix_perspective (&priv->projection,
2270 priv->perspective.fovy,
2271 priv->perspective.aspect,
2272 priv->perspective.z_near,
2273 priv->perspective.z_far);
2274 cogl_matrix_get_inverse (&priv->projection,
2275 &priv->inverse_projection);
2276 cogl_matrix_init_identity (&priv->view);
2277 cogl_matrix_view_2d_in_perspective (&priv->view,
2278 priv->perspective.fovy,
2279 priv->perspective.aspect,
2280 priv->perspective.z_near,
2281 50, /* distance to 2d plane */
2286 /* FIXME - remove for 2.0 */
2287 priv->fog.z_near = 1.0;
2288 priv->fog.z_far = 2.0;
2290 priv->relayout_pending = TRUE;
2292 clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE);
2293 clutter_stage_set_title (self, g_get_prgname ());
2294 clutter_stage_set_key_focus (self, NULL);
2296 g_signal_connect (self, "notify::min-width",
2297 G_CALLBACK (clutter_stage_notify_min_size), NULL);
2298 g_signal_connect (self, "notify::min-height",
2299 G_CALLBACK (clutter_stage_notify_min_size), NULL);
2301 _clutter_stage_set_viewport (self, 0, 0, geom.width, geom.height);
2303 _clutter_stage_set_pick_buffer_valid (self, FALSE, CLUTTER_PICK_ALL);
2304 priv->picks_per_frame = 0;
2306 priv->paint_volume_stack =
2307 g_array_new (FALSE, FALSE, sizeof (ClutterPaintVolume));
2309 priv->devices = g_hash_table_new (NULL, NULL);
2311 priv->pick_id_pool = _clutter_id_pool_new (256);
2315 * clutter_stage_get_default:
2317 * Retrieves a #ClutterStage singleton.
2319 * This function is not as useful as it sounds, and will most likely
2320 * by deprecated in the future. Application code should only create
2321 * a #ClutterStage instance using clutter_stage_new(), and manage the
2322 * lifetime of the stage manually.
2324 * The default stage singleton has a platform-specific behaviour: on
2325 * platforms without the %CLUTTER_FEATURE_STAGE_MULTIPLE feature flag
2326 * set, the first #ClutterStage instance will also be set to be the
2327 * default stage instance, and this function will always return a
2330 * On platforms with the %CLUTTER_FEATURE_STAGE_MULTIPLE feature flag
2331 * set, the default stage will be created by the first call to this
2332 * function, and every following call will return the same pointer to
2335 * Return value: (transfer none) (type Clutter.Stage): the main
2336 * #ClutterStage. You should never destroy or unref the returned
2339 * Deprecated: 1.10: Use clutter_stage_new() instead.
2342 clutter_stage_get_default (void)
2344 ClutterStageManager *stage_manager = clutter_stage_manager_get_default ();
2345 ClutterStage *stage;
2347 stage = clutter_stage_manager_get_default_stage (stage_manager);
2348 if (G_UNLIKELY (stage == NULL))
2350 /* This will take care of automatically adding the stage to the
2351 * stage manager and setting it as the default. Its floating
2352 * reference will be claimed by the stage manager.
2354 stage = g_object_new (CLUTTER_TYPE_STAGE, NULL);
2355 _clutter_stage_manager_set_default_stage (stage_manager, stage);
2357 /* the default stage is realized by default */
2358 clutter_actor_realize (CLUTTER_ACTOR (stage));
2361 return CLUTTER_ACTOR (stage);
2365 * clutter_stage_set_color:
2366 * @stage: A #ClutterStage
2367 * @color: A #ClutterColor
2369 * Sets the stage color.
2371 * Deprecated: 1.10: Use clutter_actor_set_background_color() instead.
2374 clutter_stage_set_color (ClutterStage *stage,
2375 const ClutterColor *color)
2377 clutter_actor_set_background_color (CLUTTER_ACTOR (stage), color);
2379 g_object_notify (G_OBJECT (stage), "color");
2383 * clutter_stage_get_color:
2384 * @stage: A #ClutterStage
2385 * @color: (out caller-allocates): return location for a #ClutterColor
2387 * Retrieves the stage color.
2389 * Deprecated: 1.10: Use clutter_actor_get_background_color() instead.
2392 clutter_stage_get_color (ClutterStage *stage,
2393 ClutterColor *color)
2395 clutter_actor_get_background_color (CLUTTER_ACTOR (stage), color);
2399 clutter_stage_set_perspective_internal (ClutterStage *stage,
2400 ClutterPerspective *perspective)
2402 ClutterStagePrivate *priv = stage->priv;
2404 if (priv->perspective.fovy == perspective->fovy &&
2405 priv->perspective.aspect == perspective->aspect &&
2406 priv->perspective.z_near == perspective->z_near &&
2407 priv->perspective.z_far == perspective->z_far)
2410 priv->perspective = *perspective;
2412 cogl_matrix_init_identity (&priv->projection);
2413 cogl_matrix_perspective (&priv->projection,
2414 priv->perspective.fovy,
2415 priv->perspective.aspect,
2416 priv->perspective.z_near,
2417 priv->perspective.z_far);
2418 cogl_matrix_get_inverse (&priv->projection,
2419 &priv->inverse_projection);
2421 priv->dirty_projection = TRUE;
2422 clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
2426 * clutter_stage_set_perspective:
2427 * @stage: A #ClutterStage
2428 * @perspective: A #ClutterPerspective
2430 * Sets the stage perspective. Using this function is not recommended
2431 * because it will disable Clutter's attempts to generate an
2432 * appropriate perspective based on the size of the stage.
2435 clutter_stage_set_perspective (ClutterStage *stage,
2436 ClutterPerspective *perspective)
2438 ClutterStagePrivate *priv;
2440 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2441 g_return_if_fail (perspective != NULL);
2442 g_return_if_fail (perspective->z_far - perspective->z_near != 0);
2446 /* If the application ever calls this function then we'll stop
2447 automatically updating the perspective when the stage changes
2449 priv->has_custom_perspective = TRUE;
2451 clutter_stage_set_perspective_internal (stage, perspective);
2455 * clutter_stage_get_perspective:
2456 * @stage: A #ClutterStage
2457 * @perspective: (out caller-allocates) (allow-none): return location for a
2458 * #ClutterPerspective
2460 * Retrieves the stage perspective.
2463 clutter_stage_get_perspective (ClutterStage *stage,
2464 ClutterPerspective *perspective)
2466 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2467 g_return_if_fail (perspective != NULL);
2469 *perspective = stage->priv->perspective;
2473 * clutter_stage_get_projection_matrix:
2474 * @stage: A #ClutterStage
2475 * @projection: return location for a #CoglMatrix representing the
2476 * perspective projection applied to actors on the given
2479 * Retrieves the @stage's projection matrix. This is derived from the
2480 * current perspective set using clutter_stage_set_perspective().
2485 _clutter_stage_get_projection_matrix (ClutterStage *stage,
2486 CoglMatrix *projection)
2488 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2489 g_return_if_fail (projection != NULL);
2491 *projection = stage->priv->projection;
2494 /* This simply provides a simple mechanism for us to ensure that
2495 * the projection matrix gets re-asserted before painting.
2497 * This is used when switching between multiple stages */
2499 _clutter_stage_dirty_projection (ClutterStage *stage)
2501 stage->priv->dirty_projection = TRUE;
2505 * clutter_stage_set_viewport:
2506 * @stage: A #ClutterStage
2507 * @x: The X postition to render the stage at, in window coordinates
2508 * @y: The Y position to render the stage at, in window coordinates
2509 * @width: The width to render the stage at, in window coordinates
2510 * @height: The height to render the stage at, in window coordinates
2512 * Sets the stage viewport. The viewport defines a final scale and
2513 * translation of your rendered stage and actors. This lets you render
2514 * your stage into a subregion of the stage window or you could use it to
2515 * pan a subregion of the stage if your stage window is smaller then
2516 * the stage. (XXX: currently this isn't possible)
2518 * Unlike a scale and translation done using the modelview matrix this
2519 * is done after everything has had perspective projection applied, so
2520 * for example if you were to pan across a subregion of the stage using
2521 * the viewport then you would not see a change in perspective for the
2522 * actors on the stage.
2524 * Normally the stage viewport will automatically track the size of the
2525 * stage window with no offset so the stage will fill your window. This
2526 * behaviour can be changed with the "viewport-mimics-window" property
2527 * which will automatically be set to FALSE if you use this API. If
2528 * you want to revert to the original behaviour then you should set
2529 * this property back to %TRUE using
2530 * clutter_stage_set_viewport_mimics_window().
2531 * (XXX: If we were to make this API public then we might want to do
2532 * add that property.)
2534 * Note: currently this interface only support integer precision
2535 * offsets and sizes for viewports but the interface takes floats because
2536 * OpenGL 4.0 has introduced floating point viewports which we might
2537 * want to expose via this API eventually.
2542 _clutter_stage_set_viewport (ClutterStage *stage,
2548 ClutterStagePrivate *priv;
2550 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2555 if (x == priv->viewport[0] &&
2556 y == priv->viewport[1] &&
2557 width == priv->viewport[2] &&
2558 height == priv->viewport[3])
2561 priv->viewport[0] = x;
2562 priv->viewport[1] = y;
2563 priv->viewport[2] = width;
2564 priv->viewport[3] = height;
2566 priv->dirty_viewport = TRUE;
2568 queue_full_redraw (stage);
2571 /* This simply provides a simple mechanism for us to ensure that
2572 * the viewport gets re-asserted before next painting.
2574 * This is used when switching between multiple stages */
2576 _clutter_stage_dirty_viewport (ClutterStage *stage)
2578 stage->priv->dirty_viewport = TRUE;
2582 * clutter_stage_get_viewport:
2583 * @stage: A #ClutterStage
2584 * @x: A location for the X position where the stage is rendered,
2585 * in window coordinates.
2586 * @y: A location for the Y position where the stage is rendered,
2587 * in window coordinates.
2588 * @width: A location for the width the stage is rendered at,
2589 * in window coordinates.
2590 * @height: A location for the height the stage is rendered at,
2591 * in window coordinates.
2593 * Returns the viewport offset and size set using
2594 * clutter_stage_set_viewport() or if the "viewport-mimics-window" property
2595 * is TRUE then @x and @y will be set to 0 and @width and @height will equal
2596 * the width if the stage window.
2601 _clutter_stage_get_viewport (ClutterStage *stage,
2607 ClutterStagePrivate *priv;
2609 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2613 *x = priv->viewport[0];
2614 *y = priv->viewport[1];
2615 *width = priv->viewport[2];
2616 *height = priv->viewport[3];
2620 * clutter_stage_set_fullscreen:
2621 * @stage: a #ClutterStage
2622 * @fullscreen: %TRUE to to set the stage fullscreen
2624 * Asks to place the stage window in the fullscreen or unfullscreen
2627 ( Note that you shouldn't assume the window is definitely full screen
2628 * afterward, because other entities (e.g. the user or window manager)
2629 * could unfullscreen it again, and not all window managers honor
2630 * requests to fullscreen windows.
2632 * If you want to receive notification of the fullscreen state you
2633 * should either use the #ClutterStage::fullscreen and
2634 * #ClutterStage::unfullscreen signals, or use the notify signal
2635 * for the #ClutterStage:fullscreen-set property
2640 clutter_stage_set_fullscreen (ClutterStage *stage,
2641 gboolean fullscreen)
2643 ClutterStagePrivate *priv;
2645 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2649 if (priv->is_fullscreen != fullscreen)
2651 ClutterStageWindow *impl = CLUTTER_STAGE_WINDOW (priv->impl);
2652 ClutterStageWindowIface *iface;
2654 iface = CLUTTER_STAGE_WINDOW_GET_IFACE (impl);
2656 /* Only set if backend implements.
2658 * Also see clutter_stage_event() for setting priv->is_fullscreen
2659 * on state change event.
2661 if (iface->set_fullscreen)
2662 iface->set_fullscreen (impl, fullscreen);
2665 /* If the backend did fullscreen the stage window then we need to resize
2666 * the stage and update its viewport so we queue a relayout. Note: if the
2667 * fullscreen request is handled asynchronously we can't rely on this
2668 * queue_relayout to update the viewport, but for example the X backend
2669 * will recieve a ConfigureNotify after a successful resize which is how
2670 * we ensure the viewport is updated on X.
2672 clutter_actor_queue_relayout (CLUTTER_ACTOR (stage));
2676 * clutter_stage_get_fullscreen:
2677 * @stage: a #ClutterStage
2679 * Retrieves whether the stage is full screen or not
2681 * Return value: %TRUE if the stage is full screen
2686 clutter_stage_get_fullscreen (ClutterStage *stage)
2688 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
2690 return stage->priv->is_fullscreen;
2694 * clutter_stage_set_user_resizable:
2695 * @stage: a #ClutterStage
2696 * @resizable: whether the stage should be user resizable.
2698 * Sets if the stage is resizable by user interaction (e.g. via
2699 * window manager controls)
2704 clutter_stage_set_user_resizable (ClutterStage *stage,
2707 ClutterStagePrivate *priv;
2709 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2713 if (clutter_feature_available (CLUTTER_FEATURE_STAGE_USER_RESIZE)
2714 && priv->is_user_resizable != resizable)
2716 ClutterStageWindow *impl = CLUTTER_STAGE_WINDOW (priv->impl);
2717 ClutterStageWindowIface *iface;
2719 iface = CLUTTER_STAGE_WINDOW_GET_IFACE (impl);
2720 if (iface->set_user_resizable)
2722 priv->is_user_resizable = resizable;
2724 iface->set_user_resizable (impl, resizable);
2726 g_object_notify (G_OBJECT (stage), "user-resizable");
2732 * clutter_stage_get_user_resizable:
2733 * @stage: a #ClutterStage
2735 * Retrieves the value set with clutter_stage_set_user_resizable().
2737 * Return value: %TRUE if the stage is resizable by the user.
2742 clutter_stage_get_user_resizable (ClutterStage *stage)
2744 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
2746 return stage->priv->is_user_resizable;
2750 * clutter_stage_show_cursor:
2751 * @stage: a #ClutterStage
2753 * Shows the cursor on the stage window
2756 clutter_stage_show_cursor (ClutterStage *stage)
2758 ClutterStagePrivate *priv;
2760 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2763 if (!priv->is_cursor_visible)
2765 ClutterStageWindow *impl = CLUTTER_STAGE_WINDOW (priv->impl);
2766 ClutterStageWindowIface *iface;
2768 iface = CLUTTER_STAGE_WINDOW_GET_IFACE (impl);
2769 if (iface->set_cursor_visible)
2771 priv->is_cursor_visible = TRUE;
2773 iface->set_cursor_visible (impl, TRUE);
2775 g_object_notify (G_OBJECT (stage), "cursor-visible");
2781 * clutter_stage_hide_cursor:
2782 * @stage: a #ClutterStage
2784 * Makes the cursor invisible on the stage window
2789 clutter_stage_hide_cursor (ClutterStage *stage)
2791 ClutterStagePrivate *priv;
2793 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2796 if (priv->is_cursor_visible)
2798 ClutterStageWindow *impl = CLUTTER_STAGE_WINDOW (priv->impl);
2799 ClutterStageWindowIface *iface;
2801 iface = CLUTTER_STAGE_WINDOW_GET_IFACE (impl);
2802 if (iface->set_cursor_visible)
2804 priv->is_cursor_visible = FALSE;
2806 iface->set_cursor_visible (impl, FALSE);
2808 g_object_notify (G_OBJECT (stage), "cursor-visible");
2814 * clutter_stage_read_pixels:
2815 * @stage: A #ClutterStage
2816 * @x: x coordinate of the first pixel that is read from stage
2817 * @y: y coordinate of the first pixel that is read from stage
2818 * @width: Width dimention of pixels to be read, or -1 for the
2819 * entire stage width
2820 * @height: Height dimention of pixels to be read, or -1 for the
2821 * entire stage height
2823 * Makes a screenshot of the stage in RGBA 8bit data, returns a
2824 * linear buffer with @width * 4 as rowstride.
2826 * The alpha data contained in the returned buffer is driver-dependent,
2827 * and not guaranteed to hold any sensible value.
2829 * Return value: (array): a pointer to newly allocated memory with the buffer
2830 * or %NULL if the read failed. Use g_free() on the returned data
2831 * to release the resources it has allocated.
2834 clutter_stage_read_pixels (ClutterStage *stage,
2840 ClutterGeometry geom;
2843 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
2845 /* Force a redraw of the stage before reading back pixels */
2846 clutter_stage_ensure_current (stage);
2847 clutter_actor_paint (CLUTTER_ACTOR (stage));
2849 clutter_actor_get_allocation_geometry (CLUTTER_ACTOR (stage), &geom);
2855 height = geom.height;
2857 pixels = g_malloc (height * width * 4);
2859 cogl_read_pixels (x, y, width, height,
2860 COGL_READ_PIXELS_COLOR_BUFFER,
2861 COGL_PIXEL_FORMAT_RGBA_8888,
2868 * clutter_stage_get_actor_at_pos:
2869 * @stage: a #ClutterStage
2870 * @pick_mode: how the scene graph should be painted
2871 * @x: X coordinate to check
2872 * @y: Y coordinate to check
2874 * Checks the scene at the coordinates @x and @y and returns a pointer
2875 * to the #ClutterActor at those coordinates.
2877 * By using @pick_mode it is possible to control which actors will be
2878 * painted and thus available.
2880 * Return value: (transfer none): the actor at the specified coordinates,
2884 clutter_stage_get_actor_at_pos (ClutterStage *stage,
2885 ClutterPickMode pick_mode,
2889 return _clutter_stage_do_pick (stage, x, y, pick_mode);
2893 * clutter_stage_event:
2894 * @stage: a #ClutterStage
2895 * @event: a #ClutterEvent
2897 * This function is used to emit an event on the main stage.
2899 * You should rarely need to use this function, except for
2900 * synthetised events.
2902 * Return value: the return value from the signal emission
2907 clutter_stage_event (ClutterStage *stage,
2908 ClutterEvent *event)
2910 ClutterStagePrivate *priv;
2912 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
2913 g_return_val_if_fail (event != NULL, FALSE);
2917 if (event->type == CLUTTER_DELETE)
2919 gboolean retval = FALSE;
2921 g_signal_emit_by_name (stage, "event", event, &retval);
2924 g_signal_emit_by_name (stage, "delete-event", event, &retval);
2929 if (event->type != CLUTTER_STAGE_STATE)
2932 /* emit raw event */
2933 if (clutter_actor_event (CLUTTER_ACTOR (stage), event, FALSE))
2936 if (event->stage_state.changed_mask & CLUTTER_STAGE_STATE_FULLSCREEN)
2938 if (event->stage_state.new_state & CLUTTER_STAGE_STATE_FULLSCREEN)
2940 priv->is_fullscreen = TRUE;
2941 g_signal_emit (stage, stage_signals[FULLSCREEN], 0);
2943 g_object_notify (G_OBJECT (stage), "fullscreen-set");
2947 priv->is_fullscreen = FALSE;
2948 g_signal_emit (stage, stage_signals[UNFULLSCREEN], 0);
2950 g_object_notify (G_OBJECT (stage), "fullscreen-set");
2954 if (event->stage_state.changed_mask & CLUTTER_STAGE_STATE_ACTIVATED)
2956 if (event->stage_state.new_state & CLUTTER_STAGE_STATE_ACTIVATED)
2957 g_signal_emit (stage, stage_signals[ACTIVATE], 0);
2959 g_signal_emit (stage, stage_signals[DEACTIVATE], 0);
2966 * clutter_stage_set_title:
2967 * @stage: A #ClutterStage
2968 * @title: A utf8 string for the stage windows title.
2970 * Sets the stage title.
2975 clutter_stage_set_title (ClutterStage *stage,
2978 ClutterStagePrivate *priv;
2979 ClutterStageWindow *impl;
2981 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2985 g_free (priv->title);
2986 priv->title = g_strdup (title);
2988 impl = CLUTTER_STAGE_WINDOW (priv->impl);
2989 if (CLUTTER_STAGE_WINDOW_GET_IFACE(impl)->set_title != NULL)
2990 CLUTTER_STAGE_WINDOW_GET_IFACE (impl)->set_title (impl, priv->title);
2992 g_object_notify (G_OBJECT (stage), "title");
2996 * clutter_stage_get_title:
2997 * @stage: A #ClutterStage
2999 * Gets the stage title.
3001 * Return value: pointer to the title string for the stage. The
3002 * returned string is owned by the actor and should not
3003 * be modified or freed.
3008 clutter_stage_get_title (ClutterStage *stage)
3010 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
3012 return stage->priv->title;
3016 on_key_focus_destroy (ClutterActor *actor,
3017 ClutterStage *stage)
3019 /* unset the key focus */
3020 clutter_stage_set_key_focus (stage, NULL);
3024 * clutter_stage_set_key_focus:
3025 * @stage: the #ClutterStage
3026 * @actor: (allow-none): the actor to set key focus to, or %NULL
3028 * Sets the key focus on @actor. An actor with key focus will receive
3029 * all the key events. If @actor is %NULL, the stage will receive
3035 clutter_stage_set_key_focus (ClutterStage *stage,
3036 ClutterActor *actor)
3038 ClutterStagePrivate *priv;
3040 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3041 g_return_if_fail (actor == NULL || CLUTTER_IS_ACTOR (actor));
3045 /* avoid emitting signals and notifications if we're setting the same
3046 * actor as the key focus
3048 if (priv->key_focused_actor == actor)
3051 if (priv->key_focused_actor != NULL)
3053 ClutterActor *old_focused_actor;
3055 old_focused_actor = priv->key_focused_actor;
3057 /* set key_focused_actor to NULL before emitting the signal or someone
3058 * might hide the previously focused actor in the signal handler and we'd
3059 * get re-entrant call and get glib critical from g_object_weak_unref
3061 g_signal_handlers_disconnect_by_func (priv->key_focused_actor,
3062 G_CALLBACK (on_key_focus_destroy),
3064 priv->key_focused_actor = NULL;
3066 g_signal_emit_by_name (old_focused_actor, "key-focus-out");
3069 g_signal_emit_by_name (stage, "key-focus-out");
3071 /* Note, if someone changes key focus in focus-out signal handler we'd be
3072 * overriding the latter call below moving the focus where it was originally
3073 * intended. The order of events would be:
3074 * 1st focus-out, 2nd focus-out (on stage), 2nd focus-in, 1st focus-in
3078 priv->key_focused_actor = actor;
3080 g_signal_connect (actor,
3081 "destroy", G_CALLBACK (on_key_focus_destroy),
3083 g_signal_emit_by_name (priv->key_focused_actor, "key-focus-in");
3086 g_signal_emit_by_name (stage, "key-focus-in");
3088 g_object_notify (G_OBJECT (stage), "key-focus");
3092 * clutter_stage_get_key_focus:
3093 * @stage: the #ClutterStage
3095 * Retrieves the actor that is currently under key focus.
3097 * Return value: (transfer none): the actor with key focus, or the stage
3102 clutter_stage_get_key_focus (ClutterStage *stage)
3104 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
3106 if (stage->priv->key_focused_actor)
3107 return stage->priv->key_focused_actor;
3109 return CLUTTER_ACTOR (stage);
3113 * clutter_stage_get_use_fog:
3114 * @stage: the #ClutterStage
3116 * Gets whether the depth cueing effect is enabled on @stage.
3118 * Return value: %TRUE if the depth cueing effect is enabled
3122 * Deprecated: 1.10: This function will always return %FALSE
3125 clutter_stage_get_use_fog (ClutterStage *stage)
3127 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
3129 return stage->priv->use_fog;
3133 * clutter_stage_set_use_fog:
3134 * @stage: the #ClutterStage
3135 * @fog: %TRUE for enabling the depth cueing effect
3137 * Sets whether the depth cueing effect on the stage should be enabled
3140 * Depth cueing is a 3D effect that makes actors farther away from the
3141 * viewing point less opaque, by fading them with the stage color.
3143 * The parameters of the GL fog used can be changed using the
3144 * clutter_stage_set_fog() function.
3148 * Deprecated: 1.10: Calling this function produces no visible effect
3151 clutter_stage_set_use_fog (ClutterStage *stage,
3157 * clutter_stage_set_fog:
3158 * @stage: the #ClutterStage
3159 * @fog: a #ClutterFog structure
3161 * Sets the fog (also known as "depth cueing") settings for the @stage.
3163 * A #ClutterStage will only use a linear fog progression, which
3164 * depends solely on the distance from the viewer. The cogl_set_fog()
3165 * function in COGL exposes more of the underlying implementation,
3166 * and allows changing the for progression function. It can be directly
3167 * used by disabling the #ClutterStage:use-fog property and connecting
3168 * a signal handler to the #ClutterActor::paint signal on the @stage,
3172 * clutter_stage_set_use_fog (stage, FALSE);
3173 * g_signal_connect (stage, "paint", G_CALLBACK (on_stage_paint), NULL);
3176 * The paint signal handler will call cogl_set_fog() with the
3181 * on_stage_paint (ClutterActor *actor)
3183 * ClutterColor stage_color = { 0, };
3184 * CoglColor fog_color = { 0, };
3186 * /* set the fog color to the stage background color */
3187 * clutter_stage_get_color (CLUTTER_STAGE (actor), &stage_color);
3188 * cogl_color_init_from_4ub (&fog_color,
3190 * stage_color.green,
3192 * stage_color.alpha);
3194 * /* enable fog */
3195 * cogl_set_fog (&fog_color,
3196 * COGL_FOG_MODE_EXPONENTIAL, /* mode */
3197 * 0.5, /* density */
3198 * 5.0, 30.0); /* z_near and z_far */
3202 * <note>The fogging functions only work correctly when the visible actors use
3203 * unmultiplied alpha colors. By default Cogl will premultiply textures and
3204 * cogl_set_source_color() will premultiply colors, so unless you explicitly
3205 * load your textures requesting an unmultiplied internal format and use
3206 * cogl_material_set_color() you can only use fogging with fully opaque actors.
3207 * Support for premultiplied colors will improve in the future when we can
3208 * depend on fragment shaders.</note>
3212 * Deprecated: 1.10: Fog settings are ignored.
3215 clutter_stage_set_fog (ClutterStage *stage,
3221 * clutter_stage_get_fog:
3222 * @stage: the #ClutterStage
3223 * @fog: (out): return location for a #ClutterFog structure
3225 * Retrieves the current depth cueing settings from the stage.
3229 * Deprecated: 1.10: This function will always return the default
3230 * values of #ClutterFog
3233 clutter_stage_get_fog (ClutterStage *stage,
3236 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3237 g_return_if_fail (fog != NULL);
3239 *fog = stage->priv->fog;
3242 /*** Perspective boxed type ******/
3245 clutter_perspective_copy (gpointer data)
3247 if (G_LIKELY (data))
3248 return g_slice_dup (ClutterPerspective, data);
3254 clutter_perspective_free (gpointer data)
3256 if (G_LIKELY (data))
3257 g_slice_free (ClutterPerspective, data);
3260 G_DEFINE_BOXED_TYPE (ClutterPerspective, clutter_perspective,
3261 clutter_perspective_copy,
3262 clutter_perspective_free);
3265 clutter_fog_copy (gpointer data)
3267 if (G_LIKELY (data))
3268 return g_slice_dup (ClutterFog, data);
3274 clutter_fog_free (gpointer data)
3276 if (G_LIKELY (data))
3277 g_slice_free (ClutterFog, data);
3280 G_DEFINE_BOXED_TYPE (ClutterFog, clutter_fog, clutter_fog_copy, clutter_fog_free);
3283 * clutter_stage_new:
3285 * Creates a new, non-default stage. A non-default stage is a new
3286 * top-level actor which can be used as another container. It works
3287 * exactly like the default stage, but while clutter_stage_get_default()
3288 * will always return the same instance, you will have to keep a pointer
3289 * to any #ClutterStage returned by clutter_stage_new().
3291 * The ability to support multiple stages depends on the current
3292 * backend. Use clutter_feature_available() and
3293 * %CLUTTER_FEATURE_STAGE_MULTIPLE to check at runtime whether a
3294 * backend supports multiple stages.
3296 * Return value: a new stage, or %NULL if the default backend does
3297 * not support multiple stages. Use clutter_actor_destroy() to
3298 * programmatically close the returned stage.
3303 clutter_stage_new (void)
3305 return g_object_new (CLUTTER_TYPE_STAGE, NULL);
3309 * clutter_stage_ensure_current:
3310 * @stage: the #ClutterStage
3312 * This function essentially makes sure the right GL context is
3313 * current for the passed stage. It is not intended to
3314 * be used by applications.
3319 clutter_stage_ensure_current (ClutterStage *stage)
3321 ClutterBackend *backend;
3323 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3325 backend = clutter_get_default_backend ();
3326 _clutter_backend_ensure_context (backend, stage);
3330 * clutter_stage_ensure_viewport:
3331 * @stage: a #ClutterStage
3333 * Ensures that the GL viewport is updated with the current
3334 * stage window size.
3336 * This function will queue a redraw of @stage.
3338 * This function should not be called by applications; it is used
3339 * when embedding a #ClutterStage into a toolkit with another
3340 * windowing system, like GTK+.
3345 clutter_stage_ensure_viewport (ClutterStage *stage)
3347 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3349 _clutter_stage_dirty_viewport (stage);
3351 clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
3354 /* This calculates a distance into the view frustum to position the
3355 * stage so there is a decent amount of space to position geometry
3356 * between the stage and the near clipping plane.
3358 * Some awkward issues with this problem are:
3359 * - It's not possible to have a gap as large as the stage size with
3360 * a fov > 53° which is basically always the case since the default
3362 * - This can be deduced if you consider that this requires a
3363 * triangle as wide as it is deep to fit in the frustum in front
3364 * of the z_near plane. That triangle will always have an angle
3365 * of 53.13° at the point sitting on the z_near plane, but if the
3366 * frustum has a wider fov angle the left/right clipping planes
3367 * can never converge with the two corners of our triangle no
3368 * matter what size the triangle has.
3369 * - With a fov > 53° there is a trade off between maximizing the gap
3370 * size relative to the stage size but not loosing depth precision.
3371 * - Perhaps ideally we wouldn't just consider the fov on the y-axis
3372 * that is usually used to define a perspective, we would consider
3373 * the fov of the axis with the largest stage size so the gap would
3374 * accommodate that size best.
3376 * After going around in circles a few times with how to handle these
3377 * issues, we decided in the end to go for the simplest solution to
3378 * start with instead of an elaborate function that handles arbitrary
3379 * fov angles that we currently have no use-case for.
3381 * The solution assumes a fovy of 60° and for that case gives a gap
3382 * that's 85% of the stage height. We can consider more elaborate
3383 * functions if necessary later.
3385 * One guide we had to steer the gap size we support is the
3386 * interactive test, test-texture-quality which expects to animate an
3387 * actor to +400 on the z axis with a stage size of 640x480. A gap
3388 * that's 85% of the stage height gives a gap of 408 in that case.
3391 calculate_z_translation (float z_near)
3393 /* This solution uses fairly basic trigonometry, but is seems worth
3394 * clarifying the particular geometry we are looking at in-case
3395 * anyone wants to develop this further later. Not sure how well an
3396 * ascii diagram is going to work :-)
3398 * |--- stage_height ---|
3400 * ╲━━━━━━━━━━━━━━━━━━━━━╱------------
3402 * C ╲ . │ . ╱ gap| |
3403 * =0.5°╲ . a │ . ╱ | |
3405 * ╲ B.│. ╱near plane | |
3406 * A= ╲━━━━━━━━━╱------------- |
3407 * 120° ╲ c │ ╱ | z_2d
3411 * plane ╳----------------------
3416 * The area of interest is the triangle labeled (1) at the top left
3417 * marked with the ... line (a) from where the origin line crosses
3418 * the near plane to the top left where the stage line cross the
3421 * The sides of the triangle are a, b and c and the corresponding
3422 * angles opposite those sides are A, B and C.
3424 * The angle of C is what trades off the gap size we have relative
3425 * to the stage size vs the depth precision we have.
3427 * As mentioned above we arove at the angle for C is by working
3428 * backwards from how much space we want for test-texture-quality.
3429 * With a stage_height of 480 we want a gap > 400, ideally we also
3430 * wanted a somewhat round number as a percentage of the height for
3431 * documentation purposes. ~87% or a gap of ~416 is the limit
3432 * because that's where we approach a C angle of 0° and effectively
3433 * loose all depth precision.
3435 * So for our test app with a stage_height of 480 if we aim for a
3436 * gap of 408 (85% of 480) we can get the angle D as
3437 * atan (stage_height/2/408) = 30.5°.
3439 * That gives us the angle for B as 90° - 30.5° = 59.5°
3441 * We can already determine that A has an angle of (fovy/2 + 90°) =
3444 * Therefore C = 180 - A - B = 0.5°
3446 * The length of c = z_near * tan (30°)
3448 * Now we can use the rule a/SinA = c/SinC to calculate the
3449 * length of a. After some rearranging that gives us:
3452 * ---------- = ----------
3453 * sin (120°) sin (0.5°)
3456 * a = --------------
3459 * And with that we can determine z_2d = cos (D) * a =
3460 * cos (30.5°) * a + z_near:
3462 * c * sin (120°) * cos (30.5°)
3463 * z_2d = --------------------------- + z_near
3466 #define _DEG_TO_RAD (G_PI / 180.0)
3467 return z_near * tanf (30.0f * _DEG_TO_RAD) *
3468 sinf (120.0f * _DEG_TO_RAD) * cosf (30.5f * _DEG_TO_RAD) /
3469 sinf (0.5f * _DEG_TO_RAD) +
3472 /* We expect the compiler should boil this down to z_near * CONSTANT */
3476 _clutter_stage_maybe_setup_viewport (ClutterStage *stage)
3478 ClutterStagePrivate *priv = stage->priv;
3480 if (priv->dirty_viewport)
3482 ClutterPerspective perspective;
3485 CLUTTER_NOTE (PAINT,
3486 "Setting up the viewport { w:%f, h:%f }",
3487 priv->viewport[2], priv->viewport[3]);
3489 cogl_set_viewport (priv->viewport[0],
3494 perspective = priv->perspective;
3496 /* Ideally we want to regenerate the perspective matrix whenever
3497 * the size changes but if the user has provided a custom matrix
3498 * then we don't want to override it */
3499 if (!priv->has_custom_perspective)
3501 perspective.aspect = priv->viewport[2] / priv->viewport[3];
3502 z_2d = calculate_z_translation (perspective.z_near);
3504 #define _DEG_TO_RAD (G_PI / 180.0)
3505 /* NB: z_2d is only enough room for 85% of the stage_height between
3506 * the stage and the z_near plane. For behind the stage plane we
3507 * want a more consistent gap of 10 times the stage_height before
3508 * hitting the far plane so we calculate that relative to the final
3509 * height of the stage plane at the z_2d_distance we got... */
3510 perspective.z_far = z_2d +
3511 tanf ((perspective.fovy / 2.0f) * _DEG_TO_RAD) * z_2d * 20.0f;
3514 clutter_stage_set_perspective_internal (stage, &perspective);
3517 z_2d = calculate_z_translation (perspective.z_near);
3519 cogl_matrix_init_identity (&priv->view);
3520 cogl_matrix_view_2d_in_perspective (&priv->view,
3528 priv->dirty_viewport = FALSE;
3531 if (priv->dirty_projection)
3533 cogl_set_projection_matrix (&priv->projection);
3535 priv->dirty_projection = FALSE;
3540 * clutter_stage_ensure_redraw:
3541 * @stage: a #ClutterStage
3543 * Ensures that @stage is redrawn
3545 * This function should not be called by applications: it is
3546 * used when embedding a #ClutterStage into a toolkit with
3547 * another windowing system, like GTK+.
3552 clutter_stage_ensure_redraw (ClutterStage *stage)
3554 ClutterMasterClock *master_clock;
3555 ClutterStagePrivate *priv;
3557 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3560 priv->relayout_pending = TRUE;
3561 priv->redraw_pending = TRUE;
3563 master_clock = _clutter_master_clock_get_default ();
3564 _clutter_master_clock_start_running (master_clock);
3568 * clutter_stage_queue_redraw:
3569 * @stage: the #ClutterStage
3571 * Queues a redraw for the passed stage.
3573 * <note>Applications should call clutter_actor_queue_redraw() and not
3574 * this function.</note>
3578 * Deprecated: 1.10: Use clutter_actor_queue_redraw() instead.
3581 clutter_stage_queue_redraw (ClutterStage *stage)
3583 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3585 clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
3589 * clutter_stage_is_default:
3590 * @stage: a #ClutterStage
3592 * Checks if @stage is the default stage, or an instance created using
3593 * clutter_stage_new() but internally using the same implementation.
3595 * Return value: %TRUE if the passed stage is the default one
3599 * Deprecated: 1.10: Track the stage pointer inside your application
3600 * code, or use clutter_actor_get_stage() to retrieve the stage for
3604 clutter_stage_is_default (ClutterStage *stage)
3606 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
3608 return stage_is_default (stage);
3612 _clutter_stage_set_window (ClutterStage *stage,
3613 ClutterStageWindow *stage_window)
3615 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3616 g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (stage_window));
3618 if (stage->priv->impl != NULL)
3619 g_object_unref (stage->priv->impl);
3621 stage->priv->impl = stage_window;
3624 ClutterStageWindow *
3625 _clutter_stage_get_window (ClutterStage *stage)
3627 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
3629 return CLUTTER_STAGE_WINDOW (stage->priv->impl);
3632 ClutterStageWindow *
3633 _clutter_stage_get_default_window (void)
3635 ClutterStageManager *manager = clutter_stage_manager_get_default ();
3636 ClutterStage *stage;
3638 stage = clutter_stage_manager_get_default_stage (manager);
3642 return _clutter_stage_get_window (stage);
3646 * clutter_stage_set_throttle_motion_events:
3647 * @stage: a #ClutterStage
3648 * @throttle: %TRUE to throttle motion events
3650 * Sets whether motion events received between redraws should
3651 * be throttled or not. If motion events are throttled, those
3652 * events received by the windowing system between redraws will
3653 * be compressed so that only the last event will be propagated
3654 * to the @stage and its actors.
3656 * This function should only be used if you want to have all
3657 * the motion events delivered to your application code.
3662 clutter_stage_set_throttle_motion_events (ClutterStage *stage,
3665 ClutterStagePrivate *priv;
3667 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3671 if (priv->throttle_motion_events != throttle)
3672 priv->throttle_motion_events = throttle;
3676 * clutter_stage_get_throttle_motion_events:
3677 * @stage: a #ClutterStage
3679 * Retrieves the value set with clutter_stage_set_throttle_motion_events()
3681 * Return value: %TRUE if the motion events are being throttled,
3682 * and %FALSE otherwise
3687 clutter_stage_get_throttle_motion_events (ClutterStage *stage)
3689 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
3691 return stage->priv->throttle_motion_events;
3695 * clutter_stage_set_use_alpha:
3696 * @stage: a #ClutterStage
3697 * @use_alpha: whether the stage should honour the opacity or the
3698 * alpha channel of the stage color
3700 * Sets whether the @stage should honour the #ClutterActor:opacity and
3701 * the alpha channel of the #ClutterStage:color
3706 clutter_stage_set_use_alpha (ClutterStage *stage,
3709 ClutterStagePrivate *priv;
3711 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3715 if (priv->use_alpha != use_alpha)
3717 priv->use_alpha = use_alpha;
3719 clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
3721 g_object_notify (G_OBJECT (stage), "use-alpha");
3726 * clutter_stage_get_use_alpha:
3727 * @stage: a #ClutterStage
3729 * Retrieves the value set using clutter_stage_set_use_alpha()
3731 * Return value: %TRUE if the stage should honour the opacity and the
3732 * alpha channel of the stage color
3737 clutter_stage_get_use_alpha (ClutterStage *stage)
3739 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
3741 return stage->priv->use_alpha;
3745 * clutter_stage_set_minimum_size:
3746 * @stage: a #ClutterStage
3747 * @width: width, in pixels
3748 * @height: height, in pixels
3750 * Sets the minimum size for a stage window, if the default backend
3751 * uses #ClutterStage inside a window
3753 * This is a convenience function, and it is equivalent to setting the
3754 * #ClutterActor:min-width and #ClutterActor:min-height on @stage
3756 * If the current size of @stage is smaller than the minimum size, the
3757 * @stage will be resized to the new @width and @height
3759 * This function has no effect if @stage is fullscreen
3764 clutter_stage_set_minimum_size (ClutterStage *stage,
3768 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3769 g_return_if_fail ((width > 0) && (height > 0));
3771 g_object_set (G_OBJECT (stage),
3772 "min-width", (gfloat) width,
3773 "min-height", (gfloat )height,
3778 * clutter_stage_get_minimum_size:
3779 * @stage: a #ClutterStage
3780 * @width: (out): return location for the minimum width, in pixels,
3782 * @height: (out): return location for the minimum height, in pixels,
3785 * Retrieves the minimum size for a stage window as set using
3786 * clutter_stage_set_minimum_size().
3788 * The returned size may not correspond to the actual minimum size and
3789 * it is specific to the #ClutterStage implementation inside the
3795 clutter_stage_get_minimum_size (ClutterStage *stage,
3799 gfloat width, height;
3800 gboolean width_set, height_set;
3802 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3804 g_object_get (G_OBJECT (stage),
3805 "min-width", &width,
3806 "min-width-set", &width_set,
3807 "min-height", &height,
3808 "min-height-set", &height_set,
3811 /* if not width or height have been set, then the Stage
3812 * minimum size is defined to be 1x1
3821 *width_p = (guint) width;
3824 *height_p = (guint) height;
3827 /* Returns the number of swap buffers pending completion for the stage */
3829 _clutter_stage_get_pending_swaps (ClutterStage *stage)
3831 ClutterStageWindow *stage_window;
3833 if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
3836 stage_window = _clutter_stage_get_window (stage);
3837 if (stage_window == NULL)
3840 return _clutter_stage_window_get_pending_swaps (stage_window);
3844 * clutter_stage_set_no_clear_hint:
3845 * @stage: a #ClutterStage
3846 * @no_clear: %TRUE if the @stage should not clear itself on every
3849 * Sets whether the @stage should clear itself at the beginning
3850 * of each paint cycle or not.
3852 * Clearing the #ClutterStage can be a costly operation, especially
3853 * if the stage is always covered - for instance, in a full-screen
3854 * video player or in a game with a background texture.
3856 * <note><para>This setting is a hint; Clutter might discard this
3857 * hint depending on its internal state.</para></note>
3859 * <warning><para>If parts of the stage are visible and you disable
3860 * clearing you might end up with visual artifacts while painting the
3861 * contents of the stage.</para></warning>
3866 clutter_stage_set_no_clear_hint (ClutterStage *stage,
3869 ClutterStagePrivate *priv;
3870 ClutterStageHint new_hints;
3872 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3875 new_hints = priv->stage_hints;
3878 new_hints |= CLUTTER_STAGE_NO_CLEAR_ON_PAINT;
3880 new_hints &= ~CLUTTER_STAGE_NO_CLEAR_ON_PAINT;
3882 if (priv->stage_hints == new_hints)
3885 priv->stage_hints = new_hints;
3887 g_object_notify (G_OBJECT (stage), "no-clear-hint");
3891 * clutter_stage_get_no_clear_hint:
3892 * @stage: a #ClutterStage
3894 * Retrieves the hint set with clutter_stage_set_no_clear_hint()
3896 * Return value: %TRUE if the stage should not clear itself on every paint
3897 * cycle, and %FALSE otherwise
3902 clutter_stage_get_no_clear_hint (ClutterStage *stage)
3904 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
3906 return (stage->priv->stage_hints & CLUTTER_STAGE_NO_CLEAR_ON_PAINT) != 0;
3909 ClutterPaintVolume *
3910 _clutter_stage_paint_volume_stack_allocate (ClutterStage *stage)
3912 GArray *paint_volume_stack = stage->priv->paint_volume_stack;
3914 g_array_set_size (paint_volume_stack,
3915 paint_volume_stack->len+1);
3917 return &g_array_index (paint_volume_stack,
3919 paint_volume_stack->len - 1);
3923 _clutter_stage_paint_volume_stack_free_all (ClutterStage *stage)
3925 GArray *paint_volume_stack = stage->priv->paint_volume_stack;
3928 for (i = 0; i < paint_volume_stack->len; i++)
3930 ClutterPaintVolume *pv =
3931 &g_array_index (paint_volume_stack, ClutterPaintVolume, i);
3932 clutter_paint_volume_free (pv);
3935 g_array_set_size (paint_volume_stack, 0);
3938 /* The is an out-of-band paramater available while painting that
3939 * can be used to cull actors. */
3940 const ClutterPlane *
3941 _clutter_stage_get_clip (ClutterStage *stage)
3943 return stage->priv->current_clip_planes;
3946 /* When an actor queues a redraw we add it to a list on the stage that
3947 * gets processed once all updates to the stage have been finished.
3949 * This deferred approach to processing queue_redraw requests means
3950 * that we can avoid redundant transformations of clip volumes if
3951 * something later triggers a full stage redraw anyway. It also means
3952 * we can be more sure that all the referenced actors will have valid
3953 * allocations improving the chance that we can determine the actors
3954 * paint volume so we can clip the redraw request even if the user
3955 * didn't explicitly do so.
3957 ClutterStageQueueRedrawEntry *
3958 _clutter_stage_queue_actor_redraw (ClutterStage *stage,
3959 ClutterStageQueueRedrawEntry *entry,
3960 ClutterActor *actor,
3961 ClutterPaintVolume *clip)
3963 ClutterStagePrivate *priv = stage->priv;
3965 CLUTTER_NOTE (CLIPPING, "stage_queue_actor_redraw (actor=%s, clip=%p): ",
3966 _clutter_actor_get_debug_name (actor), clip);
3968 if (!priv->redraw_pending)
3970 ClutterMasterClock *master_clock;
3972 CLUTTER_NOTE (PAINT, "First redraw request");
3974 priv->redraw_pending = TRUE;
3976 master_clock = _clutter_master_clock_get_default ();
3977 _clutter_master_clock_start_running (master_clock);
3979 #ifdef CLUTTER_ENABLE_DEBUG
3982 CLUTTER_NOTE (PAINT, "Redraw request number %lu",
3983 priv->redraw_count + 1);
3985 priv->redraw_count += 1;
3987 #endif /* CLUTTER_ENABLE_DEBUG */
3989 /* We have an optimization in _clutter_stage_do_pick to detect when
3990 * the scene is static so we can cache a full, un-clipped pick
3991 * buffer to avoid continuous pick renders.
3993 * Currently the assumption is that actors queue a redraw when some
3994 * state changes that affects painting *or* picking so we can use
3995 * this point to invalidate any currently cached pick buffer.
3997 _clutter_stage_set_pick_buffer_valid (stage, FALSE, -1);
4001 /* Ignore all requests to queue a redraw for an actor if a full
4002 * (non-clipped) redraw of the actor has already been queued. */
4003 if (!entry->has_clip)
4005 CLUTTER_NOTE (CLIPPING, "Bail from stage_queue_actor_redraw (%s): "
4006 "Unclipped redraw of actor already queued",
4007 _clutter_actor_get_debug_name (actor));
4011 /* If queuing a clipped redraw and a clipped redraw has
4012 * previously been queued for this actor then combine the latest
4013 * clip together with the existing clip */
4015 clutter_paint_volume_union (&entry->clip, clip);
4018 clutter_paint_volume_free (&entry->clip);
4019 entry->has_clip = FALSE;
4025 entry = g_slice_new (ClutterStageQueueRedrawEntry);
4026 entry->actor = g_object_ref (actor);
4030 entry->has_clip = TRUE;
4031 _clutter_paint_volume_init_static (&entry->clip, actor);
4032 _clutter_paint_volume_set_from_volume (&entry->clip, clip);
4035 entry->has_clip = FALSE;
4037 stage->priv->pending_queue_redraws =
4038 g_list_prepend (stage->priv->pending_queue_redraws, entry);
4045 free_queue_redraw_entry (ClutterStageQueueRedrawEntry *entry)
4048 g_object_unref (entry->actor);
4049 if (entry->has_clip)
4050 clutter_paint_volume_free (&entry->clip);
4051 g_slice_free (ClutterStageQueueRedrawEntry, entry);
4055 _clutter_stage_queue_redraw_entry_invalidate (ClutterStageQueueRedrawEntry *entry)
4060 if (entry->actor != NULL)
4062 g_object_unref (entry->actor);
4063 entry->actor = NULL;
4066 if (entry->has_clip)
4068 clutter_paint_volume_free (&entry->clip);
4069 entry->has_clip = FALSE;
4074 _clutter_stage_maybe_finish_queue_redraws (ClutterStage *stage)
4076 /* Note: we have to repeat until the pending_queue_redraws list is
4077 * empty because actors are allowed to queue redraws in response to
4078 * the queue-redraw signal. For example Clone actors or
4079 * texture_new_from_actor actors will have to queue a redraw if
4080 * their source queues a redraw.
4082 while (stage->priv->pending_queue_redraws)
4085 /* XXX: we need to allow stage->priv->pending_queue_redraws to
4086 * be updated while we process the current entries in the list
4087 * so we steal the list pointer and then reset it to an empty
4088 * list before processing... */
4089 GList *stolen_list = stage->priv->pending_queue_redraws;
4090 stage->priv->pending_queue_redraws = NULL;
4092 for (l = stolen_list; l; l = l->next)
4094 ClutterStageQueueRedrawEntry *entry = l->data;
4095 ClutterPaintVolume *clip;
4097 /* NB: Entries may be invalidated if the actor gets destroyed */
4098 if (G_LIKELY (entry->actor != NULL))
4100 clip = entry->has_clip ? &entry->clip : NULL;
4102 _clutter_actor_finish_queue_redraw (entry->actor, clip);
4105 free_queue_redraw_entry (entry);
4107 g_list_free (stolen_list);
4112 * clutter_stage_set_accept_focus:
4113 * @stage: a #ClutterStage
4114 * @accept_focus: %TRUE to accept focus on show
4116 * Sets whether the @stage should accept the key focus when shown.
4118 * This function should be called before showing @stage using
4119 * clutter_actor_show().
4124 clutter_stage_set_accept_focus (ClutterStage *stage,
4125 gboolean accept_focus)
4127 ClutterStagePrivate *priv;
4129 g_return_if_fail (CLUTTER_IS_STAGE (stage));
4131 accept_focus = !!accept_focus;
4135 if (priv->accept_focus != accept_focus)
4137 _clutter_stage_window_set_accept_focus (priv->impl, accept_focus);
4138 g_object_notify (G_OBJECT (stage), "accept-focus");
4143 * clutter_stage_get_accept_focus:
4144 * @stage: a #ClutterStage
4146 * Retrieves the value set with clutter_stage_set_accept_focus().
4148 * Return value: %TRUE if the #ClutterStage should accept focus, and %FALSE
4154 clutter_stage_get_accept_focus (ClutterStage *stage)
4156 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), TRUE);
4158 return stage->priv->accept_focus;
4162 _clutter_stage_add_device (ClutterStage *stage,
4163 ClutterInputDevice *device)
4165 ClutterStagePrivate *priv = stage->priv;
4167 if (g_hash_table_lookup (priv->devices, device) != NULL)
4170 g_hash_table_insert (priv->devices, device, GINT_TO_POINTER (1));
4171 _clutter_input_device_set_stage (device, stage);
4175 _clutter_stage_remove_device (ClutterStage *stage,
4176 ClutterInputDevice *device)
4178 ClutterStagePrivate *priv = stage->priv;
4180 _clutter_input_device_set_stage (device, NULL);
4181 g_hash_table_remove (priv->devices, device);
4185 _clutter_stage_has_device (ClutterStage *stage,
4186 ClutterInputDevice *device)
4188 ClutterStagePrivate *priv = stage->priv;
4190 return g_hash_table_lookup (priv->devices, device) != NULL;
4194 * clutter_stage_set_motion_events_enabled:
4195 * @stage: a #ClutterStage
4196 * @enabled: %TRUE to enable the motion events delivery, and %FALSE
4199 * Sets whether per-actor motion events (and relative crossing
4200 * events) should be disabled or not.
4202 * The default is %TRUE.
4204 * If @enable is %FALSE the following events will not be delivered
4205 * to the actors children of @stage.
4208 * <listitem><para>#ClutterActor::motion-event</para></listitem>
4209 * <listitem><para>#ClutterActor::enter-event</para></listitem>
4210 * <listitem><para>#ClutterActor::leave-event</para></listitem>
4213 * The events will still be delivered to the #ClutterStage.
4215 * The main side effect of this function is that disabling the motion
4216 * events will disable picking to detect the #ClutterActor underneath
4217 * the pointer for each motion event. This is useful, for instance,
4218 * when dragging a #ClutterActor across the @stage: the actor underneath
4219 * the pointer is not going to change, so it's meaningless to perform
4225 clutter_stage_set_motion_events_enabled (ClutterStage *stage,
4228 ClutterStagePrivate *priv;
4230 g_return_if_fail (CLUTTER_IS_STAGE (stage));
4234 enabled = !!enabled;
4236 if (priv->motion_events_enabled != enabled)
4237 priv->motion_events_enabled = enabled;
4241 * clutter_stage_get_motion_events_enabled:
4242 * @stage: a #ClutterStage
4244 * Retrieves the value set using clutter_stage_set_motion_events_enabled().
4246 * Return value: %TRUE if the per-actor motion event delivery is enabled
4247 * and %FALSE otherwise
4252 clutter_stage_get_motion_events_enabled (ClutterStage *stage)
4254 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
4256 return stage->priv->motion_events_enabled;
4259 /* NB: The presumption shouldn't be that a stage can't be comprised
4260 * of multiple internal framebuffers, so instead of simply naming
4261 * this function _clutter_stage_get_framebuffer(), the "active"
4262 * infix is intended to clarify that it gets the framebuffer that
4263 * is currently in use/being painted.
4266 _clutter_stage_get_active_framebuffer (ClutterStage *stage)
4268 return stage->priv->active_framebuffer;
4272 _clutter_stage_acquire_pick_id (ClutterStage *stage,
4273 ClutterActor *actor)
4275 ClutterStagePrivate *priv = stage->priv;
4277 g_assert (priv->pick_id_pool != NULL);
4279 return _clutter_id_pool_add (priv->pick_id_pool, actor);
4283 _clutter_stage_release_pick_id (ClutterStage *stage,
4286 ClutterStagePrivate *priv = stage->priv;
4288 g_assert (priv->pick_id_pool != NULL);
4290 _clutter_id_pool_remove (priv->pick_id_pool, pick_id);
4294 _clutter_stage_get_actor_by_pick_id (ClutterStage *stage,
4297 ClutterStagePrivate *priv = stage->priv;
4299 g_assert (priv->pick_id_pool != NULL);
4301 return _clutter_id_pool_lookup (priv->pick_id_pool, pick_id);
4305 _clutter_stage_add_drag_actor (ClutterStage *stage,
4306 ClutterInputDevice *device,
4307 ClutterActor *actor)
4309 GHashTable *drag_actors;
4311 drag_actors = g_object_get_data (G_OBJECT (stage), "__clutter_stage_drag_actors");
4312 if (drag_actors == NULL)
4314 drag_actors = g_hash_table_new (NULL, NULL);
4315 g_object_set_data_full (G_OBJECT (stage), "__clutter_stage_drag_actors",
4317 (GDestroyNotify) g_hash_table_destroy);
4320 g_hash_table_replace (drag_actors, device, actor);
4324 _clutter_stage_get_drag_actor (ClutterStage *stage,
4325 ClutterInputDevice *device)
4327 GHashTable *drag_actors;
4329 drag_actors = g_object_get_data (G_OBJECT (stage), "__clutter_stage_drag_actors");
4330 if (drag_actors == NULL)
4333 return g_hash_table_lookup (drag_actors, device);
4337 _clutter_stage_remove_drag_actor (ClutterStage *stage,
4338 ClutterInputDevice *device)
4340 GHashTable *drag_actors;
4342 drag_actors = g_object_get_data (G_OBJECT (stage), "__clutter_stage_drag_actors");
4343 if (drag_actors == NULL)
4346 g_hash_table_remove (drag_actors, device);
4348 if (g_hash_table_size (drag_actors) == 0)
4349 g_object_set_data (G_OBJECT (stage), "__clutter_stage_drag_actors", NULL);
4353 * _clutter_stage_get_state:
4354 * @stage: a #ClutterStage
4356 * Retrieves the current #ClutterStageState flags associated to the @stage.
4358 * Return value: a bitwise OR of #ClutterStageState flags
4361 _clutter_stage_get_state (ClutterStage *stage)
4363 return stage->priv->current_state;
4367 * _clutter_stage_is_activated:
4368 * @stage: a #ClutterStage
4370 * Checks whether the @stage state includes %CLUTTER_STAGE_STATE_ACTIVATED.
4372 * Return value: %TRUE if the @stage is active
4375 _clutter_stage_is_activated (ClutterStage *stage)
4377 return (stage->priv->current_state & CLUTTER_STAGE_STATE_ACTIVATED) != 0;
4381 * _clutter_stage_is_fullscreen:
4382 * @stage: a #ClutterStage
4384 * Checks whether the @stage state includes %CLUTTER_STAGE_STATE_FULLSCREEN.
4386 * Return value: %TRUE if the @stage is fullscreen
4389 _clutter_stage_is_fullscreen (ClutterStage *stage)
4391 return (stage->priv->current_state & CLUTTER_STAGE_STATE_FULLSCREEN) != 0;
4395 * _clutter_stage_update_state:
4396 * @stage: a #ClutterStage
4397 * @unset_flags: flags to unset
4398 * @set_flags: flags to set
4400 * Updates the state of @stage, by unsetting the @unset_flags and setting
4403 * If the stage state has been changed, this function will queue a
4404 * #ClutterEvent of type %CLUTTER_STAGE_STATE.
4406 * Return value: %TRUE if the state was updated, and %FALSE otherwise
4409 _clutter_stage_update_state (ClutterStage *stage,
4410 ClutterStageState unset_flags,
4411 ClutterStageState set_flags)
4413 ClutterStageState new_state;
4414 ClutterEvent *event;
4416 new_state = stage->priv->current_state;
4417 new_state |= set_flags;
4418 new_state &= ~unset_flags;
4420 if (new_state == stage->priv->current_state)
4423 event = clutter_event_new (CLUTTER_STAGE_STATE);
4424 clutter_event_set_stage (event, stage);
4426 event->stage_state.new_state = new_state;
4427 event->stage_state.changed_mask = new_state ^ stage->priv->current_state;
4429 stage->priv->current_state = new_state;
4431 _clutter_event_push (event, FALSE);