changeset 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
files examples/tank/tank_main.c src/X_main.c src/event.c src/mb_types.h src/paint.c src/paint.h src/redraw_man.c src/redraw_man.h src/shape_path.c src/shape_rect.c src/shape_text.c src/shapes.h tools/mb_c_source.m4
diffstat 13 files changed, 378 insertions(+), 165 deletions(-) [+]
line wrap: on
line diff
--- a/examples/tank/tank_main.c	Fri Oct 03 10:22:08 2008 +0800
+++ b/examples/tank/tank_main.c	Sun Oct 05 23:32:58 2008 +0800
@@ -320,15 +320,19 @@
 static void bullet_go_out_map_and_redraw(event_t *event, void *arg) {
     tank_t *tank = (tank_t *)arg;
     tank_bullet_t *bullet;
+#if 0
     ob_factory_t *factory;
     subject_t *redraw;
+#endif
 
     bullet = tank->bullet;
     mb_progm_free(bullet->progm);
     rdman_force_clean(bullet->rdman);
+#if 0
     factory = rdman_get_ob_factory(bullet->rdman);
     redraw = rdman_get_redraw_subject(bullet->rdman);
     subject_remove_observer(factory, redraw, bullet->ob_redraw);
+#endif
     tank_bullet_free(tank->bullet);
     tank->bullet = NULL;
 }
@@ -337,8 +341,10 @@
     tank_t *tank = (tank_t *)arg;
     tank_bullet_t *bullet;
     redraw_man_t *rdman;
+#if 0
     subject_t *redraw;
     ob_factory_t *factory;
+#endif
 
     bullet = tank->bullet;
     rdman = bullet->rdman;
@@ -350,11 +356,14 @@
     rdman_coord_changed(rdman, bullet->coord_pos);
     
     /*! \todo Simplify the procdure of using observer pattern. */
+#if 0
     factory = rdman_get_ob_factory(rdman);
     redraw = rdman_get_redraw_subject(rdman);
     bullet->ob_redraw =
 	subject_add_observer(factory, redraw,
 			     bullet_go_out_map_and_redraw, tank);
