changeset 170:f3366433eee5

Merge from MadButterfly official repository.
author sylee@eeepc
date Fri, 31 Oct 2008 00:14:53 +0800
parents 7ca25f18902f (current diff) f3eccc24bdb2 (diff)
children 129de2d83abe
files
diffstat 29 files changed, 1086 insertions(+), 293 deletions(-) [+]
line wrap: on
line diff
--- a/dox/first_program.h	Fri Oct 31 00:12:17 2008 +0800
+++ b/dox/first_program.h	Fri Oct 31 00:14:53 2008 +0800
@@ -16,9 +16,9 @@
  * programs.
  *
  * For example, to translate foo.svg with steps
- * - $(PREFIX)/bin/svg2code.py foo.svg foo.mb
- * - m4 -I $(PREFIX)/share/mb mb_c_source.m4 foo.mb > foo.c
- * - m4 -I $(PREFIX)/share/mb mb_c_header.m4 foo.mb > foo.h
+ * - \$(PREFIX)/bin/svg2code.py foo.svg foo.mb
+ * - m4 -I \$(PREFIX)/share/mb mb_c_source.m4 foo.mb > foo.c
+ * - m4 -I \$(PREFIX)/share/mb mb_c_header.m4 foo.mb > foo.h
  *
  * foo.h declares a structure, named 'foo' and two functions,
  * foo_new() and foo_free(). An instance of 'foo' holds all objects for
--- a/examples/calculator/Makefile	Fri Oct 31 00:12:17 2008 +0800
+++ b/examples/calculator/Makefile	Fri Oct 31 00:14:53 2008 +0800
@@ -1,10 +1,13 @@
 SVG=calculator_scr.svg
 TOOLSDIR=/usr/local/share/mb
 INCS=-I/usr/local/include
-CFLAGS+=`pkg-config --cflags cairo` $(INCS) -Wall
-LDFLAGS=-L/usr/local/lib `pkg-config --libs cairo`
+CFLAGS+=`pkg-config --cflags cairo` $(INCS) -Wall -I../../src
+LDFLAGS=-L/usr/local/lib `pkg-config --libs cairo` -L../../src
 LIBS=-lmbfly
 BINS=	calc
+SVG2CODE=	../../tools/svg2code.py
+MB_C_HEADER=    ../../tools/mb_c_header.m4
+MB_C_SOURCE=    ../../tools/mb_c_source.m4
 
 all:	$(BINS)
 
@@ -18,13 +21,13 @@
 	$(CC) -c $(CFLAGS) -o $@ $(SVG:C/.svg/.c/)
 
 $(SVG:C/.svg/.mb/): $(SVG)
-	/usr/local/bin/svg2code.py $(.ALLSRC) $@
+	$(SVG2CODE) $(.ALLSRC) $@
 
 $(SVG:C/.svg/.c/): $(SVG:C/.svg/.mb/)
-	m4 -I $(TOOLSDIR) mb_c_source.m4 $(.ALLSRC) > $@
+	m4 -I $(TOOLSDIR) $(MB_C_SOURCE) $(.ALLSRC) > $@
 
 $(SVG:C/.svg/.h/): $(SVG:C/.svg/.mb/)
-	m4 -I $(TOOLSDIR) mb_c_header.m4 $(.ALLSRC) > $@
+	m4 -I $(TOOLSDIR) $(MB_C_HEADER) $(.ALLSRC) > $@
 
 clean:
 	for i in *.mb *.o *.core *~ $(SVG:C/.svg/.c/) $(SVG:C/.svg/.h/) $(BINS); do \
--- a/examples/svg2code_ex/Makefile	Fri Oct 31 00:12:17 2008 +0800
+++ b/examples/svg2code_ex/Makefile	Fri Oct 31 00:14:53 2008 +0800
@@ -1,10 +1,13 @@
 SVG=svg2code_ex.svg
 TOOLSDIR=/usr/local/share/mb
-INCS=-I/usr/local/include
+INCS=-I/usr/local/include -I../../src/mb
 CFLAGS+=`pkg-config --cflags cairo` $(INCS) -Wall
 LDFLAGS=-L/usr/local/lib `pkg-config --libs cairo`
 LIBS=-lmbfly
 BINS=	ex1
+SVG2CODE=	../../tools/svg2code.py
+MB_C_HEADER=    ../../tools/mb_c_header.m4
+MB_C_SOURCE=    ../../tools/mb_c_source.m4
 
 all:	$(BINS)
 
@@ -18,13 +21,13 @@
 	$(CC) -c $(CFLAGS) -o $@ $(SVG:C/.svg/.c/)
 
 $(SVG:C/.svg/.mb/): $(SVG)
-	/usr/local/bin/svg2code.py $(.ALLSRC) $@
+	$(SVG2CODE) $(.ALLSRC) $@
 
 $(SVG:C/.svg/.c/): $(SVG:C/.svg/.mb/)
-	m4 -I $(TOOLSDIR) mb_c_source.m4 $(.ALLSRC) > $@
+	m4 -I $(TOOLSDIR) $(MB_C_SOURCE) $(.ALLSRC) > $@
 
 $(SVG:C/.svg/.h/): $(SVG:C/.svg/.mb/)
-	m4 -I $(TOOLSDIR) mb_c_header.m4 $(.ALLSRC) > $@
+	m4 -I $(TOOLSDIR) $(MB_C_HEADER) $(.ALLSRC) > $@
 
 clean:
 	for i in *.mb *.o *.core *~ $(SVG:C/.svg/.c/) $(SVG:C/.svg/.h/) $(BINS); do \
@@ -32,4 +35,4 @@
 			echo "delete $$i"; \
 			rm -f "$$i"; \
 		fi; \
-	done
\ No newline at end of file
+	done
--- a/examples/tank/Makefile	Fri Oct 31 00:12:17 2008 +0800
+++ b/examples/tank/Makefile	Fri Oct 31 00:14:53 2008 +0800
@@ -1,15 +1,20 @@
 SVGS =	brick.svg bullet.svg bush.svg mud.svg rock.svg \
-	tank1.svg tank2.svg tank_en.svg
+	tank1.svg tank2.svg tank_en.svg bang.svg
 SVGHS =	$(SVGS:C/\.svg/.h/)
 SVGCS =	$(SVGS:C/\.svg/.c/)
 SVGOS = $(SVGS:C/\.svg/.o/)
 MBS =	$(SVGS:C/\.svg/.mb/)
 SVG2CODE=	svg2code.py
+MB_C_HEADER=    mb_c_header.m4
+MB_C_SOURCE=    mb_c_source.m4
 M4 =	m4
-M4FLAGS =	-I /usr/local/share/mb
+M4MACRODIR?=	/usr/local/share/mb
+M4FLAGS ?=	-I $(M4MACRODIR)
 LDFLAGS +=	
-LIBS +=		-lmbfly -lX11 -L/usr/local/lib `pkg-config --libs cairo`
-CFLAGS +=	-I/usr/local/include `pkg-config --cflags cairo`
+INCDIR?=	/usr/local/include
+LIBDIR?=	/usr/local/lib
+LIBS +=		-lmbfly -lX11 -L$(LIBDIR) `pkg-config --libs cairo` -L../../src
+CFLAGS +=	-I$(INCDIR) `pkg-config --cflags cairo` -I../../src
 BIN = tank
 
 all:	tank
@@ -38,10 +43,10 @@
 	$(SVG2CODE) $(SVG) $@
 
 $(SVG:C/\.svg/.h/):	$(SVG:C/\.svg/.mb/)
