Enforce invariants on mapped, realized, visibility states

Bug 1138 - No trackable "mapped" state

* Add a VISIBLE flag tracking application programmer's
  expected showing-state for the actor, allowing us to
  always ensure we keep what the app wants while tracking
  internal implementation state separately.

* Make MAPPED reflect whether the actor will be painted;
  add notification on a ClutterActor::mapped property.
  Keep MAPPED state updated as the actor is shown,
  ancestors are shown, actor is reparented, etc.

* Require a stage and realized parents to realize; this means
  at realization time the correct window system and GL resources
  are known. But unparented actors can no longer be realized.

* Allow children to be unrealized even if parent is realized.
  Otherwise in effect either all actors or no actors are realized,
  i.e. it becomes a stage-global flag.

* Allow clutter_actor_realize() to "fail" if not inside a toplevel

* Rework clutter_actor_unrealize() so internally we have
  a flavor that does not mess with visibility flag

* Add _clutter_actor_rerealize() to encapsulate a somewhat
  tricky operation we were doing in a couple of places

* Do not realize/unrealize children in ClutterGroup,
  ClutterActor already does it

* Do not realize impl by hand in clutter_stage_show(),
  since showing impl already does that

* Do not unrealize in various dispose() methods, since
  ClutterActor dispose implementation already does it
  and chaining up is mandatory

* ClutterTexture uses COGL while unrealizable (before it's
  added to a stage). Previously this breakage was affecting
  ClutterActor because we had to allow realize outside
  a stage. Move the breakage to ClutterTexture, by making
  ClutterTexture just use COGL while not realized.

* Unrealize before we set parent to NULL in clutter_actor_unparent().
  This means unrealize() implementations can get to the stage.
  Because actors need the stage in order to detach from stage.

* Update clutter-actor-invariants.txt to reflect latest changes

* Remove explicit hide/unrealize from ClutterActor::dispose since
  unparent already forces those
  Instead just assert that unparent() occurred and did the right thing.

* Check whether parent implements unrealize before chaining up
  Needed because ClutterGroup no longer has to implement unrealize.

* Perform unrealize in the default handler for the signal.
  This allows non-containers that have children to work properly,
  and allows containers to override how it's done.

* Add map/unmap virtual methods and set MAPPED flag on self and
  children in there. This allows subclasses to hook map/unmap.
  These are not signals, because notify::mapped is better for
  anything it's legitimate for a non-subclass to do.

Signed-off-by: Emmanuele Bassi <ebassi@linux.intel.com>
This commit is contained in:
Havoc Pennington 2009-04-02 09:16:43 -04:00 committed by Emmanuele Bassi
parent b73ee6992c
commit 125bded814
17 changed files with 1145 additions and 279 deletions

File diff suppressed because it is too large Load Diff

View File