+#endif
+    bullet_go_out_map_and_redraw(NULL, tank);
 }
 
 static void bullet_bang(tank_bullet_t *bullet, int map_x, int map_y) {
--- a/src/X_main.c	Fri Oct 03 10:22:08 2008 +0800
+++ b/src/X_main.c	Sun Oct 05 23:32:58 2008 +0800
@@ -148,15 +148,15 @@
     coord2 = rdman_coord_new(&rdman, rdman.root_coord);
     coord3 = rdman_coord_new(&rdman, rdman.root_coord);
 
-    fill1 = paint_color_new(&rdman, 1, 1, 0, 0.5);
-    fill2 = paint_color_new(&rdman, 0, 1, 1, 0.5);
-    stroke = paint_color_new(&rdman, 0.4, 0.4, 0.4, 1);
-    text_stroke = paint_color_new(&rdman, 0.5, 0.5, 0.5, 1);
+    fill1 = rdman_paint_color_new(&rdman, 1, 1, 0, 0.5);
+    fill2 = rdman_paint_color_new(&rdman, 0, 1, 1, 0.5);
+    stroke = rdman_paint_color_new(&rdman, 0.4, 0.4, 0.4, 1);
+    text_stroke = rdman_paint_color_new(&rdman, 0.5, 0.5, 0.5, 1);
 
     face = cairo_get_font_face(tmpcr);
-    text = sh_text_new("hello \xe6\xbc\xa2\xe5\xad\x97", 10, h / 4,
-		       36.0, face);
-    text_fill = paint_radial_new(&rdman, 100, h / 4, 70);
+    text = rdman_shape_text_new(&rdman, "hello \xe6\xbc\xa2\xe5\xad\x97",
+				10, h / 4, 36.0, face);
+    text_fill = rdman_paint_radial_new(&rdman, 100, h / 4, 70);
     grad_stop_init(text_stops, 0, 0.2, 0.9, 0.2, 1);
     grad_stop_init(text_stops + 1, 1, 0.9, 0.2, 0.2, 0.1);
     paint_radial_stops(text_fill, 2, text_stops);
@@ -165,7 +165,7 @@
     rdman_paint_fill(&rdman, text_fill, text);
     rdman_add_shape(&rdman, text, coord3);
 
-    path1 = sh_path_new("M 22,89.36218 C -34,-0.63782 39,-9.637817 82,12.36218 C 125,34.36218 142,136.36218 142,136.36218 C 100.66667,125.36218 74.26756,123.42795 22,89.36218 z ");
+    path1 = rdman_shape_path_new(&rdman, "M 22,89.36218 C -34,-0.63782 39,-9.637817 82,12.36218 C 125,34.36218 142,136.36218 142,136.36218 C 100.66667,125.36218 74.26756,123.42795 22,89.36218 z ");
     rdman_paint_fill(&rdman, fill1, path1);
     rdman_paint_stroke(&rdman, stroke, path1);
     coord1->matrix[0] = 0.8;
@@ -174,7 +174,7 @@
     coord1->matrix[4] = 0.8;
     coord1->matrix[5] = 20;
 
-    path2 = sh_path_new("M 22,89.36218 C -34,-0.63782 39,-9.637817 82,12.36218 C 125,34.36218 142,136.36218 142,136.36218 C 100.66667,125.36218 74.26756,123.42795 22,89.36218 z ");
+    path2 = rdman_shape_path_new(&rdman, "M 22,89.36218 C -34,-0.63782 39,-9.637817 82,12.36218 C 125,34.36218 142,136.36218 142,136.36218 C 100.66667,125.36218 74.26756,123.42795 22,89.36218 z ");
     rdman_paint_fill(&rdman, fill2, path2);
     rdman_paint_stroke(&rdman, stroke, path2);
     coord2->matrix[0] = -0.8;
@@ -189,12 +189,12 @@
     rdman_add_shape(&rdman, (shape_t *)path2, coord2);
 
     
-    fill3 = paint_linear_new(&rdman, 50, 50, 150, 150);
+    fill3 = rdman_paint_linear_new(&rdman, 50, 50, 150, 150);
     grad_stop_init(fill3_stops, 0, 1, 0, 0, 0.5);
     grad_stop_init(fill3_stops + 1, 0.5, 0, 1, 0, 0.5);
     grad_stop_init(fill3_stops + 2, 1, 0, 0, 1, 0.5);
     paint_linear_stops(fill3, 3, fill3_stops);
-    rect = sh_rect_new(50, 50, 100, 100, 20, 20);
+    rect = rdman_shape_rect_new(&rdman, 50, 50, 100, 100, 20, 20);
     rdman_paint_fill(&rdman, fill3, rect);
     rdman_add_shape(&rdman, (shape_t *)rect, rdman.root_coord);
 
@@ -243,16 +243,16 @@
 	mb_tman_free(tman);
     }
 
-    fill1->free(fill1);
-    fill2->free(fill2);
-    stroke->free(stroke);
-    text_stroke->free(text_stroke);
-    text_fill->free(text_fill);
+    rdman_paint_free(&rdman, fill1);
+    rdman_paint_free(&rdman, fill2);
+    rdman_paint_free(&rdman, stroke);
+    rdman_paint_free(&rdman, text_stroke);
+    rdman_paint_free(&rdman, text_fill);
+    rdman_shape_free(&rdman, path1);
+    rdman_shape_free(&rdman, path2);
+    rdman_shape_free(&rdman, rect);
+    rdman_shape_free(&rdman, text);
     redraw_man_destroy(&rdman);
-    path1->free(path1);
-    path2->free(path2);
-    rect->free(rect);
-    text->free(text);
     cairo_destroy(tmpcr);
     cairo_surface_destroy(tmpsuf);
 }
--- a/src/event.c	Fri Oct 03 10:22:08 2008 +0800
+++ b/src/event.c	Sun Oct 05 23:32:58 2008 +0800
@@ -11,18 +11,6 @@
 #define ARRAY_EXT_SZ 64
 
 