-	$(M4) $(M4FLAGS) mb_c_header.m4 $(.ALLSRC) > $@
+	$(M4) $(M4FLAGS) $(MB_C_HEADER) $(.ALLSRC) > $@
 
 $(SVG:C/\.svg/.c/):	$(SVG:C/\.svg/.mb/)
-	$(M4) $(M4FLAGS) mb_c_source.m4 $(.ALLSRC) > $@
+	$(M4) $(M4FLAGS) $(MB_C_SOURCE) $(.ALLSRC) > $@
 
 .endfor
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/tank/README	Fri Oct 31 00:14:53 2008 +0800
@@ -0,0 +1,5 @@
+<LEFT>		Turn to left.
+<RIGHT>		Turn to right.
+<UP>		Turn to up.
+<DOWN>		Turn to down.
+<SPACE>		To Fire.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/tank/bang.svg	Fri Oct 31 00:14:53 2008 +0800
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="50"
+   height="50"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.46"
+   version="1.0"
+   sodipodi:docname="bang.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs4">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective10" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="9.24"
+     inkscape:cx="25"
+     inkscape:cy="25.183096"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="640"
+     inkscape:window-height="695"
+     inkscape:window-x="387"
+     inkscape:window-y="71" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <path
+       sodipodi:type="star"
+       style="opacity:1;fill:#800000;fill-opacity:1;stroke:#ff0000;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="path2383"
+       sodipodi:sides="8"
+       sodipodi:cx="11.904762"
+       sodipodi:cy="13.528139"
+       sodipodi:r1="18.920832"
+       sodipodi:r2="9.4604168"
+       sodipodi:arg1="0.74089316"
+       sodipodi:arg2="1.1335922"
+       inkscape:flatsided="false"
+       inkscape:rounded="0"
+       inkscape:randomized="0"
+       d="M 25.865801,26.298702 L 15.910381,22.0987 L 12.746556,32.430236 L 8.6768607,22.42084 L -0.86580035,27.489178 L 3.3342018,17.533757 L -6.9973343,14.369933 L 3.0120609,10.300238 L -2.0562764,0.7575765 L 7.8991439,4.9575786 L 11.062969,-5.3739575 L 15.132664,4.6354378 L 24.675325,-0.4328996 L 20.475323,9.5225208 L 30.806859,12.686345 L 20.797464,16.756041 L 25.865801,26.298702 z"
+       transform="matrix(1.0338511,0,0,1.0510278,12.638136,10.727438)" />
+    <text
+       xml:space="preserve"
+       style="font-size:11.00000095000000044px;font-style:normal;font-weight:normal;fill:#ffff00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans Mono"
+       x="19.194874"
+       y="13.391408"
+       id="text2385"
+       transform="matrix(0.908091,0.418773,-0.418773,0.908091,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan2387"
+         x="19.194874"
+         y="13.391408">Bang</tspan></text>
+  </g>
+</svg>
--- a/examples/tank/tank_main.c	Fri Oct 31 00:12:17 2008 +0800
+++ b/examples/tank/tank_main.c	Fri Oct 31 00:14:53 2008 +0800
@@ -1,3 +1,4 @@
+#include <math.h>
 #include <sys/time.h>
 #include <string.h>
 #include <mb/mb.h>
@@ -40,17 +41,34 @@
  * \brief Tank elf module provides control functions of tanks in game.
  * @{
  */
+struct _tank_bullet {
+    redraw_man_t *rdman;
+    coord_t *coord_pos;
+    coord_t *coord_rot;
+    bullet_t *bullet_obj;
+    int start_map_x, start_map_y;
+    int direction;
+    mb_progm_t *progm;
+    mb_timeval_t start_time;
+    observer_t *ob_redraw;
+    mb_timer_t *hit_tmr;
+    mb_tman_t *tman;
+};
+typedef struct _tank_bullet tank_bullet_t;
+enum { BU_UP = 0, BU_RIGHT, BU_DOWN, BU_LEFT };
+
 struct _tank {
     coord_t *coord_pos;		/*!< \brief coordinate for position */
     coord_t *coord_rot;		/*!< \brief coordinate for rotation */
+    coord_t *coord_center;
     int map_x, map_y;
     int direction;
     mb_progm_t *progm;
+    tank_bullet_t *bullet;
 };
 typedef struct _tank tank_t;
 enum { TD_UP = 0, TD_RIGHT, TD_DOWN, TD_LEFT };
 
-
 /* @} */
 
 typedef struct _tank_rt tank_rt_t;
@@ -92,6 +110,7 @@
     tank->map_y = map_y;
     tank->direction = TD_UP;
     tank->progm = NULL;
+    tank->bullet = NULL;
 
     memset(coord_pos->matrix, 0, sizeof(co_aix[6]));
     coord_pos->matrix[0] = 1;
@@ -108,7 +127,7 @@
 
     if(tank->progm) {
 	tman = X_MB_tman(xmb_rt);
-	mb_progm_abort(tank->progm, tman);
+	mb_progm_abort(tank->progm);
     }
     free(tank);
 }
@@ -233,6 +252,217 @@
 
 /* @} */
 
