diff src/redraw_man.c @ 159:b90abd31a281

Postponse free of coords, shapes, and paints when the rdman is dirty. - Life-cycle of shapes and paints are managed by rdman. - Add redraw_man_t::free_objs to collect objects their freeing are postonsed. Know Issue: - Bullet of tank are not removed from screen when it is go out the range of the map.
author Thinker K.F. Li <thinker@branda.to>
date Sun, 05 Oct 2008 23:32:58 +0800
parents c1cdd3fcd28f
children 147c93163ef0
line wrap: on
line diff
--- a/src/redraw_man.c	Fri Oct 03 10:22:08 2008 +0800
+++ b/src/redraw_man.c	Sun Oct 05 23:32:58 2008 +0800
@@ -74,6 +74,16 @@
     STAILQ_REMOVE((coord)->members, geo_t, coord_next, (member))
 #define FIRST_MEMBER(coord) STAILQ_HEAD((coord)->members)
 
+/* Functions for paint members. */
+#define FORPAINTMEMBERS(paint, member)			\
+    for((member) = STAILQ_HEAD((paint)->members);	\
+	(member) != NULL;				\
+	(member) = STAILQ_NEXT(paint_t, next, member))
+#define RM_PAINTMEMBER(paint, member)				\
+    STAILQ_REMOVE((paint)->members, shnode_t, next, member)
+#define RM_PAINT(rdman, paint)					\
+    STAILQ_REMOVE((rdman)->paints, paint_t, pnt_next, paint)
+
 /*! \brief Sort a list of element by a unsigned integer.
  *
  * The result is in ascend order.  The unsigned integers is
@@ -122,12 +132,42 @@
     ADD_DATA(areas, dirty_areas, area);
 }
 
-static int add_free_coord(redraw_man_t *rdman, coord_t *coord) {
-    ADD_DATA(coords, free_coords, coord);
+static int add_free_obj(redraw_man_t *rdman, void *obj,
+			free_func_t free_func) {
+    int max;
+    free_obj_t *new_objs, *free_obj;
+
+    if(rdman->free_objs.num >= rdman->free_objs.max) {
+	max = rdman->free_objs.num + ARRAY_EXT_SZ;
+	new_objs = realloc(rdman->free_objs.objs,
+				max * sizeof(free_obj_t));
+	if(new_objs == NULL)
+	    return ERR;
+	rdman->free_objs.max = max;
+	rdman->free_objs.objs = new_objs;
+    }
+
+    free_obj = rdman->free_objs.objs + rdman->free_objs.num++;
+    free_obj->obj = obj;
+    free_obj->free_func = free_func;
+
+    return OK;
 }
 
-static int add_free_geo(redraw_man_t *rdman, geo_t *geo) {
-    ADD_DATA(geos, free_geos, geo);
+static void free_free_objs(redraw_man_t *rdman) {
+    int i;
+    free_obj_t *free_obj;
+
+    for(i = 0; i < rdman->free_objs.num; i++) {
+	free_obj = &rdman->free_objs.objs[i];
+	free_obj->free_func(rdman, free_obj->obj);
+    }
+    rdman->free_objs.num = 0;
+}
+
+static void free_objs_destroy(redraw_man_t *rdman) {
+    if(rdman->free_objs.objs != NULL)
+	free(rdman->free_objs.objs);
 }
 
 static void area_to_positions(area_t *area, co_aix (*poses)[2]) {
@@ -269,29 +309,47 @@
     rdman->cr = cr;
     rdman->backend = backend;
 
+    STAILQ_INIT(rdman->shapes);
+    STAILQ_INIT(rdman->paints);
+
     return OK;
 }
 
 void redraw_man_destroy(redraw_man_t *rdman) {
     coord_t *coord, *saved_coord;
+    shape_t *shape, *saved_shape;
     geo_t *member;
 
+    free_free_objs(rdman);
+    free_objs_destroy(rdman);
+
     coord = postorder_coord_subtree(rdman->root_coord, NULL);
     while(coord) {
 	saved_coord = coord;
 	coord = postorder_coord_subtree(rdman->root_coord, coord);
 	FORMEMBERS(saved_coord, member) {
-	    rdman_remove_shape(rdman, member->shape);
+	    rdman_shape_free(rdman, member->shape);
 	}
 	rdman_coord_free(rdman, saved_coord);
     }
     FORMEMBERS(saved_coord, member) {
-	rdman_remove_shape(rdman, member->shape);
+	rdman_shape_free(rdman, member->shape);
     }
     /* Resources of root_coord is free by elmpool_free() or
      * caller; for canvas
      */
 
