2008-03-28 Matthew Allum <mallum@openedhand.com>
authorMatthew Allum <mallum@openedhand.com>
Fri, 28 Mar 2008 22:50:55 +0000 (22:50 +0000)
committerMatthew Allum <mallum@openedhand.com>
Fri, 28 Mar 2008 22:50:55 +0000 (22:50 +0000)
        * clutter/Makefile.am:
        * clutter/clutter-actor.c:
        * clutter/clutter-actor.h:
        * clutter/clutter-backend.c:
        * clutter/clutter-backend.h:
        * clutter/clutter-debug.h:
        * clutter/clutter-event.c:
        * clutter/clutter-event.h:
        * clutter/clutter-feature.h:
        * clutter/clutter-group.h:
        * clutter/clutter-main.c:
        * clutter/clutter-main.h:
        * clutter/clutter-private.h:
        * clutter/clutter-stage.c:
        * clutter/clutter-stage.h:
        * clutter/clutter-stage-manager.c
        * clutter/clutter-stage-manager.h
        * clutter/clutter-types.h:
        * clutter/glx/clutter-backend-glx.c:
        * clutter/glx/clutter-backend-glx.h:
        * clutter/glx/clutter-stage-glx.c:
        * clutter/glx/clutter-stage-glx.h:
        * clutter/x11/clutter-backend-x11.c:
        * clutter/x11/clutter-backend-x11.h:
        * clutter/x11/clutter-event-x11.c:
        * clutter/x11/clutter-stage-x11.c:
        * clutter/x11/clutter-x11.h:
        * tests/Makefile.am:
        * tests/test-multistage.c:
        Initial commit of multi stage support (mostly a merge from the
        clutter-multistage branch).
        Note, this commit will break all backends except glx.

30 files changed:
ChangeLog
clutter/Makefile.am
clutter/clutter-actor.c
clutter/clutter-actor.h
clutter/clutter-backend.c
clutter/clutter-backend.h
clutter/clutter-debug.h
clutter/clutter-event.c
clutter/clutter-event.h
clutter/clutter-feature.h
clutter/clutter-group.h
clutter/clutter-main.c
clutter/clutter-main.h
clutter/clutter-private.h
clutter/clutter-stage-manager.c [new file with mode: 0644]
clutter/clutter-stage-manager.h [new file with mode: 0644]
clutter/clutter-stage.c
clutter/clutter-stage.h
clutter/clutter-types.h
clutter/glx/clutter-backend-glx.c
clutter/glx/clutter-backend-glx.h
clutter/glx/clutter-stage-glx.c
clutter/glx/clutter-stage-glx.h
clutter/x11/clutter-backend-x11.c
clutter/x11/clutter-backend-x11.h
clutter/x11/clutter-event-x11.c
clutter/x11/clutter-stage-x11.c
clutter/x11/clutter-x11.h
tests/Makefile.am
tests/test-multistage.c [new file with mode: 0644]

index cd580f6..6336e23 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,38 @@
+2008-03-28  Matthew Allum  <mallum@openedhand.com>
+
+       * clutter/Makefile.am:
+       * clutter/clutter-actor.c:
+       * clutter/clutter-actor.h:
+       * clutter/clutter-backend.c:
+       * clutter/clutter-backend.h:
+       * clutter/clutter-debug.h:
+       * clutter/clutter-event.c:
+       * clutter/clutter-event.h:
+       * clutter/clutter-feature.h:
+       * clutter/clutter-group.h:
+       * clutter/clutter-main.c:
+       * clutter/clutter-main.h:
+       * clutter/clutter-private.h:
+       * clutter/clutter-stage.c:
+       * clutter/clutter-stage.h:
+        * clutter/clutter-stage-manager.c
+        * clutter/clutter-stage-manager.h
+       * clutter/clutter-types.h:
+       * clutter/glx/clutter-backend-glx.c:
+       * clutter/glx/clutter-backend-glx.h:
+       * clutter/glx/clutter-stage-glx.c:
+       * clutter/glx/clutter-stage-glx.h:
+       * clutter/x11/clutter-backend-x11.c:
+       * clutter/x11/clutter-backend-x11.h:
+       * clutter/x11/clutter-event-x11.c:
+       * clutter/x11/clutter-stage-x11.c:
+       * clutter/x11/clutter-x11.h:
+       * tests/Makefile.am:
+       * tests/test-multistage.c:
+        Initial commit of multi stage support (mostly a merge from the
+        clutter-multistage branch).
+        Note, this commit will break all backends except glx.
+
 2008-03-26  Neil Roberts  <neil@o-hand.com>
 
        * clutter/win32/clutter-win32.h: Added gtk-doc documentation for
index 16b6f6c..8a73cb6 100644 (file)
@@ -72,6 +72,7 @@ source_h =                                    \
        $(srcdir)/clutter-scriptable.h          \
        $(srcdir)/clutter-shader.h              \
        $(srcdir)/clutter-stage.h               \
+       $(srcdir)/clutter-stage-manager.h       \
        $(srcdir)/clutter-texture.h             \
        $(srcdir)/clutter-timeline.h            \
        $(srcdir)/clutter-timeout-pool.h        \
@@ -159,6 +160,7 @@ source_c = \
        clutter-scriptable.c            \
        clutter-shader.c                \
        clutter-stage.c                 \
+       clutter-stage-manager.c         \
        clutter-texture.c               \
        clutter-timeline.c              \
        clutter-timeout-pool.c          \
index c4d5017..b8a2418 100644 (file)
@@ -198,6 +198,8 @@ struct _ClutterActorPrivate
   ClutterFixed    scale_y;
 
   ShaderData     *shader_data;
+
+  ClutterStage   *stage;
 };
 
 enum