+/*! \brief Make coord objects for bullet elfs. */
+static void make_bullet_elf_coords(redraw_man_t *rdman, coord_t **coord_pos,
+				   coord_t **coord_rot,
+				   coord_t **coord_center) {
+    coord_t *coord_back;
+
+    *coord_pos = rdman_coord_new(rdman, rdman->root_coord);
+
+    coord_back = rdman_coord_new(rdman, *coord_pos);
+    coord_back->matrix[2] = 25;
+    coord_back->matrix[5] = 25;
+    rdman_coord_changed(rdman, coord_back);
+
+    *coord_rot = rdman_coord_new(rdman, coord_back);
+
+    *coord_center = rdman_coord_new(rdman, *coord_rot);
+    (*coord_center)->matrix[2] = -5;
+    (*coord_center)->matrix[5] = +15;
+    rdman_coord_changed(rdman, *coord_center);
+}
+
+static tank_bullet_t *tank_bullet_new(redraw_man_t *rdman,
+				      int map_x, int map_y,
+				      int direction) {
+    tank_bullet_t *bullet;
+    coord_t *coord_center;
+    co_aix *matrix;
+    static float _sins[] = { 0, 1, 0, -1};
+    static float _coss[] = { 1, 0, -1, 0};
+    float _sin, _cos;
+
+    bullet = O_ALLOC(tank_bullet_t);
+    bullet->rdman = rdman;
+
+    make_bullet_elf_coords(rdman, &bullet->coord_pos,
+			   &bullet->coord_rot,
+			   &coord_center);
+    bullet->bullet_obj = bullet_new(rdman, coord_center);
+    
+    bullet->start_map_x = map_x;
+    bullet->start_map_y = map_y;
+    bullet->direction = direction;
+    bullet->progm = NULL;
+
+    matrix = bullet->coord_pos->matrix;
+    matrix[2] = map_x * 50;
+    matrix[5] = map_y * 50;
+    rdman_coord_changed(rdman, bullet->coord_pos);
+
+    _sin = _sins[direction];
+    _cos = _coss[direction];
+    matrix = bullet->coord_rot->matrix;
+    matrix[0] = _cos;
+    matrix[1] = -_sin;
+    matrix[3] = _sin;
+    matrix[4] = _cos;
+
+    return bullet;
+}
+
+static void tank_bullet_free(tank_bullet_t *bullet) {
+    bullet_free(bullet->bullet_obj);
+    rdman_coord_subtree_free(bullet->rdman, bullet->coord_pos);
+}
+
+static void bullet_go_out_map_and_redraw(event_t *event, void *arg) {
+    tank_t *tank = (tank_t *)arg;
+    tank_bullet_t *bullet;
+
+    bullet = tank->bullet;
+    mb_progm_free(bullet->progm);
+    tank_bullet_free(tank->bullet);
+    tank->bullet = NULL;
+}
+
+static void bullet_go_out_map(event_t *event, void *arg) {
+    tank_t *tank = (tank_t *)arg;
+    tank_bullet_t *bullet;
+    redraw_man_t *rdman;
+
+    bullet = tank->bullet;
+    rdman = bullet->rdman;
+
+    if(bullet->hit_tmr != NULL)
+	mb_tman_remove(bullet->tman, bullet->hit_tmr);
+
+    coord_hide(bullet->coord_pos);
+    rdman_coord_changed(rdman, bullet->coord_pos);
+    
+    /*! \todo Simplify the procdure of using observer pattern. */
+
+    bullet_go_out_map_and_redraw(NULL, tank);
+}
+
+static void bullet_bang(tank_bullet_t *bullet, int map_x, int map_y) {
+}
+
+static void bullet_hit_chk(const mb_timeval_t *tmo,
+			   const mb_timeval_t *now,
+			   void *arg) {
+    tank_t *tank = (tank_t *)arg;
+    tank_bullet_t *bullet;
+    mb_timeval_t diff, next;
+    mb_timeval_t unit_tm;
+    float move_units_f;
+    int move_units;
+    int x, y;
+    int dir;
+    static int move_adj[][2] = {{0, -1}, {1, 0}, {0, 1}, {-1, 0}};
+
+    bullet = tank->bullet;
+    MB_TIMEVAL_CP(&diff, now);
+    MB_TIMEVAL_DIFF(&diff, &bullet->start_time);
+    MB_TIMEVAL_SET(&unit_tm, 0, 250000);
+    move_units_f = MB_TIMEVAL_DIV(&diff, &unit_tm);
+    move_units = floorl(move_units_f);
+    dir = bullet->direction;
+    x = bullet->start_map_x + move_adj[dir][0] * move_units;
+    y = bullet->start_map_y + move_adj[dir][1] * move_units;
+
+    if(map[y][x] != MUD) {
+	bullet->hit_tmr = NULL;
+	mb_progm_abort(bullet->progm);
+	bullet_go_out_map(NULL, arg);
+	bullet_bang(bullet, x, y);
+    } else {
+	MB_TIMEVAL_SET(&next, 0, 100000);
+	MB_TIMEVAL_ADD(&next, now);
+	bullet->hit_tmr = mb_tman_timeout(bullet->tman, &next,
+					  bullet_hit_chk, arg);
+    }
+}
+
+static void tank_fire_bullet(tank_rt_t *tank_rt, tank_t *tank) {
+    X_MB_runtime_t *xmb_rt;
+    redraw_man_t *rdman;
+    int map_x, map_y;
+    int shift_x, shift_y;
+    int shift_len;
+    int dir;
+    tank_bullet_t *bullet;
+    mb_progm_t *progm;
+    mb_word_t *word;
+    mb_action_t *act;
+    mb_timeval_t start, playing;
+    mb_timeval_t now, next;
+    ob_factory_t *factory;
+    mb_tman_t *tman;
+    subject_t *subject;
+    static int map_xy_adj[][2] = {{0, -1}, {1, 0}, {0, 1}, {-1, 0}};
+
+    if(tank->bullet != NULL)
+	return;
+
+    xmb_rt = tank_rt->mb_rt;
+    rdman = X_MB_rdman(xmb_rt);
+    tman = X_MB_tman(xmb_rt);
+
+    dir = tank->direction;
+    map_x = tank->map_x + map_xy_adj[dir][0];
+    map_y = tank->map_y + map_xy_adj[dir][1];
+    switch(dir) {
+    case TD_UP:
+	shift_len = map_y + 1;
+	shift_x = 0;
+	shift_y = -shift_len * 50;
+	break;
+    case TD_RIGHT:
+	shift_len = 16 - map_x;
+	shift_x = shift_len * 50;
+	shift_y = 0;
+	break;
+    case TD_DOWN:
+	shift_len = 12 - map_y;
+	shift_x = 0;
+	shift_y = shift_len * 50;
+	break;
+    case TD_LEFT:
+	shift_len = map_x + 1;
+	shift_x = -shift_len * 50;
+	shift_y = 0;
+	break;
+    }
+
+    if(shift_len <= 0)
+	return;
+
+    tank->bullet = tank_bullet_new(rdman, map_x, map_y, dir);
+    bullet = tank->bullet;
+    bullet->tman = tman;
+
+    progm = mb_progm_new(2, rdman);
+    MB_TIMEVAL_SET(&start, 0, 0);
+    MB_TIMEVAL_SET(&playing, shift_len / 4, (shift_len % 4) * 250000);
+    word = mb_progm_next_word(progm, &start, &playing);
+    act = mb_shift_new(shift_x, shift_y, bullet->coord_pos, word);
+    bullet->progm = progm;
+    
+    subject = mb_progm_get_complete(progm);
+    factory = rdman_get_ob_factory(rdman);
+    subject_add_observer(factory, subject, bullet_go_out_map, tank);
+
+    get_now(&now);
+    MB_TIMEVAL_CP(&bullet->start_time, &now);
+    mb_progm_start(progm, tman, &now);
+
+    MB_TIMEVAL_SET(&next, 0, 100000);
+    MB_TIMEVAL_ADD(&next, &now);
+    bullet->hit_tmr = mb_tman_timeout(tman, &next, bullet_hit_chk, tank);
+}
+
 #define CHANGE_POS(g, x, y) do {			\
 	(g)->root_coord->matrix[0] = 1.0;		\
 	(g)->root_coord->matrix[2] = x;			\
@@ -252,27 +482,32 @@
     switch(xkey->sym) {
     case 0xff51:		/* left */
 	direction = TD_LEFT;
+	tank_move(tank_rt->tank1, direction, tank_rt);
 	break;
 
     case 0xff52:		/* up */
 	direction = TD_UP;
+	tank_move(tank_rt->tank1, direction, tank_rt);
 	break;
 
     case 0xff53:		/* right */
 	direction = TD_RIGHT;
+	tank_move(tank_rt->tank1, direction, tank_rt);
 	break;
 
     case 0xff54:		/* down */
 	direction = TD_DOWN;
+	tank_move(tank_rt->tank1, direction, tank_rt);
 	break;
 
     case 0x20:			/* space */
+	tank_fire_bullet(tank_rt, tank_rt->tank1);
+	break;
     case 0xff0d:		/* enter */
     default:
 	return;
     }
 
-    tank_move(tank_rt->tank1, direction, tank_rt);
 }
 
 static void init_keyboard(tank_rt_t *tank_rt) {
--- a/src/X_main.c	Fri Oct 31 00:12:17 2008 +0800
+++ b/src/X_main.c	Fri Oct 31 00:14:53 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/animate.c	Fri Oct 31 00:12:17 2008 +0800
+++ b/src/animate.c	Fri Oct 31 00:14:53 2008 +0800
@@ -98,6 +98,7 @@
     int first_playing;		/*!< first playing word. */
     mb_tman_t *tman;
     subject_t *complete;	/*!< notify when a program is completed. */
+    mb_timer_t *cur_timer;
 
     int n_words;
     int max_words;
@@ -289,7 +290,9 @@
 	    MB_TIMEVAL_CP(&next_tmo, &word->abs_start);
 	timer = mb_tman_timeout(progm->tman, &next_tmo,
 				mb_progm_step, progm);	
+	progm->cur_timer = timer;
     } else {
+	/* Make program to complete. */
 #ifndef UNITTEST
 	factory = rdman_get_ob_factory(progm->rdman);
 	comp_evt.event.type = EVT_PROGM_COMPLETE;
@@ -297,6 +300,7 @@
 	comp_evt.progm = progm;
 	subject_notify(factory, progm->complete, &comp_evt.event);
 #endif /* UNITTEST */
+	progm->cur_timer = NULL;
     }
 }
 
