Try to mop up the default stage mess

The default stage was a neat concept when we started Clutter out,
somewhere in the Jurassic era; a singleton instance that gets created at
initialization time, and remains the same for the entire duration of the
process.

Worked well enough when Clutter was a small library meant to be used to
write fullscreen media browsers, but since the introduction of multiple
stages, and Clutter being used to create all sorts of applications, the
default stage is just a vestigial remainder of that past, like an
appendix; something that complicates the layout of the code and
introduces weird behaviour, so that you notice its existence only when
something goes wrong.

Some platforms we do support, though, only have one framebuffer, so it
makes sense for them to have only one stage.

At this point, the only sane thing to do is to go through the same code
paths on all platforms, and that code path is the stage instance
creation and initialization — i.e. clutter_stage_new() (or
g_object_new() with CLUTTER_TYPE_STAGE).

For platforms that support multiple stages, nothing has changed: the stage
created by clutter_stage_get_default() will be set as the default one;
if nobody calls it, the default stage is never created, and it just
lives on as a meaningless check.

For platforms that only support one stage, clutter_stage_new() and
clutter_stage_get_default() will behave exactly the same the first time
they are called: both will create a stage, and set it as the default.
Calling clutter_stage_new() a second time is treated as a programmer
error, and will result in Clutter aborting. This is a behavioural change
because the existing behaviour or creating a new ClutterStage instance
with the same ClutterStageWindow private implementation is, simply put,
utterly braindamaged and I should have *never* had written it, and I
apologize for it. In my defence, I didn't know any better at the time.

This is the first step towards the complete deprecation of
clutter_stage_get_default() and clutter_stage_is_default(), which will
come later.
This commit is contained in:
Emmanuele Bassi 2011-11-09 14:04:05 +00:00
parent 5903fcc36d
commit 26f4467392
2 changed files with 79 additions and 40 deletions

View File

