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.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;
699 CLUTTER_TIMER_START (_clutter_uprof_context, stage_clear_timer);
700 /* we use the real alpha to clear the stage if :use-alpha is
701 * set; the effect depends entirely on the Clutter backend
703 cogl_color_init_from_4ub (&stage_color,
707 priv->use_alpha ? real_alpha : 255);
708 cogl_color_premultiply (&stage_color);
709 cogl_clear (&stage_color, clear_flags);
710 CLUTTER_TIMER_STOP (_clutter_uprof_context, stage_clear_timer);
712 clutter_actor_iter_init (&iter, self);
713 while (clutter_actor_iter_next (&iter, &child))
714 clutter_actor_paint (child);
718 clutter_stage_pick (ClutterActor *self,
719 const ClutterColor *color)
721 ClutterActorIter iter;
724 /* Note: we don't chain up to our parent as we don't want any geometry
725 * emitted for the stage itself. The stage's pick id is effectively handled
726 * by the call to cogl_clear done in clutter-main.c:_clutter_do_pick_async()
728 clutter_actor_iter_init (&iter, self);
729 while (clutter_actor_iter_next (&iter, &child))
730 clutter_actor_paint (child);
734 clutter_stage_get_paint_volume (ClutterActor *self,
735 ClutterPaintVolume *volume)
737 /* Returning False effectively means Clutter has to assume it covers
743 clutter_stage_realize (ClutterActor *self)
745 ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
746 gboolean is_realized;
748 /* Make sure the viewport and projection matrix are valid for the
749 * first paint (which will likely occur before the ConfigureNotify
752 priv->dirty_viewport = TRUE;
753 priv->dirty_projection = TRUE;
755 g_assert (priv->impl != NULL);
756 is_realized = _clutter_stage_window_realize (priv->impl);
758 /* ensure that the stage is using the context if the
759 * realization sequence was successful
763 ClutterBackend *backend = clutter_get_default_backend ();
765 /* We want to select the context without calling
766 clutter_backend_ensure_context so that it doesn't call any
767 Cogl functions. Otherwise it would create the Cogl context
768 before we get a chance to check whether the GL version is
770 _clutter_backend_ensure_context_internal (backend, CLUTTER_STAGE (self));
773 CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
777 clutter_stage_unrealize (ClutterActor *self)
779 ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
781 /* and then unrealize the implementation */
782 g_assert (priv->impl != NULL);
783 _clutter_stage_window_unrealize (priv->impl);
785 CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
787 clutter_stage_ensure_current (CLUTTER_STAGE (self));
791 clutter_stage_show_all (ClutterActor *self)
793 ClutterActorIter iter;
796 /* we don't do a recursive show_all(), to maintain the old
797 * invariants from ClutterGroup
799 clutter_actor_iter_init (&iter, self);
800 while (clutter_actor_iter_next (&iter, &child))
801 clutter_actor_show (child);
803 clutter_actor_show (self);
807 clutter_stage_show (ClutterActor *self)
809 ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
811 CLUTTER_ACTOR_CLASS (clutter_stage_parent_class)->show (self);
813 /* Possibly do an allocation run so that the stage will have the
814 right size before we map it */
815 _clutter_stage_maybe_relayout (self);
817 g_assert (priv->impl != NULL);
818 _clutter_stage_window_show (priv->impl, TRUE);
822 clutter_stage_hide_all (ClutterActor *self)
824 ClutterActorIter iter;
827 clutter_actor_hide (self);
829 /* we don't do a recursive hide_all(), to maintain the old invariants
832 clutter_actor_iter_init (&iter, self);
833 while (clutter_actor_iter_next (&iter, &child))
834 clutter_actor_hide (child);
838 clutter_stage_hide (ClutterActor *self)
840 ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
842 g_assert (priv->impl != NULL);
843 _clutter_stage_window_hide (priv->impl);
845 CLUTTER_ACTOR_CLASS (clutter_stage_parent_class)->hide (self);
849 clutter_stage_emit_key_focus_event (ClutterStage *stage,
852 ClutterStagePrivate *priv = stage->priv;
854 if (priv->key_focused_actor == NULL)
858 g_signal_emit_by_name (priv->key_focused_actor, "key-focus-in");
860 g_signal_emit_by_name (priv->key_focused_actor, "key-focus-out");
862 g_object_notify (G_OBJECT (stage), "key-focus");
866 clutter_stage_real_activate (ClutterStage *stage)
868 clutter_stage_emit_key_focus_event (stage, TRUE);
872 clutter_stage_real_deactivate (ClutterStage *stage)
874 clutter_stage_emit_key_focus_event (stage, FALSE);
878 clutter_stage_real_fullscreen (ClutterStage *stage)
880 ClutterStagePrivate *priv = stage->priv;
881 cairo_rectangle_int_t geom;
884 /* we need to force an allocation here because the size
885 * of the stage might have been changed by the backend
887 * this is a really bad solution to the issues caused by
888 * the fact that fullscreening the stage on the X11 backends
889 * is really an asynchronous operation
891 _clutter_stage_window_get_geometry (priv->impl, &geom);
896 box.y2 = geom.height;
898 /* we need to blow the caching on the Stage size, given that
899 * we're about to force an allocation, because if anything
900 * ends up querying the size of the stage during the allocate()
901 * call, like constraints or signal handlers, we'll get into an
902 * inconsistent state: the stage will report the old cached size,
903 * but the allocation will be updated anyway.
905 clutter_actor_set_size (CLUTTER_ACTOR (stage), -1.0, -1.0);
906 clutter_actor_allocate (CLUTTER_ACTOR (stage),
908 CLUTTER_ALLOCATION_NONE);
912 _clutter_stage_queue_event (ClutterStage *stage,
915 ClutterStagePrivate *priv;
916 gboolean first_event;
917 ClutterInputDevice *device;
919 g_return_if_fail (CLUTTER_IS_STAGE (stage));
923 first_event = priv->event_queue->length == 0;
925 g_queue_push_tail (priv->event_queue, clutter_event_copy (event));
929 ClutterMasterClock *master_clock = _clutter_master_clock_get_default ();
930 _clutter_master_clock_start_running (master_clock);
933 /* if needed, update the state of the input device of the event.
934 * we do it here to avoid calling the same code from every backend
935 * event processing function
937 device = clutter_event_get_device (event);
940 ClutterModifierType event_state = clutter_event_get_state (event);
941 guint32 event_time = clutter_event_get_time (event);
942 gfloat event_x, event_y;
944 clutter_event_get_coords (event, &event_x, &event_y);
946 _clutter_input_device_set_coords (device, event_x, event_y);
947 _clutter_input_device_set_state (device, event_state);
948 _clutter_input_device_set_time (device, event_time);
953 _clutter_stage_has_queued_events (ClutterStage *stage)
955 ClutterStagePrivate *priv;
957 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
961 return priv->event_queue->length > 0;
965 _clutter_stage_process_queued_events (ClutterStage *stage)
967 ClutterStagePrivate *priv;
970 g_return_if_fail (CLUTTER_IS_STAGE (stage));
974 if (priv->event_queue->length == 0)
977 /* In case the stage gets destroyed during event processing */
978 g_object_ref (stage);
980 /* Steal events before starting processing to avoid reentrancy
982 events = priv->event_queue->head;
983 priv->event_queue->head = NULL;
984 priv->event_queue->tail = NULL;
985 priv->event_queue->length = 0;
987 for (l = events; l != NULL; l = l->next)
990 ClutterEvent *next_event;
991 ClutterInputDevice *device;
992 ClutterInputDevice *next_device;
993 gboolean check_device = FALSE;
996 next_event = l->next ? l->next->data : NULL;
998 device = clutter_event_get_device (event);
1000 if (next_event != NULL)
1001 next_device = clutter_event_get_device (next_event);
1005 if (device != NULL && next_device != NULL)
1006 check_device = TRUE;
1008 /* Skip consecutive motion events coming from the same device */
1009 if (priv->throttle_motion_events &&
1010 next_event != NULL &&
1011 event->type == CLUTTER_MOTION &&
1012 (next_event->type == CLUTTER_MOTION ||
1013 next_event->type == CLUTTER_LEAVE) &&
1014 (!check_device || (device == next_device)))
1016 CLUTTER_NOTE (EVENT,
1017 "Omitting motion event at %d, %d",
1018 (int) event->motion.x,
1019 (int) event->motion.y);
1023 _clutter_process_event (event);
1026 clutter_event_free (event);
1029 g_list_free (events);
1031 g_object_unref (stage);
1035 * _clutter_stage_needs_update:
1036 * @stage: A #ClutterStage
1038 * Determines if _clutter_stage_do_update() needs to be called.
1040 * Return value: %TRUE if the stage need layout or painting
1043 _clutter_stage_needs_update (ClutterStage *stage)
1045 ClutterStagePrivate *priv;
1047 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
1051 return priv->relayout_pending || priv->redraw_pending;
1055 _clutter_stage_maybe_relayout (ClutterActor *actor)
1057 ClutterStage *stage = CLUTTER_STAGE (actor);
1058 ClutterStagePrivate *priv = stage->priv;
1059 gfloat natural_width, natural_height;
1060 ClutterActorBox box = { 0, };
1061 CLUTTER_STATIC_TIMER (relayout_timer,
1062 "Mainloop", /* no parent */
1064 "The time spent reallocating the stage",
1065 0 /* no application private data */);
1067 if (!priv->relayout_pending)
1070 /* avoid reentrancy */
1071 if (!CLUTTER_ACTOR_IN_RELAYOUT (stage))
1073 priv->relayout_pending = FALSE;
1075 CLUTTER_TIMER_START (_clutter_uprof_context, relayout_timer);
1076 CLUTTER_NOTE (ACTOR, "Recomputing layout");
1078 CLUTTER_SET_PRIVATE_FLAGS (stage, CLUTTER_IN_RELAYOUT);
1080 natural_width = natural_height = 0;
1081 clutter_actor_get_preferred_size (CLUTTER_ACTOR (stage),
1083 &natural_width, &natural_height);
1087 box.x2 = natural_width;
1088 box.y2 = natural_height;
1090 CLUTTER_NOTE (ACTOR, "Allocating (0, 0 - %d, %d) for the stage",
1091 (int) natural_width,
1092 (int) natural_height);
1094 clutter_actor_allocate (CLUTTER_ACTOR (stage),
1095 &box, CLUTTER_ALLOCATION_NONE);
1097 CLUTTER_UNSET_PRIVATE_FLAGS (stage, CLUTTER_IN_RELAYOUT);
1098 CLUTTER_TIMER_STOP (_clutter_uprof_context, relayout_timer);
1103 _clutter_stage_get_pick_buffer_valid (ClutterStage *stage, ClutterPickMode mode)
1105 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
1107 if (stage->priv->pick_buffer_mode != mode)
1110 return stage->priv->have_valid_pick_buffer;
1114 _clutter_stage_set_pick_buffer_valid (ClutterStage *stage,
1116 ClutterPickMode mode)
1118 g_return_if_fail (CLUTTER_IS_STAGE (stage));
1120 stage->priv->have_valid_pick_buffer = !!valid;
1121 stage->priv->pick_buffer_mode = mode;
1125 clutter_stage_do_redraw (ClutterStage *stage)
1127 ClutterBackend *backend = clutter_get_default_backend ();
1128 ClutterActor *actor = CLUTTER_ACTOR (stage);
1129 ClutterStagePrivate *priv = stage->priv;
1131 CLUTTER_STATIC_COUNTER (redraw_counter,
1132 "clutter_stage_do_redraw counter",
1133 "Increments for each Stage redraw",
1134 0 /* no application private data */);
1135 CLUTTER_STATIC_TIMER (redraw_timer,
1136 "Master Clock", /* parent */
1138 "The time spent redrawing everything",
1139 0 /* no application private data */);
1141 if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
1144 if (priv->impl == NULL)
1147 CLUTTER_NOTE (PAINT, "Redraw started for stage '%s'[%p]",
1148 _clutter_actor_get_debug_name (actor),
1151 _clutter_stage_set_pick_buffer_valid (stage, FALSE, -1);
1152 priv->picks_per_frame = 0;
1154 _clutter_backend_ensure_context (backend, stage);
1156 if (_clutter_context_get_show_fps ())
1158 if (priv->fps_timer == NULL)
1159 priv->fps_timer = g_timer_new ();
1162 _clutter_stage_maybe_setup_viewport (stage);
1164 CLUTTER_COUNTER_INC (_clutter_uprof_context, redraw_counter);
1165 CLUTTER_TIMER_START (_clutter_uprof_context, redraw_timer);
1167 _clutter_stage_window_redraw (priv->impl);
1169 CLUTTER_TIMER_STOP (_clutter_uprof_context, redraw_timer);
1171 if (_clutter_context_get_show_fps ())
1173 priv->timer_n_frames += 1;
1175 if (g_timer_elapsed (priv->fps_timer, NULL) >= 1.0)
1177 g_print ("*** FPS for %s: %i ***\n",
1178 _clutter_actor_get_debug_name (actor),
1179 priv->timer_n_frames);
1181 priv->timer_n_frames = 0;
1182 g_timer_start (priv->fps_timer);
1186 CLUTTER_NOTE (PAINT, "Redraw finished for stage '%s'[%p]",
1187 _clutter_actor_get_debug_name (actor),
1192 * _clutter_stage_do_update:
1193 * @stage: A #ClutterStage
1195 * Handles per-frame layout and repaint for the stage.
1197 * Return value: %TRUE if the stage was updated
1200 _clutter_stage_do_update (ClutterStage *stage)
1202 ClutterStagePrivate *priv = stage->priv;
1204 /* if the stage is being destroyed, or if the destruction already
1205 * happened and we don't have an StageWindow any more, then we
1208 if (CLUTTER_ACTOR_IN_DESTRUCTION (stage) || priv->impl == NULL)
1211 if (!CLUTTER_ACTOR_IS_REALIZED (stage))
1214 /* NB: We need to ensure we have an up to date layout *before* we
1215 * check or clear the pending redraws flag since a relayout may
1218 _clutter_stage_maybe_relayout (CLUTTER_ACTOR (stage));
1220 if (!priv->redraw_pending)
1223 _clutter_stage_maybe_finish_queue_redraws (stage);
1225 clutter_stage_do_redraw (stage);
1227 /* reset the guard, so that new redraws are possible */
1228 priv->redraw_pending = FALSE;
1230 #ifdef CLUTTER_ENABLE_DEBUG
1231 if (priv->redraw_count > 0)
1233 CLUTTER_NOTE (SCHEDULER, "Queued %lu redraws during the last cycle",
1234 priv->redraw_count);
1236 priv->redraw_count = 0;
1238 #endif /* CLUTTER_ENABLE_DEBUG */
1244 clutter_stage_real_queue_relayout (ClutterActor *self)
1246 ClutterStage *stage = CLUTTER_STAGE (self);
1247 ClutterStagePrivate *priv = stage->priv;
1248 ClutterActorClass *parent_class;
1250 priv->relayout_pending = TRUE;
1253 parent_class = CLUTTER_ACTOR_CLASS (clutter_stage_parent_class);
1254 parent_class->queue_relayout (self);
1258 clutter_stage_real_queue_redraw (ClutterActor *actor,
1261 ClutterStage *stage = CLUTTER_STAGE (actor);
1262 ClutterStageWindow *stage_window;
1263 ClutterPaintVolume *redraw_clip;
1264 ClutterActorBox bounding_box;
1265 ClutterActorBox intersection_box;
1266 cairo_rectangle_int_t geom, stage_clip;
1268 if (CLUTTER_ACTOR_IN_DESTRUCTION (actor))
1271 /* If the backend can't do anything with redraw clips (e.g. it already knows
1272 * it needs to redraw everything anyway) then don't spend time transforming
1273 * any clip volume into stage coordinates... */
1274 stage_window = _clutter_stage_get_window (stage);
1275 if (stage_window == NULL)
1278 if (_clutter_stage_window_ignoring_redraw_clips (stage_window))
1280 _clutter_stage_window_add_redraw_clip (stage_window, NULL);
1284 /* Convert the clip volume into stage coordinates and then into an
1285 * axis aligned stage coordinates bounding box...
1287 redraw_clip = _clutter_actor_get_queue_redraw_clip (leaf);
1288 if (redraw_clip == NULL)
1290 _clutter_stage_window_add_redraw_clip (stage_window, NULL);
1294 _clutter_paint_volume_get_stage_paint_box (redraw_clip,
1298 _clutter_stage_window_get_geometry (stage_window, &geom);
1300 intersection_box.x1 = MAX (bounding_box.x1, 0);
1301 intersection_box.y1 = MAX (bounding_box.y1, 0);
1302 intersection_box.x2 = MIN (bounding_box.x2, geom.width);
1303 intersection_box.y2 = MIN (bounding_box.y2, geom.height);
1305 /* There is no need to track degenerate/empty redraw clips */
1306 if (intersection_box.x2 <= intersection_box.x1 ||
1307 intersection_box.y2 <= intersection_box.y1)
1310 /* when converting to integer coordinates make sure we round the edges of the
1311 * clip rectangle outwards... */
1312 stage_clip.x = intersection_box.x1;
1313 stage_clip.y = intersection_box.y1;
1314 stage_clip.width = intersection_box.x2 - stage_clip.x;
1315 stage_clip.height = intersection_box.y2 - stage_clip.y;
1317 _clutter_stage_window_add_redraw_clip (stage_window, &stage_clip);
1321 _clutter_stage_has_full_redraw_queued (ClutterStage *stage)
1323 ClutterStageWindow *stage_window = _clutter_stage_get_window (stage);
1325 if (CLUTTER_ACTOR_IN_DESTRUCTION (stage) || stage_window == NULL)
1328 if (stage->priv->redraw_pending &&
1329 !_clutter_stage_window_has_redraw_clips (stage_window))
1336 * clutter_stage_get_redraw_clip_bounds:
1337 * @stage: A #ClutterStage
1338 * @clip: (out caller-allocates): Return location for the clip bounds
1340 * Gets the bounds of the current redraw for @stage in stage pixel
1341 * coordinates. E.g., if only a single actor has queued a redraw then
1342 * Clutter may redraw the stage with a clip so that it doesn't have to
1343 * paint every pixel in the stage. This function would then return the
1344 * bounds of that clip. An application can use this information to
1345 * avoid some extra work if it knows that some regions of the stage
1346 * aren't going to be painted. This should only be called while the
1347 * stage is being painted. If there is no current redraw clip then
1348 * this function will set @clip to the full extents of the stage.
1353 clutter_stage_get_redraw_clip_bounds (ClutterStage *stage,
1354 cairo_rectangle_int_t *clip)
1356 ClutterStagePrivate *priv;
1358 g_return_if_fail (CLUTTER_IS_STAGE (stage));
1359 g_return_if_fail (clip != NULL);
1363 if (!_clutter_stage_window_get_redraw_clip_bounds (priv->impl, clip))
1365 /* Set clip to the full extents of the stage */
1366 _clutter_stage_window_get_geometry (priv->impl, clip);
1371 read_pixels_to_file (char *filename_stem,
1378 cairo_surface_t *surface;
1379 static int read_count = 0;
1380 char *filename = g_strdup_printf ("%s-%05d.png",
1384 data = g_malloc (4 * width * height);
1385 cogl_read_pixels (x, y, width, height,
1386 COGL_READ_PIXELS_COLOR_BUFFER,
1387 CLUTTER_CAIRO_FORMAT_ARGB32,
1390 surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_RGB24,
1394 cairo_surface_write_to_png (surface, filename);
1395 cairo_surface_destroy (surface);
1404 _clutter_stage_do_pick (ClutterStage *stage,
1407 ClutterPickMode mode)
1409 ClutterStagePrivate *priv;
1410 ClutterMainContext *context;
1411 guchar pixel[4] = { 0xff, 0xff, 0xff, 0xff };
1412 CoglColor stage_pick_id;
1413 gboolean dither_enabled_save;
1414 CoglFramebuffer *fb;
1415 ClutterActor *actor;
1416 gboolean is_clipped;
1417 CLUTTER_STATIC_COUNTER (do_pick_counter,
1418 "_clutter_stage_do_pick counter",
1419 "Increments for each full pick run",
1420 0 /* no application private data */);
1421 CLUTTER_STATIC_TIMER (pick_timer,
1422 "Mainloop", /* parent */
1424 "The time spent picking",
1425 0 /* no application private data */);
1426 CLUTTER_STATIC_TIMER (pick_clear,
1427 "Picking", /* parent */
1428 "Stage clear (pick)",
1429 "The time spent clearing stage for picking",
1430 0 /* no application private data */);
1431 CLUTTER_STATIC_TIMER (pick_paint,
1432 "Picking", /* parent */
1433 "Painting actors (pick mode)",
1434 "The time spent painting actors in pick mode",
1435 0 /* no application private data */);
1436 CLUTTER_STATIC_TIMER (pick_read,
1437 "Picking", /* parent */
1439 "The time spent issuing a read pixels",
1440 0 /* no application private data */);
1442 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
1446 if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_NOP_PICKING))
1447 return CLUTTER_ACTOR (stage);
1449 #ifdef CLUTTER_ENABLE_PROFILE
1450 if (clutter_profile_flags & CLUTTER_PROFILE_PICKING_ONLY)
1451 _clutter_profile_resume ();
1452 #endif /* CLUTTER_ENABLE_PROFILE */
1454 CLUTTER_COUNTER_INC (_clutter_uprof_context, do_pick_counter);
1455 CLUTTER_TIMER_START (_clutter_uprof_context, pick_timer);
1457 context = _clutter_context_get_default ();
1458 clutter_stage_ensure_current (stage);
1460 /* It's possible that we currently have a static scene and have renderered a
1461 * full, unclipped pick buffer. If so we can simply continue to read from
1462 * this cached buffer until the scene next changes. */
1463 if (_clutter_stage_get_pick_buffer_valid (stage, mode))
1465 CLUTTER_TIMER_START (_clutter_uprof_context, pick_read);
1466 cogl_read_pixels (x, y, 1, 1,
1467 COGL_READ_PIXELS_COLOR_BUFFER,
1468 COGL_PIXEL_FORMAT_RGBA_8888_PRE,
1470 CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_read);
1472 CLUTTER_NOTE (PICK, "Reusing pick buffer from previous render to fetch "
1473 "actor at %i,%i", x, y);
1478 priv->picks_per_frame++;
1480 _clutter_backend_ensure_context (context->backend, stage);
1482 /* needed for when a context switch happens */
1483 _clutter_stage_maybe_setup_viewport (stage);
1485 /* If we are seeing multiple picks per frame that means the scene is static
1486 * so we promote to doing a non-scissored pick render so that all subsequent
1487 * picks for the same static scene won't require additional renders */
1488 if (priv->picks_per_frame < 2)
1490 if (G_LIKELY (!(clutter_pick_debug_flags &
1491 CLUTTER_DEBUG_DUMP_PICK_BUFFERS)))
1492 cogl_clip_push_window_rectangle (x, y, 1, 1);
1498 CLUTTER_NOTE (PICK, "Performing %s pick at %i,%i",
1499 is_clipped ? "clippped" : "full", x, y);
1501 cogl_color_init_from_4ub (&stage_pick_id, 255, 255, 255, 255);
1502 CLUTTER_TIMER_START (_clutter_uprof_context, pick_clear);
1503 cogl_clear (&stage_pick_id,
1504 COGL_BUFFER_BIT_COLOR |
1505 COGL_BUFFER_BIT_DEPTH);
1506 CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_clear);
1508 /* Disable dithering (if any) when doing the painting in pick mode */
1509 fb = cogl_get_draw_framebuffer ();
1510 dither_enabled_save = cogl_framebuffer_get_dither_enabled (fb);
1511 cogl_framebuffer_set_dither_enabled (fb, FALSE);
1513 /* Render the entire scence in pick mode - just single colored silhouette's
1514 * are drawn offscreen (as we never swap buffers)
1516 CLUTTER_TIMER_START (_clutter_uprof_context, pick_paint);
1517 context->pick_mode = mode;
1518 _clutter_stage_do_paint (stage, NULL);
1519 context->pick_mode = CLUTTER_PICK_NONE;
1520 CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_paint);
1524 if (G_LIKELY (!(clutter_pick_debug_flags &
1525 CLUTTER_DEBUG_DUMP_PICK_BUFFERS)))
1528 _clutter_stage_set_pick_buffer_valid (stage, FALSE, -1);
1531 _clutter_stage_set_pick_buffer_valid (stage, TRUE, mode);
1533 /* Read the color of the screen co-ords pixel. RGBA_8888_PRE is used
1534 even though we don't care about the alpha component because under
1535 GLES this is the only format that is guaranteed to work so Cogl
1536 will end up having to do a conversion if any other format is
1537 used. The format is requested as pre-multiplied because Cogl
1538 assumes that all pixels in the framebuffer are premultiplied so
1539 it avoids a conversion. */
1540 CLUTTER_TIMER_START (_clutter_uprof_context, pick_read);
1541 cogl_read_pixels (x, y, 1, 1,
1542 COGL_READ_PIXELS_COLOR_BUFFER,
1543 COGL_PIXEL_FORMAT_RGBA_8888_PRE,
1545 CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_read);
1547 if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))
1550 g_strconcat ("pick-buffer-",
1551 _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage)),
1554 read_pixels_to_file (file_name, 0, 0,
1555 clutter_actor_get_width (CLUTTER_ACTOR (stage)),
1556 clutter_actor_get_height (CLUTTER_ACTOR (stage)));
1561 /* Restore whether GL_DITHER was enabled */
1562 cogl_framebuffer_set_dither_enabled (fb, dither_enabled_save);
1565 if (pixel[0] == 0xff && pixel[1] == 0xff && pixel[2] == 0xff)
1567 actor = CLUTTER_ACTOR (stage);
1571 guint32 id_ = _clutter_pixel_to_id (pixel);
1573 actor = _clutter_get_actor_by_id (stage, id_);
1576 CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_timer);
1578 #ifdef CLUTTER_ENABLE_PROFILE
1579 if (clutter_profile_flags & CLUTTER_PROFILE_PICKING_ONLY)
1580 _clutter_profile_suspend ();
1589 clutter_stage_real_delete_event (ClutterStage *stage,
1590 ClutterEvent *event)
1592 if (stage_is_default (stage))
1593 clutter_main_quit ();
1595 clutter_actor_destroy (CLUTTER_ACTOR (stage));
1601 clutter_stage_real_apply_transform (ClutterActor *stage,
1604 ClutterStagePrivate *priv = CLUTTER_STAGE (stage)->priv;
1606 /* FIXME: we probably shouldn't be explicitly reseting the matrix
1608 cogl_matrix_init_identity (matrix);
1609 cogl_matrix_multiply (matrix, matrix, &priv->view);
1613 clutter_stage_constructed (GObject *gobject)
1615 ClutterStage *self = CLUTTER_STAGE (gobject);
1616 ClutterStageManager *stage_manager;
1618 stage_manager = clutter_stage_manager_get_default ();
1620 /* this will take care to sinking the floating reference */
1621 _clutter_stage_manager_add_stage (stage_manager, self);
1623 /* if this stage has been created on a backend that does not
1624 * support multiple stages then it becomes the default stage
1625 * as well; any other attempt at creating a ClutterStage will
1628 if (!clutter_feature_available (CLUTTER_FEATURE_STAGE_MULTIPLE))
1630 if (G_UNLIKELY (clutter_stage_manager_get_default_stage (stage_manager) != NULL))
1632 g_error ("Unable to create another stage: the backend of "
1633 "type '%s' does not support multiple stages. Use "
1634 "clutter_stage_manager_get_default_stage() instead "
1635 "to access the stage singleton.",
1636 G_OBJECT_TYPE_NAME (clutter_get_default_backend ()));
1639 _clutter_stage_manager_set_default_stage (stage_manager, self);
1642 G_OBJECT_CLASS (clutter_stage_parent_class)->constructed (gobject);
1646 clutter_stage_set_property (GObject *object,
1648 const GValue *value,
1651 ClutterStage *stage = CLUTTER_STAGE (object);
1656 clutter_actor_set_background_color (CLUTTER_ACTOR (stage),
1657 clutter_value_get_color (value));
1660 case PROP_OFFSCREEN:
1661 if (g_value_get_boolean (value))
1662 g_warning ("Offscreen stages are currently not supported\n");
1665 case PROP_CURSOR_VISIBLE:
1666 if (g_value_get_boolean (value))
1667 clutter_stage_show_cursor (stage);
1669 clutter_stage_hide_cursor (stage);
1672 case PROP_PERSPECTIVE:
1673 clutter_stage_set_perspective (stage, g_value_get_boxed (value));
1677 clutter_stage_set_title (stage, g_value_get_string (value));
1680 case PROP_USER_RESIZABLE:
1681 clutter_stage_set_user_resizable (stage, g_value_get_boolean (value));
1685 clutter_stage_set_use_fog (stage, g_value_get_boolean (value));
1689 clutter_stage_set_fog (stage, g_value_get_boxed (value));
1692 case PROP_USE_ALPHA:
1693 clutter_stage_set_use_alpha (stage, g_value_get_boolean (value));
1696 case PROP_KEY_FOCUS:
1697 clutter_stage_set_key_focus (stage, g_value_get_object (value));
1700 case PROP_NO_CLEAR_HINT:
1701 clutter_stage_set_no_clear_hint (stage, g_value_get_boolean (value));
1704 case PROP_ACCEPT_FOCUS:
1705 clutter_stage_set_accept_focus (stage, g_value_get_boolean (value));
1709 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1715 clutter_stage_get_property (GObject *gobject,
1720 ClutterStagePrivate *priv = CLUTTER_STAGE (gobject)->priv;
1726 ClutterColor bg_color;
1728 clutter_actor_get_background_color (CLUTTER_ACTOR (gobject),
1730 clutter_value_set_color (value, &bg_color);
1734 case PROP_OFFSCREEN:
1735 g_value_set_boolean (value, FALSE);
1738 case PROP_FULLSCREEN_SET:
1739 g_value_set_boolean (value, priv->is_fullscreen);
1742 case PROP_CURSOR_VISIBLE:
1743 g_value_set_boolean (value, priv->is_cursor_visible);
1746 case PROP_PERSPECTIVE:
1747 g_value_set_boxed (value, &priv->perspective);
1751 g_value_set_string (value, priv->title);
1754 case PROP_USER_RESIZABLE:
1755 g_value_set_boolean (value, priv->is_user_resizable);
1759 g_value_set_boolean (value, priv->use_fog);
1763 g_value_set_boxed (value, &priv->fog);
1766 case PROP_USE_ALPHA:
1767 g_value_set_boolean (value, priv->use_alpha);
1770 case PROP_KEY_FOCUS:
1771 g_value_set_object (value, priv->key_focused_actor);
1774 case PROP_NO_CLEAR_HINT:
1777 (priv->stage_hints & CLUTTER_STAGE_NO_CLEAR_ON_PAINT) != 0;
1779 g_value_set_boolean (value, hint);
1783 case PROP_ACCEPT_FOCUS:
1784 g_value_set_boolean (value, priv->accept_focus);
1788 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
1794 clutter_stage_dispose (GObject *object)
1796 ClutterStage *stage = CLUTTER_STAGE (object);
1797 ClutterStagePrivate *priv = stage->priv;
1798 ClutterStageManager *stage_manager;
1800 clutter_actor_hide (CLUTTER_ACTOR (object));
1802 /* remove_stage() will unref() the stage instance, so we need to
1803 * add a reference here to keep it temporarily alive
1805 g_object_ref (object);
1806 stage_manager = clutter_stage_manager_get_default ();
1807 _clutter_stage_manager_remove_stage (stage_manager, stage);
1809 _clutter_clear_events_queue_for_stage (stage);
1811 if (priv->impl != NULL)
1813 CLUTTER_NOTE (BACKEND, "Disposing of the stage implementation");
1815 if (CLUTTER_ACTOR_IS_REALIZED (object))
1816 _clutter_stage_window_unrealize (priv->impl);
1818 g_object_unref (priv->impl);
1822 clutter_actor_remove_all_children (CLUTTER_ACTOR (object));
1824 G_OBJECT_CLASS (clutter_stage_parent_class)->dispose (object);
1828 clutter_stage_finalize (GObject *object)
1830 ClutterStage *stage = CLUTTER_STAGE (object);
1831 ClutterStagePrivate *priv = stage->priv;
1833 g_queue_foreach (priv->event_queue, (GFunc) clutter_event_free, NULL);
1834 g_queue_free (priv->event_queue);
1836 g_free (priv->title);
1838 g_array_free (priv->paint_volume_stack, TRUE);
1840 g_hash_table_destroy (priv->devices);
1842 _clutter_id_pool_free (priv->pick_id_pool);
1844 if (priv->fps_timer != NULL)
1845 g_timer_destroy (priv->fps_timer);
1847 G_OBJECT_CLASS (clutter_stage_parent_class)->finalize (object);
1851 clutter_stage_class_init (ClutterStageClass *klass)
1853 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1854 ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
1857 gobject_class->constructed = clutter_stage_constructed;
1858 gobject_class->set_property = clutter_stage_set_property;
1859 gobject_class->get_property = clutter_stage_get_property;
1860 gobject_class->dispose = clutter_stage_dispose;
1861 gobject_class->finalize = clutter_stage_finalize;
1863 actor_class->allocate = clutter_stage_allocate;
1864 actor_class->get_preferred_width = clutter_stage_get_preferred_width;
1865 actor_class->get_preferred_height = clutter_stage_get_preferred_height;
1866 actor_class->paint = clutter_stage_paint;
1867 actor_class->pick = clutter_stage_pick;
1868 actor_class->get_paint_volume = clutter_stage_get_paint_volume;
1869 actor_class->realize = clutter_stage_realize;
1870 actor_class->unrealize = clutter_stage_unrealize;
1871 actor_class->show = clutter_stage_show;
1872 actor_class->show_all = clutter_stage_show_all;
1873 actor_class->hide = clutter_stage_hide;
1874 actor_class->hide_all = clutter_stage_hide_all;
1875 actor_class->queue_relayout = clutter_stage_real_queue_relayout;
1876 actor_class->queue_redraw = clutter_stage_real_queue_redraw;
1877 actor_class->apply_transform = clutter_stage_real_apply_transform;
1880 * ClutterStage:fullscreen:
1882 * Whether the stage should be fullscreen or not.
1884 * This property is set by calling clutter_stage_set_fullscreen()
1885 * but since the actual implementation is delegated to the backend
1886 * you should connect to the notify::fullscreen-set signal in order
1887 * to get notification if the fullscreen state has been successfully
1892 pspec = g_param_spec_boolean ("fullscreen-set",
1893 P_("Fullscreen Set"),
1894 P_("Whether the main stage is fullscreen"),
1896 CLUTTER_PARAM_READABLE);
1897 g_object_class_install_property (gobject_class,
1898 PROP_FULLSCREEN_SET,
1901 * ClutterStage:offscreen:
1903 * Whether the stage should be rendered in an offscreen buffer.
1905 * Deprecated: 1.10: This property does not do anything.
1907 pspec = g_param_spec_boolean ("offscreen",
1909 P_("Whether the main stage should be rendered offscreen"),
1911 CLUTTER_PARAM_READWRITE | G_PARAM_DEPRECATED);
1912 g_object_class_install_property (gobject_class,
1916 * ClutterStage:cursor-visible:
1918 * Whether the mouse pointer should be visible
1920 pspec = g_param_spec_boolean ("cursor-visible",
1921 P_("Cursor Visible"),
1922 P_("Whether the mouse pointer is visible on the main stage"),
1924 CLUTTER_PARAM_READWRITE);
1925 g_object_class_install_property (gobject_class,
1926 PROP_CURSOR_VISIBLE,
1929 * ClutterStage:user-resizable:
1931 * Whether the stage is resizable via user interaction.
1935 pspec = g_param_spec_boolean ("user-resizable",
1936 P_("User Resizable"),
1937 P_("Whether the stage is able to be resized via user interaction"),
1939 CLUTTER_PARAM_READWRITE);
1940 g_object_class_install_property (gobject_class,
1941 PROP_USER_RESIZABLE,
1944 * ClutterStage:color:
1946 * The background color of the main stage.
1948 * Deprecated: 1.10: Use the #ClutterActor:background-color property of
1949 * #ClutterActor instead.
1951 pspec = clutter_param_spec_color ("color",
1953 P_("The color of the stage"),
1954 &default_stage_color,
1955 CLUTTER_PARAM_READWRITE |
1956 G_PARAM_DEPRECATED);
1957 g_object_class_install_property (gobject_class, PROP_COLOR, pspec);
1960 * ClutterStage:perspective:
1962 * The parameters used for the perspective projection from 3D
1967 pspec = g_param_spec_boxed ("perspective",
1969 P_("Perspective projection parameters"),
1970 CLUTTER_TYPE_PERSPECTIVE,
1971 CLUTTER_PARAM_READWRITE);
1972 g_object_class_install_property (gobject_class,
1977 * ClutterStage:title:
1979 * The stage's title - usually displayed in stage windows title decorations.
1983 pspec = g_param_spec_string ("title",
1987 CLUTTER_PARAM_READWRITE);
1988 g_object_class_install_property (gobject_class, PROP_TITLE, pspec);
1991 * ClutterStage:use-fog:
1993 * Whether the stage should use a linear GL "fog" in creating the
1994 * depth-cueing effect, to enhance the perception of depth by fading
1995 * actors farther from the viewpoint.
1999 * Deprecated: 1.10: This property does not do anything.
2001 pspec = g_param_spec_boolean ("use-fog",
2003 P_("Whether to enable depth cueing"),
2005 CLUTTER_PARAM_READWRITE | G_PARAM_DEPRECATED);
2006 g_object_class_install_property (gobject_class, PROP_USE_FOG, pspec);
2011 * The settings for the GL "fog", used only if #ClutterStage:use-fog
2016 * Deprecated: 1.10: This property does not do anything.
2018 pspec = g_param_spec_boxed ("fog",
2020 P_("Settings for the depth cueing"),
2022 CLUTTER_PARAM_READWRITE | G_PARAM_DEPRECATED);
2023 g_object_class_install_property (gobject_class, PROP_FOG, pspec);
2026 * ClutterStage:use-alpha:
2028 * Whether the #ClutterStage should honour the alpha component of the
2029 * #ClutterStage:color property when painting. If Clutter is run under
2030 * a compositing manager this will result in the stage being blended
2031 * with the underlying window(s)
2035 pspec = g_param_spec_boolean ("use-alpha",
2037 P_("Whether to honour the alpha component of the stage color"),
2039 CLUTTER_PARAM_READWRITE);
2040 g_object_class_install_property (gobject_class, PROP_USE_ALPHA, pspec);
2043 * ClutterStage:key-focus:
2045 * The #ClutterActor that will receive key events from the underlying
2048 * If %NULL, the #ClutterStage will receive the events.
2052 pspec = g_param_spec_object ("key-focus",
2054 P_("The currently key focused actor"),
2056 CLUTTER_PARAM_READWRITE);
2057 g_object_class_install_property (gobject_class, PROP_KEY_FOCUS, pspec);
2060 * ClutterStage:no-clear-hint:
2062 * Whether or not the #ClutterStage should clear its contents
2063 * before each paint cycle.
2065 * See clutter_stage_set_no_clear_hint() for further information.
2069 pspec = g_param_spec_boolean ("no-clear-hint",
2070 P_("No Clear Hint"),
2071 P_("Whether the stage should clear its contents"),
2073 CLUTTER_PARAM_READWRITE);
2074 g_object_class_install_property (gobject_class, PROP_NO_CLEAR_HINT, pspec);
2077 * ClutterStage:accept-focus:
2079 * Whether the #ClutterStage should accept key focus when shown.
2083 pspec = g_param_spec_boolean ("accept-focus",
2085 P_("Whether the stage should accept focus on show"),
2087 CLUTTER_PARAM_READWRITE);
2088 g_object_class_install_property (gobject_class, PROP_ACCEPT_FOCUS, pspec);
2091 * ClutterStage::fullscreen:
2092 * @stage: the stage which was fullscreened
2094 * The ::fullscreen signal is emitted when the stage is made fullscreen.
2098 stage_signals[FULLSCREEN] =
2099 g_signal_new (I_("fullscreen"),
2100 G_TYPE_FROM_CLASS (gobject_class),
2102 G_STRUCT_OFFSET (ClutterStageClass, fullscreen),
2104 _clutter_marshal_VOID__VOID,
2107 * ClutterStage::unfullscreen:
2108 * @stage: the stage which has left a fullscreen state.
2110 * The ::unfullscreen signal is emitted when the stage leaves a fullscreen
2115 stage_signals[UNFULLSCREEN] =
2116 g_signal_new (I_("unfullscreen"),
2117 G_TYPE_FROM_CLASS (gobject_class),
2119 G_STRUCT_OFFSET (ClutterStageClass, unfullscreen),
2121 _clutter_marshal_VOID__VOID,
2124 * ClutterStage::activate:
2125 * @stage: the stage which was activated
2127 * The ::activate signal is emitted when the stage receives key focus
2128 * from the underlying window system.
2132 stage_signals[ACTIVATE] =
2133 g_signal_new (I_("activate"),
2134 G_TYPE_FROM_CLASS (gobject_class),
2136 G_STRUCT_OFFSET (ClutterStageClass, activate),
2138 _clutter_marshal_VOID__VOID,
2141 * ClutterStage::deactivate:
2142 * @stage: the stage which was deactivated
2144 * The ::activate signal is emitted when the stage loses key focus
2145 * from the underlying window system.
2149 stage_signals[DEACTIVATE] =
2150 g_signal_new (I_("deactivate"),
2151 G_TYPE_FROM_CLASS (gobject_class),
2153 G_STRUCT_OFFSET (ClutterStageClass, deactivate),
2155 _clutter_marshal_VOID__VOID,
2159 * ClutterStage::delete-event:
2160 * @stage: the stage that received the event
2161 * @event: a #ClutterEvent of type %CLUTTER_DELETE
2163 * The ::delete-event signal is emitted when the user closes a
2164 * #ClutterStage window using the window controls.
2166 * Clutter by default will call clutter_main_quit() if @stage is
2167 * the default stage, and clutter_actor_destroy() for any other
2170 * It is possible to override the default behaviour by connecting
2171 * a new handler and returning %TRUE there.
2173 * <note>This signal is emitted only on Clutter backends that
2174 * embed #ClutterStage in native windows. It is not emitted for
2175 * backends that use a static frame buffer.</note>
2179 stage_signals[DELETE_EVENT] =
2180 g_signal_new (I_("delete-event"),
2181 G_TYPE_FROM_CLASS (gobject_class),
2183 G_STRUCT_OFFSET (ClutterStageClass, delete_event),
2184 _clutter_boolean_handled_accumulator, NULL,
2185 _clutter_marshal_BOOLEAN__BOXED,
2187 CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
2189 klass->fullscreen = clutter_stage_real_fullscreen;
2190 klass->activate = clutter_stage_real_activate;
2191 klass->deactivate = clutter_stage_real_deactivate;
2192 klass->delete_event = clutter_stage_real_delete_event;
2194 g_type_class_add_private (gobject_class, sizeof (ClutterStagePrivate));
2198 clutter_stage_notify_min_size (ClutterStage *self)
2200 self->priv->min_size_changed = TRUE;
2204 clutter_stage_init (ClutterStage *self)
2206 cairo_rectangle_int_t geom = { 0, };
2207 ClutterStagePrivate *priv;
2208 ClutterStageWindow *impl;
2209 ClutterBackend *backend;
2212 /* a stage is a top-level object */
2213 CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IS_TOPLEVEL);
2215 self->priv = priv = CLUTTER_STAGE_GET_PRIVATE (self);
2217 CLUTTER_NOTE (BACKEND, "Creating stage from the default backend");
2218 backend = clutter_get_default_backend ();
2221 impl = _clutter_backend_create_stage (backend, self, &error);
2223 if (G_LIKELY (impl != NULL))
2225 _clutter_stage_set_window (self, impl);
2226 _clutter_stage_window_get_geometry (priv->impl, &geom);
2232 g_critical ("Unable to create a new stage implementation: %s",
2234 g_error_free (error);
2237 g_critical ("Unable to create a new stage implementation.");
2240 priv->event_queue = g_queue_new ();
2242 priv->is_fullscreen = FALSE;
2243 priv->is_user_resizable = FALSE;
2244 priv->is_cursor_visible = TRUE;
2245 priv->use_fog = FALSE;
2246 priv->throttle_motion_events = TRUE;
2247 priv->min_size_changed = FALSE;
2249 /* XXX - we need to keep the invariant that calling
2250 * clutter_set_motion_event_enabled() before the stage creation
2251 * will cause motion event delivery to be disabled on any newly
2252 * created stage. this can go away when we break API and remove
2253 * deprecated functions.
2255 priv->motion_events_enabled = _clutter_context_get_motion_events_enabled ();
2257 clutter_actor_set_background_color (CLUTTER_ACTOR (self),
2258 &default_stage_color);
2260 priv->perspective.fovy = 60.0; /* 60 Degrees */
2261 priv->perspective.aspect = (float) geom.width / (float) geom.height;
2262 priv->perspective.z_near = 0.1;
2263 priv->perspective.z_far = 100.0;
2265 cogl_matrix_init_identity (&priv->projection);
2266 cogl_matrix_perspective (&priv->projection,
2267 priv->perspective.fovy,
2268 priv->perspective.aspect,
2269 priv->perspective.z_near,
2270 priv->perspective.z_far);
2271 cogl_matrix_get_inverse (&priv->projection,
2272 &priv->inverse_projection);
2273 cogl_matrix_init_identity (&priv->view);
2274 cogl_matrix_view_2d_in_perspective (&priv->view,
2275 priv->perspective.fovy,
2276 priv->perspective.aspect,
2277 priv->perspective.z_near,
2278 50, /* distance to 2d plane */
2283 /* FIXME - remove for 2.0 */
2284 priv->fog.z_near = 1.0;
2285 priv->fog.z_far = 2.0;
2287 priv->relayout_pending = TRUE;
2289 clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE);
2290 clutter_stage_set_title (self, g_get_prgname ());
2291 clutter_stage_set_key_focus (self, NULL);
2293 g_signal_connect (self, "notify::min-width",
2294 G_CALLBACK (clutter_stage_notify_min_size), NULL);
2295 g_signal_connect (self, "notify::min-height",
2296 G_CALLBACK (clutter_stage_notify_min_size), NULL);
2298 _clutter_stage_set_viewport (self, 0, 0, geom.width, geom.height);
2300 _clutter_stage_set_pick_buffer_valid (self, FALSE, CLUTTER_PICK_ALL);
2301 priv->picks_per_frame = 0;
2303 priv->paint_volume_stack =
2304 g_array_new (FALSE, FALSE, sizeof (ClutterPaintVolume));
2306 priv->devices = g_hash_table_new (NULL, NULL);
2308 priv->pick_id_pool = _clutter_id_pool_new (256);
2312 * clutter_stage_get_default:
2314 * Retrieves a #ClutterStage singleton.
2316 * This function is not as useful as it sounds, and will most likely
2317 * by deprecated in the future. Application code should only create
2318 * a #ClutterStage instance using clutter_stage_new(), and manage the
2319 * lifetime of the stage manually.
2321 * The default stage singleton has a platform-specific behaviour: on
2322 * platforms without the %CLUTTER_FEATURE_STAGE_MULTIPLE feature flag
2323 * set, the first #ClutterStage instance will also be set to be the
2324 * default stage instance, and this function will always return a
2327 * On platforms with the %CLUTTER_FEATURE_STAGE_MULTIPLE feature flag
2328 * set, the default stage will be created by the first call to this
2329 * function, and every following call will return the same pointer to
2332 * Return value: (transfer none) (type Clutter.Stage): the main
2333 * #ClutterStage. You should never destroy or unref the returned
2336 * Deprecated: 1.10: Use clutter_stage_new() instead.
2339 clutter_stage_get_default (void)
2341 ClutterStageManager *stage_manager = clutter_stage_manager_get_default ();
2342 ClutterStage *stage;
2344 stage = clutter_stage_manager_get_default_stage (stage_manager);
2345 if (G_UNLIKELY (stage == NULL))
2347 /* This will take care of automatically adding the stage to the
2348 * stage manager and setting it as the default. Its floating
2349 * reference will be claimed by the stage manager.
2351 stage = g_object_new (CLUTTER_TYPE_STAGE, NULL);
2352 _clutter_stage_manager_set_default_stage (stage_manager, stage);
2354 /* the default stage is realized by default */
2355 clutter_actor_realize (CLUTTER_ACTOR (stage));
2358 return CLUTTER_ACTOR (stage);
2362 * clutter_stage_set_color:
2363 * @stage: A #ClutterStage
2364 * @color: A #ClutterColor
2366 * Sets the stage color.
2368 * Deprecated: 1.10: Use clutter_actor_set_background_color() instead.
2371 clutter_stage_set_color (ClutterStage *stage,
2372 const ClutterColor *color)
2374 clutter_actor_set_background_color (CLUTTER_ACTOR (stage), color);
2376 g_object_notify (G_OBJECT (stage), "color");
2380 * clutter_stage_get_color:
2381 * @stage: A #ClutterStage
2382 * @color: (out caller-allocates): return location for a #ClutterColor
2384 * Retrieves the stage color.
2386 * Deprecated: 1.10: Use clutter_actor_get_background_color() instead.
2389 clutter_stage_get_color (ClutterStage *stage,
2390 ClutterColor *color)
2392 clutter_actor_get_background_color (CLUTTER_ACTOR (stage), color);
2396 clutter_stage_set_perspective_internal (ClutterStage *stage,
2397 ClutterPerspective *perspective)
2399 ClutterStagePrivate *priv = stage->priv;
2401 if (priv->perspective.fovy == perspective->fovy &&
2402 priv->perspective.aspect == perspective->aspect &&
2403 priv->perspective.z_near == perspective->z_near &&
2404 priv->perspective.z_far == perspective->z_far)
2407 priv->perspective = *perspective;
2409 cogl_matrix_init_identity (&priv->projection);
2410 cogl_matrix_perspective (&priv->projection,
2411 priv->perspective.fovy,
2412 priv->perspective.aspect,
2413 priv->perspective.z_near,
2414 priv->perspective.z_far);
2415 cogl_matrix_get_inverse (&priv->projection,
2416 &priv->inverse_projection);
2418 priv->dirty_projection = TRUE;
2419 clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
2423 * clutter_stage_set_perspective:
2424 * @stage: A #ClutterStage
2425 * @perspective: A #ClutterPerspective
2427 * Sets the stage perspective. Using this function is not recommended
2428 * because it will disable Clutter's attempts to generate an
2429 * appropriate perspective based on the size of the stage.
2432 clutter_stage_set_perspective (ClutterStage *stage,
2433 ClutterPerspective *perspective)
2435 ClutterStagePrivate *priv;
2437 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2438 g_return_if_fail (perspective != NULL);
2439 g_return_if_fail (perspective->z_far - perspective->z_near != 0);
2443 /* If the application ever calls this function then we'll stop
2444 automatically updating the perspective when the stage changes
2446 priv->has_custom_perspective = TRUE;
2448 clutter_stage_set_perspective_internal (stage, perspective);
2452 * clutter_stage_get_perspective:
2453 * @stage: A #ClutterStage
2454 * @perspective: (out caller-allocates) (allow-none): return location for a
2455 * #ClutterPerspective
2457 * Retrieves the stage perspective.
2460 clutter_stage_get_perspective (ClutterStage *stage,
2461 ClutterPerspective *perspective)
2463 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2464 g_return_if_fail (perspective != NULL);
2466 *perspective = stage->priv->perspective;
2470 * clutter_stage_get_projection_matrix:
2471 * @stage: A #ClutterStage
2472 * @projection: return location for a #CoglMatrix representing the
2473 * perspective projection applied to actors on the given
2476 * Retrieves the @stage's projection matrix. This is derived from the
2477 * current perspective set using clutter_stage_set_perspective().
2482 _clutter_stage_get_projection_matrix (ClutterStage *stage,
2483 CoglMatrix *projection)
2485 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2486 g_return_if_fail (projection != NULL);
2488 *projection = stage->priv->projection;
2491 /* This simply provides a simple mechanism for us to ensure that
2492 * the projection matrix gets re-asserted before painting.
2494 * This is used when switching between multiple stages */
2496 _clutter_stage_dirty_projection (ClutterStage *stage)
2498 stage->priv->dirty_projection = TRUE;
2502 * clutter_stage_set_viewport:
2503 * @stage: A #ClutterStage
2504 * @x: The X postition to render the stage at, in window coordinates
2505 * @y: The Y position to render the stage at, in window coordinates
2506 * @width: The width to render the stage at, in window coordinates
2507 * @height: The height to render the stage at, in window coordinates
2509 * Sets the stage viewport. The viewport defines a final scale and
2510 * translation of your rendered stage and actors. This lets you render
2511 * your stage into a subregion of the stage window or you could use it to
2512 * pan a subregion of the stage if your stage window is smaller then
2513 * the stage. (XXX: currently this isn't possible)
2515 * Unlike a scale and translation done using the modelview matrix this
2516 * is done after everything has had perspective projection applied, so
2517 * for example if you were to pan across a subregion of the stage using
2518 * the viewport then you would not see a change in perspective for the
2519 * actors on the stage.
2521 * Normally the stage viewport will automatically track the size of the
2522 * stage window with no offset so the stage will fill your window. This
2523 * behaviour can be changed with the "viewport-mimics-window" property
2524 * which will automatically be set to FALSE if you use this API. If
2525 * you want to revert to the original behaviour then you should set
2526 * this property back to %TRUE using
2527 * clutter_stage_set_viewport_mimics_window().
2528 * (XXX: If we were to make this API public then we might want to do
2529 * add that property.)
2531 * Note: currently this interface only support integer precision
2532 * offsets and sizes for viewports but the interface takes floats because
2533 * OpenGL 4.0 has introduced floating point viewports which we might
2534 * want to expose via this API eventually.
2539 _clutter_stage_set_viewport (ClutterStage *stage,
2545 ClutterStagePrivate *priv;
2547 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2552 if (x == priv->viewport[0] &&
2553 y == priv->viewport[1] &&
2554 width == priv->viewport[2] &&
2555 height == priv->viewport[3])
2558 priv->viewport[0] = x;
2559 priv->viewport[1] = y;
2560 priv->viewport[2] = width;
2561 priv->viewport[3] = height;
2563 priv->dirty_viewport = TRUE;
2565 queue_full_redraw (stage);
2568 /* This simply provides a simple mechanism for us to ensure that
2569 * the viewport gets re-asserted before next painting.
2571 * This is used when switching between multiple stages */
2573 _clutter_stage_dirty_viewport (ClutterStage *stage)
2575 stage->priv->dirty_viewport = TRUE;
2579 * clutter_stage_get_viewport:
2580 * @stage: A #ClutterStage
2581 * @x: A location for the X position where the stage is rendered,
2582 * in window coordinates.
2583 * @y: A location for the Y position where the stage is rendered,
2584 * in window coordinates.
2585 * @width: A location for the width the stage is rendered at,
2586 * in window coordinates.
2587 * @height: A location for the height the stage is rendered at,
2588 * in window coordinates.
2590 * Returns the viewport offset and size set using
2591 * clutter_stage_set_viewport() or if the "viewport-mimics-window" property
2592 * is TRUE then @x and @y will be set to 0 and @width and @height will equal
2593 * the width if the stage window.
2598 _clutter_stage_get_viewport (ClutterStage *stage,
2604 ClutterStagePrivate *priv;
2606 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2610 *x = priv->viewport[0];
2611 *y = priv->viewport[1];
2612 *width = priv->viewport[2];
2613 *height = priv->viewport[3];
2617 * clutter_stage_set_fullscreen:
2618 * @stage: a #ClutterStage
2619 * @fullscreen: %TRUE to to set the stage fullscreen
2621 * Asks to place the stage window in the fullscreen or unfullscreen
2624 ( Note that you shouldn't assume the window is definitely full screen
2625 * afterward, because other entities (e.g. the user or window manager)
2626 * could unfullscreen it again, and not all window managers honor
2627 * requests to fullscreen windows.
2629 * If you want to receive notification of the fullscreen state you
2630 * should either use the #ClutterStage::fullscreen and
2631 * #ClutterStage::unfullscreen signals, or use the notify signal
2632 * for the #ClutterStage:fullscreen-set property
2637 clutter_stage_set_fullscreen (ClutterStage *stage,
2638 gboolean fullscreen)
2640 ClutterStagePrivate *priv;
2642 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2646 if (priv->is_fullscreen != fullscreen)
2648 ClutterStageWindow *impl = CLUTTER_STAGE_WINDOW (priv->impl);
2649 ClutterStageWindowIface *iface;
2651 iface = CLUTTER_STAGE_WINDOW_GET_IFACE (impl);
2653 /* Only set if backend implements.
2655 * Also see clutter_stage_event() for setting priv->is_fullscreen
2656 * on state change event.
2658 if (iface->set_fullscreen)
2659 iface->set_fullscreen (impl, fullscreen);
2662 /* If the backend did fullscreen the stage window then we need to resize
2663 * the stage and update its viewport so we queue a relayout. Note: if the
2664 * fullscreen request is handled asynchronously we can't rely on this
2665 * queue_relayout to update the viewport, but for example the X backend
2666 * will recieve a ConfigureNotify after a successful resize which is how
2667 * we ensure the viewport is updated on X.
2669 clutter_actor_queue_relayout (CLUTTER_ACTOR (stage));
2673 * clutter_stage_get_fullscreen:
2674 * @stage: a #ClutterStage
2676 * Retrieves whether the stage is full screen or not
2678 * Return value: %TRUE if the stage is full screen
2683 clutter_stage_get_fullscreen (ClutterStage *stage)
2685 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
2687 return stage->priv->is_fullscreen;
2691 * clutter_stage_set_user_resizable:
2692 * @stage: a #ClutterStage
2693 * @resizable: whether the stage should be user resizable.
2695 * Sets if the stage is resizable by user interaction (e.g. via
2696 * window manager controls)
2701 clutter_stage_set_user_resizable (ClutterStage *stage,
2704 ClutterStagePrivate *priv;
2706 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2710 if (clutter_feature_available (CLUTTER_FEATURE_STAGE_USER_RESIZE)
2711 && priv->is_user_resizable != resizable)
2713 ClutterStageWindow *impl = CLUTTER_STAGE_WINDOW (priv->impl);
2714 ClutterStageWindowIface *iface;
2716 iface = CLUTTER_STAGE_WINDOW_GET_IFACE (impl);
2717 if (iface->set_user_resizable)
2719 priv->is_user_resizable = resizable;
2721 iface->set_user_resizable (impl, resizable);
2723 g_object_notify (G_OBJECT (stage), "user-resizable");
2729 * clutter_stage_get_user_resizable:
2730 * @stage: a #ClutterStage
2732 * Retrieves the value set with clutter_stage_set_user_resizable().
2734 * Return value: %TRUE if the stage is resizable by the user.
2739 clutter_stage_get_user_resizable (ClutterStage *stage)
2741 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
2743 return stage->priv->is_user_resizable;
2747 * clutter_stage_show_cursor:
2748 * @stage: a #ClutterStage
2750 * Shows the cursor on the stage window
2753 clutter_stage_show_cursor (ClutterStage *stage)
2755 ClutterStagePrivate *priv;
2757 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2760 if (!priv->is_cursor_visible)
2762 ClutterStageWindow *impl = CLUTTER_STAGE_WINDOW (priv->impl);
2763 ClutterStageWindowIface *iface;
2765 iface = CLUTTER_STAGE_WINDOW_GET_IFACE (impl);
2766 if (iface->set_cursor_visible)
2768 priv->is_cursor_visible = TRUE;
2770 iface->set_cursor_visible (impl, TRUE);
2772 g_object_notify (G_OBJECT (stage), "cursor-visible");
2778 * clutter_stage_hide_cursor:
2779 * @stage: a #ClutterStage
2781 * Makes the cursor invisible on the stage window
2786 clutter_stage_hide_cursor (ClutterStage *stage)
2788 ClutterStagePrivate *priv;
2790 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2793 if (priv->is_cursor_visible)
2795 ClutterStageWindow *impl = CLUTTER_STAGE_WINDOW (priv->impl);
2796 ClutterStageWindowIface *iface;
2798 iface = CLUTTER_STAGE_WINDOW_GET_IFACE (impl);
2799 if (iface->set_cursor_visible)
2801 priv->is_cursor_visible = FALSE;
2803 iface->set_cursor_visible (impl, FALSE);
2805 g_object_notify (G_OBJECT (stage), "cursor-visible");
2811 * clutter_stage_read_pixels:
2812 * @stage: A #ClutterStage
2813 * @x: x coordinate of the first pixel that is read from stage
2814 * @y: y coordinate of the first pixel that is read from stage
2815 * @width: Width dimention of pixels to be read, or -1 for the
2816 * entire stage width
2817 * @height: Height dimention of pixels to be read, or -1 for the
2818 * entire stage height
2820 * Makes a screenshot of the stage in RGBA 8bit data, returns a
2821 * linear buffer with @width * 4 as rowstride.
2823 * The alpha data contained in the returned buffer is driver-dependent,
2824 * and not guaranteed to hold any sensible value.
2826 * Return value: (array): a pointer to newly allocated memory with the buffer
2827 * or %NULL if the read failed. Use g_free() on the returned data
2828 * to release the resources it has allocated.
2831 clutter_stage_read_pixels (ClutterStage *stage,
2837 ClutterGeometry geom;
2840 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
2842 /* Force a redraw of the stage before reading back pixels */
2843 clutter_stage_ensure_current (stage);
2844 clutter_actor_paint (CLUTTER_ACTOR (stage));
2846 clutter_actor_get_allocation_geometry (CLUTTER_ACTOR (stage), &geom);
2852 height = geom.height;
2854 pixels = g_malloc (height * width * 4);
2856 cogl_read_pixels (x, y, width, height,
2857 COGL_READ_PIXELS_COLOR_BUFFER,
2858 COGL_PIXEL_FORMAT_RGBA_8888,
2865 * clutter_stage_get_actor_at_pos:
2866 * @stage: a #ClutterStage
2867 * @pick_mode: how the scene graph should be painted
2868 * @x: X coordinate to check
2869 * @y: Y coordinate to check
2871 * Checks the scene at the coordinates @x and @y and returns a pointer
2872 * to the #ClutterActor at those coordinates.
2874 * By using @pick_mode it is possible to control which actors will be
2875 * painted and thus available.
2877 * Return value: (transfer none): the actor at the specified coordinates,
2881 clutter_stage_get_actor_at_pos (ClutterStage *stage,
2882 ClutterPickMode pick_mode,
2886 return _clutter_stage_do_pick (stage, x, y, pick_mode);
2890 * clutter_stage_event:
2891 * @stage: a #ClutterStage
2892 * @event: a #ClutterEvent
2894 * This function is used to emit an event on the main stage.
2896 * You should rarely need to use this function, except for
2897 * synthetised events.
2899 * Return value: the return value from the signal emission
2904 clutter_stage_event (ClutterStage *stage,
2905 ClutterEvent *event)
2907 ClutterStagePrivate *priv;
2909 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
2910 g_return_val_if_fail (event != NULL, FALSE);
2914 if (event->type == CLUTTER_DELETE)
2916 gboolean retval = FALSE;
2918 g_signal_emit_by_name (stage, "event", event, &retval);
2921 g_signal_emit_by_name (stage, "delete-event", event, &retval);
2926 if (event->type != CLUTTER_STAGE_STATE)
2929 /* emit raw event */
2930 if (clutter_actor_event (CLUTTER_ACTOR (stage), event, FALSE))
2933 if (event->stage_state.changed_mask & CLUTTER_STAGE_STATE_FULLSCREEN)
2935 if (event->stage_state.new_state & CLUTTER_STAGE_STATE_FULLSCREEN)
2937 priv->is_fullscreen = TRUE;
2938 g_signal_emit (stage, stage_signals[FULLSCREEN], 0);
2940 g_object_notify (G_OBJECT (stage), "fullscreen-set");
2944 priv->is_fullscreen = FALSE;
2945 g_signal_emit (stage, stage_signals[UNFULLSCREEN], 0);
2947 g_object_notify (G_OBJECT (stage), "fullscreen-set");
2951 if (event->stage_state.changed_mask & CLUTTER_STAGE_STATE_ACTIVATED)
2953 if (event->stage_state.new_state & CLUTTER_STAGE_STATE_ACTIVATED)
2954 g_signal_emit (stage, stage_signals[ACTIVATE], 0);
2956 g_signal_emit (stage, stage_signals[DEACTIVATE], 0);
2963 * clutter_stage_set_title:
2964 * @stage: A #ClutterStage
2965 * @title: A utf8 string for the stage windows title.
2967 * Sets the stage title.
2972 clutter_stage_set_title (ClutterStage *stage,
2975 ClutterStagePrivate *priv;
2976 ClutterStageWindow *impl;
2978 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2982 g_free (priv->title);
2983 priv->title = g_strdup (title);
2985 impl = CLUTTER_STAGE_WINDOW (priv->impl);
2986 if (CLUTTER_STAGE_WINDOW_GET_IFACE(impl)->set_title != NULL)
2987 CLUTTER_STAGE_WINDOW_GET_IFACE (impl)->set_title (impl, priv->title);
2989 g_object_notify (G_OBJECT (stage), "title");
2993 * clutter_stage_get_title:
2994 * @stage: A #ClutterStage
2996 * Gets the stage title.
2998 * Return value: pointer to the title string for the stage. The
2999 * returned string is owned by the actor and should not
3000 * be modified or freed.
3005 clutter_stage_get_title (ClutterStage *stage)
3007 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
3009 return stage->priv->title;
3013 on_key_focus_destroy (ClutterActor *actor,
3014 ClutterStage *stage)
3016 /* unset the key focus */
3017 clutter_stage_set_key_focus (stage, NULL);
3021 * clutter_stage_set_key_focus:
3022 * @stage: the #ClutterStage
3023 * @actor: (allow-none): the actor to set key focus to, or %NULL
3025 * Sets the key focus on @actor. An actor with key focus will receive
3026 * all the key events. If @actor is %NULL, the stage will receive
3032 clutter_stage_set_key_focus (ClutterStage *stage,
3033 ClutterActor *actor)
3035 ClutterStagePrivate *priv;
3037 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3038 g_return_if_fail (actor == NULL || CLUTTER_IS_ACTOR (actor));
3042 /* avoid emitting signals and notifications if we're setting the same
3043 * actor as the key focus
3045 if (priv->key_focused_actor == actor)
3048 if (priv->key_focused_actor != NULL)
3050 ClutterActor *old_focused_actor;
3052 old_focused_actor = priv->key_focused_actor;
3054 /* set key_focused_actor to NULL before emitting the signal or someone
3055 * might hide the previously focused actor in the signal handler and we'd
3056 * get re-entrant call and get glib critical from g_object_weak_unref
3058 g_signal_handlers_disconnect_by_func (priv->key_focused_actor,
3059 G_CALLBACK (on_key_focus_destroy),
3061 priv->key_focused_actor = NULL;
3063 g_signal_emit_by_name (old_focused_actor, "key-focus-out");
3066 g_signal_emit_by_name (stage, "key-focus-out");
3068 /* Note, if someone changes key focus in focus-out signal handler we'd be
3069 * overriding the latter call below moving the focus where it was originally
3070 * intended. The order of events would be:
3071 * 1st focus-out, 2nd focus-out (on stage), 2nd focus-in, 1st focus-in
3075 priv->key_focused_actor = actor;
3077 g_signal_connect (actor,
3078 "destroy", G_CALLBACK (on_key_focus_destroy),
3080 g_signal_emit_by_name (priv->key_focused_actor, "key-focus-in");
3083 g_signal_emit_by_name (stage, "key-focus-in");
3085 g_object_notify (G_OBJECT (stage), "key-focus");
3089 * clutter_stage_get_key_focus:
3090 * @stage: the #ClutterStage
3092 * Retrieves the actor that is currently under key focus.
3094 * Return value: (transfer none): the actor with key focus, or the stage
3099 clutter_stage_get_key_focus (ClutterStage *stage)
3101 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
3103 if (stage->priv->key_focused_actor)
3104 return stage->priv->key_focused_actor;
3106 return CLUTTER_ACTOR (stage);
3110 * clutter_stage_get_use_fog:
3111 * @stage: the #ClutterStage
3113 * Gets whether the depth cueing effect is enabled on @stage.
3115 * Return value: %TRUE if the depth cueing effect is enabled
3119 * Deprecated: 1.10: This function will always return %FALSE
3122 clutter_stage_get_use_fog (ClutterStage *stage)
3124 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
3126 return stage->priv->use_fog;
3130 * clutter_stage_set_use_fog:
3131 * @stage: the #ClutterStage
3132 * @fog: %TRUE for enabling the depth cueing effect
3134 * Sets whether the depth cueing effect on the stage should be enabled
3137 * Depth cueing is a 3D effect that makes actors farther away from the
3138 * viewing point less opaque, by fading them with the stage color.
3140 * The parameters of the GL fog used can be changed using the
3141 * clutter_stage_set_fog() function.
3145 * Deprecated: 1.10: Calling this function produces no visible effect
3148 clutter_stage_set_use_fog (ClutterStage *stage,
3154 * clutter_stage_set_fog:
3155 * @stage: the #ClutterStage
3156 * @fog: a #ClutterFog structure
3158 * Sets the fog (also known as "depth cueing") settings for the @stage.
3160 * A #ClutterStage will only use a linear fog progression, which
3161 * depends solely on the distance from the viewer. The cogl_set_fog()
3162 * function in COGL exposes more of the underlying implementation,
3163 * and allows changing the for progression function. It can be directly
3164 * used by disabling the #ClutterStage:use-fog property and connecting
3165 * a signal handler to the #ClutterActor::paint signal on the @stage,
3169 * clutter_stage_set_use_fog (stage, FALSE);
3170 * g_signal_connect (stage, "paint", G_CALLBACK (on_stage_paint), NULL);
3173 * The paint signal handler will call cogl_set_fog() with the
3178 * on_stage_paint (ClutterActor *actor)
3180 * ClutterColor stage_color = { 0, };
3181 * CoglColor fog_color = { 0, };
3183 * /* set the fog color to the stage background color */
3184 * clutter_stage_get_color (CLUTTER_STAGE (actor), &stage_color);
3185 * cogl_color_init_from_4ub (&fog_color,
3187 * stage_color.green,
3189 * stage_color.alpha);
3191 * /* enable fog */
3192 * cogl_set_fog (&fog_color,
3193 * COGL_FOG_MODE_EXPONENTIAL, /* mode */
3194 * 0.5, /* density */
3195 * 5.0, 30.0); /* z_near and z_far */
3199 * <note>The fogging functions only work correctly when the visible actors use
3200 * unmultiplied alpha colors. By default Cogl will premultiply textures and
3201 * cogl_set_source_color() will premultiply colors, so unless you explicitly
3202 * load your textures requesting an unmultiplied internal format and use
3203 * cogl_material_set_color() you can only use fogging with fully opaque actors.
3204 * Support for premultiplied colors will improve in the future when we can
3205 * depend on fragment shaders.</note>
3209 * Deprecated: 1.10: Fog settings are ignored.
3212 clutter_stage_set_fog (ClutterStage *stage,
3218 * clutter_stage_get_fog:
3219 * @stage: the #ClutterStage
3220 * @fog: (out): return location for a #ClutterFog structure
3222 * Retrieves the current depth cueing settings from the stage.
3226 * Deprecated: 1.10: This function will always return the default
3227 * values of #ClutterFog
3230 clutter_stage_get_fog (ClutterStage *stage,
3233 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3234 g_return_if_fail (fog != NULL);
3236 *fog = stage->priv->fog;
3239 /*** Perspective boxed type ******/
3242 clutter_perspective_copy (gpointer data)
3244 if (G_LIKELY (data))
3245 return g_slice_dup (ClutterPerspective, data);
3251 clutter_perspective_free (gpointer data)
3253 if (G_LIKELY (data))
3254 g_slice_free (ClutterPerspective, data);
3257 G_DEFINE_BOXED_TYPE (ClutterPerspective, clutter_perspective,
3258 clutter_perspective_copy,
3259 clutter_perspective_free);
3262 clutter_fog_copy (gpointer data)
3264 if (G_LIKELY (data))
3265 return g_slice_dup (ClutterFog, data);
3271 clutter_fog_free (gpointer data)
3273 if (G_LIKELY (data))
3274 g_slice_free (ClutterFog, data);
3277 G_DEFINE_BOXED_TYPE (ClutterFog, clutter_fog, clutter_fog_copy, clutter_fog_free);
3280 * clutter_stage_new:
3282 * Creates a new, non-default stage. A non-default stage is a new
3283 * top-level actor which can be used as another container. It works
3284 * exactly like the default stage, but while clutter_stage_get_default()
3285 * will always return the same instance, you will have to keep a pointer
3286 * to any #ClutterStage returned by clutter_stage_new().
3288 * The ability to support multiple stages depends on the current
3289 * backend. Use clutter_feature_available() and
3290 * %CLUTTER_FEATURE_STAGE_MULTIPLE to check at runtime whether a
3291 * backend supports multiple stages.
3293 * Return value: a new stage, or %NULL if the default backend does
3294 * not support multiple stages. Use clutter_actor_destroy() to
3295 * programmatically close the returned stage.
3300 clutter_stage_new (void)
3302 return g_object_new (CLUTTER_TYPE_STAGE, NULL);
3306 * clutter_stage_ensure_current:
3307 * @stage: the #ClutterStage
3309 * This function essentially makes sure the right GL context is
3310 * current for the passed stage. It is not intended to
3311 * be used by applications.
3316 clutter_stage_ensure_current (ClutterStage *stage)
3318 ClutterBackend *backend;
3320 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3322 backend = clutter_get_default_backend ();
3323 _clutter_backend_ensure_context (backend, stage);
3327 * clutter_stage_ensure_viewport:
3328 * @stage: a #ClutterStage
3330 * Ensures that the GL viewport is updated with the current
3331 * stage window size.
3333 * This function will queue a redraw of @stage.
3335 * This function should not be called by applications; it is used
3336 * when embedding a #ClutterStage into a toolkit with another
3337 * windowing system, like GTK+.
3342 clutter_stage_ensure_viewport (ClutterStage *stage)
3344 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3346 _clutter_stage_dirty_viewport (stage);
3348 clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
3351 /* This calculates a distance into the view frustum to position the
3352 * stage so there is a decent amount of space to position geometry
3353 * between the stage and the near clipping plane.
3355 * Some awkward issues with this problem are:
3356 * - It's not possible to have a gap as large as the stage size with
3357 * a fov > 53° which is basically always the case since the default
3359 * - This can be deduced if you consider that this requires a
3360 * triangle as wide as it is deep to fit in the frustum in front
3361 * of the z_near plane. That triangle will always have an angle
3362 * of 53.13° at the point sitting on the z_near plane, but if the
3363 * frustum has a wider fov angle the left/right clipping planes
3364 * can never converge with the two corners of our triangle no
3365 * matter what size the triangle has.
3366 * - With a fov > 53° there is a trade off between maximizing the gap
3367 * size relative to the stage size but not loosing depth precision.
3368 * - Perhaps ideally we wouldn't just consider the fov on the y-axis
3369 * that is usually used to define a perspective, we would consider
3370 * the fov of the axis with the largest stage size so the gap would
3371 * accommodate that size best.
3373 * After going around in circles a few times with how to handle these
3374 * issues, we decided in the end to go for the simplest solution to
3375 * start with instead of an elaborate function that handles arbitrary
3376 * fov angles that we currently have no use-case for.
3378 * The solution assumes a fovy of 60° and for that case gives a gap
3379 * that's 85% of the stage height. We can consider more elaborate
3380 * functions if necessary later.
3382 * One guide we had to steer the gap size we support is the
3383 * interactive test, test-texture-quality which expects to animate an
3384 * actor to +400 on the z axis with a stage size of 640x480. A gap
3385 * that's 85% of the stage height gives a gap of 408 in that case.
3388 calculate_z_translation (float z_near)
3390 /* This solution uses fairly basic trigonometry, but is seems worth
3391 * clarifying the particular geometry we are looking at in-case
3392 * anyone wants to develop this further later. Not sure how well an
3393 * ascii diagram is going to work :-)
3395 * |--- stage_height ---|
3397 * ╲━━━━━━━━━━━━━━━━━━━━━╱------------
3399 * C ╲ . │ . ╱ gap| |
3400 * =0.5°╲ . a │ . ╱ | |
3402 * ╲ B.│. ╱near plane | |
3403 * A= ╲━━━━━━━━━╱------------- |
3404 * 120° ╲ c │ ╱ | z_2d
3408 * plane ╳----------------------
3413 * The area of interest is the triangle labeled (1) at the top left
3414 * marked with the ... line (a) from where the origin line crosses
3415 * the near plane to the top left where the stage line cross the
3418 * The sides of the triangle are a, b and c and the corresponding
3419 * angles opposite those sides are A, B and C.
3421 * The angle of C is what trades off the gap size we have relative
3422 * to the stage size vs the depth precision we have.
3424 * As mentioned above we arove at the angle for C is by working
3425 * backwards from how much space we want for test-texture-quality.
3426 * With a stage_height of 480 we want a gap > 400, ideally we also
3427 * wanted a somewhat round number as a percentage of the height for
3428 * documentation purposes. ~87% or a gap of ~416 is the limit
3429 * because that's where we approach a C angle of 0° and effectively
3430 * loose all depth precision.
3432 * So for our test app with a stage_height of 480 if we aim for a
3433 * gap of 408 (85% of 480) we can get the angle D as
3434 * atan (stage_height/2/408) = 30.5°.
3436 * That gives us the angle for B as 90° - 30.5° = 59.5°
3438 * We can already determine that A has an angle of (fovy/2 + 90°) =
3441 * Therefore C = 180 - A - B = 0.5°
3443 * The length of c = z_near * tan (30°)
3445 * Now we can use the rule a/SinA = c/SinC to calculate the
3446 * length of a. After some rearranging that gives us:
3449 * ---------- = ----------
3450 * sin (120°) sin (0.5°)
3453 * a = --------------
3456 * And with that we can determine z_2d = cos (D) * a =
3457 * cos (30.5°) * a + z_near:
3459 * c * sin (120°) * cos (30.5°)
3460 * z_2d = --------------------------- + z_near
3463 #define _DEG_TO_RAD (G_PI / 180.0)
3464 return z_near * tanf (30.0f * _DEG_TO_RAD) *
3465 sinf (120.0f * _DEG_TO_RAD) * cosf (30.5f * _DEG_TO_RAD) /
3466 sinf (0.5f * _DEG_TO_RAD) +
3469 /* We expect the compiler should boil this down to z_near * CONSTANT */
3473 _clutter_stage_maybe_setup_viewport (ClutterStage *stage)
3475 ClutterStagePrivate *priv = stage->priv;
3477 if (priv->dirty_viewport)
3479 ClutterPerspective perspective;
3482 CLUTTER_NOTE (PAINT,
3483 "Setting up the viewport { w:%f, h:%f }",
3484 priv->viewport[2], priv->viewport[3]);
3486 cogl_set_viewport (priv->viewport[0],
3491 perspective = priv->perspective;
3493 /* Ideally we want to regenerate the perspective matrix whenever
3494 * the size changes but if the user has provided a custom matrix
3495 * then we don't want to override it */
3496 if (!priv->has_custom_perspective)
3498 perspective.aspect = priv->viewport[2] / priv->viewport[3];
3499 z_2d = calculate_z_translation (perspective.z_near);
3501 #define _DEG_TO_RAD (G_PI / 180.0)
3502 /* NB: z_2d is only enough room for 85% of the stage_height between
3503 * the stage and the z_near plane. For behind the stage plane we
3504 * want a more consistent gap of 10 times the stage_height before
3505 * hitting the far plane so we calculate that relative to the final
3506 * height of the stage plane at the z_2d_distance we got... */
3507 perspective.z_far = z_2d +
3508 tanf ((perspective.fovy / 2.0f) * _DEG_TO_RAD) * z_2d * 20.0f;
3511 clutter_stage_set_perspective_internal (stage, &perspective);
3514 z_2d = calculate_z_translation (perspective.z_near);
3516 cogl_matrix_init_identity (&priv->view);
3517 cogl_matrix_view_2d_in_perspective (&priv->view,
3525 priv->dirty_viewport = FALSE;
3528 if (priv->dirty_projection)
3530 cogl_set_projection_matrix (&priv->projection);
3532 priv->dirty_projection = FALSE;
3537 * clutter_stage_ensure_redraw:
3538 * @stage: a #ClutterStage
3540 * Ensures that @stage is redrawn
3542 * This function should not be called by applications: it is
3543 * used when embedding a #ClutterStage into a toolkit with
3544 * another windowing system, like GTK+.
3549 clutter_stage_ensure_redraw (ClutterStage *stage)
3551 ClutterMasterClock *master_clock;
3552 ClutterStagePrivate *priv;
3554 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3557 priv->relayout_pending = TRUE;
3558 priv->redraw_pending = TRUE;
3560 master_clock = _clutter_master_clock_get_default ();
3561 _clutter_master_clock_start_running (master_clock);
3565 * clutter_stage_queue_redraw:
3566 * @stage: the #ClutterStage
3568 * Queues a redraw for the passed stage.
3570 * <note>Applications should call clutter_actor_queue_redraw() and not
3571 * this function.</note>
3575 * Deprecated: 1.10: Use clutter_actor_queue_redraw() instead.
3578 clutter_stage_queue_redraw (ClutterStage *stage)
3580 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3582 clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
3586 * clutter_stage_is_default:
3587 * @stage: a #ClutterStage
3589 * Checks if @stage is the default stage, or an instance created using
3590 * clutter_stage_new() but internally using the same implementation.
3592 * Return value: %TRUE if the passed stage is the default one
3596 * Deprecated: 1.10: Track the stage pointer inside your application
3597 * code, or use clutter_actor_get_stage() to retrieve the stage for
3601 clutter_stage_is_default (ClutterStage *stage)
3603 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
3605 return stage_is_default (stage);
3609 _clutter_stage_set_window (ClutterStage *stage,
3610 ClutterStageWindow *stage_window)
3612 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3613 g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (stage_window));
3615 if (stage->priv->impl != NULL)
3616 g_object_unref (stage->priv->impl);
3618 stage->priv->impl = stage_window;
3621 ClutterStageWindow *
3622 _clutter_stage_get_window (ClutterStage *stage)
3624 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
3626 return CLUTTER_STAGE_WINDOW (stage->priv->impl);
3629 ClutterStageWindow *
3630 _clutter_stage_get_default_window (void)
3632 ClutterStageManager *manager = clutter_stage_manager_get_default ();
3633 ClutterStage *stage;
3635 stage = clutter_stage_manager_get_default_stage (manager);
3639 return _clutter_stage_get_window (stage);
3643 * clutter_stage_set_throttle_motion_events:
3644 * @stage: a #ClutterStage
3645 * @throttle: %TRUE to throttle motion events
3647 * Sets whether motion events received between redraws should
3648 * be throttled or not. If motion events are throttled, those
3649 * events received by the windowing system between redraws will
3650 * be compressed so that only the last event will be propagated
3651 * to the @stage and its actors.
3653 * This function should only be used if you want to have all
3654 * the motion events delivered to your application code.
3659 clutter_stage_set_throttle_motion_events (ClutterStage *stage,
3662 ClutterStagePrivate *priv;
3664 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3668 if (priv->throttle_motion_events != throttle)
3669 priv->throttle_motion_events = throttle;
3673 * clutter_stage_get_throttle_motion_events:
3674 * @stage: a #ClutterStage
3676 * Retrieves the value set with clutter_stage_set_throttle_motion_events()
3678 * Return value: %TRUE if the motion events are being throttled,
3679 * and %FALSE otherwise
3684 clutter_stage_get_throttle_motion_events (ClutterStage *stage)
3686 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
3688 return stage->priv->throttle_motion_events;
3692 * clutter_stage_set_use_alpha:
3693 * @stage: a #ClutterStage
3694 * @use_alpha: whether the stage should honour the opacity or the
3695 * alpha channel of the stage color
3697 * Sets whether the @stage should honour the #ClutterActor:opacity and
3698 * the alpha channel of the #ClutterStage:color
3703 clutter_stage_set_use_alpha (ClutterStage *stage,
3706 ClutterStagePrivate *priv;
3708 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3712 if (priv->use_alpha != use_alpha)
3714 priv->use_alpha = use_alpha;
3716 clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
3718 g_object_notify (G_OBJECT (stage), "use-alpha");
3723 * clutter_stage_get_use_alpha:
3724 * @stage: a #ClutterStage
3726 * Retrieves the value set using clutter_stage_set_use_alpha()
3728 * Return value: %TRUE if the stage should honour the opacity and the
3729 * alpha channel of the stage color
3734 clutter_stage_get_use_alpha (ClutterStage *stage)
3736 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
3738 return stage->priv->use_alpha;
3742 * clutter_stage_set_minimum_size:
3743 * @stage: a #ClutterStage
3744 * @width: width, in pixels
3745 * @height: height, in pixels
3747 * Sets the minimum size for a stage window, if the default backend
3748 * uses #ClutterStage inside a window
3750 * This is a convenience function, and it is equivalent to setting the
3751 * #ClutterActor:min-width and #ClutterActor:min-height on @stage
3753 * If the current size of @stage is smaller than the minimum size, the
3754 * @stage will be resized to the new @width and @height
3756 * This function has no effect if @stage is fullscreen
3761 clutter_stage_set_minimum_size (ClutterStage *stage,
3765 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3766 g_return_if_fail ((width > 0) && (height > 0));
3768 g_object_set (G_OBJECT (stage),
3769 "min-width", (gfloat) width,
3770 "min-height", (gfloat )height,
3775 * clutter_stage_get_minimum_size:
3776 * @stage: a #ClutterStage
3777 * @width: (out): return location for the minimum width, in pixels,
3779 * @height: (out): return location for the minimum height, in pixels,
3782 * Retrieves the minimum size for a stage window as set using
3783 * clutter_stage_set_minimum_size().
3785 * The returned size may not correspond to the actual minimum size and
3786 * it is specific to the #ClutterStage implementation inside the
3792 clutter_stage_get_minimum_size (ClutterStage *stage,
3796 gfloat width, height;
3797 gboolean width_set, height_set;
3799 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3801 g_object_get (G_OBJECT (stage),
3802 "min-width", &width,
3803 "min-width-set", &width_set,
3804 "min-height", &height,
3805 "min-height-set", &height_set,
3808 /* if not width or height have been set, then the Stage
3809 * minimum size is defined to be 1x1
3818 *width_p = (guint) width;
3821 *height_p = (guint) height;
3824 /* Returns the number of swap buffers pending completion for the stage */
3826 _clutter_stage_get_pending_swaps (ClutterStage *stage)
3828 ClutterStageWindow *stage_window;
3830 if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
3833 stage_window = _clutter_stage_get_window (stage);
3834 if (stage_window == NULL)
3837 return _clutter_stage_window_get_pending_swaps (stage_window);
3841 * clutter_stage_set_no_clear_hint:
3842 * @stage: a #ClutterStage
3843 * @no_clear: %TRUE if the @stage should not clear itself on every
3846 * Sets whether the @stage should clear itself at the beginning
3847 * of each paint cycle or not.
3849 * Clearing the #ClutterStage can be a costly operation, especially
3850 * if the stage is always covered - for instance, in a full-screen
3851 * video player or in a game with a background texture.
3853 * <note><para>This setting is a hint; Clutter might discard this
3854 * hint depending on its internal state.</para></note>
3856 * <warning><para>If parts of the stage are visible and you disable
3857 * clearing you might end up with visual artifacts while painting the
3858 * contents of the stage.</para></warning>
3863 clutter_stage_set_no_clear_hint (ClutterStage *stage,
3866 ClutterStagePrivate *priv;
3867 ClutterStageHint new_hints;
3869 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3872 new_hints = priv->stage_hints;
3875 new_hints |= CLUTTER_STAGE_NO_CLEAR_ON_PAINT;
3877 new_hints &= ~CLUTTER_STAGE_NO_CLEAR_ON_PAINT;
3879 if (priv->stage_hints == new_hints)
3882 priv->stage_hints = new_hints;
3884 g_object_notify (G_OBJECT (stage), "no-clear-hint");
3888 * clutter_stage_get_no_clear_hint:
3889 * @stage: a #ClutterStage
3891 * Retrieves the hint set with clutter_stage_set_no_clear_hint()
3893 * Return value: %TRUE if the stage should not clear itself on every paint
3894 * cycle, and %FALSE otherwise
3899 clutter_stage_get_no_clear_hint (ClutterStage *stage)
3901 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
3903 return (stage->priv->stage_hints & CLUTTER_STAGE_NO_CLEAR_ON_PAINT) != 0;
3906 ClutterPaintVolume *
3907 _clutter_stage_paint_volume_stack_allocate (ClutterStage *stage)
3909 GArray *paint_volume_stack = stage->priv->paint_volume_stack;
3911 g_array_set_size (paint_volume_stack,
3912 paint_volume_stack->len+1);
3914 return &g_array_index (paint_volume_stack,
3916 paint_volume_stack->len - 1);
3920 _clutter_stage_paint_volume_stack_free_all (ClutterStage *stage)
3922 GArray *paint_volume_stack = stage->priv->paint_volume_stack;
3925 for (i = 0; i < paint_volume_stack->len; i++)
3927 ClutterPaintVolume *pv =
3928 &g_array_index (paint_volume_stack, ClutterPaintVolume, i);
3929 clutter_paint_volume_free (pv);
3932 g_array_set_size (paint_volume_stack, 0);
3935 /* The is an out-of-band paramater available while painting that
3936 * can be used to cull actors. */
3937 const ClutterPlane *
3938 _clutter_stage_get_clip (ClutterStage *stage)
3940 return stage->priv->current_clip_planes;
3943 /* When an actor queues a redraw we add it to a list on the stage that
3944 * gets processed once all updates to the stage have been finished.
3946 * This deferred approach to processing queue_redraw requests means
3947 * that we can avoid redundant transformations of clip volumes if
3948 * something later triggers a full stage redraw anyway. It also means
3949 * we can be more sure that all the referenced actors will have valid
3950 * allocations improving the chance that we can determine the actors
3951 * paint volume so we can clip the redraw request even if the user
3952 * didn't explicitly do so.
3954 ClutterStageQueueRedrawEntry *
3955 _clutter_stage_queue_actor_redraw (ClutterStage *stage,
3956 ClutterStageQueueRedrawEntry *entry,
3957 ClutterActor *actor,
3958 ClutterPaintVolume *clip)
3960 ClutterStagePrivate *priv = stage->priv;
3962 CLUTTER_NOTE (CLIPPING, "stage_queue_actor_redraw (actor=%s, clip=%p): ",
3963 _clutter_actor_get_debug_name (actor), clip);
3965 if (!priv->redraw_pending)
3967 ClutterMasterClock *master_clock;
3969 CLUTTER_NOTE (PAINT, "First redraw request");
3971 priv->redraw_pending = TRUE;
3973 master_clock = _clutter_master_clock_get_default ();
3974 _clutter_master_clock_start_running (master_clock);
3976 #ifdef CLUTTER_ENABLE_DEBUG
3979 CLUTTER_NOTE (PAINT, "Redraw request number %lu",
3980 priv->redraw_count + 1);
3982 priv->redraw_count += 1;
3984 #endif /* CLUTTER_ENABLE_DEBUG */
3986 /* We have an optimization in _clutter_stage_do_pick to detect when
3987 * the scene is static so we can cache a full, un-clipped pick
3988 * buffer to avoid continuous pick renders.
3990 * Currently the assumption is that actors queue a redraw when some
3991 * state changes that affects painting *or* picking so we can use
3992 * this point to invalidate any currently cached pick buffer.
3994 _clutter_stage_set_pick_buffer_valid (stage, FALSE, -1);
3998 /* Ignore all requests to queue a redraw for an actor if a full
3999 * (non-clipped) redraw of the actor has already been queued. */
4000 if (!entry->has_clip)
4002 CLUTTER_NOTE (CLIPPING, "Bail from stage_queue_actor_redraw (%s): "
4003 "Unclipped redraw of actor already queued",
4004 _clutter_actor_get_debug_name (actor));
4008 /* If queuing a clipped redraw and a clipped redraw has
4009 * previously been queued for this actor then combine the latest
4010 * clip together with the existing clip */
4012 clutter_paint_volume_union (&entry->clip, clip);
4015 clutter_paint_volume_free (&entry->clip);
4016 entry->has_clip = FALSE;
4022 entry = g_slice_new (ClutterStageQueueRedrawEntry);
4023 entry->actor = g_object_ref (actor);
4027 entry->has_clip = TRUE;
4028 _clutter_paint_volume_init_static (&entry->clip, actor);
4029 _clutter_paint_volume_set_from_volume (&entry->clip, clip);
4032 entry->has_clip = FALSE;
4034 stage->priv->pending_queue_redraws =
4035 g_list_prepend (stage->priv->pending_queue_redraws, entry);
4042 free_queue_redraw_entry (ClutterStageQueueRedrawEntry *entry)
4045 g_object_unref (entry->actor);
4046 if (entry->has_clip)
4047 clutter_paint_volume_free (&entry->clip);
4048 g_slice_free (ClutterStageQueueRedrawEntry, entry);
4052 _clutter_stage_queue_redraw_entry_invalidate (ClutterStageQueueRedrawEntry *entry)
4057 if (entry->actor != NULL)
4059 g_object_unref (entry->actor);
4060 entry->actor = NULL;
4063 if (entry->has_clip)
4065 clutter_paint_volume_free (&entry->clip);
4066 entry->has_clip = FALSE;
4071 _clutter_stage_maybe_finish_queue_redraws (ClutterStage *stage)
4073 /* Note: we have to repeat until the pending_queue_redraws list is
4074 * empty because actors are allowed to queue redraws in response to
4075 * the queue-redraw signal. For example Clone actors or
4076 * texture_new_from_actor actors will have to queue a redraw if
4077 * their source queues a redraw.
4079 while (stage->priv->pending_queue_redraws)
4082 /* XXX: we need to allow stage->priv->pending_queue_redraws to
4083 * be updated while we process the current entries in the list
4084 * so we steal the list pointer and then reset it to an empty
4085 * list before processing... */
4086 GList *stolen_list = stage->priv->pending_queue_redraws;
4087 stage->priv->pending_queue_redraws = NULL;
4089 for (l = stolen_list; l; l = l->next)
4091 ClutterStageQueueRedrawEntry *entry = l->data;
4092 ClutterPaintVolume *clip;
4094 /* NB: Entries may be invalidated if the actor gets destroyed */
4095 if (G_LIKELY (entry->actor != NULL))
4097 clip = entry->has_clip ? &entry->clip : NULL;
4099 _clutter_actor_finish_queue_redraw (entry->actor, clip);
4102 free_queue_redraw_entry (entry);
4104 g_list_free (stolen_list);
4109 * clutter_stage_set_accept_focus:
4110 * @stage: a #ClutterStage
4111 * @accept_focus: %TRUE to accept focus on show
4113 * Sets whether the @stage should accept the key focus when shown.
4115 * This function should be called before showing @stage using
4116 * clutter_actor_show().
4121 clutter_stage_set_accept_focus (ClutterStage *stage,
4122 gboolean accept_focus)
4124 ClutterStagePrivate *priv;
4126 g_return_if_fail (CLUTTER_IS_STAGE (stage));
4128 accept_focus = !!accept_focus;
4132 if (priv->accept_focus != accept_focus)
4134 _clutter_stage_window_set_accept_focus (priv->impl, accept_focus);
4135 g_object_notify (G_OBJECT (stage), "accept-focus");
4140 * clutter_stage_get_accept_focus:
4141 * @stage: a #ClutterStage
4143 * Retrieves the value set with clutter_stage_set_accept_focus().
4145 * Return value: %TRUE if the #ClutterStage should accept focus, and %FALSE
4151 clutter_stage_get_accept_focus (ClutterStage *stage)
4153 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), TRUE);
4155 return stage->priv->accept_focus;
4159 _clutter_stage_add_device (ClutterStage *stage,
4160 ClutterInputDevice *device)
4162 ClutterStagePrivate *priv = stage->priv;
4164 if (g_hash_table_lookup (priv->devices, device) != NULL)
4167 g_hash_table_insert (priv->devices, device, GINT_TO_POINTER (1));
4168 _clutter_input_device_set_stage (device, stage);
4172 _clutter_stage_remove_device (ClutterStage *stage,
4173 ClutterInputDevice *device)
4175 ClutterStagePrivate *priv = stage->priv;
4177 _clutter_input_device_set_stage (device, NULL);
4178 g_hash_table_remove (priv->devices, device);
4182 _clutter_stage_has_device (ClutterStage *stage,
4183 ClutterInputDevice *device)
4185 ClutterStagePrivate *priv = stage->priv;
4187 return g_hash_table_lookup (priv->devices, device) != NULL;
4191 * clutter_stage_set_motion_events_enabled:
4192 * @stage: a #ClutterStage
4193 * @enabled: %TRUE to enable the motion events delivery, and %FALSE
4196 * Sets whether per-actor motion events (and relative crossing
4197 * events) should be disabled or not.
4199 * The default is %TRUE.
4201 * If @enable is %FALSE the following events will not be delivered
4202 * to the actors children of @stage.
4205 * <listitem><para>#ClutterActor::motion-event</para></listitem>
4206 * <listitem><para>#ClutterActor::enter-event</para></listitem>
4207 * <listitem><para>#ClutterActor::leave-event</para></listitem>
4210 * The events will still be delivered to the #ClutterStage.
4212 * The main side effect of this function is that disabling the motion
4213 * events will disable picking to detect the #ClutterActor underneath
4214 * the pointer for each motion event. This is useful, for instance,
4215 * when dragging a #ClutterActor across the @stage: the actor underneath
4216 * the pointer is not going to change, so it's meaningless to perform
4222 clutter_stage_set_motion_events_enabled (ClutterStage *stage,
4225 ClutterStagePrivate *priv;
4227 g_return_if_fail (CLUTTER_IS_STAGE (stage));
4231 enabled = !!enabled;
4233 if (priv->motion_events_enabled != enabled)
4234 priv->motion_events_enabled = enabled;
4238 * clutter_stage_get_motion_events_enabled:
4239 * @stage: a #ClutterStage
4241 * Retrieves the value set using clutter_stage_set_motion_events_enabled().
4243 * Return value: %TRUE if the per-actor motion event delivery is enabled
4244 * and %FALSE otherwise
4249 clutter_stage_get_motion_events_enabled (ClutterStage *stage)
4251 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
4253 return stage->priv->motion_events_enabled;
4256 /* NB: The presumption shouldn't be that a stage can't be comprised
4257 * of multiple internal framebuffers, so instead of simply naming
4258 * this function _clutter_stage_get_framebuffer(), the "active"
4259 * infix is intended to clarify that it gets the framebuffer that
4260 * is currently in use/being painted.
4263 _clutter_stage_get_active_framebuffer (ClutterStage *stage)
4265 return stage->priv->active_framebuffer;
4269 _clutter_stage_acquire_pick_id (ClutterStage *stage,
4270 ClutterActor *actor)
4272 ClutterStagePrivate *priv = stage->priv;
4274 g_assert (priv->pick_id_pool != NULL);
4276 return _clutter_id_pool_add (priv->pick_id_pool, actor);
4280 _clutter_stage_release_pick_id (ClutterStage *stage,
4283 ClutterStagePrivate *priv = stage->priv;
4285 g_assert (priv->pick_id_pool != NULL);
4287 _clutter_id_pool_remove (priv->pick_id_pool, pick_id);
4291 _clutter_stage_get_actor_by_pick_id (ClutterStage *stage,
4294 ClutterStagePrivate *priv = stage->priv;
4296 g_assert (priv->pick_id_pool != NULL);
4298 return _clutter_id_pool_lookup (priv->pick_id_pool, pick_id);
4302 _clutter_stage_add_drag_actor (ClutterStage *stage,
4303 ClutterInputDevice *device,
4304 ClutterActor *actor)
4306 GHashTable *drag_actors;
4308 drag_actors = g_object_get_data (G_OBJECT (stage), "__clutter_stage_drag_actors");
4309 if (drag_actors == NULL)
4311 drag_actors = g_hash_table_new (NULL, NULL);
4312 g_object_set_data_full (G_OBJECT (stage), "__clutter_stage_drag_actors",
4314 (GDestroyNotify) g_hash_table_destroy);
4317 g_hash_table_replace (drag_actors, device, actor);
4321 _clutter_stage_get_drag_actor (ClutterStage *stage,
4322 ClutterInputDevice *device)
4324 GHashTable *drag_actors;
4326 drag_actors = g_object_get_data (G_OBJECT (stage), "__clutter_stage_drag_actors");
4327 if (drag_actors == NULL)
4330 return g_hash_table_lookup (drag_actors, device);
4334 _clutter_stage_remove_drag_actor (ClutterStage *stage,
4335 ClutterInputDevice *device)
4337 GHashTable *drag_actors;
4339 drag_actors = g_object_get_data (G_OBJECT (stage), "__clutter_stage_drag_actors");
4340 if (drag_actors == NULL)
4343 g_hash_table_remove (drag_actors, device);
4345 if (g_hash_table_size (drag_actors) == 0)
4346 g_object_set_data (G_OBJECT (stage), "__clutter_stage_drag_actors", NULL);
4350 * _clutter_stage_get_state:
4351 * @stage: a #ClutterStage
4353 * Retrieves the current #ClutterStageState flags associated to the @stage.
4355 * Return value: a bitwise OR of #ClutterStageState flags
4358 _clutter_stage_get_state (ClutterStage *stage)
4360 return stage->priv->current_state;
4364 * _clutter_stage_is_activated:
4365 * @stage: a #ClutterStage
4367 * Checks whether the @stage state includes %CLUTTER_STAGE_STATE_ACTIVATED.
4369 * Return value: %TRUE if the @stage is active
4372 _clutter_stage_is_activated (ClutterStage *stage)
4374 return (stage->priv->current_state & CLUTTER_STAGE_STATE_ACTIVATED) != 0;
4378 * _clutter_stage_is_fullscreen:
4379 * @stage: a #ClutterStage
4381 * Checks whether the @stage state includes %CLUTTER_STAGE_STATE_FULLSCREEN.
4383 * Return value: %TRUE if the @stage is fullscreen
4386 _clutter_stage_is_fullscreen (ClutterStage *stage)
4388 return (stage->priv->current_state & CLUTTER_STAGE_STATE_FULLSCREEN) != 0;
4392 * _clutter_stage_update_state:
4393 * @stage: a #ClutterStage
4394 * @unset_flags: flags to unset
4395 * @set_flags: flags to set
4397 * Updates the state of @stage, by unsetting the @unset_flags and setting
4400 * If the stage state has been changed, this function will queue a
4401 * #ClutterEvent of type %CLUTTER_STAGE_STATE.
4403 * Return value: %TRUE if the state was updated, and %FALSE otherwise
4406 _clutter_stage_update_state (ClutterStage *stage,
4407 ClutterStageState unset_flags,
4408 ClutterStageState set_flags)
4410 ClutterStageState new_state;
4411 ClutterEvent *event;
4413 new_state = stage->priv->current_state;
4414 new_state |= set_flags;
4415 new_state &= ~unset_flags;
4417 if (new_state == stage->priv->current_state)
4420 event = clutter_event_new (CLUTTER_STAGE_STATE);
4421 clutter_event_set_stage (event, stage);
4423 event->stage_state.new_state = new_state;
4424 event->stage_state.changed_mask = new_state ^ stage->priv->current_state;
4426 stage->priv->current_state = new_state;
4428 _clutter_event_push (event, FALSE);