@@ -329,10 +333,17 @@
     timer = mb_tman_timeout(tman, &progm->words[0].abs_start,
 			    mb_progm_step, progm);
     ASSERT(timer != NULL);
+
+    /* We need timer to abort it. */
+    progm->cur_timer = timer;
 }
 
-void mb_progm_abort(mb_progm_t *progm, mb_tman_t *tman) {
+void mb_progm_abort(mb_progm_t *progm) {
     /*! \todo Make sure abort release resources. */
+    if(progm->cur_timer) {
+	mb_tman_remove(progm->tman, progm->cur_timer);
+	progm->cur_timer = NULL;
+    }
 }
 
 /*! \brief Return event subject for completion of a program.
--- a/src/animate.h	Fri Oct 31 00:12:17 2008 +0800
+++ b/src/animate.h	Fri Oct 31 00:14:53 2008 +0800
@@ -42,7 +42,7 @@
 				     const mb_timeval_t *playing);
 extern void mb_progm_start(mb_progm_t *progm, mb_tman_t *tman,
 			   mb_timeval_t *now);
-extern void mb_progm_abort(mb_progm_t *progm, mb_tman_t *tman);
+extern void mb_progm_abort(mb_progm_t *progm);
 extern subject_t *mb_progm_get_complete(mb_progm_t *progm);
 
 /*! \defgroup ani_actions Animation Actions
--- a/src/coord.c	Fri Oct 31 00:12:17 2008 +0800
+++ b/src/coord.c	Fri Oct 31 00:14:53 2008 +0800
@@ -120,22 +120,33 @@
     return sqrt(x * x + y * y);
 }
 
+/*!
+ * \note Coords, marked with COF_SKIP_TRIVAL (for temporary), and
+ * descendants of them will not be trivaled and the flag with be removed
+ * after skipping them.
+ */
 coord_t *preorder_coord_subtree(coord_t *root, coord_t *last) {
-    coord_t *next;
+    coord_t *next = NULL;
 
     ASSERT(last != NULL);
     
-    if(STAILQ_HEAD(last->children))
+    if((!(last->flags & COF_SKIP_TRIVAL)) &&
+       STAILQ_HEAD(last->children)) {
 	next = STAILQ_HEAD(last->children);
-    else {
+	if(!(next->flags & COF_SKIP_TRIVAL))
+	    return next;
+    } else {
 	next = last;
+    }
+
+    do {
+	next->flags &= ~COF_SKIP_TRIVAL;
 	while(next != root && STAILQ_NEXT(coord_t, sibling, next) == NULL)
 	    next = next->parent;
 	if(next == root)
-	    next = NULL;
-	if(next)
-	    next = STAILQ_NEXT(coord_t, sibling, next);
-    }
+	    return NULL;
+	next = STAILQ_NEXT(coord_t, sibling, next);
+    } while(next->flags & COF_SKIP_TRIVAL);
 
     return next;
 }
--- a/src/event.c	Fri Oct 31 00:12:17 2008 +0800
+++ b/src/event.c	Fri Oct 31 00:14:53 2008 +0800
@@ -11,17 +11,7 @@
 #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.
  *
@@ -31,21 +21,10 @@
  * from a redraw manager.
  */
 static int add_gen_geo(redraw_man_t *rdman, geo_t *geo) {
-    int max_gen_geos;
     int r;
 
-    if(rdman->n_gen_geos >= rdman->max_gen_geos) {
-	max_gen_geos = rdman->max_gen_geos + ARRAY_EXT_SZ;
-	r = extend_memblk((void **)&rdman->gen_geos,
-			  sizeof(geo_t *) * rdman->n_gen_geos,
-			  sizeof(geo_t *) * max_gen_geos);
-	if(r != OK)
-	    return ERR;
-	rdman->max_gen_geos = max_gen_geos;
-    }
-
-    rdman->gen_geos[rdman->n_gen_geos++] = geo;
-    return OK;
+    r = geos_add(&rdman->gen_geos, geo);
+    return r == 0? OK: ERR;
 }
 
 static int collect_shapes_at_point(redraw_man_t *rdman,
@@ -57,7 +36,7 @@
     if(r != OK)
 	return ERR;
 
-    rdman->n_gen_geos = 0;
+    rdman->gen_geos.num = 0;
 
     for(geo = rdman_geos(rdman, NULL);
 	geo != NULL;
@@ -94,9 +73,9 @@
     cairo_t *cr;
     int i;
 
-    geos = rdman->gen_geos;
+    geos = rdman->gen_geos.ds;
     cr = rdman->cr;
-    for(i = rdman->n_gen_geos - 1; i >= 0; i--) {
+    for(i = rdman->gen_geos.num - 1; i >= 0; i--) {
 	geo = geos[i];
 	if(geo->flags & GEF_HIDDEN)
 	    continue;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/mb	Fri Oct 31 00:14:53 2008 +0800
@@ -0,0 +1,1 @@
+.
\ No newline at end of file
--- a/src/mb_timer.h	Fri Oct 31 00:12:17 2008 +0800
+++ b/src/mb_timer.h	Fri Oct 31 00:14:53 2008 +0800
@@ -67,6 +67,9 @@
 	    (a)->tv_usec -= 1000000;		\
 	}					\
     } while(0)
+#define MB_TIMEVAL_DIV(a, b)			\
+    (((a)->tv_sec * 1000000.0 + (a)->tv_usec) /	\
+     ((b)->tv_sec * 1000000.0 + (b)->tv_usec))
 
 
 extern void get_now(mb_timeval_t *tmo);
--- a/src/mb_types.h	Fri Oct 31 00:12:17 2008 +0800
+++ b/src/mb_types.h	Fri Oct 31 00:14:53 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;
@@ -52,7 +58,8 @@
     subject_t *mouse_event;
 };
 #define GEF_DIRTY 0x1
-#define GEF_HIDDEN 0x2
+#define GEF_HIDDEN 0x2		/*!< The geo is hidden. */
+#define GEF_FREE 0x4
 
 extern int is_overlay(area_t *r1, area_t *r2);
 extern void area_init(area_t *area, int n_pos, co_aix pos[][2]);
@@ -109,14 +116,21 @@
 				 * 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
-#define COF_HIDDEN 0x2
+#define COF_HIDDEN 0x2	        /*!< A coord is hidden. */
 #define COF_OWN_CANVAS 0x4	/*!< A coord owns a canvas or inherit it
-				 * from an ancestor.
+				 * from an ancestor. 
 				 */
+#define COF_SKIP_TRIVAL 0x8	/*!< temporary skip descendants
+				 * when trivaling.
+				 */
+#define COF_FREE 0x10
 
 extern void coord_init(coord_t *co, coord_t *parent);
 extern void coord_trans_pos(coord_t *co, co_aix *x, co_aix *y);
@@ -125,7 +139,13 @@
 extern void update_aggr_matrix(coord_t *start);
 extern coord_t *preorder_coord_subtree(coord_t *root, coord_t *last);
 extern coord_t *postorder_coord_subtree(coord_t *root, coord_t *last);
-#define coord_hide(co) do { co->flags |= COF_HIDDEN; } while(0)
+extern void preorder_coord_skip_subtree(coord_t *subroot);
+#define preorder_coord_skip_subtree(sub)		\
+    do { (sub)->flags |= COF_SKIP_TRIVAL; } while(0)
+#define coord_hide(co)		      \
+    do {			      \
+	(co)->flags |= COF_HIDDEN;    \
+    } while(0)
 #define coord_show(co) do { co->flags &= ~COF_HIDDEN; } while(0)
 #define coord_get_mouse_event(coord) ((coord)->mouse_event)
 
@@ -150,26 +170,20 @@
     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 };
 
-#define sh_attach_geo(sh, g)			\
-    do {					\
-	(sh)->geo = g;				\
-	(g)->shape = (shape_t *)(sh);		\
+#define sh_get_mouse_event_subject(sh) ((sh)->geo->mouse_event)
+#define sh_hide(sh)			     \
+    do {				     \
+	(sh)->geo->flags |= GEF_HIDDEN;	     \
     } while(0)
-#define sh_detach_geo(sh)			\
-    do {					\
-	(sh)->geo->shape = NULL;		\
-	(sh)->geo = NULL;			\
+#define sh_show(sh)					\
+    do {						\
+	(sh)->geo->flags &= ~GEF_HIDDEN;		\
     } while(0)
-#define sh_get_geo(sh) ((sh)->geo)
-#define sh_get_mouse_event_subject(sh) ((sh)->geo->mouse_event)
-#define sh_hide(sh) do { (sh)->geo->flags |= GEF_HIDDEN; } while(0)
-#define sh_show(sh) do { (sh)->geo->flags &= ~GEF_HIDDEN; } while(0)
-#define sh_attach_coord(sh, coord) do { (sh)->coord = coord; } while(0)
-#define sh_detach_coord(sh) do { (sh)->coord = NULL; } while(0)
 
 
 #endif /* __MB_TYPES_H_ */
--- a/src/observer.h	Fri Oct 31 00:12:17 2008 +0800
+++ b/src/observer.h	Fri Oct 31 00:14:53 2008 +0800
@@ -42,7 +42,7 @@
 #define SUBF_BUSY 0x2		/*!< \brief in subject_notify() */
 #define SUBF_FREE 0x4		/*!< \brief in postponding subject_free() */
 
-enum {OBJT_GEO, OBJT_COORD, OBJT_KB, OBJT_PROGM};
+enum {OBJT_GEO, OBJT_COORD, OBJT_KB, OBJT_PROGM, OBJT_RDMAN};
 
 struct _mouse_event {
     event_t event;
@@ -72,7 +72,8 @@
 
 enum {EVT_MOUSE_OVER, EVT_MOUSE_OUT, EVT_MOUSE_MOVE,
       EVT_MOUSE_BUT_PRESS, EVT_MOUSE_BUT_RELEASE,
-      EVT_KB_PRESS, EVT_KB_RELEASE, EVT_PROGM_COMPLETE};
+      EVT_KB_PRESS, EVT_KB_RELEASE, EVT_PROGM_COMPLETE,
+      EVT_RDMAN_REDRAW };
 
 extern subject_t *subject_new(ob_factory_t *factory,
 			      void *obj, int obj_type);
--- a/src/paint.c	Fri Oct 31 00:12:17 2008 +0800
+++ b/src/paint.c	Fri Oct 31 00:14:53 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);
-    free(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 31 00:12:17 2008 +0800
+++ b/src/paint.h	Fri Oct 31 00:14:53 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 31 00:12:17 2008 +0800
+++ b/src/redraw_man.c	Fri Oct 31 00:14:53 2008 +0800
@@ -12,6 +12,24 @@
 /* NOTE: bounding box should also consider width of stroke.
  */
 
+#define sh_attach_geo(sh, g)			\
+    do {					\
+	(sh)->geo = g;				\
+	(g)->shape = (shape_t *)(sh);		\
+    } while(0)
+#define sh_detach_geo(sh)			\
+    do {					\
+	(sh)->geo->shape = NULL;		\
+	(sh)->geo = NULL;			\
+    } while(0)
+#define sh_get_geo(sh) ((sh)->geo)
+#define sh_attach_coord(sh, coord) do { (sh)->coord = coord; } while(0)
+#define sh_detach_coord(sh) do { (sh)->coord = NULL; } while(0)
+#define rdman_is_dirty(rdman)			\
+    ((rdman)->dirty_coords.num != 0 ||		\
+     (rdman)->dirty_geos.num != 0 ||		\
+     (rdman)->dirty_areas.num != 0)
+
 #define OK 0
 #define ERR -1
 
@@ -56,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
@@ -78,55 +106,68 @@
     }
 }
 
-static int extend_memblk(void **buf, int o_size, int n_size) {
-    void *new_buf;
+DARRAY_DEFINE(coords, coord_t *);
+DARRAY_DEFINE(geos, geo_t *);
+DARRAY_DEFINE(areas, area_t *);
+
+/*! Use \brief DARRAY to implement dirty & free lists.
+ */
+#define ADD_DATA(sttype, field, v)		\
+    int r;					\
+    r = sttype ## _add(&rdman->field, v);	\
+    return r == 0? OK: ERR;
+
+
+static int add_dirty_coord(redraw_man_t *rdman, coord_t *coord) {
+    coord->flags |= COF_DIRTY;
+    ADD_DATA(coords, dirty_coords, coord);
+}
+
+static int add_dirty_geo(redraw_man_t *rdman, geo_t *geo) {
+    geo->flags |= GEF_DIRTY;
+    ADD_DATA(geos, dirty_geos, geo);
+}
 
-    new_buf = realloc(*buf, n_size);
-    if(new_buf == NULL)
-	return ERR;
+static int add_dirty_area(redraw_man_t *rdman, area_t *area) {
+    ADD_DATA(areas, dirty_areas, area);
+}
+
+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;
 
-    *buf = new_buf;
+    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_dirty_geo(redraw_man_t *rdman, geo_t *geo) {
-    int max_dirty_geos;
-    int r;
+static void free_free_objs(redraw_man_t *rdman) {
+    int i;
+    free_obj_t *free_obj;
 
-    if(rdman->n_dirty_geos >= rdman->max_dirty_geos) {
-	max_dirty_geos = rdman->max_dirty_geos + ARRAY_EXT_SZ;
-	r = extend_memblk((void **)&rdman->dirty_geos,
-			  sizeof(geo_t *) * rdman->n_dirty_geos,
-			  sizeof(geo_t *) * max_dirty_geos);
-	if(r != OK)
-	    return ERR;
-	rdman->max_dirty_geos = max_dirty_geos;
+    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->dirty_geos[rdman->n_dirty_geos++] = geo;
-    return OK;
+    rdman->free_objs.num = 0;
 }
 
-static int add_dirty_area(redraw_man_t *rdman, area_t *area) {
-    int max_dirty_areas;
-    int r;
-
-    if(rdman->n_dirty_areas >= rdman->max_dirty_areas) {
-	/* every geo object and coord object can contribute 2 areas.
-	 * rdman_draw_area() may also contribute 1 area.
-	 */
-	max_dirty_areas = rdman->max_dirty_areas + ARRAY_EXT_SZ;
-	r = extend_memblk((void **)&rdman->dirty_areas,
-			  sizeof(area_t *) * rdman->n_dirty_areas,
-			  sizeof(area_t *) * max_dirty_areas);
-	if(r != OK)
-	    return ERR;
-	rdman->max_dirty_areas = max_dirty_areas;
-    }
-
-    rdman->dirty_areas[rdman->n_dirty_areas++] = area;
-    return OK;
+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]) {
@@ -250,6 +291,9 @@
     rdman->ob_factory.observer_free = ob_observer_free;
     rdman->ob_factory.get_parent_subject = ob_get_parent_subject;
 
+    rdman->redraw =
+	subject_new(&rdman->ob_factory, rdman, OBJT_RDMAN);
+
     rdman->root_coord = elmpool_elm_alloc(rdman->coord_pool);
     if(rdman->root_coord == NULL)
 	redraw_man_destroy(rdman);
@@ -265,18 +309,46 @@
     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_shape_free(rdman, member->shape);
+	}
 	rdman_coord_free(rdman, saved_coord);
     }
+    FORMEMBERS(saved_coord, member) {
+	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);
@@ -284,12 +356,11 @@
     elmpool_free(rdman->observer_pool);
     elmpool_free(rdman->subject_pool);
     elmpool_free(rdman->paint_color_pool);
-    if(rdman->dirty_coords)
-	free(rdman->dirty_coords);
-    if(rdman->dirty_geos)
-	free(rdman->dirty_geos);
-    if(rdman->gen_geos)
-	free(rdman->gen_geos);
+
+    DARRAY_DESTROY(&rdman->dirty_coords);
+    DARRAY_DESTROY(&rdman->dirty_geos);
+    DARRAY_DESTROY(&rdman->dirty_areas);
+    DARRAY_DESTROY(&rdman->gen_geos);
 }
 
 
@@ -330,7 +401,6 @@
     geo_attach_coord(geo, coord);
 
     /* New one should be dirty to recompute it when drawing. */
-    geo->flags |= GEF_DIRTY;
     r = add_dirty_geo(rdman, geo);
     if(r != OK)
 	return ERR;
@@ -343,22 +413,101 @@
 
 /*! \brief Remove a shape object from redraw manager.
  *
+ * \note Shapes should be removed after redrawing or when rdman is in clean.
+ * \note Removing shapes or coords when a rdman is dirty, removing
+ *       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;
-    geo_detach_coord(geo, coord);
-    subject_free(&rdman->ob_factory, geo->mouse_event);
-    sh_detach_geo(shape);
-    elmpool_elm_free(rdman->geo_pool, shape->geo);
-    sh_detach_coord(shape);
+
+    if(rdman_is_dirty(rdman) && geo != NULL) {
+	if(geo->flags & GEF_FREE)
+	    return ERR;
+
+	geo->flags |= GEF_FREE;
+	sh_hide(shape);
+	if(!(geo->flags & GEF_DIRTY)) {
+	    r = add_dirty_geo(rdman, geo);
+	    if(r != OK)
+		return ERR;
+	}
+	r = add_free_obj(rdman, shape, (free_func_t)rdman_shape_free);
+	if(r != OK)
+	    return ERR;
+	return OK;
+    }
+
+    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;
@@ -391,6 +540,10 @@
 
     coord->before_pmem = parent->num_members;
 
+    /* If parent is dirty, children should be dirty. */
+    if(parent && (parent->flags & COF_DIRTY))
+	add_dirty_coord(rdman, coord);
+
     return coord;
 }
 