@ -684,14 +684,11 @@ _clutter_backend_create_stage (ClutterBackend *backend,
GError **error)
{
ClutterBackendClass *klass;
ClutterStageManager *stage_manager;
ClutterStageWindow *stage_window;
g_assert (CLUTTER_IS_BACKEND (backend));
g_assert (CLUTTER_IS_STAGE (wrapper));
stage_manager = clutter_stage_manager_get_default ();
klass = CLUTTER_BACKEND_GET_CLASS (backend);
if (klass->create_stage != NULL)
stage_window = klass->create_stage (backend, wrapper, error);
@ -702,8 +699,6 @@ _clutter_backend_create_stage (ClutterBackend *backend,
return NULL;
g_assert (CLUTTER_IS_STAGE_WINDOW (stage_window));
_clutter_stage_set_window (wrapper, stage_window);
_clutter_stage_manager_add_stage (stage_manager, wrapper);
return stage_window;
}

View File

@ -30,11 +30,6 @@
* #ClutterStage is a top level 'window' on which child actors are placed
* and manipulated.
*
* Clutter creates a default stage upon initialization, which can be retrieved
* using clutter_stage_get_default(). Clutter always provides the default
* stage, unless the backend is unable to create one. The stage returned
* by clutter_stage_get_default() is guaranteed to always be the same.
*
* Backends might provide support for multiple stages. The support for this
* feature can be checked at run-time using the clutter_feature_available()
* function and the %CLUTTER_FEATURE_STAGE_MULTIPLE flag. If the backend used
@ -1497,6 +1492,45 @@ clutter_stage_real_apply_transform (ClutterActor *stage,
cogl_matrix_multiply (matrix, matrix, &priv->view);
}
static void
clutter_stage_constructed (GObject *gobject)
{
ClutterStage *self = CLUTTER_STAGE (gobject);
ClutterStageManager *stage_manager;
stage_manager = clutter_stage_manager_get_default ();
_clutter_stage_manager_add_stage (stage_manager, self);
/* if this stage has been created on a backend that does not
* support multiple stages then it becomes the default stage
* as well; any other attempt at creating a ClutterStage will
* fail.
*/
if (!clutter_feature_available (CLUTTER_FEATURE_STAGE_MULTIPLE))
{
if (G_UNLIKELY (clutter_stage_manager_get_default_stage (stage_manager) != NULL))
{
g_error ("Unable to create another stage: the backend of "
"type '%s' does not support multiple stages. Use "
"clutter_stage_get_default() instead to access the "
"stage singleton.",
G_OBJECT_TYPE_NAME (clutter_get_default_backend ()));
}
/* This will take care of automatically adding the stage to the
* stage manager and setting it as the default. Its floating
* reference will be claimed by the stage manager.
*/
_clutter_stage_manager_set_default_stage (stage_manager, self);
/* the default stage is realized by default */
clutter_actor_realize (CLUTTER_ACTOR (self));
}
G_OBJECT_CLASS (clutter_stage_parent_class)->constructed (gobject);
}
static void
clutter_stage_set_property (GObject *object,
guint prop_id,
@ -1696,6 +1730,7 @@ clutter_stage_class_init (ClutterStageClass *klass)
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
GParamSpec *pspec;
gobject_class->constructed = clutter_stage_constructed;
gobject_class->set_property = clutter_stage_set_property;
gobject_class->get_property = clutter_stage_get_property;
gobject_class->dispose = clutter_stage_dispose;
@ -2037,8 +2072,10 @@ static void
clutter_stage_init (ClutterStage *self)
{
ClutterStagePrivate *priv;
ClutterStageWindow *impl;
ClutterBackend *backend;
cairo_rectangle_int_t geom;
GError *error;
/* a stage is a top-level object */
CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IS_TOPLEVEL);
@ -2047,25 +2084,31 @@ clutter_stage_init (ClutterStage *self)
CLUTTER_NOTE (BACKEND, "Creating stage from the default backend");
backend = clutter_get_default_backend ();
priv->impl = _clutter_backend_create_stage (backend, self, NULL);
if (!priv->impl)
{
g_warning ("Unable to create a new stage, falling back to the "
"default stage.");
priv->impl = _clutter_stage_get_default_window ();
/* at this point we must have a default stage, or we're screwed */
g_assert (priv->impl != NULL);
error = NULL;
impl = _clutter_backend_create_stage (backend, self, &error);
if (G_UNLIKELY (impl == NULL))
{
if (error != NULL)
{
g_critical ("Unable to create a new stage implementation: %s",
error->message);
g_error_free (error);
}
else
g_critical ("Unable to create a new stage implementation.");
}
_clutter_stage_set_window (self, impl);
priv->event_queue = g_queue_new ();
priv->is_fullscreen = FALSE;
priv->is_user_resizable = FALSE;
priv->is_cursor_visible = TRUE;
priv->use_fog = FALSE;
priv->is_fullscreen = FALSE;
priv->is_user_resizable = FALSE;
priv->is_cursor_visible = TRUE;
priv->use_fog = FALSE;
priv->throttle_motion_events = TRUE;
priv->min_size_changed = FALSE;
priv->min_size_changed = FALSE;
/* XXX - we need to keep the invariant that calling
* clutter_set_motion_event_enabled() before the stage creation
@ -2133,15 +2176,26 @@ clutter_stage_init (ClutterStage *self)
/**
* clutter_stage_get_default:
*
* Returns the main stage. The default #ClutterStage is a singleton,
* so the stage will be created the first time this function is
* called (typically, inside clutter_init()); all the subsequent
* calls to clutter_stage_get_default() will return the same instance.
* Retrieves a #ClutterStage singleton.
*
* Clutter guarantess the existence of the default stage.
* This function is not as useful as it sounds, and will most likely
* by deprecated in the future. Application code should only create
* a #ClutterStage instance using clutter_stage_new(), and manage the
* lifetime of the stage manually.
*
* The default stage singleton has a platform-specific behaviour: on
* platforms without the %CLUTTER_FEATURE_STAGE_MULTIPLE feature flag
* set, the first #ClutterStage instance will also be set to be the
* default stage instance, and this function will always return a
* pointer to it.
*
* On platforms with the %CLUTTER_FEATURE_STAGE_MULTIPLE feature flag
* set, the default stage will be created by the first call to this
* function, and every following call will return the same pointer to
* it.
*
* Return value: (transfer none) (type Clutter.Stage): the main
* #ClutterStage. You should never destroy or unref the returned
* #ClutterStage. You should never destroy or unref the returned
* actor.
*/
ClutterActor *
@ -3149,16 +3203,6 @@ G_DEFINE_BOXED_TYPE (ClutterFog, clutter_fog, clutter_fog_copy, clutter_fog_free
ClutterActor *
clutter_stage_new (void)
{
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;
}
/* The stage manager will grab the floating reference when the stage
is added to it in the constructor */
return g_object_new (CLUTTER_TYPE_STAGE, NULL);
}
@ -3480,7 +3524,7 @@ _clutter_stage_set_window (ClutterStage *stage,
g_return_if_fail (CLUTTER_IS_STAGE (stage));
g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (stage_window));
if (stage->priv->impl)
if (stage->priv->impl != NULL)
g_object_unref (stage->priv->impl);
stage->priv->impl = stage_window;