-static int extend_memblk(void **buf, int o_size, int n_size) {
-    void *new_buf;
-
-    new_buf = realloc(*buf, n_size);
-    if(new_buf == NULL)
-	return ERR;
-
-    *buf = new_buf;
-
-    return OK;
-}
-
 DARRAY_DEFINE(geos, geo_t *);
 
 /*! \brief Add a geo_t object to general geo list.
--- a/src/mb_types.h	Fri Oct 03 10:22:08 2008 +0800
+++ b/src/mb_types.h	Sun Oct 05 23:32:58 2008 +0800
@@ -12,20 +12,26 @@
 typedef struct _shnode shnode_t;
 typedef struct _paint paint_t;
 
+struct _redraw_man;
+
 /*! \brief Base of paint types.
  *
- * Paints should be freed by users by calling paint_t::free() of
+ * Paints should be freed by users by calling rdman_paint_free() of
  * the paint.
  *
  * \todo move member functions to a seperate structure and setup a
  * singleton fro each paint type.
  */
 struct _paint {
+    int flags;
     void (*prepare)(paint_t *paint, cairo_t *cr);
-    void (*free)(paint_t *paint);
+    void (*free)(struct _redraw_man *rdman, paint_t *paint);
     STAILQ(shnode_t) members;
+    paint_t *pnt_next;		/*!< \brief Collect all paints of a rdman. */
 };
 
+#define PNTF_FREE 0x1
+
 struct _shnode {
     shape_t *shape;
     shnode_t *next;
@@ -110,7 +116,10 @@
 				 * of parent. */
 
     int num_members;
-    STAILQ(geo_t) members;	/*!< All geo_t members in this coord. */
+    STAILQ(geo_t) members;	/*!< \brief All geo_t members in this coord. */
+
+    STAILQ(shape_t) shapes;	/*!< \brief All shapes managed by the rdman. */
+
     subject_t *mouse_event;
 } coord_t;
 #define COF_DIRTY 0x1
@@ -158,6 +167,7 @@
     co_aix stroke_width;
     int stroke_linecap:2;
     int stroke_linejoin:2;
+    struct _shape *sh_next;	/*!< Link all shapes of a rdman together. */
     void (*free)(shape_t *shape);
 };
 enum { SHT_UNKNOW, SHT_PATH, SHT_TEXT, SHT_RECT };
--- a/src/paint.c	Fri Oct 03 10:22:08 2008 +0800
+++ b/src/paint.c	Sun Oct 05 23:32:58 2008 +0800
@@ -10,7 +10,6 @@
 typedef struct _paint_color {
     paint_t paint;
     co_comp_t r, g, b, a;
-    redraw_man_t *rdman;
 } paint_color_t;
 
 int _paint_color_size = sizeof(paint_color_t);
@@ -22,22 +21,19 @@
     cairo_set_source_rgba(cr, color->r, color->g, color->b, color->a);
 }
 