@@ -398,14 +551,44 @@
  *
  * \param coord is a coord_t without children and members.
  * \return 0 for successful, -1 for error.
+ *
+ * \note Removing coords when the rdman is dirty, the removing is postponsed.
  */
 int rdman_coord_free(redraw_man_t *rdman, coord_t *coord) {
     coord_t *parent;
+    coord_t *child;
+    geo_t *member;
+    int r;
 
     parent = coord->parent;
     if(parent == NULL)
 	return ERR;
 
+    if(rdman_is_dirty(rdman)) {
+	if(coord->flags & COF_FREE)
+	    return ERR;
+
+	FORCHILDREN(coord, child) {
+	    if(!(child->flags & COF_FREE))
+		return ERR;
+	}
+	FORMEMBERS(coord, member) {
+	    if(!(member->flags & GEF_FREE))
+		return ERR;
+	}
+	coord->flags |= COF_FREE;
+	coord_hide(coord);
+	if(!(coord->flags & COF_DIRTY)) {
+	    r = add_dirty_coord(rdman, coord);
+	    if(r != OK)
+		return ERR;
+	}
+	r = add_free_obj(rdman, coord, (free_func_t)rdman_coord_free);
+	if(r != OK)
+	    return ERR;
+	return OK;
+    }
+
     if(FIRST_MEMBER(coord) != NULL)
 	return ERR;
 
@@ -424,42 +607,61 @@
     return OK;
 }
 
