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:
parent
5903fcc36d
commit
26f4467392
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user