@ -76,7 +76,7 @@ G_BEGIN_DECLS
#define CLUTTER_ACTOR_IS_MAPPED(e) ((((ClutterActor*)(e))->flags & CLUTTER_ACTOR_MAPPED) != FALSE)
#define CLUTTER_ACTOR_IS_REALIZED(e) ((((ClutterActor*)(e))->flags & CLUTTER_ACTOR_REALIZED) != FALSE)
#define CLUTTER_ACTOR_IS_VISIBLE(e) (CLUTTER_ACTOR_IS_MAPPED (e) && CLUTTER_ACTOR_IS_REALIZED (e))
#define CLUTTER_ACTOR_IS_VISIBLE(e) ((((ClutterActor*)(e))->flags & CLUTTER_ACTOR_VISIBLE) != FALSE)
#define CLUTTER_ACTOR_IS_REACTIVE(e) ((((ClutterActor*)(e))->flags & CLUTTER_ACTOR_REACTIVE) != FALSE)
typedef struct _ClutterActorClass ClutterActorClass;
@ -102,11 +102,12 @@ typedef void (*ClutterCallback) (ClutterActor *actor, gpointer data);
/**
* ClutterActorFlags:
* @CLUTTER_ACTOR_MAPPED: the actor has been painted
* @CLUTTER_ACTOR_MAPPED: the actor will be painted (is visible, and inside a toplevel, and all parents visible)
* @CLUTTER_ACTOR_REALIZED: the resources associated to the actor have been
* allocated
* @CLUTTER_ACTOR_REACTIVE: the actor 'reacts' to mouse events emmitting event
* signals
* @CLUTTER_ACTOR_VISIBLE: the actor has been shown by the application program
*
* Flags used to signal the state of an actor.
*/
@ -114,7 +115,8 @@ typedef enum
{
CLUTTER_ACTOR_MAPPED = 1 << 1,
CLUTTER_ACTOR_REALIZED = 1 << 2,
CLUTTER_ACTOR_REACTIVE = 1 << 3
CLUTTER_ACTOR_REACTIVE = 1 << 3,
CLUTTER_ACTOR_VISIBLE = 1 << 4
} ClutterActorFlags;
/**
@ -220,6 +222,8 @@ struct _ClutterActorClass
void (* hide_all) (ClutterActor *actor);
void (* realize) (ClutterActor *actor);
void (* unrealize) (ClutterActor *actor);
void (* map) (ClutterActor *actor);
void (* unmap) (ClutterActor *actor);
void (* paint) (ClutterActor *actor);
void (* parent_set) (ClutterActor *actor,
ClutterActor *old_parent);
@ -280,6 +284,8 @@ void clutter_actor_hide (ClutterActor
void clutter_actor_hide_all (ClutterActor *self);
void clutter_actor_realize (ClutterActor *self);
void clutter_actor_unrealize (ClutterActor *self);
void clutter_actor_map (ClutterActor *self);
void clutter_actor_unmap (ClutterActor *self);
void clutter_actor_paint (ClutterActor *self);
void clutter_actor_pick (ClutterActor *self,
const ClutterColor *color);

View File

@ -105,22 +105,6 @@ clutter_group_paint (ClutterActor *actor)
: "unknown");
}
static void
clutter_group_realize (ClutterActor *actor)
{
clutter_container_foreach (CLUTTER_CONTAINER (actor),
CLUTTER_CALLBACK (clutter_actor_realize),
NULL);
}
static void
clutter_group_unrealize (ClutterActor *actor)
{
clutter_container_foreach (CLUTTER_CONTAINER (actor),
CLUTTER_CALLBACK (clutter_actor_unrealize),
NULL);
}
static void
clutter_group_pick (ClutterActor *actor,
const ClutterColor *color)
@ -587,8 +571,6 @@ clutter_group_class_init (ClutterGroupClass *klass)
actor_class->pick = clutter_group_pick;
actor_class->show_all = clutter_group_real_show_all;
actor_class->hide_all = clutter_group_real_hide_all;
actor_class->realize = clutter_group_realize;
actor_class->unrealize = clutter_group_unrealize;
actor_class->get_preferred_width = clutter_group_get_preferred_width;
actor_class->get_preferred_height = clutter_group_get_preferred_height;

View File

@ -217,6 +217,10 @@ gboolean _clutter_boolean_handled_accumulator (GSignalInvocationHint *ihint,
void _clutter_actor_apply_modelview_transform_recursive (ClutterActor *self,
ClutterActor *ancestor);
void _clutter_actor_rerealize (ClutterActor *self,
ClutterCallback callback,
void *data);
void _clutter_actor_set_opacity_parent (ClutterActor *self,
ClutterActor *parent);

View File

@ -263,15 +263,13 @@ clutter_stage_realize (ClutterActor *self)
{
ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
/* Make sure the viewport and projection matrix are valid for the
first paint (which will likely occur before the ConfigureNotify
is received) */
CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_ACTOR_SYNC_MATRICES);
g_assert (priv->impl != NULL);
CLUTTER_ACTOR_GET_CLASS (priv->impl)->realize (priv->impl);
clutter_actor_realize (priv->impl);
/* ensure that the stage is using the context if the
* realization sequence was successful
@ -287,12 +285,9 @@ clutter_stage_unrealize (ClutterActor *self)
{
ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
/* unset the flag */
CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
/* and then unrealize the implementation */
g_assert (priv->impl != NULL);
CLUTTER_ACTOR_GET_CLASS (priv->impl)->unrealize (priv->impl);
clutter_actor_unrealize (priv->impl);
clutter_stage_ensure_current (CLUTTER_STAGE (self));
}
@ -304,9 +299,6 @@ clutter_stage_show (ClutterActor *self)
g_assert (priv->impl != NULL);
if (!CLUTTER_ACTOR_IS_REALIZED (priv->impl))
clutter_actor_realize (priv->impl);
clutter_actor_show (priv->impl);
CLUTTER_ACTOR_CLASS (clutter_stage_parent_class)->show (self);
@ -394,6 +386,14 @@ clutter_stage_real_queue_redraw (ClutterActor *actor,
}
}
static void
set_offscreen_while_unrealized (ClutterActor *actor,
void *data)
{
CLUTTER_STAGE (actor)->priv->is_offscreen =
GPOINTER_TO_INT (data);
}
static void
clutter_stage_set_property (GObject *object,
guint prop_id,
@ -415,24 +415,26 @@ clutter_stage_set_property (GObject *object,
break;
case PROP_OFFSCREEN:
{
gboolean was_showing;
if (priv->is_offscreen == g_value_get_boolean (value))
return;
if (CLUTTER_ACTOR_IS_REALIZED (actor))
{
was_showing = CLUTTER_ACTOR_IS_VISIBLE (actor);
/* Backend needs to check this prop and handle accordingly
* in realise.
* FIXME: More 'obvious' implementation needed?
*/
clutter_actor_unrealize (actor);
priv->is_offscreen = g_value_get_boolean (value);
clutter_actor_realize (actor);
_clutter_actor_rerealize (actor,
set_offscreen_while_unrealized,
GINT_TO_POINTER (g_value_get_boolean (value)));
if (!CLUTTER_ACTOR_IS_REALIZED (actor))
if (was_showing &&
!CLUTTER_ACTOR_IS_REALIZED (actor))
priv->is_offscreen = ~g_value_get_boolean (value);
}
else
priv->is_offscreen = g_value_get_boolean (value);
break;
case PROP_FULLSCREEN:
@ -534,7 +536,7 @@ clutter_stage_dispose (GObject *object)
ClutterStagePrivate *priv = stage->priv;
ClutterStageManager *stage_manager = clutter_stage_manager_get_default ();
clutter_actor_unrealize (CLUTTER_ACTOR (object));
clutter_actor_hide (CLUTTER_ACTOR (object));
if (priv->update_idle)
{

View File

@ -335,19 +335,15 @@ clutter_texture_realize (ClutterActor *actor)
}
else
{
if (clutter_feature_available (CLUTTER_FEATURE_TEXTURE_READ_PIXELS))
{
/* Dont allow realization with no data - note set_data
* will set realize flags.
/* If we have no data, then realization is a no-op but
* we still want to be in REALIZED state to maintain
* invariants. We may have already created the texture
* if someone set some data earlier, or we may create it
* later if someone sets some data later. The fact that
* we may have created it earlier is really a bug, since
* it means ClutterTexture can have GL resources without
* being realized.
*/
CLUTTER_NOTE (TEXTURE,
"Texture has no image data cannot realize");
CLUTTER_NOTE (TEXTURE, "flags %i", actor->flags);
CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
CLUTTER_NOTE (TEXTURE, "flags %i", actor->flags);
return;
}
}
CLUTTER_NOTE (TEXTURE, "Texture realized");
@ -539,9 +535,6 @@ clutter_texture_paint (ClutterActor *self)
return;
}
if (!CLUTTER_ACTOR_IS_REALIZED (CLUTTER_ACTOR(texture)))
clutter_actor_realize (CLUTTER_ACTOR(texture));
if (priv->fbo_handle != COGL_INVALID_HANDLE)
{
ClutterMainContext *context;
@ -1392,6 +1385,12 @@ clutter_texture_get_cogl_texture (ClutterTexture *texture)
* @cogl_tex. A reference to the texture is taken so if the handle is
* no longer needed it should be deref'd with cogl_handle_unref.
*
* This should not be called on an unrealizable texture (one that
* isn't inside a stage). (Currently the ClutterTexture
* implementation relies on being able to have a GL texture while
* unrealized, which means you can get away with it, but it's
* not correct and may change in the future.)
*
* Since: 0.8
*/
void
@ -1405,6 +1404,12 @@ clutter_texture_set_cogl_texture (ClutterTexture *texture,
g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
g_return_if_fail (cogl_is_texture (cogl_tex));
/* FIXME this implementation should realize the actor if it's in a
* stage, and warn and return if not in a stage yet. However, right
* now everything would break if we did that, so we just fudge it
* and we're broken: we can have a texture without being realized.
*/
priv = texture->priv;
width = cogl_texture_get_width (cogl_tex);
@ -1420,6 +1425,14 @@ clutter_texture_set_cogl_texture (ClutterTexture *texture,
/* Remove old texture */
texture_free_gl_resources (texture);
/* Free any saved data so realization doesn't resend it to GL */
if (priv->local_data)
{
g_free (priv->local_data);
priv->local_data = NULL;
}
/* Use the new texture */
cogl_material_set_layer (priv->material, 0, cogl_tex);
@ -1436,8 +1449,6 @@ clutter_texture_set_cogl_texture (ClutterTexture *texture,
priv->width,
priv->height);
CLUTTER_ACTOR_SET_FLAGS (CLUTTER_ACTOR (texture), CLUTTER_ACTOR_REALIZED);
if (size_change)
{
g_signal_emit (texture, texture_signals[SIZE_CHANGE], 0,
@ -1480,6 +1491,10 @@ clutter_texture_set_from_data (ClutterTexture *texture,
if (priv->filter_quality == CLUTTER_TEXTURE_QUALITY_HIGH)
flags |= COGL_TEXTURE_AUTO_MIPMAP;
/* FIXME if we are not realized, we should store the data
* for future use, instead of creating the texture.
*/
new_texture = cogl_texture_new_from_data (width, height,
max_waste, flags,
source_format,
@ -2004,15 +2019,8 @@ clutter_texture_set_filter_quality (ClutterTexture *texture,
filter_quality == CLUTTER_TEXTURE_QUALITY_HIGH) &&
CLUTTER_ACTOR_IS_REALIZED (texture))
{
gboolean was_visible;
was_visible = CLUTTER_ACTOR_IS_VISIBLE (CLUTTER_ACTOR (texture));
clutter_actor_unrealize (CLUTTER_ACTOR (texture));
clutter_actor_realize (CLUTTER_ACTOR (texture));
if (was_visible)
clutter_actor_show (CLUTTER_ACTOR (texture));
_clutter_actor_rerealize (CLUTTER_ACTOR (texture),
NULL, NULL);
}
g_object_notify (G_OBJECT (texture), "filter-quality");
@ -2172,9 +2180,13 @@ clutter_texture_get_base_size (ClutterTexture *texture,
/* Attempt to realize, mainly for subclasses ( such as labels )
* which may not create pixbuf data and thus base size until
* realization happens.
* realization happens. If we aren't in a stage we can't realize
* though. Doing this here is probably just broken; instead
* we could virtualize get_base_size, or have the subclasses
* create the pixbufs sooner, or something better.
*/
if (!CLUTTER_ACTOR_IS_REALIZED (texture))
if (!CLUTTER_ACTOR_IS_REALIZED (texture) &&
clutter_actor_get_stage (CLUTTER_ACTOR (texture)) != NULL)
clutter_actor_realize (CLUTTER_ACTOR (texture));
if (width)
@ -2251,8 +2263,15 @@ clutter_texture_set_area_from_rgb_data (ClutterTexture *texture,
if ((flags & CLUTTER_TEXTURE_RGB_FLAG_PREMULT))
source_format |= COGL_PREMULT_BIT;
/* attempt to realize ... */
if (!CLUTTER_ACTOR_IS_REALIZED (texture) &&
clutter_actor_get_stage (CLUTTER_ACTOR (texture)) != NULL)
clutter_actor_realize (CLUTTER_ACTOR (texture));
/* due to the fudging of clutter_texture_set_cogl_texture()
* which allows setting a texture pre-realize, we may end
* up having a texture even if we couldn't realize yet.
*/
cogl_texture = clutter_texture_get_cogl_texture (texture);
if (cogl_texture == COGL_INVALID_HANDLE)
{
@ -2329,8 +2348,6 @@ on_fbo_source_size_change (GObject *object,
if (priv->fbo_handle == COGL_INVALID_HANDLE)
{
g_warning ("%s: Offscreen texture creation failed", G_STRLOC);
CLUTTER_ACTOR_UNSET_FLAGS (CLUTTER_ACTOR (texture),
CLUTTER_ACTOR_REALIZED);
return;
}

View File

@ -49,6 +49,7 @@ clutter_stage_egl_unrealize (ClutterActor *actor)
CLUTTER_MARK();
if (CLUTTER_ACTOR_CLASS (clutter_stage_egl_parent_class)->unrealize != NULL)
CLUTTER_ACTOR_CLASS (clutter_stage_egl_parent_class)->unrealize (actor);
if (stage_egl->egl_surface)
@ -253,8 +254,6 @@ clutter_stage_egl_dispose (GObject *gobject)
{
ClutterStageEGL *stage_egl = CLUTTER_STAGE_EGL (gobject);
clutter_actor_unrealize (CLUTTER_ACTOR (stage_egl));
G_OBJECT_CLASS (clutter_stage_egl_parent_class)->dispose (gobject);
}

View File

@ -38,6 +38,7 @@ clutter_stage_egl_unrealize (ClutterActor *actor)
g_object_get (stage_x11->wrapper, "offscreen", &was_offscreen, NULL);
if (CLUTTER_ACTOR_CLASS (clutter_stage_egl_parent_class)->unrealize != NULL)
CLUTTER_ACTOR_CLASS (clutter_stage_egl_parent_class)->unrealize (actor);
clutter_x11_trap_x_errors ();
@ -275,9 +276,6 @@ clutter_stage_egl_dispose (GObject *gobject)
ClutterStageEGL *stage_egl = CLUTTER_STAGE_EGL (gobject);
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (gobject);
if (stage_x11->xwin)
clutter_actor_unrealize (CLUTTER_ACTOR (stage_egl));
G_OBJECT_CLASS (clutter_stage_egl_parent_class)->dispose (gobject);
}

View File

@ -393,6 +393,10 @@ typedef struct {
stage_fruity = CLUTTER_STAGE_EGL(backend_fruity->stage);
alive = FALSE;
/* FIXME why is this unrealize here? is the intent to destroy the stage?
* or hide it? Trying to clean up all manual unrealization so
* clutter_actor_unrealize() can be made private to clutter-actor.c
*/
clutter_actor_unrealize (CLUTTER_ACTOR (stage_fruity));
clutter_main_quit ();
}

View File

@ -45,6 +45,7 @@ clutter_stage_egl_unrealize (ClutterActor *actor)
CLUTTER_MARK();
if (CLUTTER_ACTOR_CLASS (clutter_stage_egl_parent_class)->unrealize != NULL)
CLUTTER_ACTOR_CLASS (clutter_stage_egl_parent_class)->unrealize (actor);
if (stage_egl->egl_surface)
@ -222,8 +223,6 @@ clutter_stage_egl_dispose (GObject *gobject)
{
ClutterStageEGL *stage_egl = CLUTTER_STAGE_EGL (gobject);
clutter_actor_unrealize (CLUTTER_ACTOR (stage_egl));
G_OBJECT_CLASS (clutter_stage_egl_parent_class)->dispose (gobject);
}

View File

@ -67,9 +67,7 @@ clutter_stage_glx_unrealize (ClutterActor *actor)
g_object_get (stage_x11->wrapper, "offscreen", &was_offscreen, NULL);
/* Chain up so all children get unrealized, needed to move texture data
* across contexts
*/
if (CLUTTER_ACTOR_CLASS (clutter_stage_glx_parent_class)->unrealize != NULL)
CLUTTER_ACTOR_CLASS (clutter_stage_glx_parent_class)->unrealize (actor);
clutter_x11_trap_x_errors ();
@ -310,12 +308,6 @@ fail:
static void
clutter_stage_glx_dispose (GObject *gobject)
{
ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (gobject);
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (gobject);
if (stage_x11->xwin)
clutter_actor_unrealize (CLUTTER_ACTOR (stage_glx));
G_OBJECT_CLASS (clutter_stage_glx_parent_class)->dispose (gobject);
}

View File

@ -199,8 +199,6 @@ clutter_stage_sdl_dispose (GObject *gobject)
{
ClutterStageSDL *stage_sdl = CLUTTER_STAGE_SDL (gobject);
clutter_actor_unrealize (CLUTTER_ACTOR (stage_sdl));
G_OBJECT_CLASS (clutter_stage_sdl_parent_class)->dispose (gobject);
}

View File

@ -574,9 +574,7 @@ clutter_stage_win32_unrealize (ClutterActor *actor)
CLUTTER_NOTE (BACKEND, "Unrealizing stage");
/* Chain up so all children get unrealized, needed to move texture
* data across contexts
*/
if (CLUTTER_ACTOR_CLASS (clutter_stage_win32_parent_class)->unrealize != NULL)
CLUTTER_ACTOR_CLASS (clutter_stage_win32_parent_class)->unrealize (actor);
if (stage_win32->client_dc)
@ -602,9 +600,6 @@ clutter_stage_win32_dispose (GObject *gobject)
{
ClutterStageWin32 *stage_win32 = CLUTTER_STAGE_WIN32 (gobject);
if (stage_win32->hwnd)
clutter_actor_unrealize (CLUTTER_ACTOR (gobject));
G_OBJECT_CLASS (clutter_stage_win32_parent_class)->dispose (gobject);
}
@ -749,6 +744,12 @@ clutter_win32_set_stage_foreign (ClutterStage *stage,
impl = _clutter_stage_get_window (stage);
stage_win32 = CLUTTER_STAGE_WIN32 (impl);
/* FIXME this needs updating to use _clutter_actor_rerealize(),
* see the analogous code in x11 backend. Probably best if
* win32 maintainer does it so they can be sure it compiles
* and works.
*/
clutter_actor_unrealize (actor);
if (!GetClientRect (hwnd, &client_rect))

View File

@ -297,8 +297,7 @@ clutter_stage_x11_allocate (ClutterActor *self,
if (stage_x11->xpixmap != None)
{
/* Need to recreate to resize */
clutter_actor_unrealize (self);
clutter_actor_realize (self);
_clutter_actor_rerealize (self, NULL, NULL);
}
}
@ -529,11 +528,6 @@ clutter_stage_x11_finalize (GObject *gobject)
static void
clutter_stage_x11_dispose (GObject *gobject)
{
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (gobject);
if (stage_x11->xwin)
clutter_actor_unrealize (CLUTTER_ACTOR (stage_x11));
G_OBJECT_CLASS (clutter_stage_x11_parent_class)->dispose (gobject);
}
@ -671,6 +665,29 @@ clutter_x11_get_stage_visual (ClutterStage *stage)
return CLUTTER_STAGE_X11 (impl)->xvisinfo;
}
typedef struct {
ClutterStageX11 *stage_x11;
ClutterGeometry geom;
Window xwindow;
} ForeignWindowData;
static void
set_foreign_window_callback (ClutterActor *actor,
void *data)
{
ForeignWindowData *fwd = data;
CLUTTER_NOTE (BACKEND, "Setting foreign window (0x%x)", (int) fwd->xwindow);
fwd->stage_x11->xwin = fwd->xwindow;
fwd->stage_x11->is_foreign_xwin = TRUE;
fwd->stage_x11->xwin_width = fwd->geom.width;
fwd->stage_x11->xwin_height = fwd->geom.height;
clutter_actor_set_geometry (actor, &fwd->geom);
}
/**
* clutter_x11_set_stage_foreign:
* @stage: a #ClutterStage
@ -693,7 +710,7 @@ clutter_x11_set_stage_foreign (ClutterStage *stage,
guint width, height, border, depth;
Window root_return;
Status status;
ClutterGeometry geom;
ForeignWindowData fwd;
g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
g_return_val_if_fail (xwindow != None, FALSE);
@ -721,20 +738,16 @@ clutter_x11_set_stage_foreign (ClutterStage *stage,
return FALSE;
}
clutter_actor_unrealize (actor);
fwd.stage_x11 = stage_x11;
fwd.xwindow = xwindow;
fwd.geom.x = x;
fwd.geom.y = y;
fwd.geom.width = width;
fwd.geom.height = height;
CLUTTER_NOTE (BACKEND, "Setting foreign window (0x%x)", (int) xwindow);
stage_x11->xwin = xwindow;
stage_x11->is_foreign_xwin = TRUE;
geom.x = x;
geom.y = y;
geom.width = stage_x11->xwin_width = width;
geom.height = stage_x11->xwin_height = height;
clutter_actor_set_geometry (actor, &geom);
clutter_actor_realize (actor);
_clutter_actor_rerealize (actor,
set_foreign_window_callback,
&fwd);
CLUTTER_SET_PRIVATE_FLAGS (actor, CLUTTER_ACTOR_SYNC_MATRICES);
@ -744,11 +757,11 @@ clutter_x11_set_stage_foreign (ClutterStage *stage,
void
clutter_stage_x11_map (ClutterStageX11 *stage_x11)
{
/* set the mapped flag on the implementation */
CLUTTER_ACTOR_SET_FLAGS (stage_x11, CLUTTER_ACTOR_MAPPED);
/* set the mapped state on the wrapper */
clutter_actor_map (CLUTTER_ACTOR (stage_x11->wrapper));
/* and on the wrapper itself */
CLUTTER_ACTOR_SET_FLAGS (stage_x11->wrapper, CLUTTER_ACTOR_MAPPED);
/* and on the implementation second */
clutter_actor_map (CLUTTER_ACTOR (stage_x11));
if (stage_x11->fullscreen_on_map)
clutter_stage_fullscreen (CLUTTER_STAGE (stage_x11->wrapper));
@ -762,8 +775,8 @@ void
clutter_stage_x11_unmap (ClutterStageX11 *stage_x11)
{
/* like above, unset the MAPPED stage on both the implementation and
* the wrapper
* the wrapper, but unmap in reverse order from map
*/
CLUTTER_ACTOR_UNSET_FLAGS (stage_x11, CLUTTER_ACTOR_MAPPED);
CLUTTER_ACTOR_UNSET_FLAGS (stage_x11->wrapper, CLUTTER_ACTOR_MAPPED);
clutter_actor_unmap (CLUTTER_ACTOR (stage_x11));
clutter_actor_unmap (CLUTTER_ACTOR (stage_x11->wrapper));
}

View File

@ -28,20 +28,56 @@ ClutterActor.
a. Public ClutterActor Flags
CLUTTER_ACTOR_REALIZED
Set by clutter_actor_realize(), unset by clutter_actor_unrealize().
Means: the actor has GPU resources associated to its paint cycle.
Once realized an actor needs to be explicitly unrealized unless
being destroyed. Hide, reparent etc will not unrealize.
Means: the actor has GPU resources associated to its paint
cycle.
Set by clutter_actor_realize(), unset by
clutter_actor_unrealize(). Generally set implicitly when the
actor becomes MAPPED (see below).
May only be set if one of the actor's ancestors is a toplevel.
May only be set if all of the actor's ancestors are realized.
Once realized an actor remains realized until removed from the
toplevel. Hide, reparent will not unrealize; but unparent or
destroy will unrealize since they remove the actor from the
toplevel.
CLUTTER_ACTOR_MAPPED
Set by clutter_actor_show(), unset by clutter_actor_hide()
May only be set if CLUTTER_ACTOR_IS_REALIZED (actor).
Means: the actor has been set as visible
Means: the actor will be painted if the stage is mapped.
On non-toplevels, will be set if all of the following are
true, and unset otherwise:
* the actor's VISIBLE flag is set
* all of the actor's non-toplevel ancestors have the MAPPED
flag set
* the actor has a toplevel ancestor
* the toplevel ancestor's VISIBLE flag is set
* the toplevel ancestor's REALIZED flag is set
On toplevels, MAPPED is set asynchronously when the window
system notifies Clutter that the toplevel has been made
visible on the screen.
The MAPPED flag requires that an actor is REALIZED. When
Clutter sets the MAPPED flag, it forces realization; this is
the "normal" way for realization to occur, though explicit
realization with clutter_actor_realize() is permitted.
CLUTTER_ACTOR_VISIBLE
Implies: CLUTTER_ACTOR_IS_REALIZED (actor)
&&
CLUTTER_ACTOR_IS_MAPPED (actor)
Means: the actor's "visible" property was set to true by
the application programmer.
Set by clutter_actor_show(), unset by clutter_actor_hide().
This is an application-controlled property, while MAPPED and
REALIZED are usually managed by Clutter (with the exception
that applications can "realize early" with
clutter_actor_realize()).
If VISIBLE is unset, the actor (and any children) must
be immediately unmapped, to maintain the invariants for
the MAPPED flag.
CLUTTER_ACTOR_REACTIVE
Set and unset by clutter_actor_set_reactive()
@ -63,7 +99,18 @@ CLUTTER_ACTOR_IS_TOPLEVEL
Set internally by the initialization of ClutterStage
CLUTTER_ACTOR_IN_REPARENT
Set internally by clutter_actor_reparent()
Set internally by clutter_actor_reparent(). This flag
optimizes the reparent process by avoiding the need
to pass through an unrealized state when the actor is
removed from the old parent.
CLUTTER_ACTOR_ABOUT_TO_UNPARENT
Set internally during part of clutter_actor_unparent().
Causes the actor to pretend it has no parent, then
update invariants; which effectively forces the actor
to unrealize. The purpose of this is to unrealize _before_ the
actor is removed from the stage, so unrealize implementations
can use clutter_actor_get_stage().
CLUTTER_ACTOR_SYNC_MATRICES
Set internally by ClutterStage implementations
@ -101,59 +148,106 @@ In the following
1) CLUTTER_ACTOR_IN_DESTRUCTION => !CLUTTER_ACTOR_IS_MAPPED (actor) &&
!CLUTTER_ACTOR_IS_REALIZED (actor)
clutter_actor_destroy() will cause an actor to be hidden
and unrealized.
clutter_actor_destroy() will cause an actor to be unparented,
which means the actor must be unmapped and unrealized as
well.
2) CLUTTER_ACTOR_IS_MAPPED (actor) => CLUTTER_ACTOR_IS_REALIZED (actor)
calling clutter_actor_show() on an unrealized actor will cause
a realization to happen.
when an actor is mapped, it must first be realized.
This is the most common way an actor becomes realized.
3) if clutter_actor_set_parent (actor, parent):
CLUTTER_ACTOR_IS_REALIZED (parent) => CLUTTER_ACTOR_IS_REALIZED (actor)
((parent_is_not_toplevel && CLUTTER_ACTOR_IS_MAPPED (parent)) ||
(parent_is_toplevel && CLUTTER_ACTOR_IS_VISIBLE(parent))) &&
CLUTTER_ACTOR_IS_VISIBLE (actor)
=> CLUTTER_ACTOR_IS_MAPPED (actor)
calling clutter_actor_set_parent() on an actor and a realized
parent will cause a realization on the actor.
calling clutter_actor_set_parent() on an actor and a mapped
parent will map the actor if it has been shown.
4) if clutter_actor_unparent (actor):
CLUTTER_ACTOR_IS_MAPPED (actor) <=> CLUTTER_ACTOR_IN_REPARENT
calling clutter_actor_unparent() on an actor will hide the actor;
calling clutter_actor_reparent() on an actor will leave the actor
in the same state.
calling clutter_actor_unparent() on an actor will unmap and
unrealize the actor since it no longer has a toplevel.
Neither will unrealize.
calling clutter_actor_reparent() on an actor will leave the
actor mapped and realized (if it was before) until it has a
new parent, at which point the invariants implied by the new
parent's state are applied.
5) CLUTTER_ACTOR_IS_REALIZED(actor) => CLUTTER_ACTOR_IS_REALIZED(parent)
Actors may only be realized if their parent is realized.
However, they may be unrealized even though their parent
is realized.
This implies that an actor may not be realized unless
it has a parent, or is a toplevel.
Since only toplevels can realize without a parent, no actor
can be realized unless it either is a toplevel or has a
toplevel ancestor.
As long as they are unmapped, actors may be unrealized. This
will force all children of the actor to be unrealized, since
children may not be realized while parents are unrealized.
6) CLUTTER_ACTOR_IS_MAPPED(actor) <=>
( ( (CLUTTER_ACTOR_IS_VISIBLE(toplevel_parent) &&
CLUTTER_ACTOR_IS_REALIZED(toplevel_parent)) ||
CLUTTER_ACTOR_IS_MAPPED(non_toplevel_parent) ) ) &&
CLUTTER_ACTOR_IS_VISIBLE(actor)
Actors _must_ be mapped if and only if they are visible and
their parent is mapped, or they are visible and their
parent is a toplevel that's realized and visible.
This invariant enables us to track whether an actor will
be painted (whether it's MAPPED) without ever traversing
the actor graph.
iii. State changes
-------------------------------------------------------------------------------
clutter_actor_show:
1. if !CLUTTER_ACTOR_REALIZED calls clutter_actor_realize
2. sets CLUTTER_ACTOR_MAPPED
1. sets VISIBLE
2. sets MAPPED if invariants are met; mapping in turn sets
REALIZED
clutter_actor_hide:
1. sets !CLUTTER_ACTOR_MAPPED
1. sets !VISIBLE
2. unsets MAPPED if actor was mapped previously
3. does not affect REALIZED
clutter_actor_destroy:
1. sets CLUTTER_ACTOR_IN_DESTRUCTION
2. unparents the actor, which in turn implies unmap and unrealize
clutter_actor_realize:
sets CLUTTER_ACTOR_REALIZED
1. attempts to set REALIZED on all parents, failing if
invariants are not met, e.g. not in a toplevel yet
2. sets REALIZED on actor if parent was successfully realized
clutter_actor_unrealized:
1. if CLUTTER_ACTOR_MAPPED calls clutter_actor_hide
2. sets !CLUTTER_ACTOR_REALIZED
clutter_actor_unrealize:
1. sets !VISIBLE which forces !MAPPED
2. sets !REALIZED
3. !MAPPED and !REALIZED forces unmap and unrealize of all
children
clutter_actor_set_parent:
1. sets actor->parent
2. if parent is CLUTTER_ACTOR_REALIZED calls clutter_actor_realize
3. if actor->show_on_set_parent is TRUE calls clutter_actor_show
2. if actor->show_on_set_parent is TRUE calls clutter_actor_show
3. sets MAPPED if all prerequisites are now met for map
4. if !CLUTTER_ACTOR_IN_REPARENT emits ::parent-set with
old_parent set to NULL
clutter_actor_unset_parent:
clutter_actor_unparent:
1. unsets actor->parent
2. if !CLUTTER_ACTOR_IN_REPARENT calls clutter_actor_hide
2. if !CLUTTER_ACTOR_IN_REPARENT, sets !MAPPED and !REALIZED
since the invariants for those flags are no longer met
3. if !CLUTTER_ACTOR_IN_REPARENT emits ::parent-set with
old_parent set to the previous parent
@ -163,6 +257,9 @@ clutter_actor_reparent:
equivalent to:
clutter_actor_unparent
clutter_actor_set_parent
3. updates state of the actor to match invariants
(may change MAPPED or REALIZED in either direction,
depending on state of the new parent)
iv. Responsibilities of a ClutterActor
@ -173,22 +270,25 @@ clutter_actor_reparent:
When adding an actor to a container, the container must:
1. call clutter_actor_set_parent (actor, container)
2. call clutter_actor_queue_relayout (container)
2. call clutter_actor_queue_relayout (container) if
adding the actor changes the container's preferred
size
b. Removing from a container
When removing an actor from a container, the container must:
1. call clutter_actor_unparent (actor)
2. call clutter_actor_queue_relayout (container)
2. call clutter_actor_queue_relayout (container) if removing
the actor changes the container's preferred size
Notes:
* here a container actor is any actor that contains children actors; it
does not imply the implementation of the ClutterContainer interface.
* clutter_actor_unparent() will hide the actor except in the special case
when CLUTTER_ACTOR_IN_REPARENT is set.
* clutter_actor_unparent() will unmap and unrealize the actor except
in the special case when CLUTTER_ACTOR_IN_REPARENT is set.
* 'Composite' Clutter actors need to pass down any allocations to children.

View File

@ -21,15 +21,38 @@ test_initial_state (TestConformSimpleFixture *fixture,
}
void
test_realized (TestConformSimpleFixture *fixture,
test_shown_not_parented (TestConformSimpleFixture *fixture,
gconstpointer data)
{
ClutterActor *actor;
actor = clutter_rectangle_new ();
clutter_actor_show (actor);
g_assert (!CLUTTER_ACTOR_IS_REALIZED (actor));
g_assert (!CLUTTER_ACTOR_IS_MAPPED (actor));
g_assert (CLUTTER_ACTOR_IS_VISIBLE (actor));
clutter_actor_destroy (actor);
}
void
test_realized (TestConformSimpleFixture *fixture,
gconstpointer data)
{
ClutterActor *actor;
ClutterActor *stage;
stage = clutter_stage_get_default ();
actor = clutter_rectangle_new ();
g_assert (!(CLUTTER_ACTOR_IS_REALIZED (actor)));
clutter_actor_hide (actor); /* don't show, so won't map */
clutter_container_add_actor (CLUTTER_CONTAINER (stage),
actor);
clutter_actor_realize (actor);
g_assert (CLUTTER_ACTOR_IS_REALIZED (actor));
@ -45,33 +68,130 @@ test_mapped (TestConformSimpleFixture *fixture,
gconstpointer data)
{
ClutterActor *actor;
ClutterActor *stage;
stage = clutter_stage_get_default ();
actor = clutter_rectangle_new ();
g_assert (!(CLUTTER_ACTOR_IS_REALIZED (actor)));
g_assert (!(CLUTTER_ACTOR_IS_MAPPED (actor)));
clutter_actor_show (actor);
clutter_container_add_actor (CLUTTER_CONTAINER (stage),
actor);
g_assert (CLUTTER_ACTOR_IS_REALIZED (actor));
g_assert (CLUTTER_ACTOR_IS_MAPPED (actor));
g_assert (CLUTTER_ACTOR_IS_VISIBLE (actor));
clutter_actor_destroy (actor);
}
void
test_realize_not_recursive (TestConformSimpleFixture *fixture,
gconstpointer data)
{
ClutterActor *actor, *group;
ClutterActor *stage;
stage = clutter_stage_get_default ();
group = clutter_group_new ();
actor = clutter_rectangle_new ();
clutter_actor_hide (group); /* don't show, so won't map */
clutter_actor_hide (actor); /* don't show, so won't map */
g_assert (!(CLUTTER_ACTOR_IS_REALIZED (group)));
g_assert (!(CLUTTER_ACTOR_IS_REALIZED (actor)));
clutter_container_add_actor (CLUTTER_CONTAINER (stage),
group);
clutter_container_add_actor (CLUTTER_CONTAINER (group),
actor);
clutter_actor_realize (group);
g_assert (CLUTTER_ACTOR_IS_REALIZED (group));
g_assert (!(CLUTTER_ACTOR_IS_MAPPED (group)));
g_assert (!(CLUTTER_ACTOR_IS_VISIBLE (group)));
/* realizing group did not realize the child */
g_assert (!CLUTTER_ACTOR_IS_REALIZED (actor));
g_assert (!(CLUTTER_ACTOR_IS_MAPPED (actor)));
g_assert (!(CLUTTER_ACTOR_IS_VISIBLE (actor)));
clutter_actor_destroy (group);
}
void
test_map_recursive (TestConformSimpleFixture *fixture,
gconstpointer data)
{
ClutterActor *actor, *group;
ClutterActor *stage;
stage = clutter_stage_get_default ();
group = clutter_group_new ();
actor = clutter_rectangle_new ();
clutter_actor_hide (group); /* hide at first */
clutter_actor_show (actor); /* show at first */
g_assert (!(CLUTTER_ACTOR_IS_REALIZED (group)));
g_assert (!(CLUTTER_ACTOR_IS_REALIZED (actor)));
g_assert (!(CLUTTER_ACTOR_IS_MAPPED (group)));
g_assert (!(CLUTTER_ACTOR_IS_MAPPED (actor)));
g_assert (!(CLUTTER_ACTOR_IS_VISIBLE (group)));
g_assert ((CLUTTER_ACTOR_IS_VISIBLE (actor)));
clutter_container_add_actor (CLUTTER_CONTAINER (stage),
group);
clutter_container_add_actor (CLUTTER_CONTAINER (group),
actor);
g_assert (!(CLUTTER_ACTOR_IS_REALIZED (group)));
g_assert (!(CLUTTER_ACTOR_IS_REALIZED (actor)));
g_assert (!(CLUTTER_ACTOR_IS_MAPPED (group)));
g_assert (!(CLUTTER_ACTOR_IS_MAPPED (actor)));
g_assert (!(CLUTTER_ACTOR_IS_VISIBLE (group)));
g_assert ((CLUTTER_ACTOR_IS_VISIBLE (actor)));
/* show group, which should map and realize both
* group and child.
*/
clutter_actor_show (group);
g_assert (CLUTTER_ACTOR_IS_REALIZED (group));
g_assert (CLUTTER_ACTOR_IS_REALIZED (actor));
g_assert (CLUTTER_ACTOR_IS_MAPPED (group));
g_assert (CLUTTER_ACTOR_IS_MAPPED (actor));
g_assert (CLUTTER_ACTOR_IS_VISIBLE (group));
g_assert (CLUTTER_ACTOR_IS_VISIBLE (actor));
clutter_actor_destroy (group);
}
void
test_show_on_set_parent (TestConformSimpleFixture *fixture,
gconstpointer data)
{
ClutterActor *actor, *group;
gboolean show_on_set_parent;
ClutterActor *stage;
stage = clutter_stage_get_default ();
group = clutter_group_new ();
g_assert (!(CLUTTER_ACTOR_IS_VISIBLE (group)));
clutter_container_add_actor (CLUTTER_CONTAINER (stage),
group);
actor = clutter_rectangle_new ();
g_object_get (G_OBJECT (actor),
"show-on-set-parent", &show_on_set_parent,
@ -94,8 +214,9 @@ test_show_on_set_parent (TestConformSimpleFixture *fixture,
"show-on-set-parent", &show_on_set_parent,
NULL);
g_assert (CLUTTER_ACTOR_IS_REALIZED (actor));
g_assert (!(CLUTTER_ACTOR_IS_VISIBLE (actor)));
g_assert (!CLUTTER_ACTOR_IS_REALIZED (actor));
g_assert (!CLUTTER_ACTOR_IS_MAPPED (actor));
g_assert (CLUTTER_ACTOR_IS_VISIBLE (actor));
g_assert (show_on_set_parent == TRUE);
clutter_actor_destroy (actor);

View File

@ -116,7 +116,10 @@ main (int argc, char **argv)
TEST_CONFORM_SIMPLE ("/fixed", test_fixed_constants);
TEST_CONFORM_SIMPLE ("/invariants", test_initial_state);
TEST_CONFORM_SIMPLE ("/invariants", test_shown_not_parented);
TEST_CONFORM_SIMPLE ("/invariants", test_realized);
TEST_CONFORM_SIMPLE ("/invariants", test_realize_not_recursive);
TEST_CONFORM_SIMPLE ("/invariants", test_map_recursive);
TEST_CONFORM_SIMPLE ("/invariants", test_mapped);
TEST_CONFORM_SIMPLE ("/invariants", test_show_on_set_parent);