-static void make_sure_dirty_coords(redraw_man_t *rdman) {
-    int max_dirty_coords;
+int rdman_coord_subtree_free(redraw_man_t *rdman, coord_t *subtree) {
+    coord_t *coord, *prev_coord;
     int r;
-    
-    if(rdman->n_dirty_coords >= rdman->max_dirty_coords) {
-	/* Max of dirty_coords is not big enough. */
-	max_dirty_coords = rdman->max_dirty_coords + 16;
-	
-	r = extend_memblk((void **)&rdman->dirty_coords,
-			  sizeof(coord_t *) * rdman->n_dirty_coords,
-			  sizeof(coord_t *) * max_dirty_coords);
-	rdman->max_dirty_coords = max_dirty_coords;
+
+    if(subtree == NULL)
+	return OK;
+
+    prev_coord = postorder_coord_subtree(subtree, NULL);
+    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;
+    }
+
+    return OK;
 }
 
 /*! \brief Mark a coord is changed.
  *
  * A changed coord_t object is marked as dirty and put
- * into dirty_coords list.
+ * into dirty_coords list.  rdman_coord_changed() should be called
+ * for a coord after it been changed to notify redraw manager to
+ * redraw shapes grouped by it.
  */
 int rdman_coord_changed(redraw_man_t *rdman, coord_t *coord) {
     coord_t *child;
 
     if(coord->flags & COF_DIRTY)
 	return OK;
-    
-    /* Make the coord and child coords dirty. */
-    for(child = coord;
+
+    add_dirty_coord(rdman, coord);
+
+    if(coord->flags & COF_HIDDEN)
+	return OK;
+
+    /* Make child coords dirty. */
+    for(child = preorder_coord_subtree(coord, coord);
 	child != NULL;
 	child = preorder_coord_subtree(coord, child)) {
-	if(child->flags & COF_DIRTY)
+	if(child->flags & (COF_DIRTY | COF_HIDDEN)) {
+	    preorder_coord_skip_subtree(child);
 	    continue;
-	make_sure_dirty_coords(rdman);
- 
-	rdman->dirty_coords[rdman->n_dirty_coords++] = child;
-	child->flags |= COF_DIRTY;
+	}
+
+	add_dirty_coord(rdman, child);
     }
 
     return OK;
@@ -477,7 +679,6 @@
     r = add_dirty_geo(rdman, geo);
     if(r == ERR)
 	return ERR;
-    geo->flags |= GEF_DIRTY;
 
     return OK;
 }
@@ -492,13 +693,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;
     }
@@ -536,9 +735,9 @@
     shape->geo->flags &= ~GEF_DIRTY;
 
     if(is_coord_subtree_hidden(shape->coord))
-	shape->geo->flags |= GEF_HIDDEN;
+	sh_hide(shape);
     else
-	shape->geo->flags &= ~GEF_HIDDEN;
+	sh_show(shape);
 }
 
 /*! \brief Setup canvas for the coord.
@@ -615,9 +814,9 @@
     int n_dirty_coords;
     int i, r;
 
-    n_dirty_coords = rdman->n_dirty_coords;
+    n_dirty_coords = rdman->dirty_coords.num;
     if(n_dirty_coords > 0) {
-	dirty_coords = rdman->dirty_coords;
+	dirty_coords = rdman->dirty_coords.ds;
 	_insert_sort((void **)dirty_coords, n_dirty_coords,
 		     OFFSET(coord_t, order));
 	for(i = 0; i < n_dirty_coords; i++) {
@@ -631,7 +830,7 @@
 	    add_dirty_area(rdman, &coord->areas[0]);
 	    add_dirty_area(rdman, &coord->areas[1]);
 	}
-	rdman->n_dirty_coords = 0;
+	rdman->dirty_coords.num = 0;
     }
     return OK;
 }
@@ -642,9 +841,9 @@
     geo_t **dirty_geos;
     geo_t *visit_geo;
 
-    n_dirty_geos = rdman->n_dirty_geos;
+    n_dirty_geos = rdman->dirty_geos.num;
     if(n_dirty_geos > 0) {
-	dirty_geos = rdman->dirty_geos;
+	dirty_geos = rdman->dirty_geos.ds;
 	for(i = 0; i < n_dirty_geos; i++) {
 	    visit_geo = dirty_geos[i];
 	    if(!(visit_geo->flags & GEF_DIRTY))
@@ -655,7 +854,7 @@
 	    add_dirty_area(rdman, visit_geo->cur_area);
 	    add_dirty_area(rdman, visit_geo->last_area);
 	}
-	rdman->n_dirty_geos = 0;
+	rdman->dirty_geos.num = 0;
     }    
 
     return OK;
@@ -837,6 +1036,9 @@
     cairo_t *canvas;
     int mem_idx;
 
+    if(coord->flags & COF_HIDDEN)
+	return OK;
+
     canvas = coord->canvas;
     member = FIRST_MEMBER(coord);
     mem_idx = 0;
@@ -848,10 +1050,12 @@
 	    child = NEXT_CHILD(child);
 	} else {
 	    ASSERT(member != NULL);
-	    if(is_geo_in_areas(member, n_areas, areas)) {
+	    if((!(member->flags & GEF_HIDDEN)) &&
+	       is_geo_in_areas(member, n_areas, areas)) {
 		draw_shape(rdman, canvas, member->shape);
 		dirty = 1;
 	    }
+
 	    member = NEXT_MEMBER(member);
 	    mem_idx++;
 	}
@@ -908,24 +1112,36 @@
     int r;
     int n_dirty_areas;
     area_t **dirty_areas;
+    event_t event;
+    ob_factory_t *factory;
+    subject_t *redraw;
 
     r = clean_rdman_dirties(rdman);
     if(r != OK)
 	return ERR;
 
-    n_dirty_areas = rdman->n_dirty_areas;
-    dirty_areas = rdman->dirty_areas;
+    n_dirty_areas = rdman->dirty_areas.num;
+    dirty_areas = rdman->dirty_areas.ds;
     if(n_dirty_areas > 0) {
 	/*! \brief Draw shapes in preorder of coord tree and support opacity
 	 * rules.
 	 */
 	clean_canvas(rdman->cr);
 	draw_shapes_in_areas(rdman, n_dirty_areas, dirty_areas);
