diff --git a/.gitignore b/.gitignore index 70aa83036..bdcab8ce2 100644 --- a/.gitignore +++ b/.gitignore @@ -236,6 +236,7 @@ TAGS /tests/conform/test-script-object-property /tests/conform/test-script-animation /tests/conform/test-script-named-object +/tests/conform/test-actor-destruction /tests/micro-bench/test-text-perf /tests/micro-bench/test-text /tests/micro-bench/test-picking diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index f0d9b516a..ddc61b88e 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -3039,8 +3039,14 @@ clutter_actor_dispose (GObject *object) { ClutterActor *parent = priv->parent_actor; - if (CLUTTER_IS_CONTAINER (parent)) - clutter_container_remove_actor (CLUTTER_CONTAINER (parent), self); + /* go through the Container implementation unless this + * is an internal child and has been marked as such + */ + if (CLUTTER_IS_CONTAINER (parent) && + !(CLUTTER_PRIVATE_FLAGS (self) & CLUTTER_ACTOR_INTERNAL_CHILD)) + { + clutter_container_remove_actor (CLUTTER_CONTAINER (parent), self); + } else priv->parent_actor = NULL; } @@ -6736,6 +6742,7 @@ clutter_actor_set_parent (ClutterActor *self, { ClutterActorPrivate *priv; ClutterTextDirection text_dir; + ClutterMainContext *ctx; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (CLUTTER_IS_ACTOR (parent)); @@ -6765,6 +6772,14 @@ clutter_actor_set_parent (ClutterActor *self, g_object_ref_sink (self); priv->parent_actor = parent; + ctx = _clutter_context_get_default (); + + /* if push_internal() has been called then we automatically set + * the flag on the actor + */ + if (ctx->internal_child) + CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_ACTOR_INTERNAL_CHILD); + /* clutter_actor_reparent() will emit ::parent-set for us */ if (!(CLUTTER_PRIVATE_FLAGS (self) & CLUTTER_ACTOR_IN_REPARENT)) g_signal_emit (self, actor_signals[PARENT_SET], 0, NULL); @@ -6942,17 +6957,22 @@ clutter_actor_reparent (ClutterActor *self, g_object_ref (self); - if (CLUTTER_IS_CONTAINER (priv->parent_actor)) + /* go through the Container implementation if this is a regular + * child and not an internal one + */ + if (CLUTTER_IS_CONTAINER (priv->parent_actor) && + !(CLUTTER_PRIVATE_FLAGS (self) & CLUTTER_ACTOR_INTERNAL_CHILD)) { ClutterContainer *parent = CLUTTER_CONTAINER (priv->parent_actor); - /* Note, will call unparent() */ + + /* this will have to call unparent() */ clutter_container_remove_actor (parent, self); } else clutter_actor_unparent (self); - if (CLUTTER_IS_CONTAINER (new_parent)) /* Note, will call parent() */ + if (CLUTTER_IS_CONTAINER (new_parent)) clutter_container_add_actor (CLUTTER_CONTAINER (new_parent), self); else clutter_actor_set_parent (self, new_parent); @@ -9588,3 +9608,83 @@ clutter_actor_get_text_direction (ClutterActor *self) return priv->text_direction; } + +/** + * clutter_actor_push_internal: + * + * Should be used by actors implementing the #ClutterContainer and with + * internal children added through clutter_actor_set_parent(), for instance: + * + * |[ + * static void + * my_actor_init (MyActor *self) + * { + * self->priv = SELF_ACTOR_GET_PRIVATE (self); + * + * clutter_actor_push_internal (); + * + * /* calling clutter_actor_set_parent() now will result in + * * the internal flag being set on a child of MyActor + * */ + * + * /* internal child: a background texture */ + * self->priv->background_tex = clutter_texture_new (); + * clutter_actor_set_parent (self->priv->background_tex, + * CLUTTER_ACTOR (self)); + * + * /* internal child: a label */ + * self->priv->label = clutter_text_new (); + * clutter_actor_set_parent (self->priv->label, + * CLUTTER_ACTOR (self)); + * + * clutter_actor_pop_internal (); + * + * /* calling clutter_actor_set_parent() now will not result in + * * the internal flag being set on a child of MyActor + * */ + * } + * ]| + * + * This function will be used by Clutter to toggle an "internal child" + * flag whenever clutter_actor_set_parent() is called; internal children + * are handled differently by Clutter, specifically when destroying their + * parent. + * + * Call clutter_actor_pop_internal() when you finished adding internal + * children. + * + * Nested calls to clutter_actor_push_internal() are allowed, but each + * one must by followed by a clutter_actor_pop_internal() call. + * + * Since: 1.2 + */ +void +clutter_actor_push_internal (void) +{ + ClutterMainContext *ctx = _clutter_context_get_default (); + + ctx->internal_child += 1; +} + +/** + * clutter_actor_pop_internal: + * + * Disables the effects of clutter_actor_pop_internal() + * + * Since: 1.2 + */ +void +clutter_actor_pop_internal (void) +{ + ClutterMainContext *ctx = _clutter_context_get_default (); + + if (ctx->internal_child == 0) + { + g_warning ("Mismatched %s: you need to call " + "clutter_actor_push_composite() at least once before " + "calling this function", G_STRFUNC); + return; + } + + ctx->internal_child -= 1; +} diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h index e1622dd85..974cc7ee1 100644 --- a/clutter/clutter-actor.h +++ b/clutter/clutter-actor.h @@ -537,6 +537,9 @@ void clutter_actor_set_text_direction (ClutterActor *sel ClutterTextDirection text_dir); ClutterTextDirection clutter_actor_get_text_direction (ClutterActor *self); +void clutter_actor_push_internal (void); +void clutter_actor_pop_internal (void); + G_END_DECLS #endif /* __CLUTTER_ACTOR_H__ */ diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 3b0f3997e..4b749f27e 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -354,19 +354,6 @@ clutter_get_motion_events_enabled (void) guint _clutter_pix_to_id (guchar pixel[4]); -static inline void init_bits (void) -{ - ClutterMainContext *ctx; - - static gboolean done = FALSE; - if (G_LIKELY (done)) - return; - - ctx = _clutter_context_get_default (); - - done = TRUE; -} - void _clutter_id_to_color (guint id, ClutterColor *col) { @@ -377,9 +364,11 @@ _clutter_id_to_color (guint id, ClutterColor *col) /* compute the numbers we'll store in the components */ red = (id >> (ctx->fb_g_mask_used+ctx->fb_b_mask_used)) - & (0xff >> (8-ctx->fb_r_mask_used)); - green = (id >> ctx->fb_b_mask_used) & (0xff >> (8-ctx->fb_g_mask_used)); - blue = (id) & (0xff >> (8-ctx->fb_b_mask_used)); + & (0xff >> (8-ctx->fb_r_mask_used)); + green = (id >> ctx->fb_b_mask_used) + & (0xff >> (8-ctx->fb_g_mask_used)); + blue = (id) + & (0xff >> (8-ctx->fb_b_mask_used)); /* shift left bits a bit and add one, this circumvents * at least some potential rounding errors in GL/GLES @@ -409,9 +398,9 @@ _clutter_id_to_color (guint id, ClutterColor *col) */ if (G_UNLIKELY (clutter_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)) { - col->red = (col->red << 4) | (col->red >> 4); + col->red = (col->red << 4) | (col->red >> 4); col->green = (col->green << 4) | (col->green >> 4); - col->blue = (col->blue << 4) | (col->blue >> 4); + col->blue = (col->blue << 4) | (col->blue >> 4); } } @@ -456,8 +445,9 @@ _clutter_pixel_to_id (guchar pixel[4]) blue = blue >> (ctx->fb_b_mask - ctx->fb_b_mask_used); /* combine the correct per component values into the final id */ - id = blue + (green << ctx->fb_b_mask_used) - + (red << (ctx->fb_b_mask_used + ctx->fb_g_mask_used)); + id = blue + + (green << ctx->fb_b_mask_used) + + (red << (ctx->fb_b_mask_used + ctx->fb_g_mask_used)); return id; } @@ -498,28 +488,34 @@ read_pixels_to_file (char *filename_stem, NULL); /* callback data */ if (pixbuf) { - char *filename = - g_strdup_printf ("%s-%05d.png", filename_stem, read_count); + char *filename = g_strdup_printf ("%s-%05d.png", + filename_stem, + read_count); GError *error = NULL; + if (!gdk_pixbuf_save (pixbuf, filename, "png", &error, NULL)) { g_warning ("Failed to save pick buffer to file %s: %s", filename, error->message); g_error_free (error); } + g_free (filename); g_object_unref (pixbuf); read_count++; } -#else - static gboolean seen = FALSE; - if (!seen) - { - g_warning ("dumping buffers to an image isn't supported on platforms " - "without gdk pixbuf support\n"); - seen = TRUE; - } -#endif +#else /* !USE_GDKPIXBUF */ + { + static gboolean seen = FALSE; + + if (!seen) + { + g_warning ("dumping buffers to an image isn't supported on platforms " + "without gdk pixbuf support\n"); + seen = TRUE; + } + } +#endif /* USE_GDKPIXBUF */ } ClutterActor * @@ -780,14 +776,14 @@ clutter_main (void) static void clutter_threads_impl_lock (void) { - if (clutter_threads_mutex) + if (G_LIKELY (clutter_threads_mutex != NULL)) g_mutex_lock (clutter_threads_mutex); } static void clutter_threads_impl_unlock (void) { - if (clutter_threads_mutex) + if (G_LIKELY (clutter_threads_mutex != NULL)) g_mutex_unlock (clutter_threads_mutex); } @@ -802,6 +798,8 @@ clutter_threads_impl_unlock (void) * * This function must be called before clutter_init(). * + * It is safe to call this function multiple times. + * * Since: 0.4 */ void @@ -810,6 +808,9 @@ clutter_threads_init (void) if (!g_thread_supported ()) g_error ("g_thread_init() must be called before clutter_threads_init()"); + if (clutter_threads_mutex != NULL) + return; + clutter_threads_mutex = g_mutex_new (); if (!clutter_threads_lock) @@ -1253,6 +1254,7 @@ _clutter_context_get_default (void) ClutterCntx = ctx = g_new0 (ClutterMainContext, 1); + /* create the default backend */ ctx->backend = g_object_new (_clutter_backend_impl_get_type (), NULL); ctx->is_initialized = FALSE; @@ -1414,7 +1416,7 @@ clutter_init_real (GError **error) resolution = clutter_backend_get_resolution (ctx->backend); cogl_pango_font_map_set_resolution (ctx->font_map, resolution); - if (G_LIKELY (!clutter_disable_mipmap_text)) + if (!clutter_disable_mipmap_text) cogl_pango_font_map_set_use_mipmapping (ctx->font_map, TRUE); clutter_text_direction = clutter_get_text_direction (); diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index 651fbea16..dd687d010 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -58,13 +58,26 @@ typedef enum { CLUTTER_ACTOR_IN_DESTRUCTION = 1 << 0, CLUTTER_ACTOR_IS_TOPLEVEL = 1 << 1, CLUTTER_ACTOR_IN_REPARENT = 1 << 2, - CLUTTER_ACTOR_SYNC_MATRICES = 1 << 3, /* Used by stage to indicate GL - * viewport / perspective etc - * needs (re)setting. - */ - CLUTTER_ACTOR_IN_PAINT = 1 << 4, /* Used to avoid recursion */ - CLUTTER_ACTOR_IN_RELAYOUT = 1 << 5, /* Used to avoid recursion */ - CLUTTER_STAGE_IN_RESIZE = 1 << 6 + + /* Used by the stage to indicate GL viewport / perspective etc needs + * (re)setting. + */ + CLUTTER_ACTOR_SYNC_MATRICES = 1 << 3, + + /* Used to avoid recursion */ + CLUTTER_ACTOR_IN_PAINT = 1 << 4, + + /* Used to avoid recursion */ + CLUTTER_ACTOR_IN_RELAYOUT = 1 << 5, + + /* Used by the stage if resizing is an asynchronous operation (like on + * X11) to delay queueing relayouts until we got a notification from the + * event handling + */ + CLUTTER_STAGE_IN_RESIZE = 1 << 6, + + /* a flag for internal children of Containers */ + CLUTTER_ACTOR_INTERNAL_CHILD = 1 << 7 } ClutterPrivateFlags; struct _ClutterInputDevice @@ -134,6 +147,8 @@ struct _ClutterMainContext gulong redraw_count; GList *repaint_funcs; + + gint internal_child; }; #define CLUTTER_CONTEXT() (_clutter_context_get_default ()) diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index c2c432bce..d1c34ba0a 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -352,6 +352,8 @@ clutter_actor_lower clutter_actor_raise_top clutter_actor_lower_bottom clutter_actor_get_stage +clutter_actor_push_internal +clutter_actor_pop_internal clutter_actor_set_depth diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am index b1bb81ec3..a131ee2ee 100644 --- a/tests/conform/Makefile.am +++ b/tests/conform/Makefile.am @@ -40,6 +40,7 @@ test_conformance_SOURCES = \ test-actor-size.c \ test-texture-fbo.c \ test-script-parser.c \ + test-actor-destroy.c \ $(NULL) # For convenience, this provides a way to easily run individual unit tests: diff --git a/tests/conform/test-actor-destroy.c b/tests/conform/test-actor-destroy.c new file mode 100644 index 000000000..8cc0d48f4 --- /dev/null +++ b/tests/conform/test-actor-destroy.c @@ -0,0 +1,168 @@ +#include +#include "test-conform-common.h" + +#define TEST_TYPE_DESTROY (test_destroy_get_type ()) +#define TEST_DESTROY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_DESTROY, TestDestroy)) +#define TEST_IS_DESTROY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_DESTROY)) + +typedef struct _TestDestroy TestDestroy; +typedef struct _TestDestroyClass TestDestroyClass; + +struct _TestDestroy +{ + ClutterActor parent_instance; + + ClutterActor *bg; + ClutterActor *label; + ClutterActor *tex; + + GList *children; +}; + +struct _TestDestroyClass +{ + ClutterActorClass parent_class; +}; + +static void clutter_container_init (ClutterContainerIface *iface); + +G_DEFINE_TYPE_WITH_CODE (TestDestroy, test_destroy, CLUTTER_TYPE_ACTOR, + G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER, + clutter_container_init)); + +static void +test_destroy_add (ClutterContainer *container, + ClutterActor *actor) +{ + TestDestroy *self = TEST_DESTROY (container); + + if (g_test_verbose ()) + g_print ("Adding '%s' (type:%s)\n", + clutter_actor_get_name (actor), + G_OBJECT_TYPE_NAME (actor)); + + self->children = g_list_prepend (self->children, actor); + clutter_actor_set_parent (actor, CLUTTER_ACTOR (container)); +} + +static void +test_destroy_remove (ClutterContainer *container, + ClutterActor *actor) +{ + TestDestroy *self = TEST_DESTROY (container); + + if (g_test_verbose ()) + g_print ("Removing '%s' (type:%s)\n", + clutter_actor_get_name (actor), + G_OBJECT_TYPE_NAME (actor)); + + g_assert (actor != self->bg); + g_assert (actor != self->label); + + if (!g_list_find (self->children, actor)) + g_assert (actor == self->tex); + else + self->children = g_list_remove (self->children, actor); + + clutter_actor_unparent (actor); +} + +static void +clutter_container_init (ClutterContainerIface *iface) +{ + iface->add = test_destroy_add; + iface->remove = test_destroy_remove; +} + +static void +test_destroy_destroy (ClutterActor *self) +{ + TestDestroy *test = TEST_DESTROY (self); + + if (test->bg != NULL) + { + if (g_test_verbose ()) + g_print ("Destroying '%s' (type:%s)\n", + clutter_actor_get_name (test->bg), + G_OBJECT_TYPE_NAME (test->bg)); + + clutter_actor_destroy (test->bg); + test->bg = NULL; + } + + if (test->label != NULL) + { + if (g_test_verbose ()) + g_print ("Destroying '%s' (type:%s)\n", + clutter_actor_get_name (test->label), + G_OBJECT_TYPE_NAME (test->label)); + + clutter_actor_destroy (test->label); + test->label = NULL; + } + + if (test->tex != NULL) + { + if (g_test_verbose ()) + g_print ("Destroying '%s' (type:%s)\n", + clutter_actor_get_name (test->tex), + G_OBJECT_TYPE_NAME (test->tex)); + + clutter_actor_destroy (test->tex); + test->tex = NULL; + } + + g_list_foreach (test->children, (GFunc) clutter_actor_destroy, NULL); + g_list_free (test->children); + test->children = NULL; + + if (CLUTTER_ACTOR_CLASS (test_destroy_parent_class)->destroy) + CLUTTER_ACTOR_CLASS (test_destroy_parent_class)->destroy (self); +} + +static void +test_destroy_class_init (TestDestroyClass *klass) +{ + ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); + + actor_class->destroy = test_destroy_destroy; +} + +static void +test_destroy_init (TestDestroy *self) +{ + clutter_actor_push_internal (); + + if (g_test_verbose ()) + g_print ("Adding internal children...\n"); + + self->bg = clutter_rectangle_new (); + clutter_actor_set_parent (self->bg, CLUTTER_ACTOR (self)); + clutter_actor_set_name (self->bg, "Background"); + + self->label = clutter_text_new (); + clutter_actor_set_parent (self->label, CLUTTER_ACTOR (self)); + clutter_actor_set_name (self->label, "Label"); + + clutter_actor_pop_internal (); + + self->tex = clutter_texture_new (); + clutter_actor_set_parent (self->tex, CLUTTER_ACTOR (self)); + clutter_actor_set_name (self->tex, "Texture"); +} + +void +test_actor_destruction (TestConformSimpleFixture *fixture, + gconstpointer dummy) +{ + ClutterActor *test = g_object_new (TEST_TYPE_DESTROY, NULL); + ClutterActor *child = clutter_rectangle_new (); + + if (g_test_verbose ()) + g_print ("Adding external child...\n"); + + clutter_actor_set_name (child, "Child"); + clutter_container_add_actor (CLUTTER_CONTAINER (test), child); + + clutter_actor_destroy (test); +} diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index be3929b33..6b2ca0239 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -151,6 +151,7 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/binding-pool", test_binding_pool); TEST_CONFORM_SIMPLE ("/actor", test_anchors); + TEST_CONFORM_SIMPLE ("/actor", test_actor_destruction); TEST_CONFORM_SIMPLE ("/model", test_list_model_populate); TEST_CONFORM_SIMPLE ("/model", test_list_model_iterate);