4 * An OpenGL based 'interactive canvas' library.
6 * Authored By Matthew Allum <mallum@openedhand.com>
8 * Copyright (C) 2006 OpenedHand
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
27 * SECTION:clutter-stage
28 * @short_description: Top level visual element to which actors are placed.
30 * #ClutterStage is a top level 'window' on which child actors are placed
33 * Backends might provide support for multiple stages. The support for this
34 * feature can be checked at run-time using the clutter_feature_available()
35 * function and the %CLUTTER_FEATURE_STAGE_MULTIPLE flag. If the backend used
36 * supports multiple stages, new #ClutterStage instances can be created
37 * using clutter_stage_new(). These stages must be managed by the developer
38 * using clutter_actor_destroy(), which will take care of destroying all the
39 * actors contained inside them.
41 * #ClutterStage is a proxy actor, wrapping the backend-specific
42 * implementation of the windowing system. It is possible to subclass
43 * #ClutterStage, as long as every overridden virtual function chains up to
44 * the parent class corresponding function.
54 #define CLUTTER_DISABLE_DEPRECATION_WARNINGS
56 #include "clutter-stage.h"
57 #include "deprecated/clutter-stage.h"
58 #include "deprecated/clutter-container.h"
60 #include "clutter-actor-private.h"
61 #include "clutter-backend-private.h"
62 #include "clutter-cairo-texture.h"
63 #include "clutter-color.h"
64 #include "clutter-container.h"
65 #include "clutter-debug.h"
66 #include "clutter-device-manager-private.h"
67 #include "clutter-enum-types.h"
68 #include "clutter-event-private.h"
69 #include "clutter-id-pool.h"
70 #include "clutter-main.h"
71 #include "clutter-marshal.h"
72 #include "clutter-master-clock.h"
73 #include "clutter-paint-volume-private.h"
74 #include "clutter-private.h"
75 #include "clutter-profile.h"
76 #include "clutter-stage-manager-private.h"
77 #include "clutter-stage-private.h"
78 #include "clutter-util.h"
79 #include "clutter-version.h" /* For flavour */
80 #include "clutter-private.h"
82 #include "cogl/cogl.h"
84 static void clutter_container_iface_init (ClutterContainerIface *iface);
86 G_DEFINE_TYPE_WITH_CODE (ClutterStage, clutter_stage, CLUTTER_TYPE_GROUP,
87 G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
88 clutter_container_iface_init))
90 #define CLUTTER_STAGE_GET_PRIVATE(obj) \
91 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_STAGE, ClutterStagePrivate))
95 * @CLUTTER_STAGE_NONE: No hint set
96 * @CLUTTER_STAGE_NO_CLEAR_ON_PAINT: When this hint is set, the stage
97 * should not clear the viewport; this flag is useful when painting
98 * fully opaque actors covering the whole visible area of the stage,
99 * i.e. when no blending with the stage color happens over the whole
102 * A series of hints that enable or disable behaviours on the stage
104 typedef enum { /*< prefix=CLUTTER_STAGE >*/
105 CLUTTER_STAGE_HINT_NONE = 0,
107 CLUTTER_STAGE_NO_CLEAR_ON_PAINT = 1 << 0
110 #define STAGE_NO_CLEAR_ON_PAINT(s) ((((ClutterStage *) (s))->priv->stage_hints & CLUTTER_STAGE_NO_CLEAR_ON_PAINT) != 0)
112 struct _ClutterStageQueueRedrawEntry
116 ClutterPaintVolume clip;
119 struct _ClutterStagePrivate
121 /* the stage implementation */
122 ClutterStageWindow *impl;
124 ClutterPerspective perspective;
125 CoglMatrix projection;
126 CoglMatrix inverse_projection;
133 ClutterActor *key_focused_actor;
137 ClutterStageHint stage_hints;
139 gint picks_per_frame;
141 GArray *paint_volume_stack;
143 ClutterPlane current_clip_planes[4];
145 GList *pending_queue_redraws;
147 ClutterPickMode pick_buffer_mode;
149 CoglFramebuffer *active_framebuffer;
154 gint32 timer_n_frames;
156 ClutterIDPool *pick_id_pool;
158 #ifdef CLUTTER_ENABLE_DEBUG
160 #endif /* CLUTTER_ENABLE_DEBUG */
162 ClutterStageState current_state;
164 guint relayout_pending : 1;
165 guint redraw_pending : 1;
166 guint is_fullscreen : 1;
167 guint is_cursor_visible : 1;
168 guint is_user_resizable : 1;
170 guint throttle_motion_events : 1;
172 guint min_size_changed : 1;
173 guint dirty_viewport : 1;
174 guint dirty_projection : 1;
175 guint have_valid_pick_buffer : 1;
176 guint accept_focus : 1;
177 guint motion_events_enabled : 1;
178 guint has_custom_perspective : 1;
211 static guint stage_signals[LAST_SIGNAL] = { 0, };
213 static const ClutterColor default_stage_color = { 255, 255, 255, 255 };
215 static void _clutter_stage_maybe_finish_queue_redraws (ClutterStage *stage);
218 clutter_stage_real_add (ClutterContainer *container,
221 clutter_actor_add_child (CLUTTER_ACTOR (container), child);
225 clutter_stage_real_remove (ClutterContainer *container,
228 clutter_actor_remove_child (CLUTTER_ACTOR (container), child);
232 clutter_stage_real_foreach (ClutterContainer *container,
233 ClutterCallback callback,
236 ClutterActorIter iter;
239 clutter_actor_iter_init (&iter, CLUTTER_ACTOR (container));
241 while (clutter_actor_iter_next (&iter, &child))
242 callback (child, user_data);
246 clutter_stage_real_raise (ClutterContainer *container,
248 ClutterActor *sibling)
250 clutter_actor_set_child_above_sibling (CLUTTER_ACTOR (container),
256 clutter_stage_real_lower (ClutterContainer *container,
258 ClutterActor *sibling)
260 clutter_actor_set_child_below_sibling (CLUTTER_ACTOR (container),
266 clutter_stage_real_sort_depth_order (ClutterContainer *container)
271 clutter_container_iface_init (ClutterContainerIface *iface)
273 iface->add = clutter_stage_real_add;
274 iface->remove = clutter_stage_real_remove;
275 iface->foreach = clutter_stage_real_foreach;
276 iface->raise = clutter_stage_real_raise;
277 iface->lower = clutter_stage_real_lower;
278 iface->sort_depth_order = clutter_stage_real_sort_depth_order;
282 clutter_stage_get_preferred_width (ClutterActor *self,
285 gfloat *natural_width_p)
287 ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
288 cairo_rectangle_int_t geom;
290 if (priv->impl == NULL)
293 _clutter_stage_window_get_geometry (priv->impl, &geom);
296 *min_width_p = geom.width;
299 *natural_width_p = geom.width;
303 clutter_stage_get_preferred_height (ClutterActor *self,
305 gfloat *min_height_p,
306 gfloat *natural_height_p)
308 ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
309 cairo_rectangle_int_t geom;
311 if (priv->impl == NULL)
314 _clutter_stage_window_get_geometry (priv->impl, &geom);
317 *min_height_p = geom.height;
319 if (natural_height_p)
320 *natural_height_p = geom.height;
324 queue_full_redraw (ClutterStage *stage)
326 ClutterStageWindow *stage_window;
328 if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
331 clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
333 /* Just calling clutter_actor_queue_redraw will typically only
334 * redraw the bounding box of the children parented on the stage but
335 * in this case we really need to ensure that the full stage is
336 * redrawn so we add a NULL redraw clip to the stage window. */
337 stage_window = _clutter_stage_get_window (stage);
338 if (stage_window == NULL)
341 _clutter_stage_window_add_redraw_clip (stage_window, NULL);
345 stage_is_default (ClutterStage *stage)
347 ClutterStageManager *stage_manager;
348 ClutterStageWindow *impl;
350 stage_manager = clutter_stage_manager_get_default ();
351 if (stage != clutter_stage_manager_get_default_stage (stage_manager))
354 impl = _clutter_stage_get_window (stage);
355 if (impl != _clutter_stage_get_default_window ())
362 clutter_stage_allocate (ClutterActor *self,
363 const ClutterActorBox *box,
364 ClutterAllocationFlags flags)
366 ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
367 ClutterGeometry prev_geom, geom;
368 cairo_rectangle_int_t window_size;
369 gboolean origin_changed;
372 origin_changed = (flags & CLUTTER_ABSOLUTE_ORIGIN_CHANGED)
376 if (priv->impl == NULL)
379 /* our old allocation */
380 clutter_actor_get_allocation_geometry (self, &prev_geom);
382 /* the current allocation */
383 width = clutter_actor_box_get_width (box);
384 height = clutter_actor_box_get_height (box);
386 /* the current Stage implementation size */
387 _clutter_stage_window_get_geometry (priv->impl, &window_size);
389 /* if the stage is fixed size (for instance, it's using a EGL framebuffer)
390 * then we simply ignore any allocation request and override the
391 * allocation chain - because we cannot forcibly change the size of the
394 if ((!clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC)))
396 CLUTTER_NOTE (LAYOUT,
397 "Following allocation to %dx%d (origin %s)",
399 origin_changed ? "changed" : "not changed");
401 clutter_actor_set_allocation (self, box,
402 flags | CLUTTER_DELEGATE_LAYOUT);
404 /* Ensure the window is sized correctly */
405 if (!priv->is_fullscreen)
407 if (priv->min_size_changed)
409 gfloat min_width, min_height;
410 gboolean min_width_set, min_height_set;
412 g_object_get (G_OBJECT (self),
413 "min-width", &min_width,
414 "min-width-set", &min_width_set,
415 "min-height", &min_height,
416 "min-height-set", &min_height_set,
424 if (width < min_width)
426 if (height < min_height)
429 priv->min_size_changed = FALSE;
432 if (window_size.width != width ||
433 window_size.height != height)
435 _clutter_stage_window_resize (priv->impl, width, height);
441 ClutterActorBox override = { 0, };
443 /* override the passed allocation */
446 override.x2 = window_size.width;
447 override.y2 = window_size.height;
449 CLUTTER_NOTE (LAYOUT,
450 "Overrigin original allocation of %dx%d "
451 "with %dx%d (origin %s)",
455 origin_changed ? "changed" : "not changed");
457 /* and store the overridden allocation */
458 clutter_actor_set_allocation (self, &override,
459 flags | CLUTTER_DELEGATE_LAYOUT);
462 /* XXX: Until Cogl becomes fully responsible for backend windows
463 * Clutter need to manually keep it informed of the current window
464 * size. We do this after the allocation above so that the stage
465 * window has a chance to update the window size based on the
468 _clutter_stage_window_get_geometry (priv->impl, &window_size);
469 cogl_onscreen_clutter_backend_set_size (window_size.width,
472 /* reset the viewport if the allocation effectively changed */
473 clutter_actor_get_allocation_geometry (self, &geom);
474 if (geom.width != prev_geom.width ||
475 geom.height != prev_geom.height)
477 _clutter_stage_set_viewport (CLUTTER_STAGE (self),
482 /* Note: we don't assume that set_viewport will queue a full redraw
483 * since it may bail-out early if something preemptively set the
484 * viewport before the stage was really allocated its new size.
486 queue_full_redraw (CLUTTER_STAGE (self));
490 typedef struct _Vector4
496 _cogl_util_get_eye_planes_for_screen_poly (float *polygon,
499 const CoglMatrix *projection,
500 const CoglMatrix *inverse_project,
501 ClutterPlane *planes)
511 tmp_poly = g_alloca (sizeof (Vector4) * n_vertices * 2);
515 /* Determine W in clip-space (Wc) for a point (0, 0, DEPTH, 1)
517 * Note: the depth could be anything except 0.
519 * We will transform the polygon into clip coordinates using this
520 * depth and then into eye coordinates. Our clip planes will be
521 * defined by triangles that extend between points of the polygon at
522 * DEPTH and corresponding points of the same polygon at DEPTH * 2.
524 * NB: Wc defines the position of the clip planes in clip
525 * coordinates. Given a screen aligned cross section through the
526 * frustum; coordinates range from [-Wc,Wc] left to right on the
527 * x-axis and [Wc,-Wc] top to bottom on the y-axis.
529 Wc = DEPTH * projection->wz + projection->ww;
531 #define CLIP_X(X) ((((float)X - viewport[0]) * (2.0 / viewport[2])) - 1) * Wc
532 #define CLIP_Y(Y) ((((float)Y - viewport[1]) * (2.0 / viewport[3])) - 1) * -Wc
534 for (i = 0; i < n_vertices; i++)
536 tmp_poly[i].x = CLIP_X (polygon[i * 2]);
537 tmp_poly[i].y = CLIP_Y (polygon[i * 2 + 1]);
538 tmp_poly[i].z = DEPTH;
542 Wc = DEPTH * 2 * projection->wz + projection->ww;
544 /* FIXME: technically we don't need to project all of the points
545 * twice, it would be enough project every other point since
546 * we can share points in this set to define the plane vectors. */
547 for (i = 0; i < n_vertices; i++)
549 tmp_poly[n_vertices + i].x = CLIP_X (polygon[i * 2]);
550 tmp_poly[n_vertices + i].y = CLIP_Y (polygon[i * 2 + 1]);
551 tmp_poly[n_vertices + i].z = DEPTH * 2;
552 tmp_poly[n_vertices + i].w = Wc;
558 cogl_matrix_project_points (inverse_project,
566 /* XXX: It's quite ugly that we end up with these casts between
567 * Vector4 types and CoglVector3s, it might be better if the
568 * cogl_vector APIs just took pointers to floats.
571 count = n_vertices - 1;
572 for (i = 0; i < count; i++)
575 memcpy (plane->v0, tmp_poly + i, sizeof (float) * 3);
576 memcpy (b, tmp_poly + n_vertices + i, sizeof (float) * 3);
577 memcpy (c, tmp_poly + n_vertices + i + 1, sizeof (float) * 3);
578 cogl_vector3_subtract (b, b, plane->v0);
579 cogl_vector3_subtract (c, c, plane->v0);
580 cogl_vector3_cross_product (plane->n, b, c);
581 cogl_vector3_normalize (plane->n);
584 plane = &planes[n_vertices - 1];
585 memcpy (plane->v0, tmp_poly + 0, sizeof (float) * 3);
586 memcpy (b, tmp_poly + (2 * n_vertices - 1), sizeof (float) * 3);
587 memcpy (c, tmp_poly + n_vertices, sizeof (float) * 3);
588 cogl_vector3_subtract (b, b, plane->v0);
589 cogl_vector3_subtract (c, c, plane->v0);
590 cogl_vector3_cross_product (plane->n, b, c);
591 cogl_vector3_normalize (plane->n);
595 _clutter_stage_update_active_framebuffer (ClutterStage *stage)
597 ClutterStagePrivate *priv = stage->priv;
599 /* We track the CoglFramebuffer that corresponds to the stage itself
600 * so, for example, we can disable culling when rendering to an
601 * offscreen framebuffer.
604 priv->active_framebuffer =
605 _clutter_stage_window_get_active_framebuffer (priv->impl);
607 if (!priv->active_framebuffer)
608 priv->active_framebuffer = cogl_get_draw_framebuffer ();
611 /* This provides a common point of entry for painting the scenegraph
612 * for picking or painting...
614 * XXX: Instead of having a toplevel 2D clip region, it might be
615 * better to have a clip volume within the view frustum. This could
616 * allow us to avoid projecting actors into window coordinates to
617 * be able to cull them.
620 _clutter_stage_do_paint (ClutterStage *stage,
621 const cairo_rectangle_int_t *clip)
623 ClutterStagePrivate *priv = stage->priv;
625 cairo_rectangle_int_t geom;
627 _clutter_stage_window_get_geometry (priv->impl, &geom);
631 clip_poly[0] = MAX (clip->x, 0);
632 clip_poly[1] = MAX (clip->y, 0);
633 clip_poly[2] = MIN (clip->x + clip->width, geom.width);
634 clip_poly[3] = clip_poly[1];
635 clip_poly[4] = clip_poly[2];
636 clip_poly[5] = MIN (clip->y + clip->height, geom.height);
637 clip_poly[6] = clip_poly[0];
638 clip_poly[7] = clip_poly[5];
644 clip_poly[2] = geom.width;
646 clip_poly[4] = geom.width;
647 clip_poly[5] = geom.height;
649 clip_poly[7] = geom.height;
652 CLUTTER_NOTE (CLIPPING, "Setting stage clip too: "
653 "x=%f, y=%f, width=%f, height=%f",
654 clip_poly[0], clip_poly[1],
655 clip_poly[2] - clip_poly[0],
656 clip_poly[5] - clip_poly[1]);
658 _cogl_util_get_eye_planes_for_screen_poly (clip_poly,
662 &priv->inverse_projection,
663 priv->current_clip_planes);
665 _clutter_stage_paint_volume_stack_free_all (stage);
666 _clutter_stage_update_active_framebuffer (stage);
667 clutter_actor_paint (CLUTTER_ACTOR (stage));
671 clutter_stage_paint (ClutterActor *self)
673 ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
674 CoglBufferBit clear_flags;
675 ClutterColor bg_color;
676 CoglColor stage_color;
677 ClutterActorIter iter;
681 CLUTTER_STATIC_TIMER (stage_clear_timer,
682 "Painting actors", /* parent */
684 "The time spent clearing the stage",
685 0 /* no application private data */);
687 CLUTTER_NOTE (PAINT, "Initializing stage paint");
689 /* composite the opacity to the stage color */
690 clutter_actor_get_background_color (self, &bg_color);
691 real_alpha = clutter_actor_get_opacity (self)
695 clear_flags = COGL_BUFFER_BIT_DEPTH;
696 if (!STAGE_NO_CLEAR_ON_PAINT (self))
697 clear_flags |= COGL_BUFFER_BIT_COLOR;
701 CLUTTER_TIMER_START (_clutter_uprof_context, stage_clear_timer);
702 /* we use the real alpha to clear the stage if :use-alpha is
703 * set; the effect depends entirely on the Clutter backend
705 cogl_color_init_from_4ub (&stage_color,
709 priv->use_alpha ? real_alpha : 255);
710 cogl_color_premultiply (&stage_color);
711 cogl_clear (&stage_color, clear_flags);
712 CLUTTER_TIMER_STOP (_clutter_uprof_context, stage_clear_timer);
714 clutter_actor_iter_init (&iter, self);
715 while (clutter_actor_iter_next (&iter, &child))
716 clutter_actor_paint (child);
720 clutter_stage_pick (ClutterActor *self,
721 const ClutterColor *color)
723 ClutterActorIter iter;
726 /* Note: we don't chain up to our parent as we don't want any geometry
727 * emitted for the stage itself. The stage's pick id is effectively handled
728 * by the call to cogl_clear done in clutter-main.c:_clutter_do_pick_async()
730 clutter_actor_iter_init (&iter, self);
731 while (clutter_actor_iter_next (&iter, &child))
732 clutter_actor_paint (child);
736 clutter_stage_get_paint_volume (ClutterActor *self,
737 ClutterPaintVolume *volume)
739 /* Returning False effectively means Clutter has to assume it covers
745 clutter_stage_realize (ClutterActor *self)
747 ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
748 gboolean is_realized;
750 /* Make sure the viewport and projection matrix are valid for the
751 * first paint (which will likely occur before the ConfigureNotify
754 priv->dirty_viewport = TRUE;
755 priv->dirty_projection = TRUE;
757 g_assert (priv->impl != NULL);
758 is_realized = _clutter_stage_window_realize (priv->impl);
760 /* ensure that the stage is using the context if the
761 * realization sequence was successful
765 ClutterBackend *backend = clutter_get_default_backend ();
767 /* We want to select the context without calling
768 clutter_backend_ensure_context so that it doesn't call any
769 Cogl functions. Otherwise it would create the Cogl context
770 before we get a chance to check whether the GL version is
772 _clutter_backend_ensure_context_internal (backend, CLUTTER_STAGE (self));
775 CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
779 clutter_stage_unrealize (ClutterActor *self)
781 ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
783 /* and then unrealize the implementation */
784 g_assert (priv->impl != NULL);
785 _clutter_stage_window_unrealize (priv->impl);
787 CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
789 clutter_stage_ensure_current (CLUTTER_STAGE (self));
793 clutter_stage_show_all (ClutterActor *self)
795 ClutterActorIter iter;
798 /* we don't do a recursive show_all(), to maintain the old
799 * invariants from ClutterGroup
801 clutter_actor_iter_init (&iter, self);
802 while (clutter_actor_iter_next (&iter, &child))
803 clutter_actor_show (child);
805 clutter_actor_show (self);
809 clutter_stage_show (ClutterActor *self)
811 ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
813 CLUTTER_ACTOR_CLASS (clutter_stage_parent_class)->show (self);
815 /* Possibly do an allocation run so that the stage will have the
816 right size before we map it */
817 _clutter_stage_maybe_relayout (self);
819 g_assert (priv->impl != NULL);
820 _clutter_stage_window_show (priv->impl, TRUE);
824 clutter_stage_hide_all (ClutterActor *self)
826 ClutterActorIter iter;
829 clutter_actor_hide (self);
831 /* we don't do a recursive hide_all(), to maintain the old invariants
834 clutter_actor_iter_init (&iter, self);
835 while (clutter_actor_iter_next (&iter, &child))
836 clutter_actor_hide (child);
840 clutter_stage_hide (ClutterActor *self)
842 ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
844 g_assert (priv->impl != NULL);
845 _clutter_stage_window_hide (priv->impl);
847 CLUTTER_ACTOR_CLASS (clutter_stage_parent_class)->hide (self);
851 clutter_stage_emit_key_focus_event (ClutterStage *stage,
854 ClutterStagePrivate *priv = stage->priv;
856 if (priv->key_focused_actor == NULL)
860 g_signal_emit_by_name (priv->key_focused_actor, "key-focus-in");
862 g_signal_emit_by_name (priv->key_focused_actor, "key-focus-out");
864 g_object_notify (G_OBJECT (stage), "key-focus");
868 clutter_stage_real_activate (ClutterStage *stage)
870 clutter_stage_emit_key_focus_event (stage, TRUE);
874 clutter_stage_real_deactivate (ClutterStage *stage)
876 clutter_stage_emit_key_focus_event (stage, FALSE);
880 clutter_stage_real_fullscreen (ClutterStage *stage)
882 ClutterStagePrivate *priv = stage->priv;
883 cairo_rectangle_int_t geom;
886 /* we need to force an allocation here because the size
887 * of the stage might have been changed by the backend
889 * this is a really bad solution to the issues caused by
890 * the fact that fullscreening the stage on the X11 backends
891 * is really an asynchronous operation
893 _clutter_stage_window_get_geometry (priv->impl, &geom);
898 box.y2 = geom.height;
900 clutter_actor_allocate (CLUTTER_ACTOR (stage),
902 CLUTTER_ALLOCATION_NONE);
906 _clutter_stage_queue_event (ClutterStage *stage,
909 ClutterStagePrivate *priv;
910 gboolean first_event;
911 ClutterInputDevice *device;
913 g_return_if_fail (CLUTTER_IS_STAGE (stage));
917 first_event = priv->event_queue->length == 0;
919 g_queue_push_tail (priv->event_queue, clutter_event_copy (event));
923 ClutterMasterClock *master_clock = _clutter_master_clock_get_default ();
924 _clutter_master_clock_start_running (master_clock);
927 /* if needed, update the state of the input device of the event.
928 * we do it here to avoid calling the same code from every backend
929 * event processing function
931 device = clutter_event_get_device (event);
934 ClutterModifierType event_state = clutter_event_get_state (event);
935 guint32 event_time = clutter_event_get_time (event);
936 gfloat event_x, event_y;
938 clutter_event_get_coords (event, &event_x, &event_y);
940 _clutter_input_device_set_coords (device, event_x, event_y);
941 _clutter_input_device_set_state (device, event_state);
942 _clutter_input_device_set_time (device, event_time);
947 _clutter_stage_has_queued_events (ClutterStage *stage)
949 ClutterStagePrivate *priv;
951 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
955 return priv->event_queue->length > 0;
959 _clutter_stage_process_queued_events (ClutterStage *stage)
961 ClutterStagePrivate *priv;
964 g_return_if_fail (CLUTTER_IS_STAGE (stage));
968 if (priv->event_queue->length == 0)
971 /* In case the stage gets destroyed during event processing */
972 g_object_ref (stage);
974 /* Steal events before starting processing to avoid reentrancy
976 events = priv->event_queue->head;
977 priv->event_queue->head = NULL;
978 priv->event_queue->tail = NULL;
979 priv->event_queue->length = 0;
981 for (l = events; l != NULL; l = l->next)
984 ClutterEvent *next_event;
985 ClutterInputDevice *device;
986 ClutterInputDevice *next_device;
987 gboolean check_device = FALSE;
990 next_event = l->next ? l->next->data : NULL;
992 device = clutter_event_get_device (event);
994 if (next_event != NULL)
995 next_device = clutter_event_get_device (next_event);
999 if (device != NULL && next_device != NULL)
1000 check_device = TRUE;
1002 /* Skip consecutive motion events coming from the same device */
1003 if (priv->throttle_motion_events &&
1004 next_event != NULL &&
1005 event->type == CLUTTER_MOTION &&
1006 (next_event->type == CLUTTER_MOTION ||
1007 next_event->type == CLUTTER_LEAVE) &&
1008 (!check_device || (device == next_device)))
1010 CLUTTER_NOTE (EVENT,
1011 "Omitting motion event at %d, %d",
1012 (int) event->motion.x,
1013 (int) event->motion.y);
1017 _clutter_process_event (event);
1020 clutter_event_free (event);
1023 g_list_free (events);
1025 g_object_unref (stage);
1029 * _clutter_stage_needs_update:
1030 * @stage: A #ClutterStage
1032 * Determines if _clutter_stage_do_update() needs to be called.
1034 * Return value: %TRUE if the stage need layout or painting
1037 _clutter_stage_needs_update (ClutterStage *stage)
1039 ClutterStagePrivate *priv;
1041 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
1045 return priv->relayout_pending || priv->redraw_pending;
1049 _clutter_stage_maybe_relayout (ClutterActor *actor)
1051 ClutterStage *stage = CLUTTER_STAGE (actor);
1052 ClutterStagePrivate *priv = stage->priv;
1053 gfloat natural_width, natural_height;
1054 ClutterActorBox box = { 0, };
1055 CLUTTER_STATIC_TIMER (relayout_timer,
1056 "Mainloop", /* no parent */
1058 "The time spent reallocating the stage",
1059 0 /* no application private data */);
1061 if (!priv->relayout_pending)
1064 /* avoid reentrancy */
1065 if (!CLUTTER_ACTOR_IN_RELAYOUT (stage))
1067 priv->relayout_pending = FALSE;
1069 CLUTTER_TIMER_START (_clutter_uprof_context, relayout_timer);
1070 CLUTTER_NOTE (ACTOR, "Recomputing layout");
1072 CLUTTER_SET_PRIVATE_FLAGS (stage, CLUTTER_IN_RELAYOUT);
1074 natural_width = natural_height = 0;
1075 clutter_actor_get_preferred_size (CLUTTER_ACTOR (stage),
1077 &natural_width, &natural_height);
1081 box.x2 = natural_width;
1082 box.y2 = natural_height;
1084 CLUTTER_NOTE (ACTOR, "Allocating (0, 0 - %d, %d) for the stage",
1085 (int) natural_width,
1086 (int) natural_height);
1088 clutter_actor_allocate (CLUTTER_ACTOR (stage),
1089 &box, CLUTTER_ALLOCATION_NONE);
1091 CLUTTER_UNSET_PRIVATE_FLAGS (stage, CLUTTER_IN_RELAYOUT);
1092 CLUTTER_TIMER_STOP (_clutter_uprof_context, relayout_timer);
1097 _clutter_stage_get_pick_buffer_valid (ClutterStage *stage, ClutterPickMode mode)
1099 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
1101 if (stage->priv->pick_buffer_mode != mode)
1104 return stage->priv->have_valid_pick_buffer;
1108 _clutter_stage_set_pick_buffer_valid (ClutterStage *stage,
1110 ClutterPickMode mode)
1112 g_return_if_fail (CLUTTER_IS_STAGE (stage));
1114 stage->priv->have_valid_pick_buffer = !!valid;
1115 stage->priv->pick_buffer_mode = mode;
1119 clutter_stage_do_redraw (ClutterStage *stage)
1121 ClutterBackend *backend = clutter_get_default_backend ();
1122 ClutterActor *actor = CLUTTER_ACTOR (stage);
1123 ClutterStagePrivate *priv = stage->priv;
1125 CLUTTER_STATIC_COUNTER (redraw_counter,
1126 "clutter_stage_do_redraw counter",
1127 "Increments for each Stage redraw",
1128 0 /* no application private data */);
1129 CLUTTER_STATIC_TIMER (redraw_timer,
1130 "Master Clock", /* parent */
1132 "The time spent redrawing everything",
1133 0 /* no application private data */);
1135 if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
1138 if (priv->impl == NULL)
1141 CLUTTER_NOTE (PAINT, "Redraw started for stage '%s'[%p]",
1142 _clutter_actor_get_debug_name (actor),
1145 _clutter_stage_set_pick_buffer_valid (stage, FALSE, -1);
1146 priv->picks_per_frame = 0;
1148 _clutter_backend_ensure_context (backend, stage);
1150 if (_clutter_context_get_show_fps ())
1152 if (priv->fps_timer == NULL)
1153 priv->fps_timer = g_timer_new ();
1156 _clutter_stage_maybe_setup_viewport (stage);
1158 CLUTTER_COUNTER_INC (_clutter_uprof_context, redraw_counter);
1159 CLUTTER_TIMER_START (_clutter_uprof_context, redraw_timer);
1161 _clutter_stage_window_redraw (priv->impl);
1163 CLUTTER_TIMER_STOP (_clutter_uprof_context, redraw_timer);
1165 if (_clutter_context_get_show_fps ())
1167 priv->timer_n_frames += 1;
1169 if (g_timer_elapsed (priv->fps_timer, NULL) >= 1.0)
1171 g_print ("*** FPS for %s: %i ***\n",
1172 _clutter_actor_get_debug_name (actor),
1173 priv->timer_n_frames);
1175 priv->timer_n_frames = 0;
1176 g_timer_start (priv->fps_timer);
1180 CLUTTER_NOTE (PAINT, "Redraw finished for stage '%s'[%p]",
1181 _clutter_actor_get_debug_name (actor),
1186 * _clutter_stage_do_update:
1187 * @stage: A #ClutterStage
1189 * Handles per-frame layout and repaint for the stage.
1191 * Return value: %TRUE if the stage was updated
1194 _clutter_stage_do_update (ClutterStage *stage)
1196 ClutterStagePrivate *priv = stage->priv;
1198 /* if the stage is being destroyed, or if the destruction already
1199 * happened and we don't have an StageWindow any more, then we
1202 if (CLUTTER_ACTOR_IN_DESTRUCTION (stage) || priv->impl == NULL)
1205 if (!CLUTTER_ACTOR_IS_REALIZED (stage))
1208 /* NB: We need to ensure we have an up to date layout *before* we
1209 * check or clear the pending redraws flag since a relayout may
1212 _clutter_stage_maybe_relayout (CLUTTER_ACTOR (stage));
1214 if (!priv->redraw_pending)
1217 _clutter_stage_maybe_finish_queue_redraws (stage);
1219 clutter_stage_do_redraw (stage);
1221 /* reset the guard, so that new redraws are possible */
1222 priv->redraw_pending = FALSE;
1224 #ifdef CLUTTER_ENABLE_DEBUG
1225 if (priv->redraw_count > 0)
1227 CLUTTER_NOTE (SCHEDULER, "Queued %lu redraws during the last cycle",
1228 priv->redraw_count);
1230 priv->redraw_count = 0;
1232 #endif /* CLUTTER_ENABLE_DEBUG */
1238 clutter_stage_real_queue_relayout (ClutterActor *self)
1240 ClutterStage *stage = CLUTTER_STAGE (self);
1241 ClutterStagePrivate *priv = stage->priv;
1242 ClutterActorClass *parent_class;
1244 priv->relayout_pending = TRUE;
1247 parent_class = CLUTTER_ACTOR_CLASS (clutter_stage_parent_class);
1248 parent_class->queue_relayout (self);
1252 clutter_stage_real_queue_redraw (ClutterActor *actor,
1255 ClutterStage *stage = CLUTTER_STAGE (actor);
1256 ClutterStageWindow *stage_window;
1257 ClutterPaintVolume *redraw_clip;
1258 ClutterActorBox bounding_box;
1259 ClutterActorBox intersection_box;
1260 cairo_rectangle_int_t geom, stage_clip;
1262 if (CLUTTER_ACTOR_IN_DESTRUCTION (actor))
1265 /* If the backend can't do anything with redraw clips (e.g. it already knows
1266 * it needs to redraw everything anyway) then don't spend time transforming
1267 * any clip volume into stage coordinates... */
1268 stage_window = _clutter_stage_get_window (stage);
1269 if (stage_window == NULL)
1272 if (_clutter_stage_window_ignoring_redraw_clips (stage_window))
1274 _clutter_stage_window_add_redraw_clip (stage_window, NULL);
1278 /* Convert the clip volume into stage coordinates and then into an
1279 * axis aligned stage coordinates bounding box...
1281 redraw_clip = _clutter_actor_get_queue_redraw_clip (leaf);
1282 if (redraw_clip == NULL)
1284 _clutter_stage_window_add_redraw_clip (stage_window, NULL);
1288 _clutter_paint_volume_get_stage_paint_box (redraw_clip,
1292 _clutter_stage_window_get_geometry (stage_window, &geom);
1294 intersection_box.x1 = MAX (bounding_box.x1, 0);
1295 intersection_box.y1 = MAX (bounding_box.y1, 0);
1296 intersection_box.x2 = MIN (bounding_box.x2, geom.width);
1297 intersection_box.y2 = MIN (bounding_box.y2, geom.height);
1299 /* There is no need to track degenerate/empty redraw clips */
1300 if (intersection_box.x2 <= intersection_box.x1 ||
1301 intersection_box.y2 <= intersection_box.y1)
1304 /* when converting to integer coordinates make sure we round the edges of the
1305 * clip rectangle outwards... */
1306 stage_clip.x = intersection_box.x1;
1307 stage_clip.y = intersection_box.y1;
1308 stage_clip.width = intersection_box.x2 - stage_clip.x;
1309 stage_clip.height = intersection_box.y2 - stage_clip.y;
1311 _clutter_stage_window_add_redraw_clip (stage_window, &stage_clip);
1315 _clutter_stage_has_full_redraw_queued (ClutterStage *stage)
1317 ClutterStageWindow *stage_window = _clutter_stage_get_window (stage);
1319 if (CLUTTER_ACTOR_IN_DESTRUCTION (stage) || stage_window == NULL)
1322 if (stage->priv->redraw_pending &&
1323 !_clutter_stage_window_has_redraw_clips (stage_window))
1330 * clutter_stage_get_redraw_clip_bounds:
1331 * @stage: A #ClutterStage
1332 * @clip: (out caller-allocates): Return location for the clip bounds
1334 * Gets the bounds of the current redraw for @stage in stage pixel
1335 * coordinates. E.g., if only a single actor has queued a redraw then
1336 * Clutter may redraw the stage with a clip so that it doesn't have to
1337 * paint every pixel in the stage. This function would then return the
1338 * bounds of that clip. An application can use this information to
1339 * avoid some extra work if it knows that some regions of the stage
1340 * aren't going to be painted. This should only be called while the
1341 * stage is being painted. If there is no current redraw clip then
1342 * this function will set @clip to the full extents of the stage.
1347 clutter_stage_get_redraw_clip_bounds (ClutterStage *stage,
1348 cairo_rectangle_int_t *clip)
1350 ClutterStagePrivate *priv;
1352 g_return_if_fail (CLUTTER_IS_STAGE (stage));
1353 g_return_if_fail (clip != NULL);
1357 if (!_clutter_stage_window_get_redraw_clip_bounds (priv->impl, clip))
1359 /* Set clip to the full extents of the stage */
1360 _clutter_stage_window_get_geometry (priv->impl, clip);
1365 read_pixels_to_file (char *filename_stem,
1372 cairo_surface_t *surface;
1373 static int read_count = 0;
1374 char *filename = g_strdup_printf ("%s-%05d.png",
1378 data = g_malloc (4 * width * height);
1379 cogl_read_pixels (x, y, width, height,
1380 COGL_READ_PIXELS_COLOR_BUFFER,
1381 CLUTTER_CAIRO_FORMAT_ARGB32,
1384 surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_RGB24,
1388 cairo_surface_write_to_png (surface, filename);
1389 cairo_surface_destroy (surface);
1398 _clutter_stage_do_pick (ClutterStage *stage,
1401 ClutterPickMode mode)
1403 ClutterStagePrivate *priv;
1404 ClutterMainContext *context;
1405 guchar pixel[4] = { 0xff, 0xff, 0xff, 0xff };
1406 CoglColor stage_pick_id;
1407 gboolean dither_enabled_save;
1408 CoglFramebuffer *fb;
1409 ClutterActor *actor;
1410 gboolean is_clipped;
1411 CLUTTER_STATIC_COUNTER (do_pick_counter,
1412 "_clutter_stage_do_pick counter",
1413 "Increments for each full pick run",
1414 0 /* no application private data */);
1415 CLUTTER_STATIC_TIMER (pick_timer,
1416 "Mainloop", /* parent */
1418 "The time spent picking",
1419 0 /* no application private data */);
1420 CLUTTER_STATIC_TIMER (pick_clear,
1421 "Picking", /* parent */
1422 "Stage clear (pick)",
1423 "The time spent clearing stage for picking",
1424 0 /* no application private data */);
1425 CLUTTER_STATIC_TIMER (pick_paint,
1426 "Picking", /* parent */
1427 "Painting actors (pick mode)",
1428 "The time spent painting actors in pick mode",
1429 0 /* no application private data */);
1430 CLUTTER_STATIC_TIMER (pick_read,
1431 "Picking", /* parent */
1433 "The time spent issuing a read pixels",
1434 0 /* no application private data */);
1436 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
1440 if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_NOP_PICKING))
1441 return CLUTTER_ACTOR (stage);
1443 #ifdef CLUTTER_ENABLE_PROFILE
1444 if (clutter_profile_flags & CLUTTER_PROFILE_PICKING_ONLY)
1445 _clutter_profile_resume ();
1446 #endif /* CLUTTER_ENABLE_PROFILE */
1448 CLUTTER_COUNTER_INC (_clutter_uprof_context, do_pick_counter);
1449 CLUTTER_TIMER_START (_clutter_uprof_context, pick_timer);
1451 context = _clutter_context_get_default ();
1452 clutter_stage_ensure_current (stage);
1454 /* It's possible that we currently have a static scene and have renderered a
1455 * full, unclipped pick buffer. If so we can simply continue to read from
1456 * this cached buffer until the scene next changes. */
1457 if (_clutter_stage_get_pick_buffer_valid (stage, mode))
1459 CLUTTER_TIMER_START (_clutter_uprof_context, pick_read);
1460 cogl_read_pixels (x, y, 1, 1,
1461 COGL_READ_PIXELS_COLOR_BUFFER,
1462 COGL_PIXEL_FORMAT_RGBA_8888_PRE,
1464 CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_read);
1466 CLUTTER_NOTE (PICK, "Reusing pick buffer from previous render to fetch "
1467 "actor at %i,%i", x, y);
1472 priv->picks_per_frame++;
1474 _clutter_backend_ensure_context (context->backend, stage);
1476 /* needed for when a context switch happens */
1477 _clutter_stage_maybe_setup_viewport (stage);
1479 /* If we are seeing multiple picks per frame that means the scene is static
1480 * so we promote to doing a non-scissored pick render so that all subsequent
1481 * picks for the same static scene won't require additional renders */
1482 if (priv->picks_per_frame < 2)
1484 if (G_LIKELY (!(clutter_pick_debug_flags &
1485 CLUTTER_DEBUG_DUMP_PICK_BUFFERS)))
1486 cogl_clip_push_window_rectangle (x, y, 1, 1);
1492 CLUTTER_NOTE (PICK, "Performing %s pick at %i,%i",
1493 is_clipped ? "clippped" : "full", x, y);
1495 cogl_disable_fog ();
1496 cogl_color_init_from_4ub (&stage_pick_id, 255, 255, 255, 255);
1497 CLUTTER_TIMER_START (_clutter_uprof_context, pick_clear);
1498 cogl_clear (&stage_pick_id,
1499 COGL_BUFFER_BIT_COLOR |
1500 COGL_BUFFER_BIT_DEPTH);
1501 CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_clear);
1503 /* Disable dithering (if any) when doing the painting in pick mode */
1504 fb = cogl_get_draw_framebuffer ();
1505 dither_enabled_save = cogl_framebuffer_get_dither_enabled (fb);
1506 cogl_framebuffer_set_dither_enabled (fb, FALSE);
1508 /* Render the entire scence in pick mode - just single colored silhouette's
1509 * are drawn offscreen (as we never swap buffers)
1511 CLUTTER_TIMER_START (_clutter_uprof_context, pick_paint);
1512 context->pick_mode = mode;
1513 _clutter_stage_do_paint (stage, NULL);
1514 context->pick_mode = CLUTTER_PICK_NONE;
1515 CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_paint);
1519 if (G_LIKELY (!(clutter_pick_debug_flags &
1520 CLUTTER_DEBUG_DUMP_PICK_BUFFERS)))
1523 _clutter_stage_set_pick_buffer_valid (stage, FALSE, -1);
1526 _clutter_stage_set_pick_buffer_valid (stage, TRUE, mode);
1528 /* Read the color of the screen co-ords pixel. RGBA_8888_PRE is used
1529 even though we don't care about the alpha component because under
1530 GLES this is the only format that is guaranteed to work so Cogl
1531 will end up having to do a conversion if any other format is
1532 used. The format is requested as pre-multiplied because Cogl
1533 assumes that all pixels in the framebuffer are premultiplied so
1534 it avoids a conversion. */
1535 CLUTTER_TIMER_START (_clutter_uprof_context, pick_read);
1536 cogl_read_pixels (x, y, 1, 1,
1537 COGL_READ_PIXELS_COLOR_BUFFER,
1538 COGL_PIXEL_FORMAT_RGBA_8888_PRE,
1540 CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_read);
1542 if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))
1545 g_strconcat ("pick-buffer-",
1546 _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage)),
1549 read_pixels_to_file (file_name, 0, 0,
1550 clutter_actor_get_width (CLUTTER_ACTOR (stage)),
1551 clutter_actor_get_height (CLUTTER_ACTOR (stage)));
1556 /* Restore whether GL_DITHER was enabled */
1557 cogl_framebuffer_set_dither_enabled (fb, dither_enabled_save);
1560 if (pixel[0] == 0xff && pixel[1] == 0xff && pixel[2] == 0xff)
1562 actor = CLUTTER_ACTOR (stage);
1566 guint32 id_ = _clutter_pixel_to_id (pixel);
1568 actor = _clutter_get_actor_by_id (stage, id_);
1571 CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_timer);
1573 #ifdef CLUTTER_ENABLE_PROFILE
1574 if (clutter_profile_flags & CLUTTER_PROFILE_PICKING_ONLY)
1575 _clutter_profile_suspend ();
1584 clutter_stage_real_delete_event (ClutterStage *stage,
1585 ClutterEvent *event)
1587 if (stage_is_default (stage))
1588 clutter_main_quit ();
1590 clutter_actor_destroy (CLUTTER_ACTOR (stage));
1596 clutter_stage_real_apply_transform (ClutterActor *stage,
1599 ClutterStagePrivate *priv = CLUTTER_STAGE (stage)->priv;
1601 /* FIXME: we probably shouldn't be explicitly reseting the matrix
1603 cogl_matrix_init_identity (matrix);
1604 cogl_matrix_multiply (matrix, matrix, &priv->view);
1608 clutter_stage_constructed (GObject *gobject)
1610 ClutterStage *self = CLUTTER_STAGE (gobject);
1611 ClutterStageManager *stage_manager;
1613 stage_manager = clutter_stage_manager_get_default ();
1615 /* this will take care to sinking the floating reference */
1616 _clutter_stage_manager_add_stage (stage_manager, self);
1618 /* if this stage has been created on a backend that does not
1619 * support multiple stages then it becomes the default stage
1620 * as well; any other attempt at creating a ClutterStage will
1623 if (!clutter_feature_available (CLUTTER_FEATURE_STAGE_MULTIPLE))
1625 if (G_UNLIKELY (clutter_stage_manager_get_default_stage (stage_manager) != NULL))
1627 g_error ("Unable to create another stage: the backend of "
1628 "type '%s' does not support multiple stages. Use "
1629 "clutter_stage_manager_get_default_stage() instead "
1630 "to access the stage singleton.",
1631 G_OBJECT_TYPE_NAME (clutter_get_default_backend ()));
1634 _clutter_stage_manager_set_default_stage (stage_manager, self);
1637 G_OBJECT_CLASS (clutter_stage_parent_class)->constructed (gobject);
1641 clutter_stage_set_property (GObject *object,
1643 const GValue *value,
1646 ClutterStage *stage = CLUTTER_STAGE (object);
1651 clutter_actor_set_background_color (CLUTTER_ACTOR (stage),
1652 clutter_value_get_color (value));
1655 case PROP_OFFSCREEN:
1656 if (g_value_get_boolean (value))
1657 g_warning ("Offscreen stages are currently not supported\n");
1660 case PROP_CURSOR_VISIBLE:
1661 if (g_value_get_boolean (value))
1662 clutter_stage_show_cursor (stage);
1664 clutter_stage_hide_cursor (stage);
1667 case PROP_PERSPECTIVE:
1668 clutter_stage_set_perspective (stage, g_value_get_boxed (value));
1672 clutter_stage_set_title (stage, g_value_get_string (value));
1675 case PROP_USER_RESIZABLE:
1676 clutter_stage_set_user_resizable (stage, g_value_get_boolean (value));
1680 clutter_stage_set_use_fog (stage, g_value_get_boolean (value));
1684 clutter_stage_set_fog (stage, g_value_get_boxed (value));
1687 case PROP_USE_ALPHA:
1688 clutter_stage_set_use_alpha (stage, g_value_get_boolean (value));
1691 case PROP_KEY_FOCUS:
1692 clutter_stage_set_key_focus (stage, g_value_get_object (value));
1695 case PROP_NO_CLEAR_HINT:
1696 clutter_stage_set_no_clear_hint (stage, g_value_get_boolean (value));
1699 case PROP_ACCEPT_FOCUS:
1700 clutter_stage_set_accept_focus (stage, g_value_get_boolean (value));
1704 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1710 clutter_stage_get_property (GObject *gobject,
1715 ClutterStagePrivate *priv = CLUTTER_STAGE (gobject)->priv;
1721 ClutterColor bg_color;
1723 clutter_actor_get_background_color (CLUTTER_ACTOR (gobject),
1725 clutter_value_set_color (value, &bg_color);
1729 case PROP_OFFSCREEN:
1730 g_value_set_boolean (value, FALSE);
1733 case PROP_FULLSCREEN_SET:
1734 g_value_set_boolean (value, priv->is_fullscreen);
1737 case PROP_CURSOR_VISIBLE:
1738 g_value_set_boolean (value, priv->is_cursor_visible);
1741 case PROP_PERSPECTIVE:
1742 g_value_set_boxed (value, &priv->perspective);
1746 g_value_set_string (value, priv->title);
1749 case PROP_USER_RESIZABLE:
1750 g_value_set_boolean (value, priv->is_user_resizable);
1754 g_value_set_boolean (value, priv->use_fog);
1758 g_value_set_boxed (value, &priv->fog);
1761 case PROP_USE_ALPHA:
1762 g_value_set_boolean (value, priv->use_alpha);
1765 case PROP_KEY_FOCUS:
1766 g_value_set_object (value, priv->key_focused_actor);
1769 case PROP_NO_CLEAR_HINT:
1772 (priv->stage_hints & CLUTTER_STAGE_NO_CLEAR_ON_PAINT) != 0;
1774 g_value_set_boolean (value, hint);
1778 case PROP_ACCEPT_FOCUS:
1779 g_value_set_boolean (value, priv->accept_focus);
1783 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
1789 clutter_stage_dispose (GObject *object)
1791 ClutterStage *stage = CLUTTER_STAGE (object);
1792 ClutterStagePrivate *priv = stage->priv;
1793 ClutterStageManager *stage_manager;
1795 clutter_actor_hide (CLUTTER_ACTOR (object));
1797 /* remove_stage() will unref() the stage instance, so we need to
1798 * add a reference here to keep it temporarily alive
1800 g_object_ref (object);
1801 stage_manager = clutter_stage_manager_get_default ();
1802 _clutter_stage_manager_remove_stage (stage_manager, stage);
1804 _clutter_clear_events_queue_for_stage (stage);
1806 if (priv->impl != NULL)
1808 CLUTTER_NOTE (BACKEND, "Disposing of the stage implementation");
1810 if (CLUTTER_ACTOR_IS_REALIZED (object))
1811 _clutter_stage_window_unrealize (priv->impl);
1813 g_object_unref (priv->impl);
1817 clutter_actor_remove_all_children (CLUTTER_ACTOR (object));
1819 G_OBJECT_CLASS (clutter_stage_parent_class)->dispose (object);
1823 clutter_stage_finalize (GObject *object)
1825 ClutterStage *stage = CLUTTER_STAGE (object);
1826 ClutterStagePrivate *priv = stage->priv;
1828 g_queue_foreach (priv->event_queue, (GFunc) clutter_event_free, NULL);
1829 g_queue_free (priv->event_queue);
1831 g_free (priv->title);
1833 g_array_free (priv->paint_volume_stack, TRUE);
1835 g_hash_table_destroy (priv->devices);
1837 _clutter_id_pool_free (priv->pick_id_pool);
1839 if (priv->fps_timer != NULL)
1840 g_timer_destroy (priv->fps_timer);
1842 G_OBJECT_CLASS (clutter_stage_parent_class)->finalize (object);
1846 clutter_stage_class_init (ClutterStageClass *klass)
1848 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1849 ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
1852 gobject_class->constructed = clutter_stage_constructed;
1853 gobject_class->set_property = clutter_stage_set_property;
1854 gobject_class->get_property = clutter_stage_get_property;
1855 gobject_class->dispose = clutter_stage_dispose;
1856 gobject_class->finalize = clutter_stage_finalize;
1858 actor_class->allocate = clutter_stage_allocate;
1859 actor_class->get_preferred_width = clutter_stage_get_preferred_width;
1860 actor_class->get_preferred_height = clutter_stage_get_preferred_height;
1861 actor_class->paint = clutter_stage_paint;
1862 actor_class->pick = clutter_stage_pick;
1863 actor_class->get_paint_volume = clutter_stage_get_paint_volume;
1864 actor_class->realize = clutter_stage_realize;
1865 actor_class->unrealize = clutter_stage_unrealize;
1866 actor_class->show = clutter_stage_show;
1867 actor_class->show_all = clutter_stage_show_all;
1868 actor_class->hide = clutter_stage_hide;
1869 actor_class->hide_all = clutter_stage_hide_all;
1870 actor_class->queue_relayout = clutter_stage_real_queue_relayout;
1871 actor_class->queue_redraw = clutter_stage_real_queue_redraw;
1872 actor_class->apply_transform = clutter_stage_real_apply_transform;
1875 * ClutterStage:fullscreen:
1877 * Whether the stage should be fullscreen or not.
1879 * This property is set by calling clutter_stage_set_fullscreen()
1880 * but since the actual implementation is delegated to the backend
1881 * you should connect to the notify::fullscreen-set signal in order
1882 * to get notification if the fullscreen state has been successfully
1887 pspec = g_param_spec_boolean ("fullscreen-set",
1888 P_("Fullscreen Set"),
1889 P_("Whether the main stage is fullscreen"),
1891 CLUTTER_PARAM_READABLE);
1892 g_object_class_install_property (gobject_class,
1893 PROP_FULLSCREEN_SET,
1896 * ClutterStage:offscreen:
1898 * Whether the stage should be rendered in an offscreen buffer.
1900 * Deprecated: 1.10: This property does not do anything.
1902 pspec = g_param_spec_boolean ("offscreen",
1904 P_("Whether the main stage should be rendered offscreen"),
1906 CLUTTER_PARAM_READWRITE | G_PARAM_DEPRECATED);
1907 g_object_class_install_property (gobject_class,
1911 * ClutterStage:cursor-visible:
1913 * Whether the mouse pointer should be visible
1915 pspec = g_param_spec_boolean ("cursor-visible",
1916 P_("Cursor Visible"),
1917 P_("Whether the mouse pointer is visible on the main stage"),
1919 CLUTTER_PARAM_READWRITE);
1920 g_object_class_install_property (gobject_class,
1921 PROP_CURSOR_VISIBLE,
1924 * ClutterStage:user-resizable:
1926 * Whether the stage is resizable via user interaction.
1930 pspec = g_param_spec_boolean ("user-resizable",
1931 P_("User Resizable"),
1932 P_("Whether the stage is able to be resized via user interaction"),
1934 CLUTTER_PARAM_READWRITE);
1935 g_object_class_install_property (gobject_class,
1936 PROP_USER_RESIZABLE,
1939 * ClutterStage:color:
1941 * The background color of the main stage.
1943 * Deprecated: 1.10: Use the #ClutterActor:background-color property of
1944 * #ClutterActor instead.
1946 pspec = clutter_param_spec_color ("color",
1948 P_("The color of the stage"),
1949 &default_stage_color,
1950 CLUTTER_PARAM_READWRITE |
1951 G_PARAM_DEPRECATED);
1952 g_object_class_install_property (gobject_class, PROP_COLOR, pspec);
1955 * ClutterStage:perspective:
1957 * The parameters used for the perspective projection from 3D
1962 pspec = g_param_spec_boxed ("perspective",
1964 P_("Perspective projection parameters"),
1965 CLUTTER_TYPE_PERSPECTIVE,
1966 CLUTTER_PARAM_READWRITE);
1967 g_object_class_install_property (gobject_class,
1972 * ClutterStage:title:
1974 * The stage's title - usually displayed in stage windows title decorations.
1978 pspec = g_param_spec_string ("title",
1982 CLUTTER_PARAM_READWRITE);
1983 g_object_class_install_property (gobject_class, PROP_TITLE, pspec);
1986 * ClutterStage:use-fog:
1988 * Whether the stage should use a linear GL "fog" in creating the
1989 * depth-cueing effect, to enhance the perception of depth by fading
1990 * actors farther from the viewpoint.
1994 * Deprecated: 1.10: This property does not do anything.
1996 pspec = g_param_spec_boolean ("use-fog",
1998 P_("Whether to enable depth cueing"),
2000 CLUTTER_PARAM_READWRITE | G_PARAM_DEPRECATED);
2001 g_object_class_install_property (gobject_class, PROP_USE_FOG, pspec);
2006 * The settings for the GL "fog", used only if #ClutterStage:use-fog
2011 * Deprecated: 1.10: This property does not do anything.
2013 pspec = g_param_spec_boxed ("fog",
2015 P_("Settings for the depth cueing"),
2017 CLUTTER_PARAM_READWRITE | G_PARAM_DEPRECATED);
2018 g_object_class_install_property (gobject_class, PROP_FOG, pspec);
2021 * ClutterStage:use-alpha:
2023 * Whether the #ClutterStage should honour the alpha component of the
2024 * #ClutterStage:color property when painting. If Clutter is run under
2025 * a compositing manager this will result in the stage being blended
2026 * with the underlying window(s)
2030 pspec = g_param_spec_boolean ("use-alpha",
2032 P_("Whether to honour the alpha component of the stage color"),
2034 CLUTTER_PARAM_READWRITE);
2035 g_object_class_install_property (gobject_class, PROP_USE_ALPHA, pspec);
2038 * ClutterStage:key-focus:
2040 * The #ClutterActor that will receive key events from the underlying
2043 * If %NULL, the #ClutterStage will receive the events.
2047 pspec = g_param_spec_object ("key-focus",
2049 P_("The currently key focused actor"),
2051 CLUTTER_PARAM_READWRITE);
2052 g_object_class_install_property (gobject_class, PROP_KEY_FOCUS, pspec);
2055 * ClutterStage:no-clear-hint:
2057 * Whether or not the #ClutterStage should clear its contents
2058 * before each paint cycle.
2060 * See clutter_stage_set_no_clear_hint() for further information.
2064 pspec = g_param_spec_boolean ("no-clear-hint",
2065 P_("No Clear Hint"),
2066 P_("Whether the stage should clear its contents"),
2068 CLUTTER_PARAM_READWRITE);
2069 g_object_class_install_property (gobject_class, PROP_NO_CLEAR_HINT, pspec);
2072 * ClutterStage:accept-focus:
2074 * Whether the #ClutterStage should accept key focus when shown.
2078 pspec = g_param_spec_boolean ("accept-focus",
2080 P_("Whether the stage should accept focus on show"),
2082 CLUTTER_PARAM_READWRITE);
2083 g_object_class_install_property (gobject_class, PROP_ACCEPT_FOCUS, pspec);
2086 * ClutterStage::fullscreen
2087 * @stage: the stage which was fullscreened
2089 * The ::fullscreen signal is emitted when the stage is made fullscreen.
2093 stage_signals[FULLSCREEN] =
2094 g_signal_new (I_("fullscreen"),
2095 G_TYPE_FROM_CLASS (gobject_class),
2097 G_STRUCT_OFFSET (ClutterStageClass, fullscreen),
2099 _clutter_marshal_VOID__VOID,
2102 * ClutterStage::unfullscreen
2103 * @stage: the stage which has left a fullscreen state.
2105 * The ::unfullscreen signal is emitted when the stage leaves a fullscreen
2110 stage_signals[UNFULLSCREEN] =
2111 g_signal_new (I_("unfullscreen"),
2112 G_TYPE_FROM_CLASS (gobject_class),
2114 G_STRUCT_OFFSET (ClutterStageClass, unfullscreen),
2116 _clutter_marshal_VOID__VOID,
2119 * ClutterStage::activate
2120 * @stage: the stage which was activated
2122 * The ::activate signal is emitted when the stage receives key focus
2123 * from the underlying window system.
2127 stage_signals[ACTIVATE] =
2128 g_signal_new (I_("activate"),
2129 G_TYPE_FROM_CLASS (gobject_class),
2131 G_STRUCT_OFFSET (ClutterStageClass, activate),
2133 _clutter_marshal_VOID__VOID,
2136 * ClutterStage::deactivate
2137 * @stage: the stage which was deactivated
2139 * The ::activate signal is emitted when the stage loses key focus
2140 * from the underlying window system.
2144 stage_signals[DEACTIVATE] =
2145 g_signal_new (I_("deactivate"),
2146 G_TYPE_FROM_CLASS (gobject_class),
2148 G_STRUCT_OFFSET (ClutterStageClass, deactivate),
2150 _clutter_marshal_VOID__VOID,
2154 * ClutterStage::delete-event:
2155 * @stage: the stage that received the event
2156 * @event: a #ClutterEvent of type %CLUTTER_DELETE
2158 * The ::delete-event signal is emitted when the user closes a
2159 * #ClutterStage window using the window controls.
2161 * Clutter by default will call clutter_main_quit() if @stage is
2162 * the default stage, and clutter_actor_destroy() for any other
2165 * It is possible to override the default behaviour by connecting
2166 * a new handler and returning %TRUE there.
2168 * <note>This signal is emitted only on Clutter backends that
2169 * embed #ClutterStage in native windows. It is not emitted for
2170 * backends that use a static frame buffer.</note>
2174 stage_signals[DELETE_EVENT] =
2175 g_signal_new (I_("delete-event"),
2176 G_TYPE_FROM_CLASS (gobject_class),
2178 G_STRUCT_OFFSET (ClutterStageClass, delete_event),
2179 _clutter_boolean_handled_accumulator, NULL,
2180 _clutter_marshal_BOOLEAN__BOXED,
2182 CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
2184 klass->fullscreen = clutter_stage_real_fullscreen;
2185 klass->activate = clutter_stage_real_activate;
2186 klass->deactivate = clutter_stage_real_deactivate;
2187 klass->delete_event = clutter_stage_real_delete_event;
2189 g_type_class_add_private (gobject_class, sizeof (ClutterStagePrivate));
2193 clutter_stage_notify_min_size (ClutterStage *self)
2195 self->priv->min_size_changed = TRUE;
2199 clutter_stage_init (ClutterStage *self)
2201 cairo_rectangle_int_t geom = { 0, };
2202 ClutterStagePrivate *priv;
2203 ClutterStageWindow *impl;
2204 ClutterBackend *backend;
2207 /* a stage is a top-level object */
2208 CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IS_TOPLEVEL);
2210 self->priv = priv = CLUTTER_STAGE_GET_PRIVATE (self);
2212 CLUTTER_NOTE (BACKEND, "Creating stage from the default backend");
2213 backend = clutter_get_default_backend ();
2216 impl = _clutter_backend_create_stage (backend, self, &error);
2218 if (G_LIKELY (impl != NULL))
2220 _clutter_stage_set_window (self, impl);
2221 _clutter_stage_window_get_geometry (priv->impl, &geom);
2227 g_critical ("Unable to create a new stage implementation: %s",
2229 g_error_free (error);
2232 g_critical ("Unable to create a new stage implementation.");
2235 priv->event_queue = g_queue_new ();
2237 priv->is_fullscreen = FALSE;
2238 priv->is_user_resizable = FALSE;
2239 priv->is_cursor_visible = TRUE;
2240 priv->use_fog = FALSE;
2241 priv->throttle_motion_events = TRUE;
2242 priv->min_size_changed = FALSE;
2244 /* XXX - we need to keep the invariant that calling
2245 * clutter_set_motion_event_enabled() before the stage creation
2246 * will cause motion event delivery to be disabled on any newly
2247 * created stage. this can go away when we break API and remove
2248 * deprecated functions.
2250 priv->motion_events_enabled = _clutter_context_get_motion_events_enabled ();
2252 clutter_actor_set_background_color (CLUTTER_ACTOR (self),
2253 &default_stage_color);
2255 priv->perspective.fovy = 60.0; /* 60 Degrees */
2256 priv->perspective.aspect = (float) geom.width / (float) geom.height;
2257 priv->perspective.z_near = 0.1;
2258 priv->perspective.z_far = 100.0;
2260 cogl_matrix_init_identity (&priv->projection);
2261 cogl_matrix_perspective (&priv->projection,
2262 priv->perspective.fovy,
2263 priv->perspective.aspect,
2264 priv->perspective.z_near,
2265 priv->perspective.z_far);
2266 cogl_matrix_get_inverse (&priv->projection,
2267 &priv->inverse_projection);
2268 cogl_matrix_init_identity (&priv->view);
2269 cogl_matrix_view_2d_in_perspective (&priv->view,
2270 priv->perspective.fovy,
2271 priv->perspective.aspect,
2272 priv->perspective.z_near,
2273 50, /* distance to 2d plane */
2278 /* FIXME - remove for 2.0 */
2279 priv->fog.z_near = 1.0;
2280 priv->fog.z_far = 2.0;
2282 priv->relayout_pending = TRUE;
2284 clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE);
2285 clutter_stage_set_title (self, g_get_prgname ());
2286 clutter_stage_set_key_focus (self, NULL);
2288 g_signal_connect (self, "notify::min-width",
2289 G_CALLBACK (clutter_stage_notify_min_size), NULL);
2290 g_signal_connect (self, "notify::min-height",
2291 G_CALLBACK (clutter_stage_notify_min_size), NULL);
2293 _clutter_stage_set_viewport (self, 0, 0, geom.width, geom.height);
2295 _clutter_stage_set_pick_buffer_valid (self, FALSE, CLUTTER_PICK_ALL);
2296 priv->picks_per_frame = 0;
2298 priv->paint_volume_stack =
2299 g_array_new (FALSE, FALSE, sizeof (ClutterPaintVolume));
2301 priv->devices = g_hash_table_new (NULL, NULL);
2303 priv->pick_id_pool = _clutter_id_pool_new (256);
2307 * clutter_stage_get_default:
2309 * Retrieves a #ClutterStage singleton.
2311 * This function is not as useful as it sounds, and will most likely
2312 * by deprecated in the future. Application code should only create
2313 * a #ClutterStage instance using clutter_stage_new(), and manage the
2314 * lifetime of the stage manually.
2316 * The default stage singleton has a platform-specific behaviour: on
2317 * platforms without the %CLUTTER_FEATURE_STAGE_MULTIPLE feature flag
2318 * set, the first #ClutterStage instance will also be set to be the
2319 * default stage instance, and this function will always return a
2322 * On platforms with the %CLUTTER_FEATURE_STAGE_MULTIPLE feature flag
2323 * set, the default stage will be created by the first call to this
2324 * function, and every following call will return the same pointer to
2327 * Return value: (transfer none) (type Clutter.Stage): the main
2328 * #ClutterStage. You should never destroy or unref the returned
2331 * Deprecated: 1.10: Use clutter_stage_new() instead.
2334 clutter_stage_get_default (void)
2336 ClutterStageManager *stage_manager = clutter_stage_manager_get_default ();
2337 ClutterStage *stage;
2339 stage = clutter_stage_manager_get_default_stage (stage_manager);
2340 if (G_UNLIKELY (stage == NULL))
2342 /* This will take care of automatically adding the stage to the
2343 * stage manager and setting it as the default. Its floating
2344 * reference will be claimed by the stage manager.
2346 stage = g_object_new (CLUTTER_TYPE_STAGE, NULL);
2347 _clutter_stage_manager_set_default_stage (stage_manager, stage);
2349 /* the default stage is realized by default */
2350 clutter_actor_realize (CLUTTER_ACTOR (stage));
2353 return CLUTTER_ACTOR (stage);
2357 * clutter_stage_set_color:
2358 * @stage: A #ClutterStage
2359 * @color: A #ClutterColor
2361 * Sets the stage color.
2363 * Deprecated: 1.10: Use clutter_actor_set_background_color() instead.
2366 clutter_stage_set_color (ClutterStage *stage,
2367 const ClutterColor *color)
2369 clutter_actor_set_background_color (CLUTTER_ACTOR (stage), color);
2371 g_object_notify (G_OBJECT (stage), "color");
2375 * clutter_stage_get_color:
2376 * @stage: A #ClutterStage
2377 * @color: (out caller-allocates): return location for a #ClutterColor
2379 * Retrieves the stage color.
2381 * Deprecated: 1.10: Use clutter_actor_get_background_color() instead.
2384 clutter_stage_get_color (ClutterStage *stage,
2385 ClutterColor *color)
2387 clutter_actor_get_background_color (CLUTTER_ACTOR (stage), color);
2391 clutter_stage_set_perspective_internal (ClutterStage *stage,
2392 ClutterPerspective *perspective)
2394 ClutterStagePrivate *priv = stage->priv;
2396 if (priv->perspective.fovy == perspective->fovy &&
2397 priv->perspective.aspect == perspective->aspect &&
2398 priv->perspective.z_near == perspective->z_near &&
2399 priv->perspective.z_far == perspective->z_far)
2402 priv->perspective = *perspective;
2404 cogl_matrix_init_identity (&priv->projection);
2405 cogl_matrix_perspective (&priv->projection,
2406 priv->perspective.fovy,
2407 priv->perspective.aspect,
2408 priv->perspective.z_near,
2409 priv->perspective.z_far);
2410 cogl_matrix_get_inverse (&priv->projection,
2411 &priv->inverse_projection);
2413 priv->dirty_projection = TRUE;
2414 clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
2418 * clutter_stage_set_perspective:
2419 * @stage: A #ClutterStage
2420 * @perspective: A #ClutterPerspective
2422 * Sets the stage perspective. Using this function is not recommended
2423 * because it will disable Clutter's attempts to generate an
2424 * appropriate perspective based on the size of the stage.
2427 clutter_stage_set_perspective (ClutterStage *stage,
2428 ClutterPerspective *perspective)
2430 ClutterStagePrivate *priv;
2432 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2433 g_return_if_fail (perspective != NULL);
2434 g_return_if_fail (perspective->z_far - perspective->z_near != 0);
2438 /* If the application ever calls this function then we'll stop
2439 automatically updating the perspective when the stage changes
2441 priv->has_custom_perspective = TRUE;
2443 clutter_stage_set_perspective_internal (stage, perspective);
2447 * clutter_stage_get_perspective:
2448 * @stage: A #ClutterStage
2449 * @perspective: (out caller-allocates) (allow-none): return location for a
2450 * #ClutterPerspective
2452 * Retrieves the stage perspective.
2455 clutter_stage_get_perspective (ClutterStage *stage,
2456 ClutterPerspective *perspective)
2458 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2459 g_return_if_fail (perspective != NULL);
2461 *perspective = stage->priv->perspective;
2465 * clutter_stage_get_projection_matrix:
2466 * @stage: A #ClutterStage
2467 * @projection: return location for a #CoglMatrix representing the
2468 * perspective projection applied to actors on the given
2471 * Retrieves the @stage's projection matrix. This is derived from the
2472 * current perspective set using clutter_stage_set_perspective().
2477 _clutter_stage_get_projection_matrix (ClutterStage *stage,
2478 CoglMatrix *projection)
2480 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2481 g_return_if_fail (projection != NULL);
2483 *projection = stage->priv->projection;
2486 /* This simply provides a simple mechanism for us to ensure that
2487 * the projection matrix gets re-asserted before painting.
2489 * This is used when switching between multiple stages */
2491 _clutter_stage_dirty_projection (ClutterStage *stage)
2493 stage->priv->dirty_projection = TRUE;
2497 * clutter_stage_set_viewport:
2498 * @stage: A #ClutterStage
2499 * @x: The X postition to render the stage at, in window coordinates
2500 * @y: The Y position to render the stage at, in window coordinates
2501 * @width: The width to render the stage at, in window coordinates
2502 * @height: The height to render the stage at, in window coordinates
2504 * Sets the stage viewport. The viewport defines a final scale and
2505 * translation of your rendered stage and actors. This lets you render
2506 * your stage into a subregion of the stage window or you could use it to
2507 * pan a subregion of the stage if your stage window is smaller then
2508 * the stage. (XXX: currently this isn't possible)
2510 * Unlike a scale and translation done using the modelview matrix this
2511 * is done after everything has had perspective projection applied, so
2512 * for example if you were to pan across a subregion of the stage using
2513 * the viewport then you would not see a change in perspective for the
2514 * actors on the stage.
2516 * Normally the stage viewport will automatically track the size of the
2517 * stage window with no offset so the stage will fill your window. This
2518 * behaviour can be changed with the "viewport-mimics-window" property
2519 * which will automatically be set to FALSE if you use this API. If
2520 * you want to revert to the original behaviour then you should set
2521 * this property back to %TRUE using
2522 * clutter_stage_set_viewport_mimics_window().
2523 * (XXX: If we were to make this API public then we might want to do
2524 * add that property.)
2526 * Note: currently this interface only support integer precision
2527 * offsets and sizes for viewports but the interface takes floats because
2528 * OpenGL 4.0 has introduced floating point viewports which we might
2529 * want to expose via this API eventually.
2534 _clutter_stage_set_viewport (ClutterStage *stage,
2540 ClutterStagePrivate *priv;
2542 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2547 if (x == priv->viewport[0] &&
2548 y == priv->viewport[1] &&
2549 width == priv->viewport[2] &&
2550 height == priv->viewport[3])
2553 priv->viewport[0] = x;
2554 priv->viewport[1] = y;
2555 priv->viewport[2] = width;
2556 priv->viewport[3] = height;
2558 priv->dirty_viewport = TRUE;
2560 queue_full_redraw (stage);
2563 /* This simply provides a simple mechanism for us to ensure that
2564 * the viewport gets re-asserted before next painting.
2566 * This is used when switching between multiple stages */
2568 _clutter_stage_dirty_viewport (ClutterStage *stage)
2570 stage->priv->dirty_viewport = TRUE;
2574 * clutter_stage_get_viewport:
2575 * @stage: A #ClutterStage
2576 * @x: A location for the X position where the stage is rendered,
2577 * in window coordinates.
2578 * @y: A location for the Y position where the stage is rendered,
2579 * in window coordinates.
2580 * @width: A location for the width the stage is rendered at,
2581 * in window coordinates.
2582 * @height: A location for the height the stage is rendered at,
2583 * in window coordinates.
2585 * Returns the viewport offset and size set using
2586 * clutter_stage_set_viewport() or if the "viewport-mimics-window" property
2587 * is TRUE then @x and @y will be set to 0 and @width and @height will equal
2588 * the width if the stage window.
2593 _clutter_stage_get_viewport (ClutterStage *stage,
2599 ClutterStagePrivate *priv;
2601 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2605 *x = priv->viewport[0];
2606 *y = priv->viewport[1];
2607 *width = priv->viewport[2];
2608 *height = priv->viewport[3];
2612 * clutter_stage_set_fullscreen:
2613 * @stage: a #ClutterStage
2614 * @fullscreen: %TRUE to to set the stage fullscreen
2616 * Asks to place the stage window in the fullscreen or unfullscreen
2619 ( Note that you shouldn't assume the window is definitely full screen
2620 * afterward, because other entities (e.g. the user or window manager)
2621 * could unfullscreen it again, and not all window managers honor
2622 * requests to fullscreen windows.
2624 * If you want to receive notification of the fullscreen state you
2625 * should either use the #ClutterStage::fullscreen and
2626 * #ClutterStage::unfullscreen signals, or use the notify signal
2627 * for the #ClutterStage:fullscreen-set property
2632 clutter_stage_set_fullscreen (ClutterStage *stage,
2633 gboolean fullscreen)
2635 ClutterStagePrivate *priv;
2637 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2641 if (priv->is_fullscreen != fullscreen)
2643 ClutterStageWindow *impl = CLUTTER_STAGE_WINDOW (priv->impl);
2644 ClutterStageWindowIface *iface;
2646 iface = CLUTTER_STAGE_WINDOW_GET_IFACE (impl);
2648 /* Only set if backend implements.
2650 * Also see clutter_stage_event() for setting priv->is_fullscreen
2651 * on state change event.
2653 if (iface->set_fullscreen)
2654 iface->set_fullscreen (impl, fullscreen);
2657 /* If the backend did fullscreen the stage window then we need to resize
2658 * the stage and update its viewport so we queue a relayout. Note: if the
2659 * fullscreen request is handled asynchronously we can't rely on this
2660 * queue_relayout to update the viewport, but for example the X backend
2661 * will recieve a ConfigureNotify after a successful resize which is how
2662 * we ensure the viewport is updated on X.
2664 clutter_actor_queue_relayout (CLUTTER_ACTOR (stage));
2668 * clutter_stage_get_fullscreen:
2669 * @stage: a #ClutterStage
2671 * Retrieves whether the stage is full screen or not
2673 * Return value: %TRUE if the stage is full screen
2678 clutter_stage_get_fullscreen (ClutterStage *stage)
2680 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
2682 return stage->priv->is_fullscreen;
2686 * clutter_stage_set_user_resizable:
2687 * @stage: a #ClutterStage
2688 * @resizable: whether the stage should be user resizable.
2690 * Sets if the stage is resizable by user interaction (e.g. via
2691 * window manager controls)
2696 clutter_stage_set_user_resizable (ClutterStage *stage,
2699 ClutterStagePrivate *priv;
2701 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2705 if (clutter_feature_available (CLUTTER_FEATURE_STAGE_USER_RESIZE)
2706 && priv->is_user_resizable != resizable)
2708 ClutterStageWindow *impl = CLUTTER_STAGE_WINDOW (priv->impl);
2709 ClutterStageWindowIface *iface;
2711 iface = CLUTTER_STAGE_WINDOW_GET_IFACE (impl);
2712 if (iface->set_user_resizable)
2714 priv->is_user_resizable = resizable;
2716 iface->set_user_resizable (impl, resizable);
2718 g_object_notify (G_OBJECT (stage), "user-resizable");
2724 * clutter_stage_get_user_resizable:
2725 * @stage: a #ClutterStage
2727 * Retrieves the value set with clutter_stage_set_user_resizable().
2729 * Return value: %TRUE if the stage is resizable by the user.
2734 clutter_stage_get_user_resizable (ClutterStage *stage)
2736 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
2738 return stage->priv->is_user_resizable;
2742 * clutter_stage_show_cursor:
2743 * @stage: a #ClutterStage
2745 * Shows the cursor on the stage window
2748 clutter_stage_show_cursor (ClutterStage *stage)
2750 ClutterStagePrivate *priv;
2752 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2755 if (!priv->is_cursor_visible)
2757 ClutterStageWindow *impl = CLUTTER_STAGE_WINDOW (priv->impl);
2758 ClutterStageWindowIface *iface;
2760 iface = CLUTTER_STAGE_WINDOW_GET_IFACE (impl);
2761 if (iface->set_cursor_visible)
2763 priv->is_cursor_visible = TRUE;
2765 iface->set_cursor_visible (impl, TRUE);
2767 g_object_notify (G_OBJECT (stage), "cursor-visible");
2773 * clutter_stage_hide_cursor:
2774 * @stage: a #ClutterStage
2776 * Makes the cursor invisible on the stage window
2781 clutter_stage_hide_cursor (ClutterStage *stage)
2783 ClutterStagePrivate *priv;
2785 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2788 if (priv->is_cursor_visible)
2790 ClutterStageWindow *impl = CLUTTER_STAGE_WINDOW (priv->impl);
2791 ClutterStageWindowIface *iface;
2793 iface = CLUTTER_STAGE_WINDOW_GET_IFACE (impl);
2794 if (iface->set_cursor_visible)
2796 priv->is_cursor_visible = FALSE;
2798 iface->set_cursor_visible (impl, FALSE);
2800 g_object_notify (G_OBJECT (stage), "cursor-visible");
2806 * clutter_stage_read_pixels:
2807 * @stage: A #ClutterStage
2808 * @x: x coordinate of the first pixel that is read from stage
2809 * @y: y coordinate of the first pixel that is read from stage
2810 * @width: Width dimention of pixels to be read, or -1 for the
2811 * entire stage width
2812 * @height: Height dimention of pixels to be read, or -1 for the
2813 * entire stage height
2815 * Makes a screenshot of the stage in RGBA 8bit data, returns a
2816 * linear buffer with @width * 4 as rowstride.
2818 * The alpha data contained in the returned buffer is driver-dependent,
2819 * and not guaranteed to hold any sensible value.
2821 * Return value: (array): a pointer to newly allocated memory with the buffer
2822 * or %NULL if the read failed. Use g_free() on the returned data
2823 * to release the resources it has allocated.
2826 clutter_stage_read_pixels (ClutterStage *stage,
2832 ClutterGeometry geom;
2835 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
2837 /* Force a redraw of the stage before reading back pixels */
2838 clutter_stage_ensure_current (stage);
2839 clutter_actor_paint (CLUTTER_ACTOR (stage));
2841 clutter_actor_get_allocation_geometry (CLUTTER_ACTOR (stage), &geom);
2847 height = geom.height;
2849 pixels = g_malloc (height * width * 4);
2851 cogl_read_pixels (x, y, width, height,
2852 COGL_READ_PIXELS_COLOR_BUFFER,
2853 COGL_PIXEL_FORMAT_RGBA_8888,
2860 * clutter_stage_get_actor_at_pos:
2861 * @stage: a #ClutterStage
2862 * @pick_mode: how the scene graph should be painted
2863 * @x: X coordinate to check
2864 * @y: Y coordinate to check
2866 * Checks the scene at the coordinates @x and @y and returns a pointer
2867 * to the #ClutterActor at those coordinates.
2869 * By using @pick_mode it is possible to control which actors will be
2870 * painted and thus available.
2872 * Return value: (transfer none): the actor at the specified coordinates,
2876 clutter_stage_get_actor_at_pos (ClutterStage *stage,
2877 ClutterPickMode pick_mode,
2881 return _clutter_stage_do_pick (stage, x, y, pick_mode);
2885 * clutter_stage_event:
2886 * @stage: a #ClutterStage
2887 * @event: a #ClutterEvent
2889 * This function is used to emit an event on the main stage.
2891 * You should rarely need to use this function, except for
2892 * synthetised events.
2894 * Return value: the return value from the signal emission
2899 clutter_stage_event (ClutterStage *stage,
2900 ClutterEvent *event)
2902 ClutterStagePrivate *priv;
2904 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
2905 g_return_val_if_fail (event != NULL, FALSE);
2909 if (event->type == CLUTTER_DELETE)
2911 gboolean retval = FALSE;
2913 g_signal_emit_by_name (stage, "event", event, &retval);
2916 g_signal_emit_by_name (stage, "delete-event", event, &retval);
2921 if (event->type != CLUTTER_STAGE_STATE)
2924 /* emit raw event */
2925 if (clutter_actor_event (CLUTTER_ACTOR (stage), event, FALSE))
2928 if (event->stage_state.changed_mask & CLUTTER_STAGE_STATE_FULLSCREEN)
2930 if (event->stage_state.new_state & CLUTTER_STAGE_STATE_FULLSCREEN)
2932 priv->is_fullscreen = TRUE;
2933 g_signal_emit (stage, stage_signals[FULLSCREEN], 0);
2935 g_object_notify (G_OBJECT (stage), "fullscreen-set");
2939 priv->is_fullscreen = FALSE;
2940 g_signal_emit (stage, stage_signals[UNFULLSCREEN], 0);
2942 g_object_notify (G_OBJECT (stage), "fullscreen-set");
2946 if (event->stage_state.changed_mask & CLUTTER_STAGE_STATE_ACTIVATED)
2948 if (event->stage_state.new_state & CLUTTER_STAGE_STATE_ACTIVATED)
2949 g_signal_emit (stage, stage_signals[ACTIVATE], 0);
2951 g_signal_emit (stage, stage_signals[DEACTIVATE], 0);
2958 * clutter_stage_set_title
2959 * @stage: A #ClutterStage
2960 * @title: A utf8 string for the stage windows title.
2962 * Sets the stage title.
2967 clutter_stage_set_title (ClutterStage *stage,
2970 ClutterStagePrivate *priv;
2971 ClutterStageWindow *impl;
2973 g_return_if_fail (CLUTTER_IS_STAGE (stage));
2977 g_free (priv->title);
2978 priv->title = g_strdup (title);
2980 impl = CLUTTER_STAGE_WINDOW (priv->impl);
2981 if (CLUTTER_STAGE_WINDOW_GET_IFACE(impl)->set_title != NULL)
2982 CLUTTER_STAGE_WINDOW_GET_IFACE (impl)->set_title (impl, priv->title);
2984 g_object_notify (G_OBJECT (stage), "title");
2988 * clutter_stage_get_title
2989 * @stage: A #ClutterStage
2991 * Gets the stage title.
2993 * Return value: pointer to the title string for the stage. The
2994 * returned string is owned by the actor and should not
2995 * be modified or freed.
3000 clutter_stage_get_title (ClutterStage *stage)
3002 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
3004 return stage->priv->title;
3008 on_key_focus_destroy (ClutterActor *actor,
3009 ClutterStage *stage)
3011 /* unset the key focus */
3012 clutter_stage_set_key_focus (stage, NULL);
3016 * clutter_stage_set_key_focus:
3017 * @stage: the #ClutterStage
3018 * @actor: (allow-none): the actor to set key focus to, or %NULL
3020 * Sets the key focus on @actor. An actor with key focus will receive
3021 * all the key events. If @actor is %NULL, the stage will receive
3027 clutter_stage_set_key_focus (ClutterStage *stage,
3028 ClutterActor *actor)
3030 ClutterStagePrivate *priv;
3032 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3033 g_return_if_fail (actor == NULL || CLUTTER_IS_ACTOR (actor));
3037 /* avoid emitting signals and notifications if we're setting the same
3038 * actor as the key focus
3040 if (priv->key_focused_actor == actor)
3043 if (priv->key_focused_actor != NULL)
3045 ClutterActor *old_focused_actor;
3047 old_focused_actor = priv->key_focused_actor;
3049 /* set key_focused_actor to NULL before emitting the signal or someone
3050 * might hide the previously focused actor in the signal handler and we'd
3051 * get re-entrant call and get glib critical from g_object_weak_unref
3053 g_signal_handlers_disconnect_by_func (priv->key_focused_actor,
3054 G_CALLBACK (on_key_focus_destroy),
3056 priv->key_focused_actor = NULL;
3058 g_signal_emit_by_name (old_focused_actor, "key-focus-out");
3061 g_signal_emit_by_name (stage, "key-focus-out");
3063 /* Note, if someone changes key focus in focus-out signal handler we'd be
3064 * overriding the latter call below moving the focus where it was originally
3065 * intended. The order of events would be:
3066 * 1st focus-out, 2nd focus-out (on stage), 2nd focus-in, 1st focus-in
3070 priv->key_focused_actor = actor;
3072 g_signal_connect (actor,
3073 "destroy", G_CALLBACK (on_key_focus_destroy),
3075 g_signal_emit_by_name (priv->key_focused_actor, "key-focus-in");
3078 g_signal_emit_by_name (stage, "key-focus-in");
3080 g_object_notify (G_OBJECT (stage), "key-focus");
3084 * clutter_stage_get_key_focus:
3085 * @stage: the #ClutterStage
3087 * Retrieves the actor that is currently under key focus.
3089 * Return value: (transfer none): the actor with key focus, or the stage
3094 clutter_stage_get_key_focus (ClutterStage *stage)
3096 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
3098 if (stage->priv->key_focused_actor)
3099 return stage->priv->key_focused_actor;
3101 return CLUTTER_ACTOR (stage);
3105 * clutter_stage_get_use_fog:
3106 * @stage: the #ClutterStage
3108 * Gets whether the depth cueing effect is enabled on @stage.
3110 * Return value: %TRUE if the depth cueing effect is enabled
3114 * Deprecated: 1.10: This function will always return %FALSE
3117 clutter_stage_get_use_fog (ClutterStage *stage)
3119 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
3121 return stage->priv->use_fog;
3125 * clutter_stage_set_use_fog:
3126 * @stage: the #ClutterStage
3127 * @fog: %TRUE for enabling the depth cueing effect
3129 * Sets whether the depth cueing effect on the stage should be enabled
3132 * Depth cueing is a 3D effect that makes actors farther away from the
3133 * viewing point less opaque, by fading them with the stage color.
3135 * The parameters of the GL fog used can be changed using the
3136 * clutter_stage_set_fog() function.
3140 * Deprecated: 1.10: Calling this function produces no visible effect
3143 clutter_stage_set_use_fog (ClutterStage *stage,
3149 * clutter_stage_set_fog:
3150 * @stage: the #ClutterStage
3151 * @fog: a #ClutterFog structure
3153 * Sets the fog (also known as "depth cueing") settings for the @stage.
3155 * A #ClutterStage will only use a linear fog progression, which
3156 * depends solely on the distance from the viewer. The cogl_set_fog()
3157 * function in COGL exposes more of the underlying implementation,
3158 * and allows changing the for progression function. It can be directly
3159 * used by disabling the #ClutterStage:use-fog property and connecting
3160 * a signal handler to the #ClutterActor::paint signal on the @stage,
3164 * clutter_stage_set_use_fog (stage, FALSE);
3165 * g_signal_connect (stage, "paint", G_CALLBACK (on_stage_paint), NULL);
3168 * The paint signal handler will call cogl_set_fog() with the
3173 * on_stage_paint (ClutterActor *actor)
3175 * ClutterColor stage_color = { 0, };
3176 * CoglColor fog_color = { 0, };
3178 * /* set the fog color to the stage background color */
3179 * clutter_stage_get_color (CLUTTER_STAGE (actor), &stage_color);
3180 * cogl_color_init_from_4ub (&fog_color,
3182 * stage_color.green,
3184 * stage_color.alpha);
3186 * /* enable fog */
3187 * cogl_set_fog (&fog_color,
3188 * COGL_FOG_MODE_EXPONENTIAL, /* mode */
3189 * 0.5, /* density */
3190 * 5.0, 30.0); /* z_near and z_far */
3194 * <note>The fogging functions only work correctly when the visible actors use
3195 * unmultiplied alpha colors. By default Cogl will premultiply textures and
3196 * cogl_set_source_color() will premultiply colors, so unless you explicitly
3197 * load your textures requesting an unmultiplied internal format and use
3198 * cogl_material_set_color() you can only use fogging with fully opaque actors.
3199 * Support for premultiplied colors will improve in the future when we can
3200 * depend on fragment shaders.</note>
3204 * Deprecated: 1.10: Fog settings are ignored.
3207 clutter_stage_set_fog (ClutterStage *stage,
3213 * clutter_stage_get_fog:
3214 * @stage: the #ClutterStage
3215 * @fog: (out): return location for a #ClutterFog structure
3217 * Retrieves the current depth cueing settings from the stage.
3221 * Deprecated: 1.10: This function will always return the default
3222 * values of #ClutterFog
3225 clutter_stage_get_fog (ClutterStage *stage,
3228 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3229 g_return_if_fail (fog != NULL);
3231 *fog = stage->priv->fog;
3234 /*** Perspective boxed type ******/
3237 clutter_perspective_copy (gpointer data)
3239 if (G_LIKELY (data))
3240 return g_slice_dup (ClutterPerspective, data);
3246 clutter_perspective_free (gpointer data)
3248 if (G_LIKELY (data))
3249 g_slice_free (ClutterPerspective, data);
3252 G_DEFINE_BOXED_TYPE (ClutterPerspective, clutter_perspective,
3253 clutter_perspective_copy,
3254 clutter_perspective_free);
3257 clutter_fog_copy (gpointer data)
3259 if (G_LIKELY (data))
3260 return g_slice_dup (ClutterFog, data);
3266 clutter_fog_free (gpointer data)
3268 if (G_LIKELY (data))
3269 g_slice_free (ClutterFog, data);
3272 G_DEFINE_BOXED_TYPE (ClutterFog, clutter_fog, clutter_fog_copy, clutter_fog_free);
3275 * clutter_stage_new:
3277 * Creates a new, non-default stage. A non-default stage is a new
3278 * top-level actor which can be used as another container. It works
3279 * exactly like the default stage, but while clutter_stage_get_default()
3280 * will always return the same instance, you will have to keep a pointer
3281 * to any #ClutterStage returned by clutter_stage_new().
3283 * The ability to support multiple stages depends on the current
3284 * backend. Use clutter_feature_available() and
3285 * %CLUTTER_FEATURE_STAGE_MULTIPLE to check at runtime whether a
3286 * backend supports multiple stages.
3288 * Return value: a new stage, or %NULL if the default backend does
3289 * not support multiple stages. Use clutter_actor_destroy() to
3290 * programmatically close the returned stage.
3295 clutter_stage_new (void)
3297 return g_object_new (CLUTTER_TYPE_STAGE, NULL);
3301 * clutter_stage_ensure_current:
3302 * @stage: the #ClutterStage
3304 * This function essentially makes sure the right GL context is
3305 * current for the passed stage. It is not intended to
3306 * be used by applications.
3311 clutter_stage_ensure_current (ClutterStage *stage)
3313 ClutterBackend *backend;
3315 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3317 backend = clutter_get_default_backend ();
3318 _clutter_backend_ensure_context (backend, stage);
3322 * clutter_stage_ensure_viewport:
3323 * @stage: a #ClutterStage
3325 * Ensures that the GL viewport is updated with the current
3326 * stage window size.
3328 * This function will queue a redraw of @stage.
3330 * This function should not be called by applications; it is used
3331 * when embedding a #ClutterStage into a toolkit with another
3332 * windowing system, like GTK+.
3337 clutter_stage_ensure_viewport (ClutterStage *stage)
3339 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3341 _clutter_stage_dirty_viewport (stage);
3343 clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
3346 /* This calculates a distance into the view frustum to position the
3347 * stage so there is a decent amount of space to position geometry
3348 * between the stage and the near clipping plane.
3350 * Some awkward issues with this problem are:
3351 * - It's not possible to have a gap as large as the stage size with
3352 * a fov > 53° which is basically always the case since the default
3354 * - This can be deduced if you consider that this requires a
3355 * triangle as wide as it is deep to fit in the frustum in front
3356 * of the z_near plane. That triangle will always have an angle
3357 * of 53.13° at the point sitting on the z_near plane, but if the
3358 * frustum has a wider fov angle the left/right clipping planes
3359 * can never converge with the two corners of our triangle no
3360 * matter what size the triangle has.
3361 * - With a fov > 53° there is a trade off between maximizing the gap
3362 * size relative to the stage size but not loosing depth precision.
3363 * - Perhaps ideally we wouldn't just consider the fov on the y-axis
3364 * that is usually used to define a perspective, we would consider
3365 * the fov of the axis with the largest stage size so the gap would
3366 * accommodate that size best.
3368 * After going around in circles a few times with how to handle these
3369 * issues, we decided in the end to go for the simplest solution to
3370 * start with instead of an elaborate function that handles arbitrary
3371 * fov angles that we currently have no use-case for.
3373 * The solution assumes a fovy of 60° and for that case gives a gap
3374 * that's 85% of the stage height. We can consider more elaborate
3375 * functions if necessary later.
3377 * One guide we had to steer the gap size we support is the
3378 * interactive test, test-texture-quality which expects to animate an
3379 * actor to +400 on the z axis with a stage size of 640x480. A gap
3380 * that's 85% of the stage height gives a gap of 408 in that case.
3383 calculate_z_translation (float z_near)
3385 /* This solution uses fairly basic trigonometry, but is seems worth
3386 * clarifying the particular geometry we are looking at in-case
3387 * anyone wants to develop this further later. Not sure how well an
3388 * ascii diagram is going to work :-)
3390 * |--- stage_height ---|
3392 * ╲━━━━━━━━━━━━━━━━━━━━━╱------------
3394 * C ╲ . │ . ╱ gap| |
3395 * =0.5°╲ . a │ . ╱ | |
3397 * ╲ B.│. ╱near plane | |
3398 * A= ╲━━━━━━━━━╱------------- |
3399 * 120° ╲ c │ ╱ | z_2d
3403 * plane ╳----------------------
3408 * The area of interest is the triangle labeled (1) at the top left
3409 * marked with the ... line (a) from where the origin line crosses
3410 * the near plane to the top left where the stage line cross the
3413 * The sides of the triangle are a, b and c and the corresponding
3414 * angles opposite those sides are A, B and C.
3416 * The angle of C is what trades off the gap size we have relative
3417 * to the stage size vs the depth precision we have.
3419 * As mentioned above we arove at the angle for C is by working
3420 * backwards from how much space we want for test-texture-quality.
3421 * With a stage_height of 480 we want a gap > 400, ideally we also
3422 * wanted a somewhat round number as a percentage of the height for
3423 * documentation purposes. ~87% or a gap of ~416 is the limit
3424 * because that's where we approach a C angle of 0° and effectively
3425 * loose all depth precision.
3427 * So for our test app with a stage_height of 480 if we aim for a
3428 * gap of 408 (85% of 480) we can get the angle D as
3429 * atan (stage_height/2/408) = 30.5°.
3431 * That gives us the angle for B as 90° - 30.5° = 59.5°
3433 * We can already determine that A has an angle of (fovy/2 + 90°) =
3436 * Therefore C = 180 - A - B = 0.5°
3438 * The length of c = z_near * tan (30°)
3440 * Now we can use the rule a/SinA = c/SinC to calculate the
3441 * length of a. After some rearranging that gives us:
3444 * ---------- = ----------
3445 * sin (120°) sin (0.5°)
3448 * a = --------------
3451 * And with that we can determine z_2d = cos (D) * a =
3452 * cos (30.5°) * a + z_near:
3454 * c * sin (120°) * cos (30.5°)
3455 * z_2d = --------------------------- + z_near
3458 #define _DEG_TO_RAD (G_PI / 180.0)
3459 return z_near * tanf (30.0f * _DEG_TO_RAD) *
3460 sinf (120.0f * _DEG_TO_RAD) * cosf (30.5f * _DEG_TO_RAD) /
3461 sinf (0.5f * _DEG_TO_RAD) +
3464 /* We expect the compiler should boil this down to z_near * CONSTANT */
3468 _clutter_stage_maybe_setup_viewport (ClutterStage *stage)
3470 ClutterStagePrivate *priv = stage->priv;
3472 if (priv->dirty_viewport)
3474 ClutterPerspective perspective;
3477 CLUTTER_NOTE (PAINT,
3478 "Setting up the viewport { w:%f, h:%f }",
3479 priv->viewport[2], priv->viewport[3]);
3481 cogl_set_viewport (priv->viewport[0],
3486 perspective = priv->perspective;
3488 /* Ideally we want to regenerate the perspective matrix whenever
3489 * the size changes but if the user has provided a custom matrix
3490 * then we don't want to override it */
3491 if (!priv->has_custom_perspective)
3493 perspective.aspect = priv->viewport[2] / priv->viewport[3];
3494 z_2d = calculate_z_translation (perspective.z_near);
3496 #define _DEG_TO_RAD (G_PI / 180.0)
3497 /* NB: z_2d is only enough room for 85% of the stage_height between
3498 * the stage and the z_near plane. For behind the stage plane we
3499 * want a more consistent gap of 10 times the stage_height before
3500 * hitting the far plane so we calculate that relative to the final
3501 * height of the stage plane at the z_2d_distance we got... */
3502 perspective.z_far = z_2d +
3503 tanf ((perspective.fovy / 2.0f) * _DEG_TO_RAD) * z_2d * 20.0f;
3506 clutter_stage_set_perspective_internal (stage, &perspective);
3509 z_2d = calculate_z_translation (perspective.z_near);
3511 cogl_matrix_init_identity (&priv->view);
3512 cogl_matrix_view_2d_in_perspective (&priv->view,
3520 priv->dirty_viewport = FALSE;
3523 if (priv->dirty_projection)
3525 cogl_set_projection_matrix (&priv->projection);
3527 priv->dirty_projection = FALSE;
3532 * clutter_stage_ensure_redraw:
3533 * @stage: a #ClutterStage
3535 * Ensures that @stage is redrawn
3537 * This function should not be called by applications: it is
3538 * used when embedding a #ClutterStage into a toolkit with
3539 * another windowing system, like GTK+.
3544 clutter_stage_ensure_redraw (ClutterStage *stage)
3546 ClutterMasterClock *master_clock;
3547 ClutterStagePrivate *priv;
3549 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3552 priv->relayout_pending = TRUE;
3553 priv->redraw_pending = TRUE;
3555 master_clock = _clutter_master_clock_get_default ();
3556 _clutter_master_clock_start_running (master_clock);
3560 * clutter_stage_queue_redraw:
3561 * @stage: the #ClutterStage
3563 * Queues a redraw for the passed stage.
3565 * <note>Applications should call clutter_actor_queue_redraw() and not
3566 * this function.</note>
3570 * Deprecated: 1.10: Use clutter_actor_queue_redraw() instead.
3573 clutter_stage_queue_redraw (ClutterStage *stage)
3575 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3577 clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
3581 * clutter_stage_is_default:
3582 * @stage: a #ClutterStage
3584 * Checks if @stage is the default stage, or an instance created using
3585 * clutter_stage_new() but internally using the same implementation.
3587 * Return value: %TRUE if the passed stage is the default one
3591 * Deprecated: 1.10: Track the stage pointer inside your application
3592 * code, or use clutter_actor_get_stage() to retrieve the stage for
3596 clutter_stage_is_default (ClutterStage *stage)
3598 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
3600 return stage_is_default (stage);
3604 _clutter_stage_set_window (ClutterStage *stage,
3605 ClutterStageWindow *stage_window)
3607 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3608 g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (stage_window));
3610 if (stage->priv->impl != NULL)
3611 g_object_unref (stage->priv->impl);
3613 stage->priv->impl = stage_window;
3616 ClutterStageWindow *
3617 _clutter_stage_get_window (ClutterStage *stage)
3619 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
3621 return CLUTTER_STAGE_WINDOW (stage->priv->impl);
3624 ClutterStageWindow *
3625 _clutter_stage_get_default_window (void)
3627 ClutterStageManager *manager = clutter_stage_manager_get_default ();
3628 ClutterStage *stage;
3630 stage = clutter_stage_manager_get_default_stage (manager);
3634 return _clutter_stage_get_window (stage);
3638 * clutter_stage_set_throttle_motion_events:
3639 * @stage: a #ClutterStage
3640 * @throttle: %TRUE to throttle motion events
3642 * Sets whether motion events received between redraws should
3643 * be throttled or not. If motion events are throttled, those
3644 * events received by the windowing system between redraws will
3645 * be compressed so that only the last event will be propagated
3646 * to the @stage and its actors.
3648 * This function should only be used if you want to have all
3649 * the motion events delivered to your application code.
3654 clutter_stage_set_throttle_motion_events (ClutterStage *stage,
3657 ClutterStagePrivate *priv;
3659 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3663 if (priv->throttle_motion_events != throttle)
3664 priv->throttle_motion_events = throttle;
3668 * clutter_stage_get_throttle_motion_events:
3669 * @stage: a #ClutterStage
3671 * Retrieves the value set with clutter_stage_set_throttle_motion_events()
3673 * Return value: %TRUE if the motion events are being throttled,
3674 * and %FALSE otherwise
3679 clutter_stage_get_throttle_motion_events (ClutterStage *stage)
3681 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
3683 return stage->priv->throttle_motion_events;
3687 * clutter_stage_set_use_alpha:
3688 * @stage: a #ClutterStage
3689 * @use_alpha: whether the stage should honour the opacity or the
3690 * alpha channel of the stage color
3692 * Sets whether the @stage should honour the #ClutterActor:opacity and
3693 * the alpha channel of the #ClutterStage:color
3698 clutter_stage_set_use_alpha (ClutterStage *stage,
3701 ClutterStagePrivate *priv;
3703 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3707 if (priv->use_alpha != use_alpha)
3709 priv->use_alpha = use_alpha;
3711 clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
3713 g_object_notify (G_OBJECT (stage), "use-alpha");
3718 * clutter_stage_get_use_alpha:
3719 * @stage: a #ClutterStage
3721 * Retrieves the value set using clutter_stage_set_use_alpha()
3723 * Return value: %TRUE if the stage should honour the opacity and the
3724 * alpha channel of the stage color
3729 clutter_stage_get_use_alpha (ClutterStage *stage)
3731 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
3733 return stage->priv->use_alpha;
3737 * clutter_stage_set_minimum_size:
3738 * @stage: a #ClutterStage
3739 * @width: width, in pixels
3740 * @height: height, in pixels
3742 * Sets the minimum size for a stage window, if the default backend
3743 * uses #ClutterStage inside a window
3745 * This is a convenience function, and it is equivalent to setting the
3746 * #ClutterActor:min-width and #ClutterActor:min-height on @stage
3748 * If the current size of @stage is smaller than the minimum size, the
3749 * @stage will be resized to the new @width and @height
3751 * This function has no effect if @stage is fullscreen
3756 clutter_stage_set_minimum_size (ClutterStage *stage,
3760 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3761 g_return_if_fail ((width > 0) && (height > 0));
3763 g_object_set (G_OBJECT (stage),
3764 "min-width", (gfloat) width,
3765 "min-height", (gfloat )height,
3770 * clutter_stage_get_minimum_size:
3771 * @stage: a #ClutterStage
3772 * @width: (out): return location for the minimum width, in pixels,
3774 * @height: (out): return location for the minimum height, in pixels,
3777 * Retrieves the minimum size for a stage window as set using
3778 * clutter_stage_set_minimum_size().
3780 * The returned size may not correspond to the actual minimum size and
3781 * it is specific to the #ClutterStage implementation inside the
3787 clutter_stage_get_minimum_size (ClutterStage *stage,
3791 gfloat width, height;
3792 gboolean width_set, height_set;
3794 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3796 g_object_get (G_OBJECT (stage),
3797 "min-width", &width,
3798 "min-width-set", &width_set,
3799 "min-height", &height,
3800 "min-height-set", &height_set,
3803 /* if not width or height have been set, then the Stage
3804 * minimum size is defined to be 1x1
3813 *width_p = (guint) width;
3816 *height_p = (guint) height;
3819 /* Returns the number of swap buffers pending completion for the stage */
3821 _clutter_stage_get_pending_swaps (ClutterStage *stage)
3823 ClutterStageWindow *stage_window;
3825 if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
3828 stage_window = _clutter_stage_get_window (stage);
3829 if (stage_window == NULL)
3832 return _clutter_stage_window_get_pending_swaps (stage_window);
3836 * clutter_stage_set_no_clear_hint:
3837 * @stage: a #ClutterStage
3838 * @no_clear: %TRUE if the @stage should not clear itself on every
3841 * Sets whether the @stage should clear itself at the beginning
3842 * of each paint cycle or not.
3844 * Clearing the #ClutterStage can be a costly operation, especially
3845 * if the stage is always covered - for instance, in a full-screen
3846 * video player or in a game with a background texture.
3848 * <note><para>This setting is a hint; Clutter might discard this
3849 * hint depending on its internal state.</para></note>
3851 * <warning><para>If parts of the stage are visible and you disable
3852 * clearing you might end up with visual artifacts while painting the
3853 * contents of the stage.</para></warning>
3858 clutter_stage_set_no_clear_hint (ClutterStage *stage,
3861 ClutterStagePrivate *priv;
3862 ClutterStageHint new_hints;
3864 g_return_if_fail (CLUTTER_IS_STAGE (stage));
3867 new_hints = priv->stage_hints;
3870 new_hints |= CLUTTER_STAGE_NO_CLEAR_ON_PAINT;
3872 new_hints &= ~CLUTTER_STAGE_NO_CLEAR_ON_PAINT;
3874 if (priv->stage_hints == new_hints)
3877 priv->stage_hints = new_hints;
3879 g_object_notify (G_OBJECT (stage), "no-clear-hint");
3883 * clutter_stage_get_no_clear_hint:
3884 * @stage: a #ClutterStage
3886 * Retrieves the hint set with clutter_stage_set_no_clear_hint()
3888 * Return value: %TRUE if the stage should not clear itself on every paint
3889 * cycle, and %FALSE otherwise
3894 clutter_stage_get_no_clear_hint (ClutterStage *stage)
3896 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
3898 return (stage->priv->stage_hints & CLUTTER_STAGE_NO_CLEAR_ON_PAINT) != 0;
3901 ClutterPaintVolume *
3902 _clutter_stage_paint_volume_stack_allocate (ClutterStage *stage)
3904 GArray *paint_volume_stack = stage->priv->paint_volume_stack;
3906 g_array_set_size (paint_volume_stack,
3907 paint_volume_stack->len+1);
3909 return &g_array_index (paint_volume_stack,
3911 paint_volume_stack->len - 1);
3915 _clutter_stage_paint_volume_stack_free_all (ClutterStage *stage)
3917 GArray *paint_volume_stack = stage->priv->paint_volume_stack;
3920 for (i = 0; i < paint_volume_stack->len; i++)
3922 ClutterPaintVolume *pv =
3923 &g_array_index (paint_volume_stack, ClutterPaintVolume, i);
3924 clutter_paint_volume_free (pv);
3927 g_array_set_size (paint_volume_stack, 0);
3930 /* The is an out-of-band paramater available while painting that
3931 * can be used to cull actors. */
3932 const ClutterPlane *
3933 _clutter_stage_get_clip (ClutterStage *stage)
3935 return stage->priv->current_clip_planes;
3938 /* When an actor queues a redraw we add it to a list on the stage that
3939 * gets processed once all updates to the stage have been finished.
3941 * This deferred approach to processing queue_redraw requests means
3942 * that we can avoid redundant transformations of clip volumes if
3943 * something later triggers a full stage redraw anyway. It also means
3944 * we can be more sure that all the referenced actors will have valid
3945 * allocations improving the chance that we can determine the actors
3946 * paint volume so we can clip the redraw request even if the user
3947 * didn't explicitly do so.
3949 ClutterStageQueueRedrawEntry *
3950 _clutter_stage_queue_actor_redraw (ClutterStage *stage,
3951 ClutterStageQueueRedrawEntry *entry,
3952 ClutterActor *actor,
3953 ClutterPaintVolume *clip)
3955 ClutterStagePrivate *priv = stage->priv;
3957 CLUTTER_NOTE (CLIPPING, "stage_queue_actor_redraw (actor=%s, clip=%p): ",
3958 _clutter_actor_get_debug_name (actor), clip);
3960 if (!priv->redraw_pending)
3962 ClutterMasterClock *master_clock;
3964 CLUTTER_NOTE (PAINT, "First redraw request");
3966 priv->redraw_pending = TRUE;
3968 master_clock = _clutter_master_clock_get_default ();
3969 _clutter_master_clock_start_running (master_clock);
3971 #ifdef CLUTTER_ENABLE_DEBUG
3974 CLUTTER_NOTE (PAINT, "Redraw request number %lu",
3975 priv->redraw_count + 1);
3977 priv->redraw_count += 1;
3979 #endif /* CLUTTER_ENABLE_DEBUG */
3981 /* We have an optimization in _clutter_stage_do_pick to detect when
3982 * the scene is static so we can cache a full, un-clipped pick
3983 * buffer to avoid continuous pick renders.
3985 * Currently the assumption is that actors queue a redraw when some
3986 * state changes that affects painting *or* picking so we can use
3987 * this point to invalidate any currently cached pick buffer.
3989 _clutter_stage_set_pick_buffer_valid (stage, FALSE, -1);
3993 /* Ignore all requests to queue a redraw for an actor if a full
3994 * (non-clipped) redraw of the actor has already been queued. */
3995 if (!entry->has_clip)
3997 CLUTTER_NOTE (CLIPPING, "Bail from stage_queue_actor_redraw (%s): "
3998 "Unclipped redraw of actor already queued",
3999 _clutter_actor_get_debug_name (actor));
4003 /* If queuing a clipped redraw and a clipped redraw has
4004 * previously been queued for this actor then combine the latest
4005 * clip together with the existing clip */
4007 clutter_paint_volume_union (&entry->clip, clip);
4010 clutter_paint_volume_free (&entry->clip);
4011 entry->has_clip = FALSE;
4017 entry = g_slice_new (ClutterStageQueueRedrawEntry);
4018 entry->actor = g_object_ref (actor);
4022 entry->has_clip = TRUE;
4023 _clutter_paint_volume_init_static (&entry->clip, actor);
4024 _clutter_paint_volume_set_from_volume (&entry->clip, clip);
4027 entry->has_clip = FALSE;
4029 stage->priv->pending_queue_redraws =
4030 g_list_prepend (stage->priv->pending_queue_redraws, entry);
4037 free_queue_redraw_entry (ClutterStageQueueRedrawEntry *entry)
4040 g_object_unref (entry->actor);
4041 if (entry->has_clip)
4042 clutter_paint_volume_free (&entry->clip);
4043 g_slice_free (ClutterStageQueueRedrawEntry, entry);
4047 _clutter_stage_queue_redraw_entry_invalidate (ClutterStageQueueRedrawEntry *entry)
4052 if (entry->actor != NULL)
4054 g_object_unref (entry->actor);
4055 entry->actor = NULL;
4058 if (entry->has_clip)
4060 clutter_paint_volume_free (&entry->clip);
4061 entry->has_clip = FALSE;
4066 _clutter_stage_maybe_finish_queue_redraws (ClutterStage *stage)
4068 /* Note: we have to repeat until the pending_queue_redraws list is
4069 * empty because actors are allowed to queue redraws in response to
4070 * the queue-redraw signal. For example Clone actors or
4071 * texture_new_from_actor actors will have to queue a redraw if
4072 * their source queues a redraw.
4074 while (stage->priv->pending_queue_redraws)
4077 /* XXX: we need to allow stage->priv->pending_queue_redraws to
4078 * be updated while we process the current entries in the list
4079 * so we steal the list pointer and then reset it to an empty
4080 * list before processing... */
4081 GList *stolen_list = stage->priv->pending_queue_redraws;
4082 stage->priv->pending_queue_redraws = NULL;
4084 for (l = stolen_list; l; l = l->next)
4086 ClutterStageQueueRedrawEntry *entry = l->data;
4087 ClutterPaintVolume *clip;
4089 /* NB: Entries may be invalidated if the actor gets destroyed */
4090 if (G_LIKELY (entry->actor != NULL))
4092 clip = entry->has_clip ? &entry->clip : NULL;
4094 _clutter_actor_finish_queue_redraw (entry->actor, clip);
4097 free_queue_redraw_entry (entry);
4099 g_list_free (stolen_list);
4104 * clutter_stage_set_accept_focus:
4105 * @stage: a #ClutterStage
4106 * @accept_focus: %TRUE to accept focus on show
4108 * Sets whether the @stage should accept the key focus when shown.
4110 * This function should be called before showing @stage using
4111 * clutter_actor_show().
4116 clutter_stage_set_accept_focus (ClutterStage *stage,
4117 gboolean accept_focus)
4119 ClutterStagePrivate *priv;
4121 g_return_if_fail (CLUTTER_IS_STAGE (stage));
4123 accept_focus = !!accept_focus;
4127 if (priv->accept_focus != accept_focus)
4129 _clutter_stage_window_set_accept_focus (priv->impl, accept_focus);
4130 g_object_notify (G_OBJECT (stage), "accept-focus");
4135 * clutter_stage_get_accept_focus:
4136 * @stage: a #ClutterStage
4138 * Retrieves the value set with clutter_stage_set_accept_focus().
4140 * Return value: %TRUE if the #ClutterStage should accept focus, and %FALSE
4146 clutter_stage_get_accept_focus (ClutterStage *stage)
4148 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), TRUE);
4150 return stage->priv->accept_focus;
4154 _clutter_stage_add_device (ClutterStage *stage,
4155 ClutterInputDevice *device)
4157 ClutterStagePrivate *priv = stage->priv;
4159 if (g_hash_table_lookup (priv->devices, device) != NULL)
4162 g_hash_table_insert (priv->devices, device, GINT_TO_POINTER (1));
4163 _clutter_input_device_set_stage (device, stage);
4167 _clutter_stage_remove_device (ClutterStage *stage,
4168 ClutterInputDevice *device)
4170 ClutterStagePrivate *priv = stage->priv;
4172 _clutter_input_device_set_stage (device, NULL);
4173 g_hash_table_remove (priv->devices, device);
4177 _clutter_stage_has_device (ClutterStage *stage,
4178 ClutterInputDevice *device)
4180 ClutterStagePrivate *priv = stage->priv;
4182 return g_hash_table_lookup (priv->devices, device) != NULL;
4186 * clutter_stage_set_motion_events_enabled:
4187 * @stage: a #ClutterStage
4188 * @enabled: %TRUE to enable the motion events delivery, and %FALSE
4191 * Sets whether per-actor motion events (and relative crossing
4192 * events) should be disabled or not.
4194 * The default is %TRUE.
4196 * If @enable is %FALSE the following events will not be delivered
4197 * to the actors children of @stage.
4200 * <listitem><para>#ClutterActor::motion-event</para></listitem>
4201 * <listitem><para>#ClutterActor::enter-event</para></listitem>
4202 * <listitem><para>#ClutterActor::leave-event</para></listitem>
4205 * The events will still be delivered to the #ClutterStage.
4207 * The main side effect of this function is that disabling the motion
4208 * events will disable picking to detect the #ClutterActor underneath
4209 * the pointer for each motion event. This is useful, for instance,
4210 * when dragging a #ClutterActor across the @stage: the actor underneath
4211 * the pointer is not going to change, so it's meaningless to perform
4217 clutter_stage_set_motion_events_enabled (ClutterStage *stage,
4220 ClutterStagePrivate *priv;
4222 g_return_if_fail (CLUTTER_IS_STAGE (stage));
4226 enabled = !!enabled;
4228 if (priv->motion_events_enabled != enabled)
4229 priv->motion_events_enabled = enabled;
4233 * clutter_stage_get_motion_events_enabled:
4234 * @stage: a #ClutterStage
4236 * Retrieves the value set using clutter_stage_set_motion_events_enabled().
4238 * Return value: %TRUE if the per-actor motion event delivery is enabled
4239 * and %FALSE otherwise
4244 clutter_stage_get_motion_events_enabled (ClutterStage *stage)
4246 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
4248 return stage->priv->motion_events_enabled;
4251 /* NB: The presumption shouldn't be that a stage can't be comprised
4252 * of multiple internal framebuffers, so instead of simply naming
4253 * this function _clutter_stage_get_framebuffer(), the "active"
4254 * infix is intended to clarify that it gets the framebuffer that
4255 * is currently in use/being painted.
4258 _clutter_stage_get_active_framebuffer (ClutterStage *stage)
4260 return stage->priv->active_framebuffer;
4264 _clutter_stage_acquire_pick_id (ClutterStage *stage,
4265 ClutterActor *actor)
4267 ClutterStagePrivate *priv = stage->priv;
4269 g_assert (priv->pick_id_pool != NULL);
4271 return _clutter_id_pool_add (priv->pick_id_pool, actor);
4275 _clutter_stage_release_pick_id (ClutterStage *stage,
4278 ClutterStagePrivate *priv = stage->priv;
4280 g_assert (priv->pick_id_pool != NULL);
4282 _clutter_id_pool_remove (priv->pick_id_pool, pick_id);
4286 _clutter_stage_get_actor_by_pick_id (ClutterStage *stage,
4289 ClutterStagePrivate *priv = stage->priv;
4291 g_assert (priv->pick_id_pool != NULL);
4293 return _clutter_id_pool_lookup (priv->pick_id_pool, pick_id);
4297 _clutter_stage_add_drag_actor (ClutterStage *stage,
4298 ClutterInputDevice *device,
4299 ClutterActor *actor)
4301 GHashTable *drag_actors;
4303 drag_actors = g_object_get_data (G_OBJECT (stage), "__clutter_stage_drag_actors");
4304 if (drag_actors == NULL)
4306 drag_actors = g_hash_table_new (NULL, NULL);
4307 g_object_set_data_full (G_OBJECT (stage), "__clutter_stage_drag_actors",
4309 (GDestroyNotify) g_hash_table_destroy);
4312 g_hash_table_replace (drag_actors, device, actor);
4316 _clutter_stage_get_drag_actor (ClutterStage *stage,
4317 ClutterInputDevice *device)
4319 GHashTable *drag_actors;
4321 drag_actors = g_object_get_data (G_OBJECT (stage), "__clutter_stage_drag_actors");
4322 if (drag_actors == NULL)
4325 return g_hash_table_lookup (drag_actors, device);
4329 _clutter_stage_remove_drag_actor (ClutterStage *stage,
4330 ClutterInputDevice *device)
4332 GHashTable *drag_actors;
4334 drag_actors = g_object_get_data (G_OBJECT (stage), "__clutter_stage_drag_actors");
4335 if (drag_actors == NULL)
4338 g_hash_table_remove (drag_actors, device);
4340 if (g_hash_table_size (drag_actors) == 0)
4341 g_object_set_data (G_OBJECT (stage), "__clutter_stage_drag_actors", NULL);
4345 * _clutter_stage_get_state:
4346 * @stage: a #ClutterStage
4348 * Retrieves the current #ClutterStageState flags associated to the @stage.
4350 * Return value: a bitwise OR of #ClutterStageState flags
4353 _clutter_stage_get_state (ClutterStage *stage)
4355 return stage->priv->current_state;
4359 * _clutter_stage_is_activated:
4360 * @stage: a #ClutterStage
4362 * Checks whether the @stage state includes %CLUTTER_STAGE_STATE_ACTIVATED.
4364 * Return value: %TRUE if the @stage is active
4367 _clutter_stage_is_activated (ClutterStage *stage)
4369 return (stage->priv->current_state & CLUTTER_STAGE_STATE_ACTIVATED) != 0;
4373 * _clutter_stage_is_fullscreen:
4374 * @stage: a #ClutterStage
4376 * Checks whether the @stage state includes %CLUTTER_STAGE_STATE_FULLSCREEN.
4378 * Return value: %TRUE if the @stage is fullscreen
4381 _clutter_stage_is_fullscreen (ClutterStage *stage)
4383 return (stage->priv->current_state & CLUTTER_STAGE_STATE_FULLSCREEN) != 0;
4387 * _clutter_stage_update_state:
4388 * @stage: a #ClutterStage
4389 * @unset_flags: flags to unset
4390 * @set_flags: flags to set
4392 * Updates the state of @stage, by unsetting the @unset_flags and setting
4395 * If the stage state has been changed, this function will queue a
4396 * #ClutterEvent of type %CLUTTER_STAGE_STATE.
4398 * Return value: %TRUE if the state was updated, and %FALSE otherwise
4401 _clutter_stage_update_state (ClutterStage *stage,
4402 ClutterStageState unset_flags,
4403 ClutterStageState set_flags)
4405 ClutterStageState new_state;
4406 ClutterEvent *event;
4408 new_state = stage->priv->current_state;
4409 new_state |= set_flags;
4410 new_state &= ~unset_flags;
4412 if (new_state == stage->priv->current_state)
4415 event = clutter_event_new (CLUTTER_STAGE_STATE);
4416 clutter_event_set_stage (event, stage);
4418 event->stage_state.new_state = new_state;
4419 event->stage_state.changed_mask = new_state ^ stage->priv->current_state;
4421 stage->priv->current_state = new_state;
4423 _clutter_event_push (event, FALSE);