-	copy_cr_2_backend(rdman, rdman->n_dirty_areas, rdman->dirty_areas);
-	rdman->n_dirty_areas = 0;
+	copy_cr_2_backend(rdman, rdman->dirty_areas.num,
+			  rdman->dirty_areas.ds);
 	reset_clip(rdman);
     }
-    rdman->n_dirty_areas = 0;
+    rdman->dirty_areas.num = 0;
+
+    /* Free postponsed removing */
+    free_free_objs(rdman);
+
+    factory = rdman_get_ob_factory(rdman);
+    redraw = rdman_get_redraw_subject(rdman);
+    event.type = EVT_RDMAN_REDRAW;
+    event.tgt = event.cur_tgt = redraw;
+    subject_notify(factory, redraw, &event);
 
     return OK;
 }
@@ -953,12 +1169,14 @@
  * \sa
  * - rdman_redraw_all()
  * - rdman_redraw_changed()
- * = draw_shapes_in_areas()
+ * - draw_shapes_in_areas()
  */
 
 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;
@@ -1027,17 +1245,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
@@ -1073,6 +1280,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
@@ -1183,7 +1412,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));
@@ -1198,14 +1432,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];
@@ -1240,7 +1473,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);
 }
@@ -1257,7 +1490,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;
@@ -1272,7 +1505,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;
@@ -1298,15 +1531,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 31 00:12:17 2008 +0800
+++ b/src/redraw_man.h	Fri Oct 31 00:14:53 2008 +0800
@@ -6,6 +6,24 @@
 #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 *);
+
 /*! \brief Manage redrawing of shapes (graphic elements).
  *
  * Every coord_t and geo_t object is assigned with a unique
@@ -21,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;
@@ -33,29 +51,24 @@
     elmpool_t *subject_pool;
     elmpool_t *paint_color_pool;
 
-    int max_dirty_coords;
-    int n_dirty_coords;
-    coord_t **dirty_coords;	/*!< coordinates their transform
-				 * matric are chagned.
-				 */
+    coords_t dirty_coords;
+    geos_t dirty_geos;
+    areas_t dirty_areas;
 
-    int max_dirty_geos;
-    int n_dirty_geos;
-    geo_t **dirty_geos;		/*!< geometries that need re-computed */
+    geos_t gen_geos;
 
-    int max_dirty_areas;
-    int n_dirty_areas;
-    area_t **dirty_areas;	/*!< \brief are areas need to redraw. */
+    STAILQ(shape_t) shapes;	/*!< \brief All managed shapes.  */
+    STAILQ(paint_t) paints;	/*!< \brief All managed paints. */
 
-    int max_gen_geos;
-    int n_gen_geos;
-    geo_t **gen_geos;		/* general geo list (for temporary store) */
+    free_objs_t free_objs;
 
     cairo_t *cr;
     cairo_t *backend;
 
     ob_factory_t ob_factory;
-} redraw_man_t;
+
+    subject_t *redraw;		/*!< \brief Notified after redrawing. */
+};
 
 extern int redraw_man_init(redraw_man_t *rdman, cairo_t *cr,
 			   cairo_t *backend);
@@ -65,9 +78,18 @@
 				      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);
 extern int rdman_coord_changed(redraw_man_t *rdman, coord_t *coord);
 extern int rdman_shape_changed(redraw_man_t *rdman, shape_t *shape);
 extern int rdman_redraw_changed(redraw_man_t *rdman);
@@ -101,21 +123,38 @@
 			    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);
 
 extern shape_t *find_shape_at_pos(redraw_man_t *rdman,
 				  co_aix x, co_aix y, int *in_stroke);
 #define rdman_get_ob_factory(rdman) (&(rdman)->ob_factory)
+#define rdman_get_redraw_subject(rdman) ((rdman)->redraw)
 
 
 #endif /* __REDRAW_MAN_H_ */
--- a/src/shape_path.c	Fri Oct 31 00:12:17 2008 +0800
+++ b/src/shape_path.c	Fri Oct 31 00:14:53 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 31 00:12:17 2008 +0800
+++ b/src/shape_rect.c	Fri Oct 31 00:14:53 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 31 00:12:17 2008 +0800
+++ b/src/shape_text.c	Fri Oct 31 00:14:53 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 31 00:12:17 2008 +0800
+++ b/src/shapes.h	Fri Oct 31 00:14:53 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/src/shift.c	Fri Oct 31 00:12:17 2008 +0800
+++ b/src/shift.c	Fri Oct 31 00:14:53 2008 +0800
@@ -91,6 +91,7 @@
     shift->action.stop = mb_shift_stop;
     shift->action.free = mb_shift_free;
 