-static void paint_color_free(paint_t *paint) {
-    paint_color_t *color = (paint_color_t *)paint;
-
-    shnode_list_free(color->rdman, paint->members);
-    elmpool_elm_free(color->rdman->paint_color_pool, paint);
+static void paint_color_free(redraw_man_t *rdman, paint_t *paint) {
+    shnode_list_free(rdman, paint->members);
+    elmpool_elm_free(rdman->paint_color_pool, paint);
 }
 
-paint_t *paint_color_new(redraw_man_t *rdman,
-			 co_comp_t r, co_comp_t g,
-			 co_comp_t b, co_comp_t a) {
+paint_t *rdman_paint_color_new(redraw_man_t *rdman,
+			       co_comp_t r, co_comp_t g,
+			       co_comp_t b, co_comp_t a) {
     paint_color_t *color;
 
     color = (paint_color_t *)elmpool_elm_alloc(rdman->paint_color_pool);
     if(color == NULL)
 	return NULL;
-    color->rdman = rdman;
     color->r = r;
     color->g = g;
     color->b = b;
@@ -107,7 +103,7 @@
     cairo_set_source(cr, ptn);
 }
 
-static void paint_linear_free(paint_t *paint) {
+static void paint_linear_free(redraw_man_t *rdman, paint_t *paint) {
     paint_linear_t *linear = (paint_linear_t *)paint;
 
     if(linear->ptn)
@@ -115,8 +111,9 @@
     free(paint);
 }
 
-paint_t *paint_linear_new(redraw_man_t *rdman,
-			  co_aix x1, co_aix y1, co_aix x2, co_aix y2) {
+paint_t *rdman_paint_linear_new(redraw_man_t *rdman,
+				co_aix x1, co_aix y1,
+				co_aix x2, co_aix y2) {
     paint_linear_t *linear;
 
     linear = (paint_linear_t *)malloc(sizeof(paint_linear_t));
@@ -196,7 +193,7 @@
     cairo_set_source(cr, radial->ptn);
 }
 
-static void paint_radial_free(paint_t *paint) {
+static void paint_radial_free(redraw_man_t *rdman, paint_t *paint) {
     paint_radial_t *radial = (paint_radial_t *)paint;
 
     if(radial->ptn)
@@ -204,8 +201,8 @@
     free(paint);
 }
 
-paint_t *paint_radial_new(redraw_man_t *rdman,
-			  co_aix cx, co_aix cy, co_aix r) {
+paint_t *rdman_paint_radial_new(redraw_man_t *rdman,
+				co_aix cx, co_aix cy, co_aix r) {
     paint_radial_t *radial;
 
     radial = O_ALLOC(paint_radial_t);
--- a/src/paint.h	Fri Oct 03 10:22:08 2008 +0800
+++ b/src/paint.h	Sun Oct 05 23:32:58 2008 +0800
@@ -8,9 +8,9 @@
 
 typedef float co_comp_t;
 
-extern paint_t *paint_color_new(redraw_man_t *rdman,
-				co_comp_t r, co_comp_t g,
-				co_comp_t b, co_comp_t a);
+extern paint_t *rdman_paint_color_new(redraw_man_t *rdman,
+				      co_comp_t r, co_comp_t g,
+				      co_comp_t b, co_comp_t a);
 extern void paint_color_set(paint_t *paint,
 			    co_comp_t r, co_comp_t g,
 			    co_comp_t b, co_comp_t a);
@@ -19,9 +19,11 @@
 			    co_comp_t *b, co_comp_t *a);
 #define paint_init(_paint, _prepare, _free)	\
      do {					\
+	 (_paint)->flags = 0;			\
 	 (_paint)->prepare = _prepare;		\
 	 (_paint)->free = _free;		\
 	 STAILQ_INIT((_paint)->members);	\
+	 (_paint)->pnt_next = NULL;		\
      } while(0)					\
 
 
@@ -30,13 +32,14 @@
     co_comp_t r, g, b, a;
 } grad_stop_t;
 
-extern paint_t *paint_linear_new(redraw_man_t *rdman,
-				 co_aix x1, co_aix y1, co_aix x2, co_aix y2);
+extern paint_t *rdman_paint_linear_new(redraw_man_t *rdman,
+				       co_aix x1, co_aix y1,
+				       co_aix x2, co_aix y2);
 extern grad_stop_t *paint_linear_stops(paint_t *paint,
 				       int n_stops,
 				       grad_stop_t *stops);
-extern paint_t *paint_radial_new(redraw_man_t *rdman,
-				 co_aix cx, co_aix cy, co_aix r);
+extern paint_t *rdman_paint_radial_new(redraw_man_t *rdman,
+				       co_aix cx, co_aix cy, co_aix r);
 extern grad_stop_t *paint_radial_stops(paint_t *paint,
 				       int n_stops,
 				       grad_stop_t *stops);
--- 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;
 }
--- a/src/redraw_man.h	Fri Oct 03 10:22:08 2008 +0800
+++ b/src/redraw_man.h	Sun Oct 05 23:32:58 2008 +0800
@@ -6,6 +6,20 @@
 #include "mb_types.h"
 #include "observer.h"
 
+typedef struct _redraw_man redraw_man_t;
+
+typedef void (*free_func_t)(redraw_man_t *rdman, void *obj);
+struct _free_obj {
+    void *obj;
+    free_func_t free_func;
+};
+typedef struct _free_obj free_obj_t;
+struct _free_objs {
+    int num, max;
+    free_obj_t *objs;
+};
+typedef struct _free_objs free_objs_t;
+
 DARRAY(coords, coord_t *);
 DARRAY(geos, geo_t *);
 DARRAY(areas, area_t *);
@@ -25,7 +39,7 @@
  * Dirty flag is clear when the transformation matrix of a coord
  * object been recomputed or when a geo_t objects been redrawed.
  */
-typedef struct _redraw_man {
+struct _redraw_man {
     unsigned int next_coord_order;
     int n_coords;
     coord_t *root_coord;
@@ -43,8 +57,10 @@
 
     geos_t gen_geos;
 
-    coords_t free_coords;
-    geos_t free_geos;
+    STAILQ(shape_t) shapes;	/*!< \brief All managed shapes.  */
+    STAILQ(paint_t) paints;	/*!< \brief All managed paints. */
+
+    free_objs_t free_objs;
 
     cairo_t *cr;
     cairo_t *backend;
@@ -52,7 +68,7 @@
     ob_factory_t ob_factory;
 
     subject_t *redraw;		/*!< \brief Notified after redrawing. */
-} redraw_man_t;
+};
 
 extern int redraw_man_init(redraw_man_t *rdman, cairo_t *cr,
 			   cairo_t *backend);
@@ -62,7 +78,15 @@
 				      geo_t ***overlays);
 extern int rdman_add_shape(redraw_man_t *rdman,
 			   shape_t *shape, coord_t *coord);
-extern int rdman_remove_shape(redraw_man_t *rdman, shape_t *shape);
+/*! \brief Make a shape been managed by a redraw manager. */
+#define rdman_shape_man(rdman, shape)				\
+    STAILQ_INS_TAIL(rdman->shapes, shape_t, sh_next, shape)
+extern int rdman_shape_free(redraw_man_t *rdman, shape_t *shape);
+
+#define rdman_paint_man(rdman, paint)		\
+    STAILQ_INS_TAIL(rdman->paints, paint_t, pnt_next, shape)
+extern int rdman_paint_free(redraw_man_t *rdman, paint_t *paint);
+
 extern coord_t *rdman_coord_new(redraw_man_t *rdman, coord_t *parent);
 extern int rdman_coord_free(redraw_man_t *rdman, coord_t *coord);
 extern int rdman_coord_subtree_free(redraw_man_t *rdman, coord_t *subtree);
@@ -99,15 +123,31 @@
 			    shnode_t, next, __node);	\
 	}						\
     } while(0)
