From aacd25ef6b2ebc67d588781b4e1ea6534ac5cb0e Mon Sep 17 00:00:00 2001 From: Jean-Philippe Andre Date: Mon, 6 Mar 2017 23:01:07 +0900 Subject: [PATCH] evas: Fix layer usage count and deletion When using smart objects (quite likely, isn't it?), the internal layer usage count was not perfectly tracked. This was especially true if layer_set() was called on a (top-level) smart object. As a consequence, there could be no objects in the layer but the usage would still be > 0. Thus, the layer was not deleted, not removed from the inlist of layers, and efl_gfx_stack_above_get() could return NULL as the layer above a certain object was empty. Fixes T5201 --- src/lib/evas/canvas/evas_layer.c | 37 +++++++++------------ src/lib/evas/canvas/evas_object_smart.c | 44 ++++++++++++++++++------- 2 files changed, 49 insertions(+), 32 deletions(-) diff --git a/src/lib/evas/canvas/evas_layer.c b/src/lib/evas/canvas/evas_layer.c index a92f6d7a0a..5295b30201 100644 --- a/src/lib/evas/canvas/evas_layer.c +++ b/src/lib/evas/canvas/evas_layer.c @@ -1,8 +1,6 @@ #include "evas_common_private.h" #include "evas_private.h" -static void _evas_layer_free(Evas_Layer *lay); - void evas_object_inject(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj, Evas *e) { @@ -44,7 +42,6 @@ evas_object_release(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj, int cl if (obj->layer->usage <= 0) { evas_layer_del(obj->layer); - _evas_layer_free(obj->layer); } } obj->layer = NULL; @@ -66,12 +63,6 @@ evas_layer_new(Evas *eo_e) return lay; } -static void -_evas_layer_free(Evas_Layer *lay) -{ - free(lay); -} - void _evas_layer_flush_removes(Evas_Layer *lay) { @@ -90,7 +81,6 @@ _evas_layer_flush_removes(Evas_Layer *lay) if (lay->usage <= 0) { evas_layer_del(lay); - _evas_layer_free(lay); } } @@ -130,7 +120,6 @@ evas_layer_clean(Evas *eo_e) { tmp = e->layers; evas_layer_del(tmp); - _evas_layer_free(tmp); } } @@ -172,20 +161,26 @@ evas_layer_del(Evas_Layer *lay) e = lay->evas; e->layers = (Evas_Layer *)eina_inlist_remove(EINA_INLIST_GET(e->layers), EINA_INLIST_GET(lay)); - efl_data_unref(e->evas, e); - lay->evas = NULL; + eina_freeq_ptr_main_add(lay, free, sizeof(*lay)); } static void -_evas_object_layer_set_child(Evas_Object *eo_obj, Evas_Object *par, short l) +_evas_object_layer_set_child(Evas_Object_Protected_Data *obj, Evas_Object_Protected_Data *par_obj, short l) { - Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS); - Evas_Object_Protected_Data *par_obj = efl_data_scope_get(par, EFL_CANVAS_OBJECT_CLASS); - if (obj->delete_me) return; if (obj->cur->layer == l) return; - evas_object_release(eo_obj, obj, 1); + if (EINA_UNLIKELY(obj->in_layer)) + { + ERR("Invalid internal state of object %p (child marked as being a " + "top-level object)!", obj->object); + evas_object_release(obj->object, obj, 1); + } + else if ((--obj->layer->usage) == 0) + { + evas_layer_del(obj->layer); + } + EINA_COW_STATE_WRITE_BEGIN(obj, state_write, cur) { state_write->layer = l; @@ -199,10 +194,10 @@ _evas_object_layer_set_child(Evas_Object *eo_obj, Evas_Object *par, short l) Eina_Inlist *contained; Evas_Object_Protected_Data *member; - contained = (Eina_Inlist *)evas_object_smart_members_get_direct(eo_obj); + contained = (Eina_Inlist *)evas_object_smart_members_get_direct(obj->object); EINA_INLIST_FOREACH(contained, member) { - _evas_object_layer_set_child(member->object, eo_obj, l); + _evas_object_layer_set_child(member, obj, l); } } } @@ -260,7 +255,7 @@ _efl_canvas_object_efl_gfx_stack_layer_set(Eo *eo_obj, Evas_Object_Protected_Dat contained = (Eina_Inlist *)evas_object_smart_members_get_direct(eo_obj); EINA_INLIST_FOREACH(contained, member) { - _evas_object_layer_set_child(member->object, eo_obj, l); + _evas_object_layer_set_child(member, obj, l); } } evas_object_inform_call_restack(eo_obj); diff --git a/src/lib/evas/canvas/evas_object_smart.c b/src/lib/evas/canvas/evas_object_smart.c index 870955ba4e..d2f0a8337a 100644 --- a/src/lib/evas/canvas/evas_object_smart.c +++ b/src/lib/evas/canvas/evas_object_smart.c @@ -253,16 +253,27 @@ _efl_canvas_group_group_member_add(Eo *smart_obj, Evas_Smart_Data *o, Evas_Objec evas_object_async_block(obj); if (obj->smart.parent) evas_object_smart_member_del(eo_obj); - o->member_count++; - evas_object_release(eo_obj, obj, 1); - obj->layer = smart->layer; - EINA_COW_STATE_WRITE_BEGIN(obj, state_write, cur) + if (obj->layer != smart->layer) { - state_write->layer = obj->layer->layer; + if (obj->in_layer) + evas_object_release(eo_obj, obj, 1); + else if ((--obj->layer->usage) == 0) + evas_layer_del(obj->layer); } - EINA_COW_STATE_WRITE_END(obj, state_write, cur); - + else if (obj->in_layer) + { + evas_object_release(eo_obj, obj, 1); + } + obj->layer = smart->layer; obj->layer->usage++; + if (obj->layer->layer != obj->cur->layer) + { + EINA_COW_STATE_WRITE_BEGIN(obj, state_write, cur) + state_write->layer = obj->layer->layer; + EINA_COW_STATE_WRITE_END(obj, state_write, cur); + } + + o->member_count++; obj->smart.parent = smart_obj; obj->smart.parent_data = o; obj->smart.parent_object_data = smart; @@ -324,13 +335,13 @@ _efl_canvas_group_group_member_del(Eo *smart_obj, Evas_Smart_Data *_pd EINA_UNUS o->member_count--; obj->smart.parent = NULL; evas_object_smart_member_cache_invalidate(eo_obj, EINA_TRUE, EINA_TRUE, EINA_TRUE); - obj->layer->usage--; - EINA_COW_STATE_WRITE_BEGIN(obj, state_write, cur) + if (obj->layer->layer != obj->cur->layer) { - state_write->layer = obj->layer->layer; + EINA_COW_STATE_WRITE_BEGIN(obj, state_write, cur) + state_write->layer = obj->layer->layer; + EINA_COW_STATE_WRITE_END(obj, state_write, cur); } - EINA_COW_STATE_WRITE_END(obj, state_write, cur); if (obj->is_smart) { @@ -344,6 +355,17 @@ _efl_canvas_group_group_member_del(Eo *smart_obj, Evas_Smart_Data *_pd EINA_UNUS } } + if (EINA_UNLIKELY(obj->in_layer)) + { + ERR("Invalid internal state of object %p (child marked as being a" + "top-level object)!", obj->object); + evas_object_release(obj->object, obj, 1); + } + else + { + // Layer usage shouldn't reach 0 here (as parent is still in layer) + obj->layer->usage--; + } evas_object_inject(eo_obj, obj, obj->layer->evas->evas); obj->restack = 1; evas_object_change(eo_obj, obj);