+    /*! \note mb_shift_new() will add itself to the specified word. */
     mb_word_add_action(word, (mb_action_t *)shift);
 
     return (mb_action_t *)shift;
--- a/src/timer.c	Fri Oct 31 00:12:17 2008 +0800
+++ b/src/timer.c	Fri Oct 31 00:14:53 2008 +0800
@@ -61,7 +61,8 @@
     for(visit = STAILQ_HEAD(tman->timers);
 	visit != NULL;
 	visit = STAILQ_NEXT(mb_timer_t, next, visit)) {
-	if(MB_TIMEVAL_LATER(&visit->tmo, tmo))
+	if(MB_TIMEVAL_LATER(&visit->tmo, tmo) ||
+	   MB_TIMEVAL_EQ(&visit->tmo, tmo))
 	    break;
 	last = visit;
     }
--- a/src/tools.h	Fri Oct 31 00:12:17 2008 +0800
+++ b/src/tools.h	Fri Oct 31 00:14:53 2008 +0800
@@ -55,13 +55,75 @@
 		  _stailq_cur->field != (elm))		\
 		_stailq_cur = _stailq_cur->field;	\
 	    if(_stailq_cur != NULL) {			\
-		_stailq_cur->field = elm;		\
+		_stailq_cur->field = (elm)->field;	\
 		if((q).tail == (elm))			\
 		    (q).tail = _stailq_cur;		\
 	    }						\
 	}						\
     } while(0)
 
+/*! \defgroup darray Dynamic Array
+ *
+ * DARRAY is a dynamic sized array/list, it's length is a variable.
+ * It is extended, automatically, if it is full and more elemnts are
+ * putted in.
+ *
+ * Users of DARRAY must declare a new type to store data.  The way to
+ * declear a new type is to invoke DARRAY() with paramters of name of
+ * type and type of data to be stored in.  The new storage type is named
+ * with foo_t where foo is the name you pass in.
+ *
+ * DARRAY_DEFINE() is inovked to define foo_add() function; foo is name
+ * of storage type.  You can call foo_add() to add a data element
+ * into a storage object.
+ *
+ * Get ith element in a storage object, use
+ * \code
+ * obj->ds[i]
+ * \endcode
+ *
+ * To loop over elements in a storage object, us
+ * \code
+ * for(i = 0; i < obj->num; i++) {
+ *	v = obj->ds[i];
+ *	......
+ * }
+ * \endcode
+ * @{
+ */
+/*! \brief Declare a DARRAY storage type.
+ *
+ * \param name is name of storage type.
+ * \param type is type of data elements that will be stored in.
+ *
+ * Type of <name>_t is defined by the macro.  It is used to define a
+ * storage object to contain data elements.
+ */
+#define DARRAY(name, type)				\
+    struct _ ## name {					\
+	int max, num;					\
+	type *ds;					\
+    };							\
+    typedef struct _ ## name name ## _t
+#define DARRAY_DEFINE(name, type)			\
+    static int name ## _add(name ## _t *da, type v) {	\
+	type *new_ds;					\
+	int max;					\
+	if(da->num >= (da)->max) {			\
+	    max = (da)->max + 32;			\
+	    new_ds = realloc(da->ds,			\
+			     max * sizeof(type));	\
+	    if(new_ds == NULL) return -1;		\
+	    da->ds = new_ds;				\
+	    da->max = max;				\
+	}						\
+	da->ds[da->num++] = v;				\
+	return 0;					\
+    }
+#define DARRAY_CLEAN(da) do { (da)->num = 0; } while(0)
+#define DARRAY_INIT(da) do { (da)->num = (da)->max = 0; (da)->ds = NULL; }
+#define DARRAY_DESTROY(da) do { if((da)->ds) free((da)->ds); } while(0)
+/* @} */
 
 #include <stdlib.h>
 
--- a/tools/mb_c_header.m4	Fri Oct 31 00:12:17 2008 +0800
+++ b/tools/mb_c_header.m4	Fri Oct 31 00:14:53 2008 +0800
@@ -48,6 +48,7 @@
 typedef struct $1 $1_t;
 
 struct $1 {
+    redraw_man_t *rdman;
     coord_t *root_coord;]
 $2[]dnl
 [};
--- a/tools/mb_c_source.m4	Fri Oct 31 00:12:17 2008 +0800
+++ b/tools/mb_c_source.m4	Fri Oct 31 00:14:53 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,33 +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],[[
-    obj->$1->free(obj->$1);
+    rdman_shape_free(rdman, obj->$1);
 ]])
 
 define([F_ADD_RECT],[[
-    obj->$1->free(obj->$1);
+    rdman_shape_free(rdman, obj->$1);
 ]])
 
 define([F_ADD_TEXT],[[
-    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])
@@ -257,6 +258,42 @@
 define([SHAPE_MATRIX],)
 divert[]])
 
+define([REVERSE_VARS],[divert([-1])
+define([__REV_VAR],[])
+define([PUSH_REV], [
+	pushdef([__REV_VAR])
+	define([__REV_VAR], ]QUOTE(QUOTE($[]1))[)])
+define([POP_ALL_REV], [dnl
+ifelse(__REV_VAR, [], ,[UNQUOTE(__REV_VAR)[]dnl
+popdef([__REV_VAR])[]POP_ALL_REV[]])])
+define([RIMPORT], [
+	define(]QUOTE($[]1)[,
+		[PUSH_REV(]]QUOTE(QUOTE($[]1))[[(]QUOTE($[]@)[))])
+])
+RIMPORT([ADD_LINEAR_PAINT])
+RIMPORT([ADD_RADIAL_PAINT])
+RIMPORT([COLOR_STOP])
+RIMPORT([REF_STOPS_RADIAL])
+RIMPORT([REF_STOPS_LINEAR])
+RIMPORT([ADD_PATH])
+RIMPORT([ADD_RECT])
+RIMPORT([ADD_COORD])
+RIMPORT([ADD_TEXT])
+RIMPORT([FILL_SHAPE])
+RIMPORT([STROKE_SHAPE])
+RIMPORT([FILL_SHAPE_WITH_PAINT])
+RIMPORT([STROKE_SHAPE_WITH_PAINT])
+RIMPORT([STROKE_WIDTH])
+RIMPORT([GROUP_HIDE])
+RIMPORT([RECT_HIDE])
+RIMPORT([PATH_HIDE])
+RIMPORT([COORD_TRANSLATE])
+RIMPORT([COORD_MATRIX])
+RIMPORT([SHAPE_TRANSLATE])
+RIMPORT([SHAPE_MATRIX])
+divert[]dnl
+])
+
 define([MADBUTTERFLY],[dnl
 [#include <stdio.h>
 #include <stdlib.h>
@@ -274,6 +311,7 @@
 [
     obj = ($1_t *)malloc(sizeof($1_t));
     if(obj == NULL) return NULL;
+    obj->rdman = rdman;
 ]SETUP_VARS
     obj->root_coord = rdman_coord_new(rdman, parent_coord);
 $2
@@ -282,7 +320,16 @@
 
 void $1_free($1_t *obj) {
     grad_stop_t *stops = NULL;
-]CLEAR_VARS[]$2[
+    redraw_man_t *rdman;
+
+    rdman = obj->rdman;
+]REVERSE_VARS[]dnl
+divert([-1])dnl
+$2[]dnl
+divert[]dnl
+CLEAR_VARS[]dnl
+POP_ALL_REV[
+    rdman_coord_subtree_free(rdman, obj->root_coord);
     free(obj);
 }
 ]dnl