+extern void _rdman_paint_real_remove_child(redraw_man_t *rdman,
+					   paint_t *paint,
+					   shape_t *shape);
+#define _rdman_paint_remove_child(rdman, paint, shape)		\
+    do {							\
+	if((shape)->fill == (shape)->stroke &&			\
+	   (shape)->stroke == paint)				\
+	    break;						\
+	_rdman_paint_real_remove_child(rdman, paint, shape);	\
+    } while(0)
 #define rdman_paint_fill(rdman, paint, shape)		\
     do {						\
+	if((shape)->fill == paint)			\
+	    break;					\
+	_rdman_paint_remove_child(rdman, paint, shape);	\
 	_rdman_paint_child(rdman, paint, shape);	\
-	shape->fill = paint;				\
+	(shape)->fill = paint;				\
     } while(0)
 #define rdman_paint_stroke(rdman, paint, shape)		\
     do {						\
+	if((shape)->stroke == paint)			\
+	    break;					\
+	_rdman_paint_remove_child(rdman, paint, shape);	\
 	_rdman_paint_child(rdman, paint, shape);	\
-	shape->stroke = paint;				\
+	(shape)->stroke = paint;			\
     } while(0)
 extern int rdman_paint_changed(redraw_man_t *rdman, paint_t *paint);
 