+    shape = saved_shape = STAILQ_HEAD(rdman->shapes);
+    while(shape && (shape = STAILQ_NEXT(shape_t, sh_next, shape))) {
+	rdman_shape_free(rdman, saved_shape);
+#if 0
+	STAILQ_REMOVE(rdman->shapes, shape_t, sh_next, saved_shape);
+#endif
+	saved_shape = shape;
+    }
+    if(saved_shape != NULL)
+	rdman_shape_free(rdman, saved_shape);
+
     elmpool_free(rdman->coord_pool);
     elmpool_free(rdman->geo_pool);
     elmpool_free(rdman->shnode_pool);
@@ -303,8 +361,6 @@
     DARRAY_DESTROY(&rdman->dirty_geos);
     DARRAY_DESTROY(&rdman->dirty_areas);
     DARRAY_DESTROY(&rdman->gen_geos);
-    DARRAY_DESTROY(&rdman->free_coords);
-    DARRAY_DESTROY(&rdman->free_geos);
 }
 
 
@@ -362,35 +418,95 @@
  *       is postponsed.
  * \todo redraw shape objects that overlaid with removed one.
  */
-int rdman_remove_shape(redraw_man_t *rdman, shape_t *shape) {
+int rdman_shape_free(redraw_man_t *rdman, shape_t *shape) {
     geo_t *geo;
-    coord_t *coord;
     int r;
 
     geo = shape->geo;
-    coord = shape->coord;
 
-    if(rdman_is_dirty(rdman)) {
+    if(rdman_is_dirty(rdman) && geo != NULL) {
+	if(geo->flags & GEF_FREE)
+	    return ERR;
+
 	geo->flags |= GEF_FREE | GEF_HIDDEN;
 	if(!(geo->flags & GEF_DIRTY)) {
 	    r = add_dirty_geo(rdman, geo);
 	    if(r != OK)
 		return ERR;
 	}
-	r = add_free_geo(rdman, geo);
+	r = add_free_obj(rdman, shape, (free_func_t)rdman_shape_free);
 	if(r != OK)
 	    return ERR;
 	return OK;
     }
 
-    geo_detach_coord(geo, coord);
-    subject_free(&rdman->ob_factory, geo->mouse_event);
-    sh_detach_geo(shape);
-    elmpool_elm_free(rdman->geo_pool, geo);
-    sh_detach_coord(shape);
+    if(geo != NULL) {
+	geo_detach_coord(geo, shape->coord);
+	sh_detach_coord(shape);
+	sh_detach_geo(shape);
+	subject_free(&rdman->ob_factory, geo->mouse_event);
+	elmpool_elm_free(rdman->geo_pool, geo);
+    }
+    STAILQ_REMOVE(rdman->shapes, shape_t, sh_next, shape);
+    shape->free(shape);
     return OK;
 }
 
+shnode_t *shnode_new(redraw_man_t *rdman, shape_t *shape) {
+    shnode_t *node;
+
+    node = (shnode_t *)elmpool_elm_alloc(rdman->shnode_pool);
+    if(node) {
+	node->shape = shape;
+	node->next = NULL;
+    }
+    return node;
+}
+
+int rdman_paint_free(redraw_man_t *rdman, paint_t *paint) {
+    shnode_t *shnode, *saved_shnode;
+
+    if(rdman_is_dirty(rdman)) {
+	if(!(paint->flags & PNTF_FREE))
+	    return ERR;
+	add_free_obj(rdman, paint, (free_func_t)rdman_paint_free);
+	paint->flags |= PNTF_FREE;
+	return OK;
+    }
+
+    /* Free member shapes that using this paint. */
+    saved_shnode = NULL;
+    FORPAINTMEMBERS(paint, shnode) {
+	if(saved_shnode) {
+	    RM_PAINTMEMBER(paint, saved_shnode);
+	    shnode_free(rdman, saved_shnode);
+	}
+	saved_shnode = shnode;
+    }
+    if(saved_shnode) {
+	RM_PAINTMEMBER(paint, saved_shnode);
+	shnode_free(rdman, saved_shnode);
+    }
+
+    RM_PAINT(rdman, paint);
+    paint->free(rdman, paint);
+    return OK;
+}
+
+void _rdman_paint_real_remove_child(redraw_man_t *rdman,
+				    paint_t *paint,
+				    shape_t *shape) {
+    shnode_t *shnode;
+
+    FORPAINTMEMBERS(paint, shnode) {
+	if(shnode->shape == shape) {
+	    RM_PAINTMEMBER(paint, shnode);
+	    shnode_free(rdman, shnode);
+	    break;
+	}
+    }
+}
+
 coord_t *rdman_coord_new(redraw_man_t *rdman, coord_t *parent) {
     coord_t *coord, *root_coord;
     coord_t *visit;
@@ -448,6 +564,9 @@
 	return ERR;
 
     if(rdman_is_dirty(rdman)) {
+	if(coord->flags & COF_FREE)
+	    return ERR;
+
 	FORCHILDREN(coord, child) {
 	    if(!(child->flags & COF_FREE))
 		return ERR;
@@ -462,7 +581,7 @@
 	    if(r != OK)
 		return ERR;
 	}
-	r = add_free_coord(rdman, coord);
+	r = add_free_obj(rdman, coord, (free_func_t)rdman_coord_free);
 	if(r != OK)
 	    return ERR;
 	return OK;
@@ -474,9 +593,6 @@
     if(FIRST_CHILD(coord) != NULL)
 	return ERR;
 
-    if(coord->flags & COF_FREE)
-	return ERR;
-
     /* Free canvas (\ref redraw) */
     if(coord->flags & COF_OWN_CANVAS)
 	free_canvas(coord->canvas);
@@ -500,14 +616,18 @@
     for(coord = postorder_coord_subtree(subtree, prev_coord);
 	coord != NULL;
 	coord = postorder_coord_subtree(subtree, coord)) {
+	if(!(prev_coord->flags & COF_FREE)) {
+	    r = rdman_coord_free(rdman, prev_coord);
+	    if(r != OK)
+		return ERR;
+	}
+	prev_coord = coord;
+    }
+    if(!(prev_coord->flags & COF_FREE)) {
 	r = rdman_coord_free(rdman, prev_coord);
 	if(r != OK)
 	    return ERR;
-	prev_coord = coord;
     }
-    r = rdman_coord_free(rdman, prev_coord);
-    if(r != OK)
-	return ERR;
 
     return OK;
 }
@@ -566,13 +686,11 @@
 }
 
 int rdman_paint_changed(redraw_man_t *rdman, paint_t *paint) {
-    shnode_t *node;
+    shnode_t *shnode;
     int r;
 
-    for(node = STAILQ_HEAD(paint->members);
-	node != NULL;
-	node = STAILQ_NEXT(shnode_t, next, node)) {
-	r = _rdman_shape_changed(rdman, node->shape);
+    FORPAINTMEMBERS(paint, shnode) {
+	r = _rdman_shape_changed(rdman, shnode->shape);
 	if(r != OK)
 	    return ERR;
     }
@@ -986,9 +1104,6 @@
     event_t event;
     ob_factory_t *factory;
     subject_t *redraw;
-    geo_t *geo;
-    coord_t *coord;
-    int i;
 
     r = clean_rdman_dirties(rdman);
     if(r != OK)
@@ -1010,17 +1125,7 @@
     rdman->dirty_areas.num = 0;
 
     /* Free postponsed removing */
-    for(i = 0; i < rdman->free_geos.num; i++) {
-	geo = rdman->free_geos.ds[i];
-	rdman_remove_shape(rdman, geo->shape);
-    }
-    DARRAY_CLEAN(&rdman->free_geos);
-
-    for(i = 0; i < rdman->free_coords.num; i++) {
-	coord = rdman->free_coords.ds[i];
-	rdman_remove_shape(rdman, coord);
-    }
-    DARRAY_CLEAN(&rdman->free_coords);
+    free_free_objs(rdman);
 
     factory = rdman_get_ob_factory(rdman);
     redraw = rdman_get_redraw_subject(rdman);
@@ -1058,8 +1163,10 @@
  */
 
 int rdman_redraw_all(redraw_man_t *rdman) {
+    area_t area;
+#ifndef UNITTEST
     cairo_surface_t *surface;
-    area_t area;
+#endif
     int r;
 
     area.x = area.y = 0;
@@ -1128,17 +1235,6 @@
     return r;
 }
 
-shnode_t *shnode_new(redraw_man_t *rdman, shape_t *shape) {
-    shnode_t *node;
-
-    node = (shnode_t *)elmpool_elm_alloc(rdman->shnode_pool);
-    if(node) {
-	node->shape = shape;
-	node->next = NULL;
-    }
-    return node;
-}
-
 /*! \page dirty Dirty geo, coord, and area.
  *
  * \section dirty_of_ego Dirty of geo
@@ -1174,6 +1270,28 @@
  * coords will also clean member geos.
  */
 
+/*! \page man_obj Manage Objects.
+ *
+ * Shapes and paints should also be managed by redraw manager.  Redraw
+ * manager must know life-cycle of shapes and paints to avoid to use them
+ * after being free.  If a shape is released when it is dirty, redraw
+ * manager will try to access them, after released, for redrawing.
+ * We can make a copy information need by redraw manager to redraw them,
+ * but it is more complicate, and induce runtime overhead.
+ *
+ * So, redraw manage had better also manage life-cycle of shapes and paints.
+ * Shapes and paints should be created and freed through interfaces
+ * provided by redraw manager.  To reduce overhead of interfaces, they can
+ * be implemented as C macros.
+ *
+ * To refactory redraw manage to manage life-cycle of shapes and paints,
+ * following functions/macros are introduced.
+ * - rdman_paint_*_new()
+ * - rdman_paint_free()
+ * - rdman_shape_*_new()
+ * - rdman_shape_free()
+ */
+
 /*
  * When redraw an area, the affected elements may also extend to
  * outside of the area.  Since the order of drawing will change
@@ -1284,7 +1402,12 @@
     int draw_cnt;
 };
 
-shape_t *sh_dummy_new(co_aix x, co_aix y, co_aix w, co_aix h) {
+void sh_dummy_free(shape_t *sh) {
+    free(sh);
+}
+
+shape_t *sh_dummy_new(redraw_man_t *rdman,
+		      co_aix x, co_aix y, co_aix w, co_aix h) {
     sh_dummy_t *dummy;
 
     dummy = (sh_dummy_t *)malloc(sizeof(sh_dummy_t));
@@ -1299,14 +1422,13 @@
     dummy->h = h;
     dummy->trans_cnt = 0;
     dummy->draw_cnt = 0;
+    dummy->shape.free = sh_dummy_free;
+
+    rdman_shape_man(rdman, (shape_t *)dummy);
 
     return (shape_t *)dummy;
 }
 
-void sh_dummy_free(shape_t *sh) {
-    free(sh);
-}
-
 void sh_dummy_transform(shape_t *shape) {
     sh_dummy_t *dummy = (sh_dummy_t *)shape;
     co_aix poses[2][2];
@@ -1341,7 +1463,7 @@
 static void dummy_paint_prepare(paint_t *paint, cairo_t *cr) {
 }
 
-static void dummy_paint_free(paint_t *paint) {
+static void dummy_paint_free(redraw_man_t *rdman, paint_t *paint) {
     if(paint)
 	free(paint);
 }
@@ -1358,7 +1480,7 @@
     return paint;
 }
 
-void test_rdman_redraw_changed(void) {
+static void test_rdman_redraw_changed(void) {
     coord_t *coords[3];
     shape_t *shapes[3];
     sh_dummy_t **dummys;
@@ -1373,7 +1495,7 @@
     redraw_man_init(rdman, NULL, NULL);
     paint = dummy_paint_new(rdman);
     for(i = 0; i < 3; i++) {
-	shapes[i] = sh_dummy_new(0, 0, 50, 50);
+	shapes[i] = sh_dummy_new(rdman, 0, 0, 50, 50);
 	rdman_paint_fill(rdman, paint, shapes[i]);
 	coords[i] = rdman_coord_new(rdman, rdman->root_coord);
 	coords[i]->matrix[2] = 10 + i * 100;
@@ -1399,15 +1521,39 @@
     CU_ASSERT(dummys[1]->draw_cnt == 2);
     CU_ASSERT(dummys[2]->draw_cnt == 2);
 
-    paint->free(paint);
+    rdman_paint_free(rdman, paint);
     redraw_man_destroy(rdman);
 }
 
+static int test_free_pass = 0;
+
+static void test_free(redraw_man_t *rdman, void *obj) {
+    test_free_pass++;
+}
+
+static void test_rdman_free_objs(void) {
+    redraw_man_t *rdman;
+    redraw_man_t _rdman;
+    int i;
+
+    redraw_man_init(&_rdman, NULL, NULL);
+    rdman = &_rdman;
+
+    test_free_pass = 0;
+
+    for(i = 0; i < 4; i++)
+	add_free_obj(rdman, NULL, test_free);
+
+    redraw_man_destroy(rdman);
+    CU_ASSERT(test_free_pass == 4);
+}
+
 CU_pSuite get_redraw_man_suite(void) {
     CU_pSuite suite;
 
     suite = CU_add_suite("Suite_redraw_man", NULL, NULL);
     CU_ADD_TEST(suite, test_rdman_redraw_changed);
+    CU_ADD_TEST(suite, test_rdman_free_objs);
 
     return suite;
 }