+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
$(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 \
clutter-scriptable.c \
clutter-shader.c \
clutter-stage.c \
+ clutter-stage-manager.c \
clutter-texture.c \
clutter-timeline.c \
clutter-timeout-pool.c \
ClutterFixed scale_y;
ShaderData *shader_data;
+
+ ClutterStage *stage;
};
enum
-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)
void
clutter_actor_realize (ClutterActor *self)
{
- ClutterActorClass *klass;
+ ClutterActorClass *klass;
if (CLUTTER_ACTOR_IS_REALIZED (self))
return;
clutter_actor_unrealize (ClutterActor *self)
{
ClutterActorClass *klass;
+ ClutterActorPrivate *priv;
+
+ priv = self->priv;
if (!CLUTTER_ACTOR_IS_REALIZED (self))
return;
if (klass->unrealize)
(klass->unrealize) (self);
+
+ priv->stage = NULL;
}
static void
* 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)
{
* 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)
{
_clutter_actor_apply_modelview_transform_recursive (ClutterActor *self,
ClutterActor *ancestor)
{
- ClutterActor * parent;
+ ClutterActor *parent, *stage;
parent = clutter_actor_get_parent (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);
}
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)
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));
}
/**
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;
+}
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 */
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)
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)
{
#include <glib-object.h>
#include <clutter/clutter-actor.h>
+#include <clutter/clutter-stage.h>
#include <clutter/clutter-event.h>
#include <clutter/clutter-feature.h>
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;
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
}
/**
+ * 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
*
ClutterEventType type;
guint32 time;
ClutterEventFlags flags;
+ ClutterStage *stage;
ClutterActor *source;
};
ClutterEventType type;
guint32 time;
ClutterEventFlags flags;
+ ClutterStage *stage;
ClutterActor *source;
ClutterModifierType modifier_state;
guint keyval;
ClutterEventType type;
guint32 time;
ClutterEventFlags flags;
+ ClutterStage *stage;
ClutterActor *source;
gint x;
gint y;
ClutterEventType type;
guint32 time;
ClutterEventFlags flags;
+ ClutterStage *stage;
ClutterActor *source;
gint x;
gint y;
ClutterEventType type;
guint32 time;
ClutterEventFlags flags;
+ ClutterStage *stage;
ClutterActor *source;
gint x;
gint y;
ClutterEventType type;
guint32 time;
ClutterEventFlags flags;
+ ClutterStage *stage;
ClutterActor *source;
gint x;
gint y;
ClutterEventType type;
guint32 time;
ClutterEventFlags flags;
+ ClutterStage *stage;
ClutterActor *source; /* unused XXX: should probably be the stage itself */
ClutterStageState changed_mask;
ClutterStageState new_state;
guint32 clutter_keysym_to_unicode (guint keyval);
+ClutterStage* clutter_event_get_stage (ClutterEvent *event);
G_END_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.
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);
#define __CLUTTER_GROUP_H__
#include <glib-object.h>
+#include <clutter/clutter-types.h>
#include <clutter/clutter-actor.h>
G_BEGIN_DECLS
{ "scheduler", CLUTTER_DEBUG_SCHEDULER },
{ "script", CLUTTER_DEBUG_SCRIPT },
{ "shader", CLUTTER_DEBUG_SHADER },
+ { "multistage", CLUTTER_DEBUG_MULTISTAGE },
};
#endif /* CLUTTER_ENABLE_DEBUG */
* 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)
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,
* 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 ())
}
}
- 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);
}
/**
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);
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);
GOptionGroup *group;
gboolean res;
GError *stage_error;
+ ClutterActor *stage;
if (clutter_is_initialized)
return CLUTTER_INIT_SUCCESS;
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;
char ***argv)
{
ClutterMainContext *context;
+ ClutterActor *stage;
GError *stage_error;
if (clutter_is_initialized)
/* 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);
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;
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;
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;
context = clutter_context_get_default ();
backend = context->backend;
- stage = _clutter_backend_get_stage (backend);
+ stage = CLUTTER_ACTOR(event->any.stage);
if (!stage)
return;
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:
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);
#include <pango/pangoft2.h>
+#include "clutter-stage-manager.h"
#include "clutter-event.h"
#include "clutter-backend.h"
#include "clutter-stage.h"
{
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;
#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);
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);
--- /dev/null
+#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);
+}
--- /dev/null
+/*
+ * 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__ */
#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"
gchar *title;
ClutterActor *key_focused_actor;
+
+ guint update_idle; /* repaint idler id */
};
enum
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));
}
/**
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);
+ }
+}
#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>
typedef struct _ClutterPerspective ClutterPerspective;
typedef struct _ClutterFog ClutterFog;
-typedef struct _ClutterStage ClutterStage;
+
typedef struct _ClutterStageClass ClutterStageClass;
typedef struct _ClutterStagePrivate ClutterStagePrivate;
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))) \
/* Forward delarations to avoid header catch 22's */
typedef struct _ClutterActor ClutterActor;
+typedef struct _ClutterStage ClutterStage;
/**
* ClutterGravity:
{
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 */
}
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));
}
}
-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;
}
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
{
ClutterBackendX11 parent_instance;
+ /* Single context for all wins */
+ GLXContext gl_context;
+
/* Vblank stuff */
GetVideoSyncProc get_video_sync;
WaitVideoSyncProc wait_video_sync;
#include "../clutter-shader.h"
#include "../clutter-group.h"
#include "../clutter-container.h"
+#include "../clutter-stage.h"
#include "cogl.h"
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 ();
{
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[] =
/* 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)
{
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
{
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,
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 ())
{
ClutterStageX11 parent_instance;
GLXPixmap glxpixmap;
- GLXContext gl_context;
};
struct _ClutterStageGLXClass
_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[] =
{
{
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");
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;
}
{
ClutterBackend parent_instance;
- /* main stage singleton */
- ClutterActor *stage;
-
Display *xdpy;
Window xwin_root;
Screen *xscreen;
_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;
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
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;
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;
{
ClutterActor *stage;
- stage = _clutter_backend_get_stage (CLUTTER_BACKEND (backend_x11));
+ stage = clutter_stage_get_default ();
switch (xevent->xclient.data.l[1])
{
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;
* (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)
/* 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;
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;
/* 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);
}
/**
+ * 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
*
void clutter_x11_disable_event_retrieval (void);
+ClutterStage *clutter_x11_get_stage_from_window (Window win);
+
G_END_DECLS
#endif /* __CLUTTER_X11_H__ */
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
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
--- /dev/null
+#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;
+}