--- a/src/shape_path.c	Fri Oct 03 10:22:08 2008 +0800
+++ b/src/shape_path.c	Sun Oct 05 23:32:58 2008 +0800
@@ -4,6 +4,7 @@
 #include <string.h>
 #include <cairo.h>
 #include "mb_types.h"
+#include "redraw_man.h"
 
 /*! \brief Implement respective objects for SVG path tag.
  *
@@ -660,7 +661,7 @@
 
 /*! \brief Create a path from value of 'data' of SVG path.
  */
-shape_t *sh_path_new(char *data) {
+shape_t *rdman_shape_path_new(redraw_man_t *rdman, char *data) {
     sh_path_t *path;
     int cmd_cnt, arg_cnt, fix_arg_cnt;
     int msz;
@@ -705,6 +706,10 @@
 
     path->shape.free = sh_path_free;
 
+#ifndef UNITTEST
+    rdman_shape_man(rdman, (shape_t *)path);
+#endif
+
     return (shape_t *)path;
 }
 
@@ -834,11 +839,11 @@
 
 #include <CUnit/Basic.h>
 
-void test_sh_path_new(void) {
+void test_rdman_shape_path_new(void) {
     sh_path_t *path;
     co_aix *args;
 
-    path = (sh_path_t *)sh_path_new("M 33 25l33 55c 33 87 44 22 55 99L33 77z");
+    path = (sh_path_t *)rdman_shape_path_new(NULL, "M 33 25l33 55c 33 87 44 22 55 99L33 77z");
     CU_ASSERT(path != NULL);
     CU_ASSERT(path->cmd_len == ((5 + RESERVED_AIXS + 3) & ~0x3));
     CU_ASSERT(path->arg_len == 12);
@@ -867,7 +872,7 @@
     coord_t coord;
     geo_t geo;
 
-    path = (sh_path_t *)sh_path_new("M 33 25l33 55C 33 87 44 22 55 99L33 77z");
+    path = (sh_path_t *)rdman_shape_path_new(NULL, "M 33 25l33 55C 33 87 44 22 55 99L33 77z");
     CU_ASSERT(path != NULL);
     CU_ASSERT(path->cmd_len == ((5 + RESERVED_AIXS + 3) & ~0x3));
     CU_ASSERT(path->arg_len == 12);
@@ -908,7 +913,8 @@
     sh_path_t *path;
 
     path = (sh_path_t *)
-	sh_path_new(" M 33 25l33 55C 33 87 44 22 55 99L33 77z ");
+	rdman_shape_path_new(NULL,
+			     " M 33 25l33 55C 33 87 44 22 55 99L33 77z ");
     CU_ASSERT(path != NULL);
     sh_path_free((shape_t *)path);
 }
@@ -917,7 +923,7 @@
     CU_pSuite suite;
 
     suite = CU_add_suite("Suite_shape_path", NULL, NULL);
-    CU_ADD_TEST(suite, test_sh_path_new);
+    CU_ADD_TEST(suite, test_rdman_shape_path_new);
     CU_ADD_TEST(suite, test_path_transform);
 
     return suite;
--- a/src/shape_rect.c	Fri Oct 03 10:22:08 2008 +0800
+++ b/src/shape_rect.c	Sun Oct 05 23:32:58 2008 +0800
@@ -16,8 +16,9 @@
     free(shape);
 }
 
-shape_t *sh_rect_new(co_aix x, co_aix y, co_aix w, co_aix h,
-		    co_aix rx, co_aix ry) {
+shape_t *rdman_shape_rect_new(redraw_man_t *rdman,
+			      co_aix x, co_aix y, co_aix w, co_aix h,
+			      co_aix rx, co_aix ry) {
     sh_rect_t *rect;
 
     rect = (sh_rect_t *)malloc(sizeof(sh_rect_t));
@@ -35,6 +36,8 @@
     rect->ry = ry;
     rect->shape.free = sh_rect_free;
 
+    rdman_shape_man(rdman, (shape_t *)rect);
+
     return (shape_t *)rect;
 }
 
--- a/src/shape_text.c	Fri Oct 03 10:22:08 2008 +0800
+++ b/src/shape_text.c	Sun Oct 05 23:32:58 2008 +0800
@@ -32,8 +32,9 @@
     cairo_font_face_destroy(text->face);
 }
 
-shape_t *sh_text_new(const char *txt, co_aix x, co_aix y,
-		     co_aix font_size, cairo_font_face_t *face) {
+shape_t *rdman_shape_text_new(redraw_man_t *rdman,
+			      const char *txt, co_aix x, co_aix y,
+			      co_aix font_size, cairo_font_face_t *face) {
     sh_text_t *text;
 
     text = (sh_text_t *)malloc(sizeof(sh_text_t));
@@ -56,6 +57,8 @@
 
     text->shape.free = sh_text_free;
 
+    rdman_shape_man(rdman, (shape_t *)text);
+
     return (shape_t *)text;
 }
 
--- a/src/shapes.h	Fri Oct 03 10:22:08 2008 +0800
+++ b/src/shapes.h	Sun Oct 05 23:32:58 2008 +0800
@@ -9,12 +9,18 @@
 
 #include <cairo.h>
 #include "mb_types.h"
+#include "redraw_man.h"
 
 /*! \page define_shape How to Define Shapes
  *
- * A shape must include
- * - *_new() and *_free()
+ * A shape implementation must include
+ * - rdman_shape_*_new()
  *   - clear memory for shape_t member.
+ *   - assign *_free() to \ref shape_t::free.
+ *   - make new object been managed by a redraw manager.
+ *     - call rdman_shape_man()
+ * - *_free()
+ *   - assigned to \ref shape_t::free.
  * - *_transform()
  * - *_draw()
  * - first member variable of a shape type must be a shape_t.
@@ -32,7 +38,7 @@
 /*! \defgroup shape_path Shape of Path
  * @{
  */
-extern shape_t *sh_path_new(char *data);
+extern shape_t *rdman_shape_path_new(redraw_man_t *rdman, char *data);
 extern void sh_path_transform(shape_t *shape);
 extern void sh_path_draw(shape_t *shape, cairo_t *cr);
 /* @} */
@@ -40,8 +46,10 @@
 /*! \defgroup shape_text Shape of Text
  * @{
  */
-extern shape_t *sh_text_new(const char *txt, co_aix x, co_aix y,
-			    co_aix font_size, cairo_font_face_t *face);
+extern shape_t *rdman_shape_text_new(redraw_man_t *rdman,
+				     const char *txt, co_aix x, co_aix y,
+				     co_aix font_size,
+				     cairo_font_face_t *face);
 extern void sh_text_set_text(shape_t *shape, const char *txt);
 extern void sh_text_transform(shape_t *shape);
 extern void sh_text_draw(shape_t *shape, cairo_t *cr);
@@ -50,8 +58,10 @@
 /*! \defgroup shape_rect Shape of Rectangle
  * @{
  */
-extern shape_t *sh_rect_new(co_aix x, co_aix y, co_aix w, co_aix h,
-			    co_aix rx, co_aix ry);
+extern shape_t *rdman_shape_rect_new(redraw_man_t *rdman,
+				     co_aix x, co_aix y,
+				     co_aix w, co_aix h,
+				     co_aix rx, co_aix ry);
 extern void sh_rect_transform(shape_t *shape);
 extern void sh_rect_draw(shape_t *shape, cairo_t *cr);
 extern void sh_rect_set(shape_t *shape, co_aix x, co_aix y,
--- a/tools/mb_c_source.m4	Fri Oct 03 10:22:08 2008 +0800
+++ b/tools/mb_c_source.m4	Sun Oct 05 23:32:58 2008 +0800
@@ -53,7 +53,7 @@
 divert[]])
 
 define([S_ADD_LINEAR_PAINT],[
-    obj->$1 = paint_linear_new(rdman, $2, $3, $4, $5);
+    obj->$1 = rdman_paint_linear_new(rdman, $2, $3, $4, $5);
 ifelse(COUNT($6),0,,[dnl
     stops = (grad_stop_t *)malloc(sizeof(grad_stop_t) * n_$1_stops);
     memcpy(stops, $1_stops, sizeof(grad_stop_t) * n_$1_stops);
@@ -62,7 +62,7 @@
 ])
 
 define([S_ADD_RADIAL_PAINT],[
-    obj->$1 = paint_radial_new(rdman, $2, $3, $4);
+    obj->$1 = rdman_paint_radial_new(rdman, $2, $3, $4);
 ifelse(COUNT($5),0,,[
     stops = (grad_stop_t *)malloc(sizeof(grad_stop_t) * n_$1_stops);
     memcpy(stops, $1_stops, sizeof(grad_stop_t) * n_$1_stops);
@@ -85,12 +85,12 @@
 ]])
 
 define([S_ADD_RECT],[[
-    obj->$1 = sh_rect_new($2, $3, $4, $5, $6, $7);
+    obj->$1 = rdman_shape_rect_new(rdman, $2, $3, $4, $5, $6, $7);
     rdman_add_shape(rdman, obj->$1, obj->$8);
 ]])
 
 define([S_ADD_PATH],[[
-    obj->$1 = sh_path_new("$2");
+    obj->$1 = rdman_shape_path_new(rdman, "$2");
     rdman_add_shape(rdman, obj->$1, obj->$3);
 ]])
 
@@ -99,7 +99,8 @@
 ]])
 
 define([S_ADD_TEXT],[[
-    obj->$1 = sh_text_new("$2", $3, $4, $5, cairo_get_font_face(rdman->cr));
+    obj->$1 = rdman_shape_text_new(rdman, "$2", $3, $4, $5,
+    	      				  cairo_get_font_face(rdman->cr));
     rdman_add_shape(rdman, obj->$1, obj->$6);
 ]])
 
@@ -112,12 +113,12 @@
 ]])
 
 define([S_FILL_SHAPE],[dnl
-[    obj->$1_fill = paint_color_new(rdman, $2, $3, $4, $5);
+[    obj->$1_fill = rdman_paint_color_new(rdman, $2, $3, $4, $5);
     rdman_paint_fill(rdman, obj->$1_fill, obj->$1);
 ]])
 
 define([S_STROKE_SHAPE],[dnl
-[    obj->$1_stroke = paint_color_new(rdman, $2, $3, $4, $5);
+[    obj->$1_stroke = rdman_paint_color_new(rdman, $2, $3, $4, $5);
     rdman_paint_stroke(rdman, obj->$1_stroke, obj->$1);
 ]])
 
@@ -203,36 +204,33 @@
 define([F_ADD_LINEAR_PAINT],[[
     stops = paint_linear_stops(obj->$1, 0, NULL);
     free(stops);
-    obj->$1->free(obj->$1);
+    rdman_paint_free(rdman, obj->$1);
 ]])
 
 define([F_ADD_RADIAL_PAINT],[[
     stops = paint_radial_stops(obj->$1, 0, NULL);
     free(stops);
-    obj->$1->free(obj->$1);
+    rdman_paint_free(rdman, obj->$1);
 ]])
 
 define([F_ADD_PATH],[[
-    rdman_remove_shape(rdman, obj->$1);
-    obj->$1->free(obj->$1);
+    rdman_shape_free(rdman, obj->$1);
 ]])
 
 define([F_ADD_RECT],[[
-    rdman_remove_shape(rdman, obj->$1);
-    obj->$1->free(obj->$1);
+    rdman_shape_free(rdman, obj->$1);
 ]])
 
 define([F_ADD_TEXT],[[
-    rdman_remove_shape(rdman, obj->$1);
-    obj->$1->free(obj->$1);
+    rdman_shape_free(rdman, obj->$1);
 ]])
 
 define([F_FILL_SHAPE],[[
-    obj->$1_fill->free(obj->$1_fill);
+    rdman_paint_free(rdman, obj->$1_fill);
 ]])
 
 define([F_STROKE_SHAPE],[[
-    obj->$1_stroke->free(obj->$1_stroke);
+    rdman_paint_free(rdman, obj->$1_stroke);
 ]])
 
 define([CLEAR_VARS],[divert([-1])