@@ -278,21 +280,6 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (ClutterActor,
 
 
 
-static gboolean
-redraw_update_idle (gpointer data)
-{
-  ClutterMainContext *ctx = CLUTTER_CONTEXT();
-
-  if (ctx->update_idle)
-    {
-      g_source_remove (ctx->update_idle);
-      ctx->update_idle = 0;
-    }
-
-  clutter_redraw ();
-
-  return FALSE;
-}
 
 static void
 clutter_actor_real_show (ClutterActor *self)
@@ -423,7 +410,7 @@ clutter_actor_hide_all (ClutterActor *self)
 void
 clutter_actor_realize (ClutterActor *self)
 {
-  ClutterActorClass *klass;
+  ClutterActorClass   *klass;
 
   if (CLUTTER_ACTOR_IS_REALIZED (self))
     return;
@@ -447,6 +434,9 @@ void
 clutter_actor_unrealize (ClutterActor *self)
 {
   ClutterActorClass *klass;
+  ClutterActorPrivate *priv;
+
+  priv = self->priv;
 
   if (!CLUTTER_ACTOR_IS_REALIZED (self))
     return;
@@ -457,6 +447,8 @@ clutter_actor_unrealize (ClutterActor *self)
 
   if (klass->unrealize)
     (klass->unrealize) (self);
+
+  priv->stage = NULL;
 }
 
 static void
@@ -910,7 +902,15 @@ clutter_actor_get_relative_vertices (ClutterActor  *self,
    * Simply duping code for now in wait for Cogl cleanup that can hopefully
    * address this in a nicer way.
   */
-  stage = clutter_stage_get_default ();
+  stage = clutter_actor_get_stage (self);
+
+  /* FIXME: if were not yet added to a stage, its probably unsafe to
+   * return default - idealy the func should fail. 
+  */
+  if (stage == NULL)
+    stage = clutter_stage_get_default ();
+
+  clutter_stage_ensure_current (CLUTTER_STAGE(stage));
 
   if (CLUTTER_PRIVATE_FLAGS (stage) & CLUTTER_ACTOR_SYNC_MATRICES)
     {
@@ -991,7 +991,15 @@ clutter_actor_get_vertices (ClutterActor  *self,
    * Simply duping code for now in wait for Cogl cleanup that can hopefully
    * address this in a nicer way.
   */
-  stage = clutter_stage_get_default ();
+  stage = clutter_actor_get_stage (self);
+
+  /* FIXME: if were not yet added to a stage, its probably unsafe to
+   * return default - idealy the func should fail. 
+  */
+  if (stage == NULL)
+    stage = clutter_stage_get_default ();
+
+  clutter_stage_ensure_current (CLUTTER_STAGE(stage));
 
   if (CLUTTER_PRIVATE_FLAGS (stage) & CLUTTER_ACTOR_SYNC_MATRICES)
     {
@@ -1146,7 +1154,7 @@ static void
 _clutter_actor_apply_modelview_transform_recursive (ClutterActor *self,
                                                    ClutterActor *ancestor)
 {
-  ClutterActor * parent;
+  ClutterActor *parent, *stage;
 
   parent = clutter_actor_get_parent (self);
 
@@ -1158,10 +1166,18 @@ _clutter_actor_apply_modelview_transform_recursive (ClutterActor *self,
   if (self == ancestor)
     return;
 
+  stage = clutter_actor_get_stage (self);
+
+  /* FIXME: if were not yet added to a stage, its probably unsafe to
+   * return default - idealy the func should fail. 
+  */
+  if (stage == NULL)
+    stage = clutter_stage_get_default ();
+
   if (parent)
     _clutter_actor_apply_modelview_transform_recursive (parent, ancestor);
-  else if (self != clutter_stage_get_default ())
-    _clutter_actor_apply_modelview_transform (clutter_stage_get_default());
+  else if (self != stage)
+    _clutter_actor_apply_modelview_transform (stage);
 
   _clutter_actor_apply_modelview_transform (self);
 }
@@ -2352,14 +2368,6 @@ clutter_actor_destroy (ClutterActor *self)
 
   g_return_if_fail (CLUTTER_IS_ACTOR (self));
 
-  if (CLUTTER_PRIVATE_FLAGS (self) & CLUTTER_ACTOR_IS_TOPLEVEL)
-    {
-      g_warning ("Calling clutter_actor_destroy() on an actor of type `%s' "
-                 "is not possible. This is usually an application bug.",
-                 g_type_name (G_OBJECT_TYPE (self)));
-      return;
-    }
-
   priv = self->priv;
 
   if (priv->parent_actor)
@@ -2395,17 +2403,13 @@ clutter_actor_destroy (ClutterActor *self)
 void
 clutter_actor_queue_redraw (ClutterActor *self)
 {
-  ClutterMainContext *ctx = CLUTTER_CONTEXT();
+  ClutterActor *stage;
 
-  if (!ctx->update_idle)
-    {
-      CLUTTER_TIMESTAMP (SCHEDULER, "Adding idle source for actor: %p", self);
+  g_return_if_fail (CLUTTER_IS_ACTOR (self));
 
-      ctx->update_idle =
-        clutter_threads_add_idle_full (G_PRIORITY_DEFAULT + 10,
-                                       redraw_update_idle,
-                                       NULL, NULL);
-    }
+  /* FIXME: should we check we're visible here? */
+  if ((stage = clutter_actor_get_stage (self)) != NULL)
+    clutter_stage_queue_redraw (CLUTTER_STAGE(stage));
 }
 
 /**
@@ -5728,3 +5732,14 @@ clutter_actor_get_box_from_vertices (ClutterVertex    vtx[4],
   box->y1 = y_1;
   box->y2 = y_2;
 }
+
+ClutterActor*
+clutter_actor_get_stage (ClutterActor *actor)
+{
+  g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL);
+
+  while (actor && !(CLUTTER_PRIVATE_FLAGS (actor) & CLUTTER_ACTOR_IS_TOPLEVEL))
+    actor = clutter_actor_get_parent (actor);
+
+  return actor;
+}
index 1ad5b54..4377dac 100644 (file)
@@ -487,6 +487,8 @@ gboolean clutter_actor_is_scaled                      (ClutterActor   *self);
 void     clutter_actor_box_get_from_vertices          (ClutterVertex    vtx[4],
                                                       ClutterActorBox *box);
 
+ClutterActor* clutter_actor_get_stage                 (ClutterActor *actor);
+
 G_END_DECLS
 
 #endif /* _HAVE_CLUTTER_ACTOR_H */
index 0b83081..aedb393 100644 (file)
@@ -95,14 +95,6 @@ clutter_backend_init (ClutterBackend *backend)
   priv->resolution = -1.0;
 }
 
-ClutterActor *
-_clutter_backend_get_stage (ClutterBackend *backend)
-{
-  g_return_val_if_fail (CLUTTER_IS_BACKEND (backend), NULL);
-
-  return CLUTTER_BACKEND_GET_CLASS (backend)->get_stage (backend);
-}
-
 void
 _clutter_backend_add_options (ClutterBackend *backend,
                               GOptionGroup   *group)
@@ -146,31 +138,65 @@ _clutter_backend_post_parse (ClutterBackend  *backend,
   return TRUE;
 }
 
-gboolean
-_clutter_backend_init_stage (ClutterBackend  *backend,
-                             GError         **error)
+ClutterActor*
+_clutter_backend_create_stage (ClutterBackend  *backend,
+                               GError         **error)
 {
+  ClutterMainContext  *context;
   ClutterBackendClass *klass;
+  ClutterActor        *stage = NULL;
 
   g_return_val_if_fail (CLUTTER_IS_BACKEND (backend), FALSE);
 
+  context = clutter_context_get_default ();
+
+  if (!context->stage_manager)
+    context->stage_manager = clutter_stage_manager_get_default ();
+
   klass = CLUTTER_BACKEND_GET_CLASS (backend);
-  if (klass->init_stage)
-    return klass->init_stage (backend, error);
+  if (klass->create_stage)
+    stage = klass->create_stage (backend, error);
 
-  return TRUE;
+  if (!stage)
+    return NULL;
+
+  _clutter_stage_manager_add_stage (context->stage_manager, 
+                                    CLUTTER_STAGE(stage));
+  return stage;
 }
 
 void
-_clutter_backend_redraw (ClutterBackend *backend)
+_clutter_backend_redraw (ClutterBackend *backend, ClutterStage *stage)
 {
   ClutterBackendClass *klass;
 
   klass = CLUTTER_BACKEND_GET_CLASS (backend);
   if (G_LIKELY(klass->redraw))
-    klass->redraw (backend);
+    klass->redraw (backend, stage);
 }
 
+void
+_clutter_backend_ensure_context (ClutterBackend *backend, ClutterStage *stage)
+{
+  ClutterBackendClass *klass;
+  static ClutterStage *current_context_stage = NULL;
+
+  g_return_if_fail (CLUTTER_IS_BACKEND (backend));
+  g_return_if_fail (CLUTTER_IS_STAGE (stage));
+
+  if (stage != current_context_stage)
+    {
+     klass = CLUTTER_BACKEND_GET_CLASS (backend);
+      if (G_LIKELY(klass->ensure_context))
+        klass->ensure_context (backend, stage);
+
+      current_context_stage = stage;
+
+      CLUTTER_SET_PRIVATE_FLAGS (stage, CLUTTER_ACTOR_SYNC_MATRICES);
+    }
+}
+
+
 ClutterFeatureFlags
 _clutter_backend_get_features (ClutterBackend *backend)
 {
index 89c1925..83d8e42 100644 (file)
@@ -28,6 +28,7 @@
 
 #include <glib-object.h>
 #include <clutter/clutter-actor.h>
+#include <clutter/clutter-stage.h>
 #include <clutter/clutter-event.h>
 #include <clutter/clutter-feature.h>
 
@@ -60,15 +61,17 @@ struct _ClutterBackendClass
                                    GError         **error);
   gboolean      (* post_parse)    (ClutterBackend  *backend,
                                    GError         **error);
-  gboolean      (* init_stage)    (ClutterBackend  *backend,
+  ClutterActor *(* create_stage)   (ClutterBackend  *backend,
                                    GError         **error);
   void          (* init_events)   (ClutterBackend  *backend);
   void          (* init_features) (ClutterBackend  *backend);
-  ClutterActor *(* get_stage)     (ClutterBackend  *backend);
   void          (* add_options)   (ClutterBackend  *backend,
                                    GOptionGroup    *group);
   ClutterFeatureFlags (* get_features)  (ClutterBackend  *backend);
-  void                (* redraw)  (ClutterBackend *backend);
+  void                (* redraw)  (ClutterBackend *backend, 
+                                   ClutterStage   *stage);
+  void        (* ensure_context)  (ClutterBackend *backend, 
+                                   ClutterStage   *stage);
 };
 
 GType clutter_backend_get_type    (void) G_GNUC_CONST;
index f425259..9233f45 100644 (file)
@@ -19,7 +19,8 @@ typedef enum {
   CLUTTER_DEBUG_BACKEND         = 1 << 9,
   CLUTTER_DEBUG_SCHEDULER       = 1 << 10,
   CLUTTER_DEBUG_SCRIPT          = 1 << 11,
-  CLUTTER_DEBUG_SHADER          = 1 << 12
+  CLUTTER_DEBUG_SHADER          = 1 << 12,
+  CLUTTER_DEBUG_MULTISTAGE      = 1 << 13
 } ClutterDebugFlag;
 
 #ifdef CLUTTER_ENABLE_DEBUG
index d53e38e..813e4ac 100644 (file)
@@ -181,6 +181,25 @@ clutter_event_get_source (ClutterEvent *event)
 }
 
 /**
+ * clutter_event_get_stage:
+ * @event: a #ClutterEvent
+ *
+ * Retrieves the source #ClutterStage the event originated for, or
+ * NULL if the event has no stage.
+ *
+ * Return value: a #ClutterStage
+ *
+ * Since: 0.8
+ */
+ClutterStage*
+clutter_event_get_stage (ClutterEvent *event)
+{
+  g_return_val_if_fail (event != NULL, NULL);
+
+  return event->any.stage;
+}
+
+/**
  * clutter_button_event_button:
  * @buttev: a #ClutterButtonEvent
  *
index b6c50fc..e93a2bf 100644 (file)
@@ -200,6 +200,7 @@ struct _ClutterAnyEvent
   ClutterEventType  type;
   guint32           time;
   ClutterEventFlags flags;
+  ClutterStage *stage;
   ClutterActor *source;
 };
 
@@ -223,6 +224,7 @@ struct _ClutterKeyEvent
   ClutterEventType type;
   guint32 time;
   ClutterEventFlags flags;
+  ClutterStage *stage;
   ClutterActor *source;
   ClutterModifierType modifier_state;
   guint keyval;
@@ -254,6 +256,7 @@ struct _ClutterButtonEvent
   ClutterEventType type;
   guint32 time;
   ClutterEventFlags flags;
+  ClutterStage *stage;
   ClutterActor *source;
   gint x;
   gint y;
@@ -269,6 +272,7 @@ struct _ClutterCrossingEvent
   ClutterEventType type;
   guint32 time;
   ClutterEventFlags flags;
+  ClutterStage *stage;
   ClutterActor *source;
   gint x;
   gint y;
@@ -281,6 +285,7 @@ struct _ClutterMotionEvent
   ClutterEventType type;
   guint32 time;
   ClutterEventFlags flags;
+  ClutterStage *stage;
   ClutterActor *source;
   gint x;
   gint y;
@@ -294,6 +299,7 @@ struct _ClutterScrollEvent
   ClutterEventType type;
   guint32 time;
   ClutterEventFlags flags;
+  ClutterStage *stage;
   ClutterActor *source;
   gint x;
   gint y;
@@ -308,6 +314,7 @@ struct _ClutterStageStateEvent
   ClutterEventType type;
   guint32 time;
   ClutterEventFlags flags;
+  ClutterStage *stage;
   ClutterActor *source; /* unused XXX: should probably be the stage itself */
   ClutterStageState changed_mask;
   ClutterStageState new_state;
@@ -351,6 +358,7 @@ guint32             clutter_button_event_button (ClutterButtonEvent *buttev);
 
 guint32             clutter_keysym_to_unicode   (guint               keyval);
 
+ClutterStage*       clutter_event_get_stage     (ClutterEvent *event);
 
 G_END_DECLS
 
index ec771ba..3602732 100644 (file)
@@ -48,6 +48,7 @@ G_BEGIN_DECLS
  * @CLUTTER_FEATURE_STAGE_CURSOR: Set if stage has a graphical cursor.
  * @CLUTTER_FEATURE_SHADERS_GLSL: Set if the backend supports GLSL shaders.
  * @CLUTTER_FEATURE_OFFSCREEN: Set if the backend supports offscreen rendering.
+ * @CLUTTER_FEATURE_STAGE_MULTIPLE: Set if multiple stages are supported.
  *
  * Runtime flags indicating specific features available via Clutter window
  * sysytem and graphics backend.
@@ -64,7 +65,8 @@ typedef enum
   CLUTTER_FEATURE_STAGE_USER_RESIZE      = (1 << 6),
   CLUTTER_FEATURE_STAGE_CURSOR           = (1 << 7),
   CLUTTER_FEATURE_SHADERS_GLSL           = (1 << 8),
-  CLUTTER_FEATURE_OFFSCREEN              = (1 << 9)
+  CLUTTER_FEATURE_OFFSCREEN              = (1 << 9),
+  CLUTTER_FEATURE_STAGE_MULTIPLE         = (1 << 10)
 } ClutterFeatureFlags;
 
 gboolean            clutter_feature_available       (ClutterFeatureFlags feature);
index 45c1fea..d4a4e6c 100644 (file)
@@ -27,6 +27,7 @@
 #define __CLUTTER_GROUP_H__
 
 #include <glib-object.h>
+#include <clutter/clutter-types.h>
 #include <clutter/clutter-actor.h>
 
 G_BEGIN_DECLS
index a0283ff..5f47c1e 100644 (file)
@@ -82,6 +82,7 @@ static const GDebugKey clutter_debug_keys[] = {
   { "scheduler", CLUTTER_DEBUG_SCHEDULER },
   { "script", CLUTTER_DEBUG_SCRIPT },
   { "shader", CLUTTER_DEBUG_SHADER },
+  { "multistage", CLUTTER_DEBUG_MULTISTAGE },
 };
 #endif /* CLUTTER_ENABLE_DEBUG */
 
@@ -111,22 +112,21 @@ clutter_get_show_fps (void)
  * function, but queue a redraw using clutter_actor_queue_redraw().
  */
 void
-clutter_redraw (void)
+clutter_redraw (ClutterStage *stage)
 {
   ClutterMainContext *ctx;
-  ClutterActor       *stage;
   static GTimer      *timer = NULL;
   static guint        timer_n_frames = 0;
 
   ctx  = clutter_context_get_default ();
 
-  stage = _clutter_backend_get_stage (ctx->backend);
+  CLUTTER_TIMESTAMP (SCHEDULER, "Redraw start for stage:%p", stage);
+  CLUTTER_NOTE (PAINT, " Redraw enter for stage:%p", stage);
+  CLUTTER_NOTE (MULTISTAGE, "redraw called for stage:%p", stage);
 
-  CLUTTER_TIMESTAMP (SCHEDULER, "Redraw start");
+  _clutter_backend_ensure_context (ctx->backend, stage);
 
-  CLUTTER_NOTE (PAINT, " Redraw enter");
-
-  /* Setup FPS count */
+  /* Setup FPS count - not currently across *all* stages rather than per */
   if (clutter_get_show_fps ())
     {
       if (!timer)
@@ -142,8 +142,8 @@ clutter_redraw (void)
 
       clutter_stage_get_perspectivex (CLUTTER_STAGE (stage), &perspective);
 
-      cogl_setup_viewport (clutter_actor_get_width (stage),
-                          clutter_actor_get_height (stage),
+      cogl_setup_viewport (clutter_actor_get_width (CLUTTER_ACTOR(stage)),
+                          clutter_actor_get_height (CLUTTER_ACTOR(stage)),
                           perspective.fovy,
                           perspective.aspect,
                           perspective.z_near,
@@ -156,7 +156,7 @@ clutter_redraw (void)
    * the stage. It will likely need to swap buffers, vblank sync etc
    * which will be windowing system dependant.
   */
-  _clutter_backend_redraw (ctx->backend);
+  _clutter_backend_redraw (ctx->backend, stage);
 
   /* Complete FPS info */
   if (clutter_get_show_fps ())
@@ -171,9 +171,8 @@ clutter_redraw (void)
        }
     }
 
-  CLUTTER_NOTE (PAINT, " Redraw leave");
-
-  CLUTTER_TIMESTAMP (SCHEDULER, "Redraw finish");
+  CLUTTER_NOTE (PAINT, " Redraw leave for stage:%p", stage);
+  CLUTTER_TIMESTAMP (SCHEDULER, "Redraw finish for stage:%p", stage);
 }
 
 /**
@@ -233,6 +232,27 @@ _clutter_do_pick (ClutterStage   *stage,
 
   context = clutter_context_get_default ();
 
+  _clutter_backend_ensure_context (context->backend, stage);
+
+  /* FIXME: needed for when a context switch happens - probably
+   * should put into its own function somewhere..
+  */
+  if (CLUTTER_PRIVATE_FLAGS (stage) & CLUTTER_ACTOR_SYNC_MATRICES)
+    {
+      ClutterPerspective perspective;
+
+      clutter_stage_get_perspectivex (CLUTTER_STAGE (stage), &perspective);
+
+      cogl_setup_viewport (clutter_actor_get_width (CLUTTER_ACTOR(stage)),
+                          clutter_actor_get_height (CLUTTER_ACTOR(stage)),
+                          perspective.fovy,
+                          perspective.aspect,
+                          perspective.z_near,
+                          perspective.z_far);
+
+      CLUTTER_UNSET_PRIVATE_FLAGS (stage, CLUTTER_ACTOR_SYNC_MATRICES);
+    }
+
   cogl_paint_init (&white);
   cogl_enable (0);
 
@@ -259,7 +279,7 @@ _clutter_do_pick (ClutterStage   *stage,
   glReadPixels(x, viewport[3] - y -1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
 
   if (pixel[0] == 0xff && pixel[1] == 0xff && pixel[2] == 0xff)
-    return CLUTTER_ACTOR (stage);
+      return CLUTTER_ACTOR (stage);
 
   cogl_get_bitmasks (&r, &g, &b, NULL);
 
@@ -969,6 +989,7 @@ clutter_init_with_args (int            *argc,
   GOptionGroup *group;
   gboolean res;
   GError *stage_error;
+  ClutterActor *stage;
 
   if (clutter_is_initialized)
     return CLUTTER_INIT_SUCCESS;
@@ -998,7 +1019,10 @@ clutter_init_with_args (int            *argc,
   clutter_context = clutter_context_get_default ();
 
   stage_error = NULL;
-  if (!_clutter_backend_init_stage (clutter_context->backend, &stage_error))
+  stage = _clutter_backend_create_stage (clutter_context->backend, 
+                                         &stage_error);
+
+  if (!stage)
     {
       g_propagate_error (error, stage_error);
       return CLUTTER_INIT_ERROR_INTERNAL;
@@ -1064,6 +1088,7 @@ clutter_init (int    *argc,
               char ***argv)
 {
   ClutterMainContext *context;
+  ClutterActor *stage;
   GError *stage_error;
 
   if (clutter_is_initialized)
@@ -1091,7 +1116,10 @@ clutter_init (int    *argc,
 
   /* Stage will give us a GL Context etc */
   stage_error = NULL;
-  if (!_clutter_backend_init_stage (context->backend, &stage_error))
+
+  stage = _clutter_backend_create_stage (context->backend, &stage_error);
+
+  if (!stage)
     {
       CLUTTER_NOTE (MISC, "stage failed to initialise.");
       g_critical (stage_error->message);
@@ -1165,7 +1193,9 @@ event_click_count_generate (ClutterEvent *event)
             previous_button_number = event->button.button;
           }
 
-        /* store time and position for this click for comparison with next event */
+        /* store time and position for this click for comparison with
+         * next event 
+         */
         previous_time = event->button.time;
         previous_x    = event->button.x;
         previous_y    = event->button.y;
@@ -1301,6 +1331,7 @@ generate_enter_leave_events (ClutterEvent *event)
               cev.crossing.x       = event->motion.x;
               cev.crossing.y       = event->motion.y;
               cev.crossing.source  = context->motion_last_actor;
+              cev.crossing.stage   = event->any.stage;
               /* unref in free  */
               cev.crossing.related = motion_current_actor;
 
@@ -1314,6 +1345,7 @@ generate_enter_leave_events (ClutterEvent *event)
           cev.crossing.x       = event->motion.x;
           cev.crossing.y       = event->motion.y;
           cev.crossing.source  = motion_current_actor;
+          cev.crossing.stage   = event->any.stage;
 
           if (context->motion_last_actor)
             cev.crossing.related = context->motion_last_actor;
@@ -1371,7 +1403,7 @@ clutter_do_event (ClutterEvent *event)
 
   context = clutter_context_get_default ();
   backend = context->backend;
-  stage   = _clutter_backend_get_stage (backend);
+  stage   = CLUTTER_ACTOR(event->any.stage);
 
   if (!stage)
     return;
@@ -1394,7 +1426,13 @@ clutter_do_event (ClutterEvent *event)
         event->any.source = stage;
         /* the stage did not handle the event, so we just quit */
         if (!clutter_stage_event (CLUTTER_STAGE (stage), event))
-          clutter_main_quit ();
+          {
+            if (stage == clutter_stage_get_default())
+              clutter_main_quit ();
+            else
+              clutter_actor_destroy (stage);
+          }
+        
         break;
 
       case CLUTTER_KEY_PRESS:
index d63a911..59d51e7 100644 (file)
@@ -72,7 +72,7 @@ void             clutter_main                       (void);
 void             clutter_main_quit                  (void);
 gint             clutter_main_level                 (void);
 
-void             clutter_redraw                     (void);
+void             clutter_redraw                     (ClutterStage *stage);
 
 void             clutter_do_event                   (ClutterEvent *event);
 
index 815c94a..375b063 100644 (file)
@@ -40,6 +40,7 @@
 
 #include <pango/pangoft2.h>
 
+#include "clutter-stage-manager.h"
 #include "clutter-event.h"
 #include "clutter-backend.h"
 #include "clutter-stage.h"
@@ -72,28 +73,33 @@ struct _ClutterMainContext
 {
   ClutterBackend  *backend;            /* holds a pointer to the windowing 
                                           system backend */
+  ClutterStageManager *stage_manager;  /* stages */
   GQueue          *events_queue;       /* the main event queue */
   PangoFT2FontMap *font_map;
-  guint            update_idle;               /* repaint idler id */
   
   guint            is_initialized : 1;  
   GTimer          *timer;             /* Used for debugging scheduler */
 
   ClutterPickMode  pick_mode;          /* Indicates pick render mode   */
-  guint            motion_events_per_actor : 1;/* set for enter/leave events */
+
+  guint            motion_events_per_actor : 1;/* set f
+or enter/leave events */
+
   guint            motion_frequency;   /* Motion events per second */
   gint             num_reactives;      /* Num of reactive actors */
 
-  ClutterIDPool   *id_pool;            /* mapping between reused integer ids and actors */
-
+  ClutterIDPool   *id_pool;            /* mapping between reused integer ids 
+                                        * and actors 
+                                        */
   guint            frame_rate;         /* Default FPS */
 
   ClutterActor    *pointer_grab_actor; /* The actor having the pointer grab
-                                          (or NULL if there is no pointer grab) 
+                                        * (or NULL if there is no pointer grab
                                         */
   ClutterActor    *keyboard_grab_actor; /* The actor having the pointer grab
-                                          (or NULL if there is no pointer grab) 
-                                        */
+                                         * (or NULL if there is no pointer 
+                                         *  grab) 
+                                         */
   GSList          *shaders;            /* stack of overridden shaders */
 
   ClutterActor    *motion_last_actor;
@@ -115,13 +121,32 @@ ClutterMainContext *clutter_context_get_default (void);
 
 #define I_(str)  (g_intern_static_string ((str)))
 
+/* stage manager */
+
+struct _ClutterStageManager
+{
+  GObject parent_instance;
+
+  GSList *stages;
+};
+
+void _clutter_stage_manager_add_stage    (ClutterStageManager *stage_manager,
+                                          ClutterStage        *stage);
+void _clutter_stage_manager_remove_stage (ClutterStageManager *stage_manager,
+                                          ClutterStage        *stage);
+
 /* vfuncs implemnted by backend */
 
 GType _clutter_backend_impl_get_type (void);
 
-ClutterActor *_clutter_backend_get_stage     (ClutterBackend  *backend);
+void          _clutter_backend_redraw        (ClutterBackend *backend,
+                                              ClutterStage   *stage);
 
-void          _clutter_backend_redraw        (ClutterBackend *backend);
+ClutterActor* _clutter_backend_create_stage (ClutterBackend  *backend,
+                                             GError         **error);
+
+void          _clutter_backend_redraw        (ClutterBackend *backend,
+                                              ClutterStage   *stage);
 
 void          _clutter_backend_add_options   (ClutterBackend  *backend,
                                               GOptionGroup    *group);
@@ -129,10 +154,11 @@ gboolean      _clutter_backend_pre_parse     (ClutterBackend  *backend,
                                               GError         **error);
 gboolean      _clutter_backend_post_parse    (ClutterBackend  *backend,
                                               GError         **error);
-gboolean      _clutter_backend_init_stage    (ClutterBackend  *backend,
-                                              GError         **error);
 void          _clutter_backend_init_events   (ClutterBackend  *backend);
 
+void          _clutter_backend_ensure_context (ClutterBackend *backend, 
+                                               ClutterStage   *stage);
+
 ClutterFeatureFlags _clutter_backend_get_features (ClutterBackend *backend);
 
 void          _clutter_feature_init (void);
diff --git a/clutter/clutter-stage-manager.c b/clutter/clutter-stage-manager.c
new file mode 100644 (file)
index 0000000..1fd81ba
--- /dev/null
@@ -0,0 +1,282 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+#include "clutter-marshal.h"
+#include "clutter-debug.h"
+#include "clutter-private.h"
+#include "clutter-version.h"  
+#include "clutter-stage-manager.h"
+
+enum
+{
+  PROP_0,
+  PROP_DEFAULT_STAGE
+};
+
+enum
+{
+  STAGE_ADDED,
+  STAGE_REMOVED,
+
+  LAST_SIGNAL
+};
+
+static guint manager_signals[LAST_SIGNAL] = { 0, };
+static ClutterStage *default_stage = NULL;
+
+G_DEFINE_TYPE (ClutterStageManager, clutter_stage_manager, G_TYPE_OBJECT);
+
+static void
+clutter_stage_manager_set_property (GObject      *gobject,
+                                    guint         prop_id,
+                                    const GValue *value,
+                                    GParamSpec   *pspec)
+{
+  switch (prop_id)
+    {
+    case PROP_DEFAULT_STAGE:
+      clutter_stage_manager_set_default_stage (CLUTTER_STAGE_MANAGER (gobject),
+                                               g_value_get_object (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+clutter_stage_manager_get_property (GObject    *gobject,
+                                    guint       prop_id,
+                                    GValue     *value,
+                                    GParamSpec *pspec)
+{
+  switch (prop_id)
+    {
+    case PROP_DEFAULT_STAGE:
+      g_value_set_object (value, default_stage);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+clutter_stage_manager_dispose (GObject *gobject)
+{
+  ClutterStageManager *stage_manager;
+  GSList *l;
+
+  stage_manager = CLUTTER_STAGE_MANAGER (gobject);
+
+  for (l = stage_manager->stages; l; l = l->next)
+    {
+      ClutterActor *stage = l->data;
+
+      if (stage)
+        clutter_actor_destroy (stage);
+    }
+
+  g_slist_free (stage_manager->stages);
+  stage_manager->stages = NULL;
+
+  G_OBJECT_CLASS (clutter_stage_manager_parent_class)->dispose (gobject);
+}
+
+static void
+clutter_stage_manager_class_init (ClutterStageManagerClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->dispose      = clutter_stage_manager_dispose;
+  gobject_class->set_property = clutter_stage_manager_set_property;
+  gobject_class->get_property = clutter_stage_manager_get_property;
+
+  /**
+   * ClutterStageManager:default-stage:
+   *
+   * The default stage used by Clutter.
+   *
+   * Since: 0.8
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_DEFAULT_STAGE,
+                                   g_param_spec_object ("default-stage",
+                                                        "Default Stage",
+                                                        "The default stage",
+                                                        CLUTTER_TYPE_STAGE,
+                                                        CLUTTER_PARAM_READWRITE));
+
+  /**
+   * ClutterStageManager:stage-added:
+   * @stage_manager: the object which received the signal
+   * @stage: the added stage
+   *
+   * The ::stage-added signal is emitted each time a new #ClutterStage
+   * has been added to the stage manager.
+   *
+   * Since: 0.8
+   */
+  manager_signals[STAGE_ADDED] =
+    g_signal_new ("stage-added",
+                  G_OBJECT_CLASS_TYPE (gobject_class),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (ClutterStageManagerClass, stage_added),
+                  NULL, NULL,
+                  clutter_marshal_VOID__OBJECT,
+                  G_TYPE_NONE, 1,
+                  CLUTTER_TYPE_STAGE);
+  /**
+   * ClutterStageManager::stage-removed:
+   * @stage_manager: the object which received the signal
+   * @stage: the removed stage
+   *
+   * The ::stage-removed signal is emitted each time a #ClutterStage
+   * has been removed from the stage manager.
+   *
+   * Since: 0.8
+   */
+  manager_signals[STAGE_REMOVED] =
+    g_signal_new ("stage-removed",
+                  G_OBJECT_CLASS_TYPE (gobject_class),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (ClutterStageManagerClass, stage_removed),
+                  NULL, NULL,
+                  clutter_marshal_VOID__OBJECT,
+                  G_TYPE_NONE, 1,
+                  CLUTTER_TYPE_STAGE);
+}
+
+static void
+clutter_stage_manager_init (ClutterStageManager *stage_manager)
+{
+
+}
+
+/**
+ * clutter_stage_manager_get_default:
+ *
+ * Returns the default #ClutterStageManager.
+ *
+ * Return value: the default stage manager instance. The returned object
+ *   is owned by Clutter and you should not reference or unreference it.
+ *
+ * Since: 0.8
+ */
+ClutterStageManager *
+clutter_stage_manager_get_default (void)
+{
+  static ClutterStageManager *stage_manager = NULL;
+
+  if (G_UNLIKELY (stage_manager == NULL))
+    stage_manager = g_object_new (CLUTTER_TYPE_STAGE_MANAGER, NULL);
+
+  return stage_manager;
+}
+
+/**
+ * clutter_stage_manager_set_default_stage:
+ * @stage_manager: a #ClutterStageManager
+ * @stage: a #ClutterStage
+ *
+ * Sets @stage as the default stage.
+ *
+ * Since: 0.8
+ */
+void
+clutter_stage_manager_set_default_stage (ClutterStageManager *stage_manager,
+                                         ClutterStage        *stage)
+{
+  g_return_if_fail (CLUTTER_IS_STAGE_MANAGER (stage_manager));
+  g_return_if_fail (CLUTTER_IS_STAGE (stage));
+
+  if (!g_slist_find (stage_manager->stages, stage))
+    _clutter_stage_manager_add_stage (stage_manager, stage);
+
+  default_stage = stage;
+
+  g_object_notify (G_OBJECT (stage_manager), "default-stage");
+}
+
+/**
+ * clutter_stage_manager_get_default_stage:
+ * @stage_manager: a #ClutterStageManager
+ *
+ * Returns the default #ClutterStage.
+ *
+ * Return value: the default stage. The returned object is owned by
+ *   Clutter and you should never reference or unreference it
+ *
+ * Since: 0.8
+ */
+ClutterStage *
+clutter_stage_manager_get_default_stage (ClutterStageManager *stage_manager)
+{
+  return default_stage;
+}
+
+/**
+ * clutter_stage_manager_list_stage:
+ * @stage_manager: a #ClutterStageManager
+ *
+ * Lists all currently used stages.
+ *
+ * Return value: a newly allocated list of #ClutterStage objects. Use
+ *   g_slist_free() to deallocate it when done.
+ *
+ * Since: 0.8
+ */
+GSList *
+clutter_stage_manager_list_stages (ClutterStageManager *stage_manager)
+{
+  return g_slist_copy (stage_manager->stages);
+}
+
+void
+_clutter_stage_manager_add_stage (ClutterStageManager *stage_manager,
+                                  ClutterStage        *stage)
+{
+  if (g_slist_find (stage_manager->stages, stage))
+    {
+      g_warning ("Trying to add a stage to the list of managed stages, "
+                 "but it is already in it, aborting.");
+      return;
+    }
+
+  g_object_ref_sink (stage);
+  stage_manager->stages = g_slist_append (stage_manager->stages, stage);
+
+  if (!default_stage)
+    {
+      default_stage = stage;
+
+      g_object_notify (G_OBJECT (stage_manager), "default-stage");
+    }
+  
+  g_signal_emit (stage_manager, manager_signals[STAGE_ADDED], 0, stage);
+}
+
+void
+_clutter_stage_manager_remove_stage (ClutterStageManager *stage_manager,
+                                     ClutterStage        *stage)
+{
+  if (!g_slist_find (stage_manager->stages, stage))
+    {
+      g_warning ("Trying to remove an unknown stage from the list "
+                 "of managed stages, aborting.");
+      return;
+    }
+
+  stage_manager->stages = g_slist_remove (stage_manager->stages, stage);
+
+  /* if it's the default stage, get the first available from the list */
+  if (default_stage == stage)
+    default_stage = stage_manager->stages ? stage_manager->stages->data
+                                          : NULL;
+
+  g_signal_emit (stage_manager, manager_signals[STAGE_REMOVED], 0, stage);
+
+  g_object_unref (stage);
+}
diff --git a/clutter/clutter-stage-manager.h b/clutter/clutter-stage-manager.h
new file mode 100644 (file)
index 0000000..1b4a7b5
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Authored By Matthew Allum  <mallum@openedhand.com>
+ *
+ * Copyright (C) 2008 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __CLUTTER_STAGE_MANAGER_H__
+#define __CLUTTER_STAGE_MANAGER_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <clutter/clutter-stage.h>
+
+G_BEGIN_DECLS
+
+#define CLUTTER_TYPE_STAGE_MANAGER              (clutter_stage_manager_get_type ())
+#define CLUTTER_STAGE_MANAGER(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_STAGE_MANAGER, ClutterStageManager))
+#define CLUTTER_IS_STAGE_MANAGER(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_STAGE_MANAGER))
+#define CLUTTER_STAGE_MANAGER_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_STAGE_MANAGER, ClutterStageManagerClass))
+#define CLUTTER_IS_STAGE_MANAGER_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_STAGE_MANAGER))
+#define CLUTTER_STAGE_MANAGER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_STAGE_MANAGER, ClutterStageManagerClass))
+
+typedef struct _ClutterStageManager             ClutterStageManager;
+typedef struct _ClutterStageManagerClass        ClutterStageManagerClass;
+
+struct _ClutterStageManagerClass
+{
+  GObjectClass parent_class;
+
+  void (* stage_added)   (ClutterStageManager *stage_manager,
+                          ClutterStage        *stage);
+  void (* stage_removed) (ClutterStageManager *stage_manager,
+                          ClutterStage        *stage);
+};
+
+GType clutter_stage_manager_get_type (void) G_GNUC_CONST;
+
+ClutterStageManager *clutter_stage_manager_get_default       (void);
+void                 clutter_stage_manager_set_default_stage (ClutterStageManager *stage_manager,
+                                                              ClutterStage        *stage);
+ClutterStage *       clutter_stage_manager_get_default_stage (ClutterStageManager *stage_manager);
+GSList *             clutter_stage_manager_list_stages       (ClutterStageManager *stage_manager);
+
+G_END_DECLS
+
+#endif /* __CLUTTER_STAGE_MANAGER_H__ */
index 53c3b6f..271cbfc 100644 (file)
@@ -44,7 +44,9 @@
 #include "clutter-enum-types.h"
 #include "clutter-private.h"
 #include "clutter-debug.h"
+#include "clutter-stage-manager.h"
 #include "clutter-version.h"   /* For flavour */
+#include "clutter-id-pool.h"
 
 #include "cogl.h"
 
@@ -67,6 +69,8 @@ struct _ClutterStagePrivate
 
   gchar              *title;
   ClutterActor       *key_focused_actor;
+
+  guint               update_idle;            /* repaint idler id */
 };
 
 enum
@@ -469,12 +473,9 @@ clutter_stage_init (ClutterStage *self)
 ClutterActor *
 clutter_stage_get_default (void)
 {
-  ClutterMainContext *context;
+  ClutterStageManager *stage_manager = clutter_stage_manager_get_default ();
 
-  context = clutter_context_get_default ();
-  g_assert (context != NULL);
-
-  return _clutter_backend_get_stage (context->backend);
+  return CLUTTER_ACTOR(clutter_stage_manager_get_default_stage(stage_manager));
 }
 
 /**
@@ -1349,3 +1350,115 @@ clutter_fog_get_type (void)
 
   return our_type;
 }
+
+/**
+ * clutter_stage_create_new:
+ *
+ * Creates a new, non-default stage. A non-default stage is a new
+ * top-level actor which can be used as another container. It works
+ * exactly like the default stage, but while clutter_stage_get_default()
+ * will always return the same instance, you will have to keep a pointer
+ * to any #ClutterStage returned by clutter_stage_create().
+ *
+ * The ability to support multiple stages depends on the current
+ * backend. Use clutter_feature_available() and
+ * %CLUTTER_FEATURE_STAGE_MULTIPLE to check at runtime whether a
+ * backend supports multiple stages.
+ *
+ * Return value: a new stage, or %NULL if the default backend does
+ *   not support multiple stages. Use clutter_actor_destroy() to
+ *   close the returned stage.
+ *
+ * Since: 0.8
+ */
+ClutterActor*
+clutter_stage_create_new (void)
+{
+  ClutterBackend *backend = clutter_get_default_backend ();
+  GError *error = NULL;
+  ClutterActor *retval;
+
+  if (!clutter_feature_available (CLUTTER_FEATURE_STAGE_MULTIPLE))
+    {
+      g_warning ("Unable to create a new stage: the %s backend does not "
+                 "support multiple stages.",
+                 CLUTTER_FLAVOUR);
+      return NULL;
+    }
+
+  retval = _clutter_backend_create_stage (backend, &error);
+  if (error)
+    {
+      g_warning ("Unable to create a secondary stage: %s", error->message);
+      g_error_free (error);
+      retval = NULL;
+    }
+
+  return retval;
+}
+
+/**
+ * clutter_stage_ensure_current:
+ * @stage: the #ClutterStage
+ *
+ * This function essentially makes sure the right GL context is
+ * current for the passed stage. It is not intended to
+ * be used by applications.
+ *
+ * Since: 0.8
+ */
+void
+clutter_stage_ensure_current (ClutterStage *stage)
+{
+  ClutterMainContext *ctx;
+
+  g_return_if_fail (CLUTTER_IS_STAGE (stage));
+
+  ctx  = clutter_context_get_default ();
+
+  _clutter_backend_ensure_context (ctx->backend, stage);
+}
+
+static gboolean
+redraw_update_idle (gpointer data)
+{
+  ClutterStage *stage = CLUTTER_STAGE(data);
+
+  if (stage->priv->update_idle)
+    {
+      g_source_remove (stage->priv->update_idle);
+      stage->priv->update_idle = 0;
+    }
+
+  CLUTTER_NOTE (MULTISTAGE, "redrawing via idle for stage:%p", stage);
+  clutter_redraw (stage);
+
+  return FALSE;
+}
+
+/**
+ * clutter_stage_queue_redraw:
+ * @stage: the #ClutterStage
+ *
+ * Queues a redraw for the passed stage. Note applications should call
+ * #clutter_actor_queue_redraw over this.
+ *
+ * Since: 0.8
+ */
+void
+clutter_stage_queue_redraw (ClutterStage *stage)
+{
+  g_return_if_fail (CLUTTER_IS_STAGE (stage));
+
+  if (!stage->priv->update_idle)
+    {
+      CLUTTER_TIMESTAMP (SCHEDULER, "Adding idle source for stage: %p", stage);
+
+      /* FIXME: weak_ref self in case we dissapear before paint? */
+      stage->priv->update_idle =
+        clutter_threads_add_idle_full (G_PRIORITY_DEFAULT + 10,
+                                       redraw_update_idle,
+                                       stage, 
+                                       NULL);
+    }
+}
index c34b4e8..0973cbe 100644 (file)
@@ -26,6 +26,7 @@
 #ifndef __CLUTTER_STAGE_H__
 #define __CLUTTER_STAGE_H__
 
+#include <clutter/clutter-types.h>
 #include <clutter/clutter-group.h>
 #include <clutter/clutter-color.h>
 #include <clutter/clutter-event.h>
@@ -80,7 +81,7 @@ G_BEGIN_DECLS
 typedef struct _ClutterPerspective  ClutterPerspective;
 typedef struct _ClutterFog          ClutterFog;
 
-typedef struct _ClutterStage        ClutterStage;
+
 typedef struct _ClutterStageClass   ClutterStageClass;
 typedef struct _ClutterStagePrivate ClutterStagePrivate;
 
@@ -237,6 +238,12 @@ void                  clutter_stage_set_key_focus      (ClutterStage *stage,
                                                         ClutterActor *actor);
 ClutterActor *        clutter_stage_get_key_focus      (ClutterStage *stage);
 
+ClutterActor*         clutter_stage_create_new         (void);
+
+void                  clutter_stage_ensure_current     (ClutterStage *stage);
+
+void                  clutter_stage_queue_redraw       (ClutterStage *stage);
+
 /* Commodity macro */
 #define clutter_stage_add(stage,actor)                  G_STMT_START {  \
   if (CLUTTER_IS_STAGE ((stage)) && CLUTTER_IS_ACTOR ((actor)))         \
index fff26a1..0a09611 100644 (file)
@@ -39,6 +39,7 @@ G_BEGIN_DECLS
 
 /* Forward delarations to avoid header catch 22's */
 typedef struct _ClutterActor         ClutterActor;
+typedef struct _ClutterStage         ClutterStage;
 
 /**
  * ClutterGravity:
index 99264b2..b6ea035 100644 (file)
@@ -213,7 +213,7 @@ clutter_backend_glx_get_features (ClutterBackend *backend)
 {
   ClutterBackendGLX  *backend_glx = CLUTTER_BACKEND_GLX (backend);
   const gchar        *glx_extensions = NULL;
-  ClutterFeatureFlags flags = 0;
+  ClutterFeatureFlags flags = CLUTTER_FEATURE_STAGE_MULTIPLE;
 
   /* FIXME: we really need to check if gl context is set */
 
@@ -343,14 +343,32 @@ clutter_backend_glx_get_features (ClutterBackend *backend)
 }
 
 static void
-clutter_backend_glx_redraw (ClutterBackend *backend)
+clutter_backend_glx_ensure_context (ClutterBackend *backend, 
+                                    ClutterStage   *stage)
 {
-  ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend);
+  ClutterBackendGLX *backend_glx;
   ClutterStageGLX   *stage_glx;
   ClutterStageX11   *stage_x11;
 
-  stage_x11 = CLUTTER_STAGE_X11(backend_x11->stage);
-  stage_glx = CLUTTER_STAGE_GLX(backend_x11->stage);
+  stage_x11 = CLUTTER_STAGE_X11(stage);
+  stage_glx = CLUTTER_STAGE_GLX(stage);
+  backend_glx = CLUTTER_BACKEND_GLX(backend);
+
+  CLUTTER_NOTE (MULTISTAGE, "setting context for stage:%p", stage );
+
+  glXMakeCurrent (stage_x11->xdpy, 
+                  stage_x11->xwin, 
+                  backend_glx->gl_context);
+}
+
+static void
+clutter_backend_glx_redraw (ClutterBackend *backend, ClutterStage *stage)
+{
+  ClutterStageGLX   *stage_glx;
+  ClutterStageX11   *stage_x11;
+
+  stage_x11 = CLUTTER_STAGE_X11(stage);
+  stage_glx = CLUTTER_STAGE_GLX(stage);
 
   clutter_actor_paint (CLUTTER_ACTOR (stage_glx));
 
@@ -370,47 +388,42 @@ clutter_backend_glx_redraw (ClutterBackend *backend)
     }
 }
 
-gboolean
-clutter_backend_glx_init_stage (ClutterBackend  *backend,
-                                GError         **error)
+ClutterActor*
+clutter_backend_glx_create_stage (ClutterBackend  *backend,
+                                  GError         **error)
 {
   ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend);
+  ClutterStageX11   *stage_x11;
+  ClutterActor      *stage;
 
-  if (!backend_x11->stage)
-    {
-      ClutterStageX11 *stage_x11;
-      ClutterActor *stage;
-
-      stage = g_object_new (CLUTTER_TYPE_STAGE_GLX, NULL);
-
-      /* copy backend data into the stage */
-      stage_x11 = CLUTTER_STAGE_X11 (stage);
-      stage_x11->xdpy = backend_x11->xdpy;
-      stage_x11->xwin_root = backend_x11->xwin_root;
-      stage_x11->xscreen = backend_x11->xscreen_num;
-      stage_x11->backend = backend_x11;
+  stage = g_object_new (CLUTTER_TYPE_STAGE_GLX, NULL);
 
-      CLUTTER_NOTE (MISC, "X11 stage created (display:%p, screen:%d, root:%u)",
-                    stage_x11->xdpy,
-                    stage_x11->xscreen,
-                    (unsigned int) stage_x11->xwin_root);
+  /* copy backend data into the stage */
+  stage_x11 = CLUTTER_STAGE_X11 (stage);
+  stage_x11->xdpy = backend_x11->xdpy;
+  stage_x11->xwin_root = backend_x11->xwin_root;
+  stage_x11->xscreen = backend_x11->xscreen_num;
+  stage_x11->backend = backend_x11;
 
-      g_object_set_data (G_OBJECT (stage), "clutter-backend", backend);
+  CLUTTER_NOTE (MISC, "X11 stage created (display:%p, screen:%d, root:%u)",
+                stage_x11->xdpy,
+                stage_x11->xscreen,
+                (unsigned int) stage_x11->xwin_root);
 
-      backend_x11->stage = g_object_ref_sink (stage);
-    }
+  /* needed ? */
+  g_object_set_data (G_OBJECT (stage), "clutter-backend", backend);
 
-  clutter_actor_realize (backend_x11->stage);
+  clutter_actor_realize (stage);
 
-  if (!CLUTTER_ACTOR_IS_REALIZED (backend_x11->stage))
+  if (!CLUTTER_ACTOR_IS_REALIZED (stage))
     {
       g_set_error (error, CLUTTER_INIT_ERROR,
                    CLUTTER_INIT_ERROR_INTERNAL,
                    "Unable to realize the main stage");
-      return FALSE;
+      return NULL;
     }
 
-  return TRUE;
+  return stage;
 }
 
 
@@ -421,15 +434,16 @@ clutter_backend_glx_class_init (ClutterBackendGLXClass *klass)
   ClutterBackendClass *backend_class = CLUTTER_BACKEND_CLASS (klass);
 
   gobject_class->constructor = clutter_backend_glx_constructor;
-  gobject_class->dispose = clutter_backend_glx_dispose;
-  gobject_class->finalize = clutter_backend_glx_finalize;
+  gobject_class->dispose     = clutter_backend_glx_dispose;
+  gobject_class->finalize    = clutter_backend_glx_finalize;
 
   backend_class->pre_parse   = clutter_backend_glx_pre_parse;
   backend_class->post_parse   = clutter_backend_glx_post_parse;
-  backend_class->init_stage   = clutter_backend_glx_init_stage;
+  backend_class->create_stage = clutter_backend_glx_create_stage;
   backend_class->add_options  = clutter_backend_glx_add_options;
   backend_class->get_features = clutter_backend_glx_get_features;
   backend_class->redraw       = clutter_backend_glx_redraw;
+  backend_class->ensure_context = clutter_backend_glx_ensure_context;
 }
 
 static void
index 6e14c06..9f0755c 100644 (file)
@@ -72,6 +72,9 @@ struct _ClutterBackendGLX
 {
   ClutterBackendX11 parent_instance;
 
+  /* Single context for all wins */
+  GLXContext             gl_context;
+
   /* Vblank stuff */
   GetVideoSyncProc       get_video_sync;
   WaitVideoSyncProc      wait_video_sync;
index 479513e..2e77f6f 100644 (file)
@@ -39,6 +39,7 @@
 #include "../clutter-shader.h"
 #include "../clutter-group.h"
 #include "../clutter-container.h"
+#include "../clutter-stage.h"
 
 #include "cogl.h"
 
@@ -98,12 +99,6 @@ clutter_stage_glx_unrealize (ClutterActor *actor)
 
   glXMakeCurrent (stage_x11->xdpy, None, NULL);
 
-  if (stage_glx->gl_context != None)
-    {
-      glXDestroyContext (stage_x11->xdpy, stage_glx->gl_context);
-      stage_glx->gl_context = None;
-    }
-
   XSync (stage_x11->xdpy, False);
 
   clutter_x11_untrap_x_errors ();
@@ -116,12 +111,15 @@ clutter_stage_glx_realize (ClutterActor *actor)
 {
   ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (actor);
   ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (actor);
-  gboolean is_offscreen;
+  ClutterBackendGLX *backend_glx;
+  gboolean         is_offscreen;
 
   CLUTTER_NOTE (MISC, "Realizing main stage");
 
   g_object_get (actor, "offscreen", &is_offscreen, NULL);
 
+  backend_glx = CLUTTER_BACKEND_GLX(clutter_get_default_backend());
+
   if (G_LIKELY (!is_offscreen))
     {
       int gl_attributes[] = 
@@ -142,7 +140,7 @@ clutter_stage_glx_realize (ClutterActor *actor)
       /* The following check seems strange */
       if (stage_x11->xvisinfo == None)
         stage_x11->xvisinfo = glXChooseVisual (stage_x11->xdpy,
-                                           stage_x11->xscreen,
+                                               stage_x11->xscreen,
                                                gl_attributes);
       if (!stage_x11->xvisinfo)
         {
@@ -195,26 +193,27 @@ clutter_stage_glx_realize (ClutterActor *actor)
 
       clutter_stage_x11_set_wm_protocols (stage_x11);
 
-      if (stage_glx->gl_context)
-        glXDestroyContext (stage_x11->xdpy, stage_glx->gl_context);
-
-      CLUTTER_NOTE (GL, "Creating GL Context");
-      stage_glx->gl_context = glXCreateContext (stage_x11->xdpy, 
-                                                stage_x11->xvisinfo, 
-                                                0, 
-                                                True);
-
-      if (stage_glx->gl_context == None)
+      if (backend_glx->gl_context == None)
         {
-          g_critical ("Unable to create suitable GL context.");
+          CLUTTER_NOTE (GL, "Creating GL Context");
+          backend_glx->gl_context =  glXCreateContext (stage_x11->xdpy, 
+                                                       stage_x11->xvisinfo, 
+                                                       0,
+                                                       True);
 
-          CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
+          if (backend_glx->gl_context == None)
+            {
+              g_critical ("Unable to create suitable GL context.");
 
-          return;
+              CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
+
+              return;
+            }
         }
 
       CLUTTER_NOTE (GL, "glXMakeCurrent");
-      glXMakeCurrent (stage_x11->xdpy, stage_x11->xwin, stage_glx->gl_context);
+
+      clutter_stage_ensure_current (CLUTTER_STAGE(stage_glx));
     }
   else
     {
@@ -244,9 +243,7 @@ clutter_stage_glx_realize (ClutterActor *actor)
           goto fail;
         }
 
-      if (stage_glx->gl_context)
-        glXDestroyContext (stage_x11->xdpy, stage_glx->gl_context);
-
+     
       stage_x11->xpixmap = XCreatePixmap (stage_x11->xdpy,
                                           stage_x11->xwin_root,
                                           stage_x11->xwin_width, 
@@ -258,17 +255,34 @@ clutter_stage_glx_realize (ClutterActor *actor)
                                                  stage_x11->xvisinfo,
                                                  stage_x11->xpixmap);
 
-      /* indirect */
-      stage_glx->gl_context = glXCreateContext (stage_x11->xdpy, 
-                                                stage_x11->xvisinfo, 
-                                                0, 
-                                                False);
+      if (backend_glx->gl_context == None)
+        {
+          CLUTTER_NOTE (GL, "Creating GL Context");
+
+          /* FIXME: we probably need a seperate offscreen context here
+           * - though it likely makes most sense to drop offscreen stages
+           * and rely on FBO's instead and GLXPixmaps seems mostly broken
+           * anyway..
+          */
+          backend_glx->gl_context =  glXCreateContext (stage_x11->xdpy, 
+                                                       stage_x11->xvisinfo, 
+                                                       0,
+                                                       False);
+
+          if (backend_glx->gl_context == None)
+            {
+              g_critical ("Unable to create suitable GL context.");
+
+              CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
+
+              return;
+            }
+        }
 
       clutter_x11_trap_x_errors ();
 
-      glXMakeCurrent (stage_x11->xdpy,
-                      stage_glx->glxpixmap,
-                      stage_glx->gl_context);
+      /* below will call glxMakeCurrent */
+      clutter_stage_ensure_current (CLUTTER_STAGE(stage_glx));
 
       if (clutter_x11_untrap_x_errors ())
         {
index 0810f5a..bd1646a 100644 (file)
@@ -49,7 +49,6 @@ struct _ClutterStageGLX
   ClutterStageX11 parent_instance;
 
   GLXPixmap glxpixmap;
-  GLXContext gl_context;
 };
 
 struct _ClutterStageGLXClass
index b0ac582..73e2783 100644 (file)
@@ -192,14 +192,6 @@ clutter_backend_x11_init_events (ClutterBackend *backend)
   _clutter_backend_x11_events_init (backend);
 }
 
-ClutterActor *
-clutter_backend_x11_get_stage (ClutterBackend *backend)
-{
-  ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend);
-
-  return backend_x11->stage;
-}
-
 static const GOptionEntry entries[] =
 {
   {
@@ -247,19 +239,20 @@ clutter_backend_x11_finalize (GObject *gobject)
 static void
 clutter_backend_x11_dispose (GObject *gobject)
 {
-  ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (gobject);
+  ClutterBackendX11   *backend_x11 = CLUTTER_BACKEND_X11 (gobject);
+  ClutterMainContext  *context;
+  ClutterStageManager *stage_manager;
+  GSList              *l;
 
-  if (backend_x11->stage)
-    {
-      CLUTTER_NOTE (BACKEND, "Disposing the main stage");
+  CLUTTER_NOTE (BACKEND, "Disposing the of stages");
 
-      /* we unset the private flag on the stage so we can safely
-       * destroy it without a warning from clutter_actor_destroy()
-       */
-      CLUTTER_UNSET_PRIVATE_FLAGS (backend_x11->stage,
-                                   CLUTTER_ACTOR_IS_TOPLEVEL);
-      clutter_actor_destroy (backend_x11->stage);
-      backend_x11->stage = NULL;
+  context = clutter_context_get_default ();
+  stage_manager = context->stage_manager;
+
+  for (l = stage_manager->stages; l; l = l->next)
+    {
+      ClutterActor *stage = CLUTTER_ACTOR (l->data);
+      clutter_actor_destroy (stage);
     }
 
   CLUTTER_NOTE (BACKEND, "Removing the event source");
@@ -314,10 +307,9 @@ clutter_backend_x11_class_init (ClutterBackendX11Class *klass)
   gobject_class->dispose = clutter_backend_x11_dispose;
   gobject_class->finalize = clutter_backend_x11_finalize;
 
-  backend_class->pre_parse   = clutter_backend_x11_pre_parse;
+  backend_class->pre_parse    = clutter_backend_x11_pre_parse;
   backend_class->post_parse   = clutter_backend_x11_post_parse;
   backend_class->init_events  = clutter_backend_x11_init_events;
-  backend_class->get_stage    = clutter_backend_x11_get_stage;
   backend_class->add_options  = clutter_backend_x11_add_options;
   backend_class->get_features = clutter_backend_x11_get_features;
 }
index 357da80..48b4325 100644 (file)
@@ -53,9 +53,6 @@ struct _ClutterBackendX11
 {
   ClutterBackend parent_instance;
 
-  /* main stage singleton */
-  ClutterActor *stage;
-
   Display *xdpy;
   Window   xwin_root;
   Screen  *xscreen;
index e6e86d2..94806cb 100644 (file)
@@ -158,7 +158,6 @@ void
 _clutter_backend_x11_events_init (ClutterBackend *backend)
 {
   ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend);
-  ClutterStage *stage = CLUTTER_STAGE (backend_x11->stage);
   GSource *source;
   ClutterEventSource *event_source;
   int connection_number;
@@ -182,9 +181,6 @@ _clutter_backend_x11_events_init (ClutterBackend *backend)
   g_source_set_can_recurse (source, TRUE);
   g_source_attach (source, NULL);
 
-  xembed_set_info (backend_x11,
-                   clutter_x11_get_stage_window (stage),
-                   0);
 }
 
 void
@@ -243,14 +239,13 @@ translate_key_event (ClutterBackend *backend,
 
 static gboolean
 handle_wm_protocols_event (ClutterBackendX11 *backend_x11,
+                           Window             window,
                            XEvent            *xevent)
 {
   Atom atom = (Atom) xevent->xclient.data.l[0];
-  ClutterStage *stage = CLUTTER_STAGE (backend_x11->stage);
-  Window stage_xwindow = clutter_x11_get_stage_window (stage);
 
   if (atom == backend_x11->atom_WM_DELETE_WINDOW &&
-      xevent->xany.window == stage_xwindow)
+      xevent->xany.window == window)
     {
       /* the WM_DELETE_WINDOW is a request: we do not destroy
        * the window right away, as it might contain vital data;
@@ -261,13 +256,13 @@ handle_wm_protocols_event (ClutterBackendX11 *backend_x11,
                     xevent->xclient.window);
 
       set_user_time (backend_x11,
-                     &stage_xwindow,
+                     &window,
                      xevent->xclient.data.l[1]);
 
       return TRUE;
     }
   else if (atom == backend_x11->atom_NET_WM_PING &&
-           xevent->xany.window == stage_xwindow)
+           xevent->xany.window == window)
     {
       XClientMessageEvent xclient = xevent->xclient;
 
@@ -289,7 +284,7 @@ handle_xembed_event (ClutterBackendX11 *backend_x11,
 {
   ClutterActor *stage;
 
-  stage = _clutter_backend_get_stage (CLUTTER_BACKEND (backend_x11));
+  stage = clutter_stage_get_default ();
 
   switch (xevent->xclient.data.l[1])
     {
@@ -340,9 +335,6 @@ event_translate (ClutterBackend *backend,
   Window             xwindow, stage_xwindow;
 
   backend_x11    = CLUTTER_BACKEND_X11 (backend);
-  stage          = CLUTTER_STAGE (_clutter_backend_get_stage (backend));
-  stage_x11      = CLUTTER_STAGE_X11 (stage);
-  stage_xwindow  = clutter_x11_get_stage_window (stage);
 
   xwindow = xevent->xany.window;
 
@@ -378,9 +370,16 @@ event_translate (ClutterBackend *backend,
    * (the x11 filters might be getting events for other windows, so do not
    * mess them about.
    */
-  if (xwindow != stage_xwindow)
+  stage = clutter_x11_get_stage_from_window (xwindow);
+
+  if (stage == NULL)
     return FALSE;
 
+  stage_x11      = CLUTTER_STAGE_X11 (stage);
+  stage_xwindow  = xwindow; /* clutter_x11_get_stage_window (stage); */
+
+  event->any.stage = stage;
+
   res = TRUE;
 
   switch (xevent->type)
@@ -514,7 +513,8 @@ event_translate (ClutterBackend *backend,
         /* FIXME: need to make stage an 'actor' so can que
          * a paint direct from there rather than hack here...
          */
-        clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+        CLUTTER_NOTE (MULTISTAGE, "expose for stage:%p, redrawing", stage);
+        clutter_redraw (stage);
         res = FALSE;
       }
       break;
@@ -614,7 +614,7 @@ event_translate (ClutterBackend *backend,
         res = handle_xembed_event (backend_x11, xevent);
       else if (xevent->xclient.message_type == backend_x11->atom_WM_PROTOCOLS)
         {
-          res = handle_wm_protocols_event (backend_x11, xevent);
+          res = handle_wm_protocols_event (backend_x11, stage_xwindow, xevent);
           event->type = event->any.type = CLUTTER_DELETE;
         }
       break;
index aed72b1..c5d935c 100644 (file)
@@ -117,7 +117,7 @@ clutter_stage_x11_show (ClutterActor *actor)
       /* Fire off a redraw to avoid flicker on first map.
        * Appears not to work perfectly on intel drivers at least.
       */
-      clutter_redraw();
+      clutter_redraw(CLUTTER_STAGE(actor));
 
       XSync (stage_x11->xdpy, FALSE);
       XMapWindow (stage_x11->xdpy, stage_x11->xwin);
@@ -448,6 +448,39 @@ clutter_x11_get_stage_window (ClutterStage *stage)
 }
 
 /**
+ * clutter_x11_get_stage_from_window:
+ * @win: an X Window ID
+ *
+ * Gets the stage for a particular X window.  
+ *
+ * Return value: The stage or NULL if a stage does not exist for the window.
+ *
+ * Since: 0.8
+ */
+ClutterStage*
+clutter_x11_get_stage_from_window (Window win)
+{
+  ClutterMainContext  *context;
+  ClutterStageManager *stage_manager;
+  GSList              *l;
+
+  context = clutter_context_get_default ();
+
+  stage_manager = context->stage_manager;
+
+  /* FIXME: use a hash here for performance resaon */
+  for (l = stage_manager->stages; l; l = l->next)
+    {
+      ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (l->data);
+
+      if (stage_x11->xwin == win)
+        return CLUTTER_STAGE(stage_x11);
+    }
+
+  return NULL;
+}
+
+/**
  * clutter_x11_get_stage_visual:
  * @stage: a #ClutterStage
  *
index 28a5e5a..5575294 100644 (file)
@@ -101,6 +101,8 @@ ClutterX11FilterReturn clutter_x11_handle_event (XEvent *xevent);
 
 void         clutter_x11_disable_event_retrieval (void);
 
+ClutterStage *clutter_x11_get_stage_from_window (Window win);
+
 G_END_DECLS
 
 #endif /* __CLUTTER_X11_H__ */
index 8ca66f8..a5186bf 100644 (file)
@@ -7,7 +7,7 @@ noinst_PROGRAMS = test-textures test-events test-offscreen test-scale \
                  test-threads test-timeline test-score test-script \
                  test-model test-grab test-effects test-fullscreen \
                  test-shader test-unproject test-viewport test-fbo \
-                 test-opacity
+                 test-opacity test-multistage
 
 INCLUDES = -I$(top_srcdir)/
 LDADD = $(top_builddir)/clutter/libclutter-@CLUTTER_FLAVOUR@-@CLUTTER_MAJORMINOR@.la
@@ -43,5 +43,6 @@ test_fullscreen_SOURCES  = test-fullscreen.c
 test_viewport_SOURCES    = test-viewport.c
 test_fbo_SOURCES         = test-fbo.c
 test_opacity_SOURCES     = test-opacity.c
+test_multistage_SOURCES     = test-multistage.c
 
 EXTRA_DIST = redhand.png test-script.json
diff --git a/tests/test-multistage.c b/tests/test-multistage.c
new file mode 100644 (file)
index 0000000..ac6e7fe
--- /dev/null
@@ -0,0 +1,129 @@
+#include <clutter/clutter.h>
+
+static gint n_stages = 1;
+
+static gboolean
+tex_button_cb (ClutterActor    *actor,
+               ClutterEvent    *event,
+               gpointer         data)
+{
+  clutter_actor_hide (actor);
+}
+
+static void
+on_button_press (ClutterActor *actor,
+                 ClutterEvent *event,
+                 gpointer      data)
+{
+  ClutterActor *new_stage;
+  ClutterActor *label, *tex;
+  gint width, height;
+  gchar *stage_label, *win_title;
+  ClutterColor color = { 0xdd, 0x33, 0xdd, 0xff };
+  ClutterColor white = { 0x99, 0x99, 0x99, 0xff };
+  GdkPixbuf      *pixb;
+  ClutterTimeline  *timeline;
+  ClutterAlpha     *alpha;
+  ClutterBehaviour *r_behave;
+
+  new_stage = clutter_stage_create_new ();
+
+  /* FIXME: below should really be automatic */
+  /* clutter_stage_ensure_cogl_context (CLUTTER_STAGE(new_stage)); */
+
+  clutter_stage_set_color (CLUTTER_STAGE (new_stage), &color);
+  clutter_actor_set_size (new_stage, 320, 240);
+
+  pixb = gdk_pixbuf_new_from_file ("redhand.png", NULL);
+
+  if (!pixb)
+    g_error("pixbuf load failed");
+
+  tex = clutter_texture_new_from_pixbuf (pixb);
+  clutter_actor_set_reactive (tex, TRUE);
+  g_signal_connect (tex, "button-press-event", 
+                    G_CALLBACK (tex_button_cb), NULL);
+
+  clutter_container_add_actor (CLUTTER_CONTAINER (new_stage), tex);
+
+  stage_label = g_strdup_printf ("<b>Stage: %d</b>", ++n_stages); 
+  label = clutter_label_new_with_text ("Mono 12", stage_label);
+
+  clutter_label_set_color (CLUTTER_LABEL (label), &white);
+  clutter_label_set_use_markup (CLUTTER_LABEL (label), TRUE);
+  width = (clutter_actor_get_width (new_stage) 
+           - clutter_actor_get_width (label)) / 2;
+  height = (clutter_actor_get_height (new_stage) 
+            - clutter_actor_get_height (label)) / 2;
+  clutter_actor_set_position (label, width, height);
+  clutter_container_add_actor (CLUTTER_CONTAINER (new_stage), label);
+  clutter_actor_show (label);
+  g_free (stage_label);
+
+  /*
+  g_signal_connect (new_stage, "button-press-event",
+                    G_CALLBACK (clutter_actor_destroy),
+                    NULL);
+  */
+
+  win_title =   g_strdup_printf ("Stage:%p", new_stage);
+  clutter_stage_set_title (CLUTTER_STAGE(new_stage), win_title);
+
+  timeline = clutter_timeline_new_for_duration (2000);
+  g_object_set (timeline, "loop", TRUE, NULL);  
+
+  alpha = clutter_alpha_new_full (timeline,
+                                  CLUTTER_ALPHA_RAMP_INC,
+                                  NULL, NULL);
+
+  r_behave = clutter_behaviour_rotate_new (alpha,
+                                          CLUTTER_Y_AXIS,
+                                          CLUTTER_ROTATE_CW,
+                                          0.0, 360.0); 
+
+  clutter_behaviour_rotate_set_center (CLUTTER_BEHAVIOUR_ROTATE (r_behave),
+                                       clutter_actor_get_width (label)/2, 
+                                       0, 
+                                       0);
+  
+  clutter_behaviour_apply (r_behave, label);
+  clutter_timeline_start (timeline);
+
+  clutter_actor_show_all (new_stage);
+}
+
+int
+main (int argc, char *argv[])
+{
+  ClutterActor *stage_default;
+  ClutterActor *label;
+  gint width, height;
+  gchar *win_title;
+
+  clutter_init (&argc, &argv);
+  
+  stage_default = clutter_stage_get_default ();
+  g_signal_connect (stage_default, "button-press-event",
+                    G_CALLBACK (on_button_press),
+                    NULL);
+
+  label = clutter_label_new_with_text ("Mono 16", "Default stage");
+  width = (clutter_actor_get_width (stage_default) 
+           - clutter_actor_get_width (label))
+             / 2;
+  height = (clutter_actor_get_height (stage_default) 
+            - clutter_actor_get_height (label))
+            / 2;
+  clutter_actor_set_position (label, width, height);
+  clutter_container_add_actor (CLUTTER_CONTAINER (stage_default), label);
+  clutter_actor_show (label);
+
+  win_title =   g_strdup_printf ("Stage:%p", stage_default);
+  clutter_stage_set_title (CLUTTER_STAGE(stage_default), win_title);
+
+  clutter_actor_show (stage_default);
+
+  clutter_main ();
+
+  return 0;
+}