diff --git a/ChangeLog b/ChangeLog index cd580f6e8..6336e2358 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,38 @@ +2008-03-28 Matthew Allum + + * 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 * clutter/win32/clutter-win32.h: Added gtk-doc documentation for diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 16b6f6c62..8a73cb613 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -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 \ diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index c4d501705..b8a2418e7 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -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; +} diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h index 1ad5b541e..4377daced 100644 --- a/clutter/clutter-actor.h +++ b/clutter/clutter-actor.h @@ -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 */ diff --git a/clutter/clutter-backend.c b/clutter/clutter-backend.c index 0b83081bc..aedb3937a 100644 --- a/clutter/clutter-backend.c +++ b/clutter/clutter-backend.c @@ -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); - klass = CLUTTER_BACKEND_GET_CLASS (backend); - if (klass->init_stage) - return klass->init_stage (backend, error); + context = clutter_context_get_default (); - return TRUE; + if (!context->stage_manager) + context->stage_manager = clutter_stage_manager_get_default (); + + klass = CLUTTER_BACKEND_GET_CLASS (backend); + if (klass->create_stage) + stage = klass->create_stage (backend, error); + + 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) { diff --git a/clutter/clutter-backend.h b/clutter/clutter-backend.h index 89c192527..83d8e4243 100644 --- a/clutter/clutter-backend.h +++ b/clutter/clutter-backend.h @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -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; diff --git a/clutter/clutter-debug.h b/clutter/clutter-debug.h index f4252595c..9233f45f0 100644 --- a/clutter/clutter-debug.h +++ b/clutter/clutter-debug.h @@ -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 diff --git a/clutter/clutter-event.c b/clutter/clutter-event.c index d53e38e18..813e4ac4d 100644 --- a/clutter/clutter-event.c +++ b/clutter/clutter-event.c @@ -180,6 +180,25 @@ clutter_event_get_source (ClutterEvent *event) return event->any.source; } +/** + * 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 diff --git a/clutter/clutter-event.h b/clutter/clutter-event.h index b6c50fc51..e93a2bf0b 100644 --- a/clutter/clutter-event.h +++ b/clutter/clutter-event.h @@ -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 diff --git a/clutter/clutter-feature.h b/clutter/clutter-feature.h index ec771ba5d..360273271 100644 --- a/clutter/clutter-feature.h +++ b/clutter/clutter-feature.h @@ -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); diff --git a/clutter/clutter-group.h b/clutter/clutter-group.h index 45c1fea3b..d4a4e6cc1 100644 --- a/clutter/clutter-group.h +++ b/clutter/clutter-group.h @@ -27,6 +27,7 @@ #define __CLUTTER_GROUP_H__ #include +#include #include G_BEGIN_DECLS diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index a0283ff6c..5f47c1ee1 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -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: diff --git a/clutter/clutter-main.h b/clutter/clutter-main.h index d63a911c9..59d51e709 100644 --- a/clutter/clutter-main.h +++ b/clutter/clutter-main.h @@ -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); diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index 815c94a30..375b06363 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -40,6 +40,7 @@ #include +#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 index 000000000..1fd81ba6e --- /dev/null +++ b/clutter/clutter-stage-manager.c @@ -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 index 000000000..1b4a7b590 --- /dev/null +++ b/clutter/clutter-stage-manager.h @@ -0,0 +1,65 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Authored By Matthew Allum + * + * 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 +#include +#include + +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__ */ diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index 53c3b6f1a..271cbfc32 100644 --- a/clutter/clutter-stage.c +++ b/clutter/clutter-stage.c @@ -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); + } +} diff --git a/clutter/clutter-stage.h b/clutter/clutter-stage.h index c34b4e8b2..0973cbe96 100644 --- a/clutter/clutter-stage.h +++ b/clutter/clutter-stage.h @@ -26,6 +26,7 @@ #ifndef __CLUTTER_STAGE_H__ #define __CLUTTER_STAGE_H__ +#include #include #include #include @@ -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))) \ diff --git a/clutter/clutter-types.h b/clutter/clutter-types.h index fff26a141..0a096113c 100644 --- a/clutter/clutter-types.h +++ b/clutter/clutter-types.h @@ -39,6 +39,7 @@ G_BEGIN_DECLS /* Forward delarations to avoid header catch 22's */ typedef struct _ClutterActor ClutterActor; +typedef struct _ClutterStage ClutterStage; /** * ClutterGravity: diff --git a/clutter/glx/clutter-backend-glx.c b/clutter/glx/clutter-backend-glx.c index 99264b232..b6ea0353a 100644 --- a/clutter/glx/clutter-backend-glx.c +++ b/clutter/glx/clutter-backend-glx.c @@ -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); - 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; - /* 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; + CLUTTER_NOTE (MISC, "X11 stage created (display:%p, screen:%d, root:%u)", + stage_x11->xdpy, + stage_x11->xscreen, + (unsigned int) stage_x11->xwin_root); - CLUTTER_NOTE (MISC, "X11 stage created (display:%p, screen:%d, root:%u)", - stage_x11->xdpy, - stage_x11->xscreen, - (unsigned int) stage_x11->xwin_root); + /* needed ? */ + g_object_set_data (G_OBJECT (stage), "clutter-backend", backend); - g_object_set_data (G_OBJECT (stage), "clutter-backend", backend); + clutter_actor_realize (stage); - backend_x11->stage = g_object_ref_sink (stage); - } - - clutter_actor_realize (backend_x11->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 diff --git a/clutter/glx/clutter-backend-glx.h b/clutter/glx/clutter-backend-glx.h index 6e14c0618..9f0755cd3 100644 --- a/clutter/glx/clutter-backend-glx.h +++ b/clutter/glx/clutter-backend-glx.h @@ -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; diff --git a/clutter/glx/clutter-stage-glx.c b/clutter/glx/clutter-stage-glx.c index 479513eff..2e77f6f0c 100644 --- a/clutter/glx/clutter-stage-glx.c +++ b/clutter/glx/clutter-stage-glx.c @@ -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 ()) { diff --git a/clutter/glx/clutter-stage-glx.h b/clutter/glx/clutter-stage-glx.h index 0810f5ac4..bd1646abc 100644 --- a/clutter/glx/clutter-stage-glx.h +++ b/clutter/glx/clutter-stage-glx.h @@ -49,7 +49,6 @@ struct _ClutterStageGLX ClutterStageX11 parent_instance; GLXPixmap glxpixmap; - GLXContext gl_context; }; struct _ClutterStageGLXClass diff --git a/clutter/x11/clutter-backend-x11.c b/clutter/x11/clutter-backend-x11.c index b0ac582d9..73e27832e 100644 --- a/clutter/x11/clutter-backend-x11.c +++ b/clutter/x11/clutter-backend-x11.c @@ -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 of stages"); + + context = clutter_context_get_default (); + stage_manager = context->stage_manager; + + for (l = stage_manager->stages; l; l = l->next) { - CLUTTER_NOTE (BACKEND, "Disposing the main stage"); - - /* 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; + 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; } diff --git a/clutter/x11/clutter-backend-x11.h b/clutter/x11/clutter-backend-x11.h index 357da8028..48b4325ff 100644 --- a/clutter/x11/clutter-backend-x11.h +++ b/clutter/x11/clutter-backend-x11.h @@ -53,9 +53,6 @@ struct _ClutterBackendX11 { ClutterBackend parent_instance; - /* main stage singleton */ - ClutterActor *stage; - Display *xdpy; Window xwin_root; Screen *xscreen; diff --git a/clutter/x11/clutter-event-x11.c b/clutter/x11/clutter-event-x11.c index e6e86d272..94806cb3b 100644 --- a/clutter/x11/clutter-event-x11.c +++ b/clutter/x11/clutter-event-x11.c @@ -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; diff --git a/clutter/x11/clutter-stage-x11.c b/clutter/x11/clutter-stage-x11.c index aed72b13d..c5d935c3c 100644 --- a/clutter/x11/clutter-stage-x11.c +++ b/clutter/x11/clutter-stage-x11.c @@ -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); @@ -447,6 +447,39 @@ clutter_x11_get_stage_window (ClutterStage *stage) return CLUTTER_STAGE_X11 (stage)->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 diff --git a/clutter/x11/clutter-x11.h b/clutter/x11/clutter-x11.h index 28a5e5a0b..557529434 100644 --- a/clutter/x11/clutter-x11.h +++ b/clutter/x11/clutter-x11.h @@ -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__ */ diff --git a/tests/Makefile.am b/tests/Makefile.am index 8ca66f821..a5186bf93 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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 index 000000000..ac6e7fe9d --- /dev/null +++ b/tests/test-multistage.c @@ -0,0 +1,129 @@ +#include + +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 ("Stage: %d", ++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; +}