changeset 1067:7b4e80ab671a openvg

merge from default branch
author Thinker K.F. Li <thinker@codemud.net>
date Wed, 01 Dec 2010 12:25:56 +0800
parents bd18951b51d5 (current diff) 292fbb86d8f3 (diff)
children aed05ad5102d
files configure.ac examples/calculator/Makefile.am include/mb_basic_types.h include/mb_config.h.in include/mb_graph_engine.h include/mb_graph_engine_cairo.h include/mb_graph_engine_skia.h include/mb_types.h nodejs/X_supp_njs.c nodejs/X_supp_njs.h nodejs/testcase.js src/Makefile.am src/X_supp.c src/dfb_supp.c src/event.c src/graph_engine_cairo.c src/graph_engine_dfb.c src/graph_engine_skia.cpp src/img_ldr.c src/img_ldr_imlib2.c src/paint.c src/redraw_man.c
diffstat 161 files changed, 15226 insertions(+), 2478 deletions(-) [+]
line wrap: on
line diff
--- a/COPYING.h	Mon Jul 19 15:44:49 2010 +0800
+++ b/COPYING.h	Wed Dec 01 12:25:56 2010 +0800
@@ -12,7 +12,7 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 4. Neither the name of the University nor the names of its contributors
+ * 3. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  *
--- a/Doxyfile	Mon Jul 19 15:44:49 2010 +0800
+++ b/Doxyfile	Wed Dec 01 12:25:56 2010 +0800
@@ -534,7 +534,7 @@
 # directories like "/usr/src/myproject". Separate the files or directories 
 # with spaces.
 
-INPUT                  = src tools include dox README.h COPYING.h
+INPUT                  = src tools include dox README.h COPYING.h nodejs
 
 # This tag can be used to specify the character encoding of the source files 
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/build-mipsel.sh	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+autoreconf -ifs
+./configure --host mipsel-linux --build i686-pc-linux-gnu --prefix ${TANGO} --disable-sh_text --with-backend=dfb
+make && make install
--- a/config.mk	Mon Jul 19 15:44:49 2010 +0800
+++ b/config.mk	Wed Dec 01 12:25:56 2010 +0800
@@ -1,1 +1,9 @@
 INCLUDES = -I$(top_srcdir)/include
+
+if SH_TEXT
+APPCFLAGS = @pangocairo_CFLAGS@
+APPLDFLAGS = @pangocairo_LIBS@
+else
+APPCFLAGS = @cairo_CFLAGS@
+APPLDFLAGS = @cairo_LIBS@
+endif
\ No newline at end of file
--- a/configure.ac	Mon Jul 19 15:44:49 2010 +0800
+++ b/configure.ac	Wed Dec 01 12:25:56 2010 +0800
@@ -26,6 +26,12 @@
 AC_TYPE_UINT32_T
 AC_TYPE_UINT64_T
 
+# Explicit initialize pkg-config for conditional PKG_CHECK_MODULE
+# see http://www.flameeyes.eu/autotools-mythbuster/autoconf/macros.html
+# and http://www.flameeyes.eu/autotools-mythbuster/ \
+#		pkgconfig/pkg_check_modules.html
+PKG_PROG_PKG_CONFIG
+
 # Checks for library functions.
 AC_FUNC_MALLOC
 AC_FUNC_REALLOC
@@ -33,32 +39,35 @@
 AC_CHECK_FUNCS([gettimeofday memset select sqrt strdup])
 
 AC_ARG_ENABLE([testcase],
-	[  --enable-testcase	Enable testcase],
-[case "${enableval}" in
-  yes) testcase=true ;;
-  no) testcase=false ;;
-  *) AC_MSG_ERROR([bad value ${enableval} for --enable-testcase]) ;;
-esac],[testcase=false])
+        [AS_HELP_STRING([--enable-testcase],[Enable testcase])],
+        [case "${enableval}" in
+            (yes) testcase=true ;;
+            (no) testcase=false ;;
+            (*) AC_MSG_ERROR([bad value ${enableval} for --enable-testcase]) ;;
+        esac],[testcase=false])
+
 AM_CONDITIONAL([TESTCASE], [test x$testcase = xtrue])
 
 AC_ARG_ENABLE([sh_text],
-	[  --disable-sh_text	Turn off sh_text],
-[case "${enableval}" in
-  yes) sh_text=true ;;
-  no) sh_text=false ;;
-  *) AC_MSG_ERROR([bad value ${enableval} for --disable-sh_text]) ;;
-esac],[sh_text=true])
+        [AS_HELP_STRING([--disable-sh_text],[Turn off sh_text])],
+        [case "${enableval}" in
+            (yes) sh_text=true ;;
+            (no) sh_text=false ;;
+            (*) AC_MSG_ERROR([bad value ${enableval} for --disable-sh_text]) ;;
+        esac],[sh_text=true])
 
 AC_ARG_ENABLE([sh_stext],
-	[  --disable-sh_stext	Turn off sh_stext],
-[case "${enableval}" in
-  yes) sh_stext=true ;;
-  no) sh_stext=false ;;
-  *) AC_MSG_ERROR([bad value ${enableval} for --disable-sh_stext]) ;;
-esac],[sh_stext=true])
+        [AS_HELP_STRING([--disable-sh_stext],[Turn off sh_stext])],
+        [case "${enableval}" in
+            (yes) sh_stext=true ;;
+            (no) sh_stext=false ;;
+            (*) AC_MSG_ERROR([bad value ${enableval} for --disable-sh_stext]) ;;
+        esac],[sh_stext=true])
+
 AM_CONDITIONAL([SH_STEXT], [test x$sh_stext = xtrue])
+
 [if [ x"${sh_stext}" = xtrue ]; then]
-AC_DEFINE([SH_STEXT])
+    AC_DEFINE([SH_STEXT])
 [fi]
 
 AC_ARG_ENABLE([mbaf],
@@ -69,109 +78,174 @@
   *) AC_MSG_ERROR([bad value ${enableval} for --disable-mbaf]) ;;
 esac],[mbaf=true])
 
-AC_ARG_ENABLE([X_supp],
-	[  --disable-X_supp	Turn off X backend],
-[case "${enableval}" in
-  yes) X_supp=true ;;
-  no) X_supp=false ;;
-  *) AC_MSG_ERROR([bad value ${enableval} for --disable-X_supp]) ;;
-esac],[X_supp=true])
+AC_ARG_WITH([backend],
+        [AS_HELP_STRING([--with-backend=FOO],[Use FOO as display backend (default is 'X')])],
+        [case "${withval}" in
+            ('X') backend='X'; default_graphic_engine="cairo" ;;
+            ('dfb') backend='dfb'; default_graphic_engine="dfb" ;;
+            ('no') backend='none'; default_graphic_engine="cairo" ;;
+            (*) AC_MSG_ERROR([bad value ${withval} for --with-backend]) ;;
+        esac],[backend='X'; default_graphic_engine="cairo"])
+
+AC_ARG_WITH([graphic-engine],
+	[AS_HELP_STRING([--with-graphic-engine=FOO],
+			[Use FOO as graphic engine (default is '${default_graphic_engine}')])],
+	[case "${withval}" in
+	    ('cairo') graphic_engine="cairo" ;;
+	    ('skia') graphic_engine="skia" ;;
+	    ('openvg') graphic_engine="openvg" ;;
+	    ('dfb') graphic_engine="dfb" ;;
+	    (*) AC_MSG_ERROR([bad value ${withval} for --with-graphic-engine])
+	    	;;
+	esac], [graphic_engine="${default_graphic_engine}"])
 
-AC_ARG_ENABLE([skia],
-	[  --enable-skia	Turn on Skia instead of Cairo],
-[case "${enableval}" in
-  yes) skia=true ;;
-  no) skia=false ;;
-  *) AC_MSG_ERROR([bad value ${enableval} for --enable-skia]) ;;
-esac],[skia=false])
+AC_ARG_WITH([image-loader],
+	[AS_HELP_STRING([--with-image-loader=FOO],
+			[Use FOO as image loader (default is 'cairo')])],
+	[case "${withval}" in
+	    ('cairo') image_loader="cairo" ;;
+	    ('imlib2') image_loader="imlib2" ;;
+	    ('no') image_loader="none" ;;
+	    (*) AC_MSG_ERROR([bad value ${withval} for --with-image-loader])
+	    	;;
+	esac], [image_loader="cairo"])
 
-AC_ARG_ENABLE([openvg],
-	[  --enable-openvg	Turn on OpenVG instead of Cairo or Skia],
-[case "${enableval}" in
-  yes) openvg=true ;;
-  no) openvg=false ;;
-  *)  AC_MSG_ERROR([bad value ${enableval} for --enable-openvg]) ;;
-esac],[openvg=false])
-
-[if [ x"$skia-%openvg" = xtrue-true ]; then]
-   AC_MSG_ERROR([confliction on --enable-skia and --enable-openvg])
+xshm=false
+[if [ x"${backend}" = x"X" ]; then ]
+AC_ARG_ENABLE([xshm],
+	[AS_HELP_STRING([--disable-xshm],[Turn off XSHM supporting])],
+	[case "${enableval}" in
+	    (yes) xshm=true ;;
+	    (no) xshm=false ;;
+	    (*) AC_MSG_ERROR([bad value ${enableval} for --disable-xshm]) ;;
+	esac], [xshm=true])
 [fi]
 
-[if [ x"${skia}" = xtrue ]; then ]
-AC_DEFINE([SKIA_GRAPH_ENGINE])
-cairo=false
-[elif [ x"${openvg}" = xtrue ]; then ]
-AC_DEFINE([OPENVG_GRAPH_ENGINE])
-cairo=false
-mbaf=false
+[if [ x"${graphic_engine}" = x"skia" ]; then ]
+    AC_DEFINE([SKIA_GRAPH_ENGINE])
+[elif [ x"${graphic_engine}" = x"openvg" ]; then ]
+    AC_DEFINE([OPENVG_GRAPH_ENGINE])
+[elif [ x"${graphic_engine}" = x"dfb" ]; then ]
+    AC_DEFINE([DFB_GRAPH_ENGINE])
 [else]
-AC_DEFINE([CAIRO_GRAPH_ENGINE])
-cairo=true
+    AC_DEFINE([CAIRO_GRAPH_ENGINE])
 [fi]
 
-AM_CONDITIONAL([SKIA_GRAPH_ENGINE], [test x$skia = xtrue])
-AM_CONDITIONAL([OPENVG_GRAPH_ENGINE], [test x$openvg = xtrue])
-AM_CONDITIONAL([CAIRO_GRAPH_ENGINE], [test x"$cairo" = xtrue])
+AM_CONDITIONAL([SKIA_GRAPH_ENGINE], [test x"${graphic_engine}" = x"skia"])
+AM_CONDITIONAL([OPENVG_GRAPH_ENGINE], [test x"${graphic_engine}" = x"openvg"])
+AM_CONDITIONAL([DFB_GRAPH_ENGINE], [test x"${graphic_engine}" = x"dfb"])
+AM_CONDITIONAL([CAIRO_GRAPH_ENGINE], [test x"${graphic_engine}" = x"cairo"])
 AM_CONDITIONAL([MBAF], [test x"$mbaf" = xtrue])
 
 AC_ARG_ENABLE([nodejs],
-	[  --enable-nodejs	Turn on nodejs support],
-[case "${enableval}" in
-  yes) nodejs=true ;;
-  no) nodejs=false ;;
-  *) AC_MSG_ERROR([bad value ${enableval} for --enable-nodejs]) ;;
-esac],[nodejs=false])
+        [AS_HELP_STRING([--enable-nodejs],[Turn on nodejs support])],
+        [case "${enableval}" in
+            (yes) nodejs=true ;;
+            (no) nodejs=false ;;
+            (*) AC_MSG_ERROR([bad value ${enableval} for --enable-nodejs]) ;;
+        esac],[nodejs=false])
+
 [if [ x"${nodejs}" = xtrue ]; then ]
-  AC_PATH_PROG([NODE_WAF], [node-waf])
-  AC_CHECK_PROG([has_node], [node], [true], [false])
+    AC_PATH_PROG([NODE_WAF], [node-waf])
+    AC_CHECK_PROG([has_node], [node], [true], [false])
 [fi]
+
 AM_CONDITIONAL([NODEJS], [test x"${nodejs}-${has_node}" = xtrue-true])
 
 # Define AM and AC variable for sh_text
-AM_CONDITIONAL([SH_TEXT], [test x$sh_text = xtrue -a x$cairo = xtrue])
-[if [ x"${sh_text}" = xtrue -a x$cairo = xtrue ]; then]
-AC_DEFINE([SH_TEXT])
+
+AM_CONDITIONAL([SH_TEXT],
+	[test x$sh_text = xtrue -a x"${graphic_engine}" = x"cairo"])
+
+[if [ x"${sh_text}" = xtrue -a x"${graphic_engine}" = x"cairo" ]; then]
+    AC_DEFINE([SH_TEXT])
+[fi]
+
+# Define AM and AC variable for display backend
+
+AM_CONDITIONAL([X_BACKEND],
+	[test x$backend = x'X' -a x"${graphic_engine}" = x"cairo"])
+
+[if [ x"${backend}" = x'X' -a x"${graphic_engine}" = x"cairo" ]; then]
+    AC_DEFINE([X_BACKEND])
 [fi]
 
-# Define AM and AC variable for X_supp
-AM_CONDITIONAL([X_SUPP],
-	 [test x$X_supp = xtrue -a \( x$cairo = xtrue -o x$openvg = xtrue \)])
-[if [ x"${X_supp}" = xtrue -a \( x"${cairo}" = xtrue -o x"${openvg}" = xtrue \) ]; then]
-AC_DEFINE([X_SUPP])
+AM_CONDITIONAL([DFB_BACKEND],
+	[test x$backend = x'dfb' -a x"${graphic_engine}" = x"cairo"])
+
+[if [ x"${backend}" = x'dfb' -a x"${graphic_engine}" = x"cairo" ]; then]
+    AC_DEFINE([DFB_BACKEND])
+[fi]
+
+AM_CONDITIONAL([CAIRO_IMG_LOADER],
+	[test x"${image_loader}" = x"cairo"])
+
+[if [ x"${image_loader}" = x"cairo" ]; then]
+    AC_DEFINE([CAIRO_IMG_LOADER])
+    [if [ x"${graphic_engine}" != x"cairo" ]; then]
+        AC_MSG_ERROR([bad value cairo for --with-image-loader while value of --with-graphic-engine is not cairo])
+    [fi]
+[fi]
+
+AM_CONDITIONAL([IMLIB2_IMG_LOADER],
+	[test x"${image_loader}" = x"imlib2"])
+
+[if [ x"${image_loader}" = x"imlib2" ]; then]
+    AC_DEFINE([IMLIB2_IMG_LOADER])
+[fi]
+
+AM_CONDITIONAL([XSHM],
+	[test x"${xshm}" = xtrue -a x"${graphic_engine}" = x"cairo" -a x$backend = x'X'])
+
+[if [ x"${xshm}" = xtrue -a x"${graphic_engine}" = x"cairo" -a x$backend = x'X' ]; then]
+    AC_DEFINE([XSHM])
 [fi]
 
 # Checks for libraries.
-[if [ x"${openvg}" = xtrue ]; then]
+[if [ x"${graphic_engine}" = x"openvg" ]; then]
 AC_CHECK_HEADERS([GL/glut.h],, [AC_MSG_ERROR([can not find GL/glut.h])])
 [fi]
 
+[if [ x"${image_loader}" = x"imlib2" ]; then]
 PKG_CHECK_MODULES([imlib2], [imlib2 >= 1.4.1], , AC_MSG_ERROR([imlib2 >= 1.4.1 not found]))
-[if [ x"${cairo}" = xtrue ]; then]
-PKG_CHECK_MODULES([cairo], [cairo >= 1.6], , AC_MSG_ERROR([cairo >= 1.6 not found]))
-PKG_CHECK_MODULES([pangocairo], [pangocairo >= 1.0], , AC_MSG_ERROR([pangocairo >= 1.0 not found]))
+[fi]
+
+[if [ x"${graphic_engine}" = x"cairo" ]; then]
+    PKG_CHECK_MODULES([cairo], [cairo >= 1.6], , AC_MSG_ERROR([cairo >= 1.6 not found]))
+    #PKG_CHECK_MODULES([cairo2], [cairo2 >= 2.0], , AC_MSG_ERROR([cairo2 >= 2.0 not found]))
+    [if [ x"${sh_text}" = xtrue ]; then]
+        PKG_CHECK_MODULES([pangocairo], [pangocairo >= 1.0], , AC_MSG_ERROR([pangocairo >= 1.0 not found: use --disable-sh_text to configure without pangocairo.]))
+    [fi]
 [fi]
 
-AC_CONFIG_FILES([Makefile
-                 libmbfly.pc
-                 libmbfly-uninstalled.pc
-                 examples/Makefile
-                 examples/calculator/Makefile
-                 examples/svg2code_ex/Makefile
-                 examples/tank/Makefile
-                 examples/drag/Makefile
-		 examples/dynamic/Makefile
-                 examples/menu/Makefile
-                 src/Makefile
-                 include/Makefile
-                 inkscape/Makefile
-		 nodejs/Makefile
-                 tools/Makefile])
+AC_CONFIG_FILES([
+        Makefile
+        libmbfly.pc
+        libmbfly-uninstalled.pc
+        examples/Makefile
+        examples/calculator/Makefile
+        examples/svg2code_ex/Makefile
+        examples/tank/Makefile
+        examples/drag/Makefile
+        examples/dynamic/Makefile
+        examples/menu/Makefile
+        src/Makefile
+        include/Makefile
+        inkscape/Makefile
+        nodejs/Makefile
+        tools/Makefile
+        ])
 
 AH_TEMPLATE([SH_TEXT],[Enable sh_text object])
 AH_TEMPLATE([SH_STEXT],[Enable sh_stext object])
 AH_TEMPLATE([SKIA_GRAPH_ENGINE], [Enable Skia Graphic Engine])
 AH_TEMPLATE([OPENVG_GRAPH_ENGINE], [Enable OpenVG Graphic Engine])
 AH_TEMPLATE([CAIRO_GRAPH_ENGINE], [Enable Cairo Graphic Engine])
-AH_TEMPLATE([X_SUPP], [Enable X backend])
+AH_TEMPLATE([DFB_GRAPH_ENGINE], [Enable DirectFB Graphic Engine])
+AH_TEMPLATE([CAIRO_IMG_LOADER], [Enable Cairo Image Loader])
+AH_TEMPLATE([IMLIB2_IMG_LOADER], [Enable Imlib2 Image Loader])
+AH_TEMPLATE([X_BACKEND], [Enable X backend])
+AH_TEMPLATE([DFB_BACKEND], [Enable DirectFB backend])
+AH_TEMPLATE([XSHM], [Enable XSHM])
 
 AC_OUTPUT
--- a/dox/define_backend.h	Mon Jul 19 15:44:49 2010 +0800
+++ b/dox/define_backend.h	Wed Dec 01 12:25:56 2010 +0800
@@ -6,24 +6,84 @@
  * that will show everything drawing on it.  It also translate and
  * relay input events, mouse or keyboard, to MadButterfly.
  * The tasks that a backend should do are listed following,
- *  - to prepare a backend surface,
- *  - to prepare a front surface,
- *  - to translate and relay input events to MadButterfly,
- *  - to handle a timer, and relay timeout events to MadButterfly.
+ *  - tranlsate and relay mouse events to MadButterfly.
+ *  - to provide
+ *    - *_MB_new()/*_MB_free function to create and free a runtime.
+ *      - prepare a backend surface,
+ *      - prepare a front surface,
+ *    - *_MB_kbevents() to return a subject, for keyboard
+ *      events, associated with a runtime.
+ *      - translate and relay keyboard events to MadButterfly,
+ *    - *_MB_tman() to return a timer manager associated with
+ *      a runtime.
+ *      - to handle a timer, and relay timeout events to MadButterfly.
+ *    - *_MB_ob_factory() to return an observer factory.
+ *    - *_MB_img_ldr() to return an image loader.
  * 
- * The output device surface for X Window is a surface return by
- * cairo_xlib_surface_create().  MadButterfly will copy everything
- * from front surface to backend surface to show graphy to user.
- * The copying is to avoid user find slowly redrawing.  The latency
- * between X client and server can be large.  For this situation,
- * we need a font surface as a buffer drawing, and copy image from
- * front surface to backend surface after completion of a series
- * of drawing.  A front surface can be an image surface for this
- * situation.
+ * \section backend_mb_new_n_free *_MB_new()/*_MB_free()
+ *
+ * MadButterfly supposes that application programmers may create more
+ * than one instance of MadButterfly to draw mutliple windows for an
+ * application.  So, we need you, backend developer, to provide a
+ * *_MB_new()/*_MB_free() to create/free an instance respective.
+ *
+ * *_MB_new() should return an *_MB_runtime_t object to keep states of
+ * an instance.  The definition of *_MB_runtime_t is up to backend
+ * developers.
+ *
+ * For each *_MB_runtime_t, backend should create a redraw manager,
+ * a.k.a rdman, by calling malloc() and redraw_man_init().  For each
+ * rdman, you should give it one or two surfaces.  Rdman draws shapes
+ * on first one, called 'cr' (should be changed), as an off-screen
+ * buffer and copy it to 2nd one, called 'backend' surface, as an
+ * on-screen buffer.  For X, the 'backend' should be a window surface.
+ * The reason of two surfaces are to prevent user from seeing
+ * intermediate result of drawing procedure.  You can also pass a NULL
+ * pointer for 2nd surface if you dont care intermediate result, HW
+ * accelerator is fast enough, or with HW dobule buffering.
+ *
+ * \section backend_kbevents Keyboard Events
+ *
+ * *_MB_kbevents() returns a subject (\see
+ * http://en.wikipedia.org/wiki/Observer_pattern).  Applications want
+ * to receive keyboard events would register an observer on this
+ * subject.  A backend should translate keyboard events for
+ * MadButterfly, and notify observers of this subject.
+ * Subject-observer had implemented by MadBuffery.
  *
- * The input events of X Window should be translated to raw events of
- * MadButterfly and sent them to rdman through notify_coord_or_shape()
- * function.
+ * \section backend_timer Timer
+ *
+ * *_MB_tman() should return a timer manager, with type of mb_tman_t.
+ * When an application want to get notified at some time later, it
+ * would register a callback with the mb_tman_t.  A backend should
+ * call mb_tman_handle_timeout() at proper time.  You can call
+ * mb_tman_next_timeout() to get the time to call
+ * mb_tman_handle_timeout() for next timeout.  For nodejs or other
+ * binding that has their-owned timer, you can skip *_MB_tman().  But,
+ * C code need this one.
+ *
+ * \section backend_obfactory Observer Factory
+ *
+ * *_MB_ob_factory() returns an observer factory, ob_factory_t.  It is
+ * reponsible for creation of observers.  Applications call observer
+ * to allocate and free observers and subjects.  ob_factory_t is
+ * defined in mb_observer.h, there are 5 function pointers in it.  You
+ * can use functions of default implementation instead of new ones.
+ *
+ * \section backend_img_ldr Image Loader
+ *
+ * *_MB_img_ldr() returns an image loader, mb_img_ldr_t.  A backend
+ * developer can use the default implementation.  He can also
+ * implement a new one, but it should implement the interface defined
+ * by mb_img_ldr_t and mb_img_data_t.
+ *
+ * \section backend_mouse_events Mouse Events
+ *
+ * A backend should also translate and relay mouse events to
+ * MadButterfly.  The mouse events should be dispatched by notifing
+ * observers of the subject of a coord or shape of an rdman.  The
+ * backend developers can check handle_single_x_event() in X_supp.c
+ * for how to dispatch mouse events.
  *
  * \see X_supp.c
  */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dox/dictionary.h	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,23 @@
+/*! \page dictionary Dictionary
+ *
+ * This page is a list of acronyms and their meaning.
+ *
+ * - \b cb stands for CallBack.
+ * - \b coord stands for COORDinate.
+ * - \b elm stands for ELMent.
+ * - \b fact stands for FACTory.
+ * - \b geo stands for GEOmetry.
+ * - \b ldr stands for LoaDeR.
+ * - \b man stands for MANager.
+ * - \b mb stands for MadButterfly.
+ * - \b mbe stands for MadButterfly graphic Engine.
+ * - \b obj stands for OBJect.
+ * - \b prop stands for PROPerty.
+ * - \b rdman stands for ReDraw MANager.
+ * - \b rect stands for RECTangle.
+ * - \b rm stands for ReMove.
+ * - \b rt stands for RunTime.
+ * - \b sh stands for SHapes.
+ * - \b tman stands for Timer MANager.
+ * - \b val stands for VALue.
+ */
--- a/examples/Makefile.am	Mon Jul 19 15:44:49 2010 +0800
+++ b/examples/Makefile.am	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,6 @@
-if X_SUPP
-SUBDIRS = calculator svg2code_ex tank dynamic drag menu
+if X_BACKEND
+SUBDIRS = calculator tank drag
+if SH_TEXT
+SUBDIRS += svg2code_ex dynamic menu
 endif
+endif
--- a/examples/calculator/Makefile.am	Mon Jul 19 15:44:49 2010 +0800
+++ b/examples/calculator/Makefile.am	Wed Dec 01 12:25:56 2010 +0800
@@ -5,16 +5,12 @@
 
 calc_SOURCES = main.c
 nodist_calc_SOURCES = calculator_scr.c calculator_scr.h
-calc_CPPFLAGS = @pangocairo_CFLAGS@ $(INCLUDES)
-calc_LDFLAGS = @pangocairo_LIBS@ 
+calc_CPPFLAGS = $(APPCFLAGS) $(INCLUDES)
+calc_LDFLAGS = $(APPLDFLAGS)
 calc_LDADD = $(top_builddir)/src/libmbfly.la
 BUILT_SOURCES = calculator_scr.c calculator_scr.h calculator_scr.mb
 CLEANFILES = calculator_scr.c calculator_scr.h calculator_scr.mb
 
-if OPENVG_GRAPH_ENGINE
-calc_LDFLAGS += -lOpenVG -lEGL
-endif
-
 calculator_scr.mb: $(srcdir)/calculator_scr.svg
 	$(top_srcdir)/tools/svg2code.py -s $? $@
 
--- a/examples/calculator/main.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/examples/calculator/main.c	Wed Dec 01 12:25:56 2010 +0800
@@ -5,7 +5,7 @@
 
 typedef struct _calc_data calc_data_t;
 struct _calc_data {
-    X_MB_runtime_t *rt;
+    mb_rt_t *rt;
     calculator_scr_t *code;
 };
 
@@ -64,7 +64,7 @@
     char buf[20];
     redraw_man_t *rdman;
 
-    rdman = X_MB_rdman(calc_data->rt);
+    rdman = mb_runtime_rdman(calc_data->rt);
 
     sprintf(buf, "%d%s", num, suffix);
     sh_stext_set_text(calc_data->code->screen_text_u, buf);
@@ -142,7 +142,7 @@
 	    break;
 	}
     }
-    rdman = X_MB_rdman(calc_data->rt);
+    rdman = mb_runtime_rdman(calc_data->rt);
     rdman_redraw_changed(rdman);
 }
 
@@ -174,24 +174,24 @@
 }
 
 int main(int argc, char * const argv[]) {
-    X_MB_runtime_t *rt;
+    mb_rt_t *rt;
     redraw_man_t *rdman;
     calculator_scr_t *calculator_scr;
     calc_data_t calc_data;
 
-    rt = X_MB_new(":0.0", 300, 400);
+    rt = mb_runtime_new(":0.0", 300, 400);
 
-    rdman = X_MB_rdman(rt);
+    rdman = mb_runtime_rdman(rt);
     calculator_scr = calculator_scr_new(rdman, rdman->root_coord);
 
     calc_data.rt = rt;
     calc_data.code = calculator_scr;
     setup_observers(&calc_data);
 
-    X_MB_handle_connection(rt);
+    mb_runtime_event_loop(rt);
 
     calculator_scr_free(calculator_scr);
-    X_MB_free(rt);
+    mb_runtime_free(rt);
 
     return 0;
 }
--- a/examples/drag/Makefile.am	Mon Jul 19 15:44:49 2010 +0800
+++ b/examples/drag/Makefile.am	Wed Dec 01 12:25:56 2010 +0800
@@ -5,8 +5,8 @@
 
 ex1_SOURCES = main.c
 nodist_ex1_SOURCES = menu.c menu.h
-ex1_CPPFLAGS = @pangocairo_CFLAGS@ -I$(top_srcdir)
-ex1_LDFLAGS = @pangocairo_LIBS@ 
+ex1_CPPFLAGS = $(APPCFLAGS) -I$(top_srcdir)
+ex1_LDFLAGS = $(APPLDFLAGS)
 ex1_LDADD = $(top_builddir)/src/libmbfly.la
 BUILT_SOURCES = menu.c menu.h menu.mb
 CLEANFILES = menu.c menu.h menu.mb
--- a/examples/drag/main.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/examples/drag/main.c	Wed Dec 01 12:25:56 2010 +0800
@@ -10,7 +10,7 @@
 
 typedef struct _engine engine_t;
 struct _engine {
-    X_MB_runtime_t *rt;
+    mb_rt_t *rt;
     redraw_man_t *rdman;
     menu_t *menu;
     int state;
@@ -21,12 +21,12 @@
 engine_t *engine_init()
 {
 
-    X_MB_runtime_t *rt;
-    rt = X_MB_new(":0.0", 800, 600);
+    mb_rt_t *rt;
+    rt = mb_runtime_new(":0.0", 800, 600);
     engine_t *en = (engine_t *) malloc(sizeof(engine_t));
 
     en->rt = rt;
-    en->rdman =  X_MB_rdman(rt);
+    en->rdman =  mb_runtime_rdman(rt);
     en->state = 0;
     return en;
 }
@@ -36,13 +36,13 @@
      * Start handle connections, includes one to X server.
      * User start to interact with the application.
      */
-    X_MB_handle_connection(en->rt);
+    mb_runtime_event_loop(en->rt);
 
     /*
      * Clean
      */
     menu_free(en->menu);
-    X_MB_free(en->rt);
+    mb_runtime_free(en->rt);
     free(en);
 }
 #define COORD_SHOW(group) coord_show(group);rdman_coord_changed(X_MB_rdman(ex_rt->rt), group)
--- a/examples/dynamic/main.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/examples/dynamic/main.c	Wed Dec 01 12:25:56 2010 +0800
@@ -109,7 +109,7 @@
     get_now(&timer);
     MB_TIMEVAL_SET(&interval, 1 ,0);
     MB_TIMEVAL_ADD(&timer, &interval);
-    mb_tman_timeout( mbaf_get_timer(app), &timer, switch_scene, app);
+    mb_timer_man_timeout( mbaf_get_timer(app), &timer, switch_scene, app);
 
     en->currentscene = (en->currentscene + 1) % 2;
     printf("switch to scene %d\n", en->currentscene + 1);
@@ -147,7 +147,7 @@
     MyApp_InitContent();
     get_now(&tmo);
     MB_TIMEVAL_SET(&interval, 1 ,0);
-    mb_tman_timeout( mbaf_get_timer(app), &tmo, switch_scene, app);
+    mb_timer_man_timeout( mbaf_get_timer(app), &tmo, switch_scene, app);
     
 
     mbaf_loop(app);
--- a/examples/svg2code_ex/main.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/examples/svg2code_ex/main.c	Wed Dec 01 12:25:56 2010 +0800
@@ -10,31 +10,41 @@
 
 typedef struct _ex_rt ex_rt_t;
 struct _ex_rt {
-    X_MB_runtime_t *rt;
+    mb_rt_t *rt;
     svg2code_ex_t *code;
 };
 
-#define COORD_SHOW(group) coord_show(group);rdman_coord_changed(X_MB_rdman(ex_rt->rt), group)
-#define COORD_HIDE(group) coord_hide(group);rdman_coord_changed(X_MB_rdman(ex_rt->rt), group)
+#define COORD_SHOW(group) do {						\
+	coord_show(group);						\
+	rdman_coord_changed(mb_runtime_rdman(ex_rt->rt), group);	\
+    } while(0)
+#define COORD_HIDE(group) do {						\
+	coord_hide(group);						\
+	rdman_coord_changed(mb_runtime_rdman(ex_rt->rt), group);	\
+    } while(0)
 
 static void file_button_handler(event_t *evt, void *arg) {
     ex_rt_t *ex_rt = (ex_rt_t *)arg;
+    redraw_man_t *rdman;
 
     COORD_SHOW(ex_rt->code->file_menu);
+    rdman = mb_runtime_rdman(ex_rt->rt);
     /* Update changed part to UI. */
-    rdman_redraw_changed(X_MB_rdman(ex_rt->rt));
+    rdman_redraw_changed(rdman);
 }
 
 static void file_menu_handler(event_t *evt, void *arg) {
     ex_rt_t *ex_rt = (ex_rt_t *)arg;
+    redraw_man_t *rdman;
 
     COORD_HIDE(ex_rt->code->file_menu);
+    rdman = mb_runtime_rdman(ex_rt->rt);
     /* Update changed part to UI. */
-    rdman_redraw_changed(X_MB_rdman(ex_rt->rt));
+    rdman_redraw_changed(rdman);
 }
 
 int main(int argc, char * const argv[]) {
-    X_MB_runtime_t *rt;
+    mb_rt_t *rt;
     redraw_man_t *rdman;
     svg2code_ex_t *svg2code;
     subject_t *subject;
@@ -43,12 +53,12 @@
     /*
      * Initialize a runtime with XLib as backend.
      */
-    rt = X_MB_new(":0.0", 800, 600);
+    rt = mb_runtime_new(":0.0", 800, 600);
 
     /*
      * Instantiate objects from a SVG file.
      */
-    rdman = X_MB_rdman(rt);
+    rdman = mb_runtime_rdman(rt);
     svg2code = svg2code_ex_new(rdman, rdman->root_coord);
 
     /*
@@ -65,13 +75,13 @@
      * Start handle connections, includes one to X server.
      * User start to interact with the application.
      */
-    X_MB_handle_connection(rt);
+    mb_runtime_event_loop(rt);
 
     /*
      * Clean
      */
     svg2code_ex_free(svg2code);
-    X_MB_free(rt);
+    mb_runtime_free(rt);
 
     return 0;
 }
--- a/examples/tank/Makefile.am	Mon Jul 19 15:44:49 2010 +0800
+++ b/examples/tank/Makefile.am	Wed Dec 01 12:25:56 2010 +0800
@@ -7,8 +7,8 @@
 tank_SOURCES = tank_main.c
 nodist_tank_SOURCES = svgs.h \
 	$(svg_sources) $(svg_sources:.c=.h) $(svg_sources:.c=.mb)
-tank_CPPFLAGS = @pangocairo_CFLAGS@
-tank_LDFLAGS = @pangocairo_LIBS@ 
+tank_CPPFLAGS = $(APPCFLAGS)
+tank_LDFLAGS = $(APPLDFLAGS)
 tank_LDADD = $(top_builddir)/src/libmbfly.la
 BUILT_SOURCES = svgs.h \
 	$(svg_sources) $(svg_sources:.c=.h) $(svg_sources:.c=.mb)
--- a/examples/tank/tank_main.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/examples/tank/tank_main.c	Wed Dec 01 12:25:56 2010 +0800
@@ -58,9 +58,9 @@
     int direction;
     mb_progm_t *progm;
     mb_timeval_t start_time;
-    observer_t *ob_redraw;
-    mb_timer_t *hit_tmr;
-    mb_tman_t *tman;
+    observer_t *observer_redraw;
+    int hit_tmr;
+    mb_timer_man_t *timer_man;
 };
 typedef struct _tank_bullet tank_bullet_t;
 /*! \brief The direction a bullet is going.
@@ -106,7 +106,7 @@
     tank_t *tanks[12];
     int n_tanks;
     void *map[12][16];
-    X_MB_runtime_t *mb_rt;
+    mb_rt_t *mb_rt;
     observer_t *kb_observer;
 };
 
@@ -124,7 +124,7 @@
     if(tank == NULL)
 	return NULL;
 
-    rdman = X_MB_rdman(tank_rt->mb_rt);
+    rdman = mb_runtime_rdman(tank_rt->mb_rt);
 
     tank->coord_pos = coord_pos;
     tank->coord_rot = coord_rot;
@@ -145,11 +145,8 @@
     return tank;
 }
 
-static void tank_free(tank_t *tank, X_MB_runtime_t *xmb_rt) {
-    mb_tman_t *tman;
-
+static void tank_free(tank_t *tank, mb_rt_t *mb_rt) {
     if(tank->progm) {
-	tman = X_MB_tman(xmb_rt);
 	mb_progm_abort(tank->progm);
     }
     free(tank);
@@ -170,10 +167,10 @@
 
 static void tank_move(tank_t *tank, int direction,
 		      tank_rt_t *tank_rt) {
-    X_MB_runtime_t *xmb_rt = tank_rt->mb_rt;
+    mb_rt_t *mb_rt = tank_rt->mb_rt;
     redraw_man_t *rdman;
-    mb_tman_t *tman;
-    ob_factory_t *factory;
+    mb_timer_man_t *timer_man;
+    observer_factory_t *factory;
     /* for the program */
     mb_progm_t *progm;
     subject_t *comp_sub;
@@ -239,9 +236,9 @@
 	}
     }
 
-    rdman = X_MB_rdman(xmb_rt);
-    tman = X_MB_tman(xmb_rt);
-    factory = X_MB_ob_factory(xmb_rt);
+    rdman = mb_runtime_rdman(mb_rt);
+    timer_man = mb_runtime_timer_man(mb_rt);
+    factory = mb_runtime_observer_factory(mb_rt);
 
     progm = mb_progm_new(1, rdman);
     tank->progm = progm;
@@ -269,7 +266,7 @@
     subject_add_observer(comp_sub, clean_tank_progm_handler, tank);
 
     get_now(&now);
-    mb_progm_start(progm, tman, &now);
+    mb_progm_start(progm, timer_man, &now);
 }
 
 /* @} */
@@ -310,6 +307,7 @@
 
     bullet = O_ALLOC(tank_bullet_t);
     bullet->rdman = rdman;
+    bullet->hit_tmr = -1;
 
     make_bullet_elf_coords(rdman, &bullet->coord_pos,
 			   &bullet->coord_rot,
@@ -338,6 +336,8 @@
 }
 
 static void tank_bullet_free(tank_bullet_t *bullet) {
+    if(bullet->hit_tmr != -1)
+	mb_timer_man_remove(bullet->timer_man, bullet->hit_tmr);
     bullet_free(bullet->bullet_obj);
     rdman_coord_subtree_free(bullet->rdman, bullet->coord_pos);
 }
@@ -350,8 +350,10 @@
     bullet = tank->bullet;
     rdman = bullet->rdman;
 
-    if(bullet->hit_tmr != NULL)
-	mb_tman_remove(bullet->tman, bullet->hit_tmr);
+    if(bullet->hit_tmr != -1) {
+	mb_timer_man_remove(bullet->timer_man, bullet->hit_tmr);
+	bullet->hit_tmr = -1;
+    }
 
     coord_hide(bullet->coord_pos);
     rdman_coord_changed(rdman, bullet->coord_pos);
@@ -364,7 +366,7 @@
 
 static void bullet_bang(tank_bullet_t *bullet, int map_x, int map_y) {
     redraw_man_t *rdman;
-    mb_tman_t *tman;
+    mb_timer_man_t *timer_man;
     mb_progm_t *progm;
     mb_word_t *word;
     mb_timeval_t start, playing;
@@ -373,7 +375,7 @@
     co_aix *matrix;
 
     rdman = bullet->rdman;
-    tman = bullet->tman;
+    timer_man = bullet->timer_man;
 
     bang = bang_new(rdman, rdman->root_coord);
     matrix = bang->root_coord->matrix;
@@ -401,11 +403,12 @@
     mb_progm_free_completed(progm);
 
     get_now(&now);
-    mb_progm_start(progm, tman, &now);
+    mb_progm_start(progm, timer_man, &now);
 }
 
 /*! \brief To check if a bullet hits walls or tanks. */
-static void bullet_hit_chk(const mb_timeval_t *tmo,
+static void bullet_hit_chk(int hdl,
+			   const mb_timeval_t *tmo,
 			   const mb_timeval_t *now,
 			   void *arg) {
     tank_t *tank = (tank_t *)arg;
@@ -422,9 +425,9 @@
     static int move_adj[][2] = {{0, -1}, {1, 0}, {0, 1}, {-1, 0}};
 
     bullet = tank->bullet;
-    bullet->hit_tmr = NULL;	/* clear hit timer that bullet_go_out_map()
-				 * can not remove it & currupt memory.
-				 */
+    bullet->hit_tmr = -1;  /* clear hit timer that bullet_go_out_map()
+			    * can not remove it & currupt memory.
+			    */
 
     MB_TIMEVAL_CP(&diff, now);
     MB_TIMEVAL_DIFF(&diff, &bullet->start_time);
@@ -480,13 +483,13 @@
 
     MB_TIMEVAL_SET(&next, 0, 100000);
     MB_TIMEVAL_ADD(&next, now);
-    bullet->hit_tmr = mb_tman_timeout(bullet->tman, &next,
-				      bullet_hit_chk, arg);
+    bullet->hit_tmr = mb_timer_man_timeout(bullet->timer_man, &next,
+					   bullet_hit_chk, arg);
 }
 
 /*! \brief To fire a bullet for a tank. */
 static void tank_fire_bullet(tank_rt_t *tank_rt, tank_t *tank) {
-    X_MB_runtime_t *xmb_rt;
+    mb_rt_t *mb_rt;
     redraw_man_t *rdman;
     int map_x, map_y;
     int shift_x, shift_y;
@@ -498,16 +501,16 @@
     mb_action_t *act;
     mb_timeval_t start, playing;
     mb_timeval_t now, next;
-    mb_tman_t *tman;
+    mb_timer_man_t *timer_man;
     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);
+    mb_rt = tank_rt->mb_rt;
+    rdman = mb_runtime_rdman(mb_rt);
+    timer_man = mb_runtime_timer_man(mb_rt);
 
     dir = tank->direction;
     map_x = tank->map_x + map_xy_adj[dir][0];
@@ -540,7 +543,7 @@
 
     tank->bullet = tank_bullet_new(rdman, map_x, map_y, dir);
     bullet = tank->bullet;
-    bullet->tman = tman;
+    bullet->timer_man = timer_man;
 
     progm = mb_progm_new(2, rdman);
     MB_TIMEVAL_SET(&start, 0, 0);
@@ -555,11 +558,12 @@
 
     get_now(&now);
     MB_TIMEVAL_CP(&bullet->start_time, &now);
-    mb_progm_start(progm, tman, &now);
+    mb_progm_start(progm, timer_man, &now);
 
     MB_TIMEVAL_SET(&next, 0, 100000);
     MB_TIMEVAL_ADD(&next, &now);
-    bullet->hit_tmr = mb_tman_timeout(tman, &next, bullet_hit_chk, tank);
+    bullet->hit_tmr = mb_timer_man_timeout(timer_man, &next,
+					   bullet_hit_chk, tank);
 }
 
 /* @} */
@@ -615,14 +619,14 @@
 }
 
 static void init_keyboard(tank_rt_t *tank_rt) {
-    X_MB_runtime_t *mb_rt;
+    mb_rt_t *mb_rt;
     subject_t *kbevents;
     redraw_man_t *rdman;
 
     mb_rt = tank_rt->mb_rt;
-    kbevents = X_MB_kbevents(mb_rt);
+    kbevents = mb_runtime_kbevents(mb_rt);
 
-    rdman = X_MB_rdman(mb_rt);
+    rdman = mb_runtime_rdman(mb_rt);
 
     tank_rt->kb_observer =
 	subject_add_observer(kbevents, keyboard_handler, tank_rt);
@@ -654,7 +658,7 @@
 }
 
 void
-initial_tank(tank_rt_t *tank_rt, X_MB_runtime_t *mb_rt) {
+initial_tank(tank_rt_t *tank_rt, mb_rt_t *mb_rt) {
     redraw_man_t *rdman;
     /* for map areas */
     mud_t *mud;
@@ -668,7 +672,7 @@
     tank_en_t *tank_en_o;
     int i, j;
 
-    rdman = X_MB_rdman(mb_rt);
+    rdman = mb_runtime_rdman(mb_rt);
 
     tank_rt->mb_rt = mb_rt;
     for(i = 0; i < 12; i++) {
@@ -727,16 +731,16 @@
 
 int
 main(int argc, char *const argv[]) {
-    X_MB_runtime_t *rt;
+    mb_rt_t *rt;
     tank_rt_t tank_rt;
 
-    rt = X_MB_new(":0.0", 800, 600);
+    rt = mb_runtime_new(":0.0", 800, 600);
 
     initial_tank(&tank_rt, rt);
     
-    X_MB_handle_connection(rt);
+    mb_runtime_event_loop(rt);
 
-    X_MB_free(rt);
+    mb_runtime_free(rt);
 }
 
 /* @} */
Binary file img/highlevel.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/img/highlevel.svg	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,912 @@
+<?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="744.09448819"
+   height="1052.3622047"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.47 r22583"
+   sodipodi:docname="highlevel.svg">
+  <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" />
+    <inkscape:perspective
+       id="perspective2830"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective2852"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective2877"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective2905"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective2938"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective2987"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3015"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3043"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3071"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3099"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3207"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3256"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3287"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3339"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1"
+     inkscape:cx="195.30247"
+     inkscape:cy="627.63476"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1440"
+     inkscape:window-height="900"
+     inkscape:window-x="-1"
+     inkscape:window-y="-1"
+     inkscape:window-maximized="0" />
+  <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" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <g
+       id="g3128"
+       transform="matrix(0.49,0,0,0.49,150.2233,284.31953)"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="213.8497"
+       inkscape:bbox-y="653.14279"
+       inkscape:bbox-width="134.79007"
+       inkscape:bbox-height="31.352081"
+       inkscape:export-filename="/home/thinker/progm/MadButterfly/img/highlevel.png"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90">
+      <rect
+         inkscape:bbox-height="31.352081"
+         inkscape:bbox-width="134.79007"
+         inkscape:bbox-y="653.14279"
+         inkscape:bbox-x="213.8497"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         y="173.17871"
+         x="132.52283"
+         height="58.637783"
+         width="269.73572"
+         id="rect2816"
+         style="fill:#003380;fill-opacity:1;stroke:#00112b;stroke-width:5.34605646" />
+      <text
+         inkscape:bbox-height="10.432598"
+         inkscape:bbox-width="120.12752"
+         inkscape:bbox-y="663.60253"
+         inkscape:bbox-x="221.18097"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         sodipodi:linespacing="125%"
+         id="text2818"
+         y="208.56694"
+         x="142.96393"
+         style="font-size:22px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold"
+         xml:space="preserve"><tspan
+           inkscape:bbox-height="10.432598"
+           inkscape:bbox-width="120.12752"
+           inkscape:bbox-y="663.60253"
+           inkscape:bbox-x="221.18097"
+           y="208.56694"
+           x="142.96393"
+           inkscape:transform-center-y="0"
+           inkscape:transform-center-x="0"
+           id="tspan2820"
+           sodipodi:role="line">rdman/paint/shapes</tspan></text>
+    </g>
+    <g
+       id="g3133"
+       transform="matrix(0,-0.49,0.49,0,449.04412,564.75137)"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="569.75296"
+       inkscape:bbox-y="549.7048"
+       inkscape:bbox-width="31.352081"
+       inkscape:bbox-height="134.79007"
+       inkscape:export-filename="/home/thinker/progm/MadButterfly/img/highlevel.png"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90">
+      <rect
+         inkscape:bbox-height="134.79007"
+         inkscape:bbox-width="31.352081"
+         inkscape:bbox-y="549.70479"
+         inkscape:bbox-x="569.75296"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         y="249.01759"
+         x="129.39545"
+         height="58.637783"
+         width="269.73572"
+         id="rect2816-7"
+         style="fill:#003380;fill-opacity:1;stroke:#00112b;stroke-width:5.34605646" />
+      <text
+         inkscape:bbox-height="53.352578"
+         inkscape:bbox-width="8.3429199"
+         inkscape:bbox-y="590.42354"
+         inkscape:bbox-x="581.25754"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         sodipodi:linespacing="125%"
+         id="text2818-9"
+         y="286.53815"
+         x="208.87659"
+         style="font-size:22px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold"
+         xml:space="preserve"><tspan
+           inkscape:bbox-height="53.352578"
+           inkscape:bbox-width="8.3429199"
+           inkscape:bbox-y="590.42354"
+           inkscape:bbox-x="581.25754"
+           y="286.53815"
+           x="208.87659"
+           inkscape:transform-center-y="0"
+           inkscape:transform-center-x="0"
+           id="tspan2820-3"
+           sodipodi:role="line">observer</tspan></text>
+    </g>
+    <g
+       id="g3181"
+       transform="matrix(0.49,0,0,0.49,57.15012,178.96602)"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="352.12911"
+       inkscape:bbox-y="595.67724"
+       inkscape:bbox-width="134.79007"
+       inkscape:bbox-height="31.352081"
+       inkscape:export-filename="/home/thinker/progm/MadButterfly/img/highlevel.png"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90">
+      <rect
+         inkscape:bbox-height="31.352081"
+         inkscape:bbox-width="134.79007"
+         inkscape:bbox-y="595.67724"
+         inkscape:bbox-x="352.12911"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         y="505.46249"
+         x="604.67096"
+         height="58.637783"
+         width="269.73572"
+         id="rect2816-7-8"
+         style="fill:#003380;fill-opacity:1;stroke:#00112b;stroke-width:5.34605646" />
+      <text
+         inkscape:bbox-height="10.516816"
+         inkscape:bbox-width="90.161436"
+         inkscape:bbox-y="606.09488"
+         inkscape:bbox-x="374.44342"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         sodipodi:linespacing="125%"
+         id="text2818-9-0"
+         y="540.76477"
+         x="647.24731"
+         style="font-size:22px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold"
+         xml:space="preserve"><tspan
+           inkscape:bbox-height="10.516816"
+           inkscape:bbox-width="90.161436"
+           inkscape:bbox-y="606.09488"
+           inkscape:bbox-x="374.44342"
+           y="540.76477"
+           x="647.24731"
+           inkscape:transform-center-y="0"
+           inkscape:transform-center-x="0"
+           id="tspan2820-3-2"
+           sodipodi:role="line">timer manager</tspan></text>
+    </g>
+    <g
+       id="g3143"
+       transform="matrix(0.49,0,0,0.49,-1.3250335,168.4974)"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="263.04767"
+       inkscape:bbox-y="711.11625"
+       inkscape:bbox-width="134.79007"
+       inkscape:bbox-height="31.352081"
+       inkscape:export-filename="/home/thinker/progm/MadButterfly/img/highlevel.png"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90">
+      <rect
+         inkscape:bbox-height="31.352081"
+         inkscape:bbox-width="134.79007"
+         inkscape:bbox-y="711.11626"
+         inkscape:bbox-x="263.04767"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         y="291.23721"
+         x="542.20917"
+         height="58.637783"
+         width="269.73572"
+         id="rect2816-7-8-4"
+         style="fill:#003380;fill-opacity:1;stroke:#00112b;stroke-width:5.34605646" />
+      <text
+         inkscape:bbox-height="8.3429199"
+         inkscape:bbox-width="49.194277"
+         inkscape:bbox-y="722.62084"
+         inkscape:bbox-x="305.84557"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         sodipodi:linespacing="125%"
+         id="text2818-9-0-8"
+         y="328.75775"
+         x="625.03113"
+         style="font-size:22px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold"
+         xml:space="preserve"><tspan
+           inkscape:bbox-height="8.3429199"
+           inkscape:bbox-width="49.194277"
+           inkscape:bbox-y="722.62084"
+           inkscape:bbox-x="305.84557"
+           y="328.75775"
+           x="625.03113"
+           inkscape:transform-center-y="0"
+           inkscape:transform-center-x="0"
+           id="tspan2820-3-2-3"
+           sodipodi:role="line">backend</tspan></text>
+    </g>
+    <g
+       id="g3153"
+       transform="matrix(0.49,0,0,0.49,108.0819,221.28227)"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="213.8497"
+       inkscape:bbox-y="557.95824"
+       inkscape:bbox-width="134.79007"
+       inkscape:bbox-height="31.352081"
+       inkscape:export-filename="/home/thinker/progm/MadButterfly/img/highlevel.png"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90">
+      <rect
+         inkscape:bbox-height="31.352081"
+         inkscape:bbox-width="134.79007"
+         inkscape:bbox-y="557.95824"
+         inkscape:bbox-x="213.8497"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         y="496.08035"
+         x="218.52568"
+         height="58.637783"
+         width="269.73572"
+         id="rect2816-7-8-9"
+         style="fill:#003380;fill-opacity:1;stroke:#00112b;stroke-width:5.34605646" />
+      <text
+         inkscape:bbox-height="10.516816"
+         inkscape:bbox-width="90.019316"
+         inkscape:bbox-y="568.37588"
+         inkscape:bbox-x="236.23509"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         sodipodi:linespacing="125%"
+         id="text2818-9-0-0"
+         y="531.38263"
+         x="260.54883"
+         style="font-size:22px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold"
+         xml:space="preserve"><tspan
+           inkscape:bbox-height="10.516816"
+           inkscape:bbox-width="90.019316"
+           inkscape:bbox-y="568.37588"
+           inkscape:bbox-x="236.23509"
+           y="531.38263"
+           x="260.54883"
+           inkscape:transform-center-y="0"
+           inkscape:transform-center-x="0"
+           id="tspan2820-3-2-5"
+           sodipodi:role="line">graphic engine</tspan></text>
+    </g>
+    <g
+       id="g3163"
+       transform="matrix(0.49,0,0,0.49,145.98816,39.516328)"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="352.12911"
+       inkscape:bbox-y="653.14278"
+       inkscape:bbox-width="134.79007"
+       inkscape:bbox-height="31.352081"
+       inkscape:export-filename="/home/thinker/progm/MadButterfly/img/highlevel.png"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90">
+      <rect
+         inkscape:bbox-height="31.352081"
+         inkscape:bbox-width="134.79007"
+         inkscape:bbox-y="653.14278"
+         inkscape:bbox-x="352.12911"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         y="672.7771"
+         x="423.36884"
+         height="58.637783"
+         width="269.73572"
+         id="rect2816-7-8-9-2"
+         style="fill:#003380;fill-opacity:1;stroke:#00112b;stroke-width:5.34605646" />
+      <text
+         inkscape:bbox-height="8.3429199"
+         inkscape:bbox-width="48.688965"
+         inkscape:bbox-y="664.64735"
+         inkscape:bbox-x="395.17966"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         sodipodi:linespacing="125%"
+         id="text2818-9-0-0-2"
+         y="710.29767"
+         x="507.60876"
+         style="font-size:22px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold"
+         xml:space="preserve"><tspan
+           inkscape:bbox-height="8.3429199"
+           inkscape:bbox-width="48.688965"
+           inkscape:bbox-y="664.64735"
+           inkscape:bbox-x="395.17966"
+           y="710.29767"
+           x="507.60876"
+           inkscape:transform-center-y="0"
+           inkscape:transform-center-x="0"
+           id="tspan2820-3-2-5-7"
+           sodipodi:role="line">animate</tspan></text>
+    </g>
+    <g
+       id="g3168"
+       transform="matrix(0,-0.49,0.49,0,111.91294,803.04184)"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="494.66467"
+       inkscape:bbox-y="549.70479"
+       inkscape:bbox-width="31.352081"
+       inkscape:bbox-height="134.79007"
+       inkscape:export-filename="/home/thinker/progm/MadButterfly/img/highlevel.png"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90">
+      <rect
+         inkscape:bbox-height="134.79007"
+         inkscape:bbox-width="31.352081"
+         inkscape:bbox-y="549.70479"
+         inkscape:bbox-x="494.66467"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         y="783.79901"
+         x="615.70251"
+         height="58.637783"
+         width="269.73572"
+         id="rect2816-7-8-9-2-3"
+         style="fill:#003380;fill-opacity:1;stroke:#00112b;stroke-width:5.34605646" />
+      <text
+         inkscape:bbox-height="78.82875"
+         inkscape:bbox-width="10.516816"
+         inkscape:bbox-y="577.68545"
+         inkscape:bbox-x="505.08231"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         sodipodi:linespacing="125%"
+         id="text2818-9-0-0-2-7"
+         y="819.10132"
+         x="668.28522"
+         style="font-size:22px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold"
+         xml:space="preserve"><tspan
+           inkscape:bbox-height="78.82875"
+           inkscape:bbox-width="10.516816"
+           inkscape:bbox-y="577.68545"
+           inkscape:bbox-x="505.08231"
+           y="819.10132"
+           x="668.28522"
+           inkscape:transform-center-y="0"
+           inkscape:transform-center-x="0"
+           id="tspan2820-3-2-5-7-9"
+           sodipodi:role="line">image loader</tspan></text>
+    </g>
+    <g
+       id="g3173"
+       transform="matrix(0,-0.49,0.49,0,141.02882,584.67276)"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="534.50744"
+       inkscape:bbox-y="549.70479"
+       inkscape:bbox-width="31.352081"
+       inkscape:bbox-height="134.79007"
+       inkscape:export-filename="/home/thinker/progm/MadButterfly/img/highlevel.png"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90">
+      <rect
+         inkscape:bbox-height="134.79007"
+         inkscape:bbox-width="31.352081"
+         inkscape:bbox-y="549.70479"
+         inkscape:bbox-x="534.50743"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         y="805.69061"
+         x="170.05133"
+         height="58.637783"
+         width="269.73572"
+         id="rect2816-7-8-9-2-0"
+         style="fill:#003380;fill-opacity:1;stroke:#00112b;stroke-width:5.34605646" />
+      <text
+         inkscape:bbox-height="51.910332"
+         inkscape:bbox-width="9.8957031"
+         inkscape:bbox-y="591.14466"
+         inkscape:bbox-x="545.23563"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         sodipodi:linespacing="125%"
+         id="text2818-9-0-0-2-2"
+         y="840.35913"
+         x="250.10181"
+         style="font-size:22px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold"
+         xml:space="preserve"><tspan
+           inkscape:bbox-height="51.910332"
+           inkscape:bbox-width="9.8957031"
+           inkscape:bbox-y="591.14466"
+           inkscape:bbox-x="545.23563"
+           y="840.35913"
+           x="250.10181"
+           inkscape:transform-center-y="0"
+           inkscape:transform-center-x="0"
+           id="tspan2820-3-2-5-7-3"
+           sodipodi:role="line">property</tspan></text>
+    </g>
+    <g
+       id="g3158"
+       transform="matrix(0.49,0,0,0.49,174.74193,126.86394)"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="213.8497"
+       inkscape:bbox-y="595.67724"
+       inkscape:bbox-width="134.79007"
+       inkscape:bbox-height="31.352081"
+       inkscape:export-filename="/home/thinker/progm/MadButterfly/img/highlevel.png"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90">
+      <rect
+         inkscape:bbox-height="31.352081"
+         inkscape:bbox-width="134.79007"
+         inkscape:bbox-y="595.67724"
+         inkscape:bbox-x="213.8497"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         y="611.79327"
+         x="82.48481"
+         height="58.637783"
+         width="269.73572"
+         id="rect2816-7-8-9-9"
+         style="fill:#003380;fill-opacity:1;stroke:#00112b;stroke-width:5.34605646" />
+      <text
+         inkscape:bbox-height="10.432598"
+         inkscape:bbox-width="78.333965"
+         inkscape:bbox-y="606.13697"
+         inkscape:bbox-x="242.07775"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         sodipodi:linespacing="125%"
+         id="text2818-9-0-0-9"
+         y="647.18152"
+         x="136.47473"
+         style="font-size:22px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold"
+         xml:space="preserve"><tspan
+           inkscape:bbox-height="10.432598"
+           inkscape:bbox-width="78.333965"
+           inkscape:bbox-y="606.13697"
+           inkscape:bbox-x="242.07775"
+           y="647.18152"
+           x="136.47473"
+           inkscape:transform-center-y="0"
+           inkscape:transform-center-x="0"
+           id="tspan2820-3-2-5-70"
+           sodipodi:role="line">element pool</tspan></text>
+    </g>
+    <g
+       id="g3148"
+       transform="matrix(0.49,0,0,0.49,36.985323,103.36979)"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="408.62703"
+       inkscape:bbox-y="711.11625"
+       inkscape:bbox-width="134.79007"
+       inkscape:bbox-height="31.352081"
+       inkscape:export-filename="/home/thinker/progm/MadButterfly/img/highlevel.png"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90">
+      <rect
+         inkscape:bbox-height="31.352081"
+         inkscape:bbox-width="134.79007"
+         inkscape:bbox-y="711.11625"
+         inkscape:bbox-x="408.62703"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         y="424.1507"
+         x="761.12549"
+         height="58.637783"
+         width="269.73572"
+         id="rect2816-7-8-4-9"
+         style="fill:#003380;fill-opacity:1;stroke:#00112b;stroke-width:5.34605646" />
+      <text
+         inkscape:bbox-height="8.3429199"
+         inkscape:bbox-width="46.68877"
+         inkscape:bbox-y="722.62084"
+         inkscape:bbox-x="452.67768"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         sodipodi:linespacing="125%"
+         id="text2818-9-0-8-8"
+         y="461.67123"
+         x="846.50409"
+         style="font-size:22px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold"
+         xml:space="preserve"><tspan
+           inkscape:bbox-height="8.3429199"
+           inkscape:bbox-width="46.68877"
+           inkscape:bbox-y="722.62084"
+           inkscape:bbox-x="452.67768"
+           y="461.67123"
+           x="846.50409"
+           inkscape:transform-center-y="0"
+           inkscape:transform-center-x="0"
+           id="tspan2820-3-2-3-6"
+           sodipodi:role="line">runtime</tspan></text>
+    </g>
+    <g
+       id="g3194"
+       transform="matrix(0.49,0,0,0.49,290.43922,204.63397)"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="222.3793"
+       inkscape:bbox-y="634.08892"
+       inkscape:bbox-width="111.6012"
+       inkscape:bbox-height="9.6956378"
+       inkscape:export-filename="/home/thinker/progm/MadButterfly/img/highlevel.png"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90">
+      <rect
+         inkscape:bbox-height="9.6956378"
+         inkscape:bbox-width="111.6012"
+         inkscape:bbox-y="634.08891"
+         inkscape:bbox-x="222.3793"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         y="416.21155"
+         x="-138.8978"
+         height="19.787016"
+         width="227.75755"
+         id="rect3186"
+         style="fill:#cccccc;fill-opacity:1;stroke:none" />
+      <text
+         inkscape:bbox-height="6.7528125"
+         inkscape:bbox-width="21.877734"
+         inkscape:bbox-y="635.56033"
+         inkscape:bbox-x="267.24104"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         sodipodi:linespacing="125%"
+         id="text3190"
+         y="432.65582"
+         x="-49.218239"
+         style="font-size:24px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#800000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold"
+         xml:space="preserve"><tspan
+           inkscape:bbox-height="6.7528125"
+           inkscape:bbox-width="21.877734"
+           inkscape:bbox-y="635.56033"
+           inkscape:bbox-x="267.24104"
+           y="432.65582"
+           x="-49.218239"
+           inkscape:transform-center-y="0"
+           inkscape:transform-center-x="0"
+           id="tspan3192"
+           sodipodi:role="line">use</tspan></text>
+    </g>
+    <g
+       id="g3243"
+       transform="matrix(0.49,0,0,0.49,164.01503,215.36088)"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="219.31448"
+       inkscape:bbox-y="691.17135"
+       inkscape:bbox-width="368.28064"
+       inkscape:bbox-height="9.6956378"
+       inkscape:export-filename="/home/thinker/progm/MadButterfly/img/highlevel.png"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90">
+      <rect
+         inkscape:bbox-height="9.6956378"
+         inkscape:bbox-width="368.28064"
+         inkscape:bbox-y="691.17135"
+         inkscape:bbox-x="219.31448"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         y="277.82513"
+         x="112.85602"
+         height="19.787016"
+         width="751.59314"
+         id="rect3186-7"
+         style="fill:#cccccc;fill-opacity:1;stroke:none" />
+      <text
+         inkscape:bbox-height="6.7528125"
+         inkscape:bbox-width="21.877734"
+         inkscape:bbox-y="692.64277"
+         inkscape:bbox-x="392.51593"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         sodipodi:linespacing="125%"
+         id="text3190-6"
+         y="294.26941"
+         x="464.45337"
+         style="font-size:24px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#800000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold"
+         xml:space="preserve"><tspan
+           inkscape:bbox-height="6.7528125"
+           inkscape:bbox-width="21.877734"
+           inkscape:bbox-y="692.64277"
+           inkscape:bbox-x="392.51593"
+           y="294.26941"
+           x="464.45337"
+           inkscape:transform-center-y="0"
+           inkscape:transform-center-x="0"
+           id="tspan3192-2"
+           sodipodi:role="line">use</tspan></text>
+    </g>
+    <g
+       id="g3194-7"
+       transform="matrix(0.49,0,0,0.49,431.78345,204.63397)"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="363.72353"
+       inkscape:bbox-y="634.08892"
+       inkscape:bbox-width="111.6012"
+       inkscape:bbox-height="9.6956378"
+       inkscape:export-filename="/home/thinker/progm/MadButterfly/img/highlevel.png"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90">
+      <rect
+         inkscape:bbox-height="9.6956378"
+         inkscape:bbox-width="111.6012"
+         inkscape:bbox-y="634.08891"
+         inkscape:bbox-x="363.72353"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         y="416.21155"
+         x="-138.8978"
+         height="19.787016"
+         width="227.75755"
+         id="rect3186-0"
+         style="fill:#cccccc;fill-opacity:1;stroke:none" />
+      <text
+         inkscape:bbox-height="6.7528125"
+         inkscape:bbox-width="21.877734"
+         inkscape:bbox-y="635.56033"
+         inkscape:bbox-x="408.58527"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         sodipodi:linespacing="125%"
+         id="text3190-3"
+         y="432.65582"
+         x="-49.218239"
+         style="font-size:24px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#800000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold"
+         xml:space="preserve"><tspan
+           inkscape:bbox-height="6.7528125"
+           inkscape:bbox-width="21.877734"
+           inkscape:bbox-y="635.56033"
+           inkscape:bbox-x="408.58527"
+           y="432.65582"
+           x="-49.218239"
+           inkscape:transform-center-y="0"
+           inkscape:transform-center-x="0"
+           id="tspan3192-9"
+           sodipodi:role="line">use</tspan></text>
+    </g>
+    <g
+       id="g3404"
+       transform="translate(-10.31929,0)"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="211.22823"
+       inkscape:bbox-y="781.89572"
+       inkscape:bbox-width="384.44808"
+       inkscape:bbox-height="31.35208"
+       inkscape:export-filename="/home/thinker/progm/MadButterfly/img/highlevel.png"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90">
+      <rect
+         inkscape:bbox-height="31.35208"
+         inkscape:bbox-width="384.44808"
+         inkscape:bbox-y="781.89572"
+         inkscape:bbox-x="211.22823"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         y="241.26933"
+         x="223.70247"
+         height="27.042181"
+         width="380.13818"
+         id="rect2816-7-9"
+         style="fill:#003380;fill-opacity:1;stroke:#00112b;stroke-width:4.30989933" />
+      <text
+         inkscape:bbox-height="10.432597"
+         inkscape:bbox-width="67.943473"
+         inkscape:bbox-y="792.35545"
+         inkscape:bbox-x="369.48054"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         sodipodi:linespacing="125%"
+         id="text2818-9-1"
+         y="257.7644"
+         x="379.74719"
+         style="font-size:10.77999973px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold"
+         xml:space="preserve"><tspan
+           inkscape:bbox-height="10.432597"
+           inkscape:bbox-width="67.943473"
+           inkscape:bbox-y="792.35545"
+           inkscape:bbox-x="369.48054"
+           y="257.7644"
+           x="379.74719"
+           inkscape:transform-center-y="0"
+           inkscape:transform-center-x="0"
+           id="tspan2820-3-7"
+           sodipodi:role="line">Application</tspan></text>
+    </g>
+    <g
+       id="g3396"
+       transform="translate(-10,0)"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="210.06335"
+       inkscape:bbox-y="765.72395"
+       inkscape:bbox-width="386.77783"
+       inkscape:bbox-height="9.6956377"
+       inkscape:export-filename="/home/thinker/progm/MadButterfly/img/highlevel.png"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90">
+      <rect
+         inkscape:bbox-height="9.6956377"
+         inkscape:bbox-width="386.77783"
+         inkscape:bbox-y="765.72395"
+         inkscape:bbox-x="210.06335"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         y="276.9426"
+         x="220.06335"
+         height="9.6956377"
+         width="386.77783"
+         id="rect3186-7-3"
+         style="fill:#cccccc;fill-opacity:1;stroke:none" />
+      <text
+         inkscape:bbox-height="6.7528126"
+         inkscape:bbox-width="21.877735"
+         inkscape:bbox-y="767.19538"
+         inkscape:bbox-x="392.51342"
+         inkscape:transform-center-y="0"
+         inkscape:transform-center-x="0"
+         sodipodi:linespacing="125%"
+         id="text3190-6-6"
+         y="285.00027"
+         x="401.59467"
+         style="font-size:11.76000023px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#800000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold"
+         xml:space="preserve"><tspan
+           inkscape:bbox-height="6.7528126"
+           inkscape:bbox-width="21.877735"
+           inkscape:bbox-y="767.19538"
+           inkscape:bbox-x="392.51342"
+           y="285.00027"
+           x="401.59467"
+           inkscape:transform-center-y="0"
+           inkscape:transform-center-x="0"
+           id="tspan3192-2-5"
+           sodipodi:role="line">use</tspan></text>
+    </g>
+    <rect
+       style="fill:none;stroke:#999999;stroke-width:0.83045471"
+       id="rect3375"
+       width="401.16956"
+       height="206.16954"
+       x="206.91522"
+       y="301.2774"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="206.49999"
+       inkscape:bbox-y="544.50001"
+       inkscape:bbox-width="402.00001"
+       inkscape:bbox-height="207"
+       inkscape:export-filename="/home/thinker/progm/MadButterfly/img/highlevel.png"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90" />
+  </g>
+</svg>
--- a/include/Makefile.am	Mon Jul 19 15:44:49 2010 +0800
+++ b/include/Makefile.am	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+# -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+# vim: sw=4:ts=8:sts=4
 include_HEADERS = 	\
 	mb_config.h	\
 	mb_animate.h 	\
@@ -10,9 +12,16 @@
 	mb_shapes.h	\
 	mb_tools.h	\
 	mb_prop.h	\
-	mb_X_supp.h	\
 	mb_img_ldr.h    \
 	mb_af.h         \
 	mb_ani_menu.h \
 	mbbutton.h      \
 	mb_so.h
+
+if X_BACKEND
+include_HEADERS += mb_X_supp.h
+endif
+
+if DFB_BACKEND
+include_HEADERS += mb_dfb_supp.h
+endif
--- a/include/mb.h	Mon Jul 19 15:44:49 2010 +0800
+++ b/include/mb.h	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #ifndef __MB_H_
 #define __MB_H_
 
@@ -5,10 +7,15 @@
 
 #include "mb_types.h"
 #include "mb_redraw_man.h"
-#ifdef X_SUPP
+#ifdef X_BACKEND
 #include "mb_X_supp.h"
 #endif
+#ifdef DFB_BACKEND
+#include "mb_dfb_supp.h"
+#endif
 #include "mb_animate.h"
 #include "mb_shapes.h"
+#include "mb_backend.h"
+#include "mb_sprite.h"
 
 #endif /* __MB_H_ */
--- a/include/mb_X_supp.h	Mon Jul 19 15:44:49 2010 +0800
+++ b/include/mb_X_supp.h	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #ifndef __X_SUPP_H_
 #define __X_SUPP_H_
 
@@ -21,16 +23,9 @@
 
 /* @} */
 
-typedef struct _X_MB_runtime X_MB_runtime_t;
-
-extern void X_MB_handle_connection(void *rt);
-extern void *X_MB_new(const char *display_name, int w, int h);
-extern void X_MB_free(void *xmb_rt);
+typedef struct _X_supp_runtime X_supp_runtime_t;
 
-extern subject_t *X_MB_kbevents(void *xmb_rt);
-extern redraw_man_t *X_MB_rdman(void *xmb_rt);
-extern mb_tman_t *X_MB_tman(void *xmb_rt);
-extern ob_factory_t *X_MB_ob_factory(void *xmb_rt);
-extern mb_img_ldr_t *X_MB_img_ldr(void *xmb_rt);
+typedef Window MB_WINDOW;
+typedef Display *MB_DISPLAY;
 
 #endif
--- a/include/mb_af.h	Mon Jul 19 15:44:49 2010 +0800
+++ b/include/mb_af.h	Wed Dec 01 12:25:56 2010 +0800
@@ -1,9 +1,13 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #ifndef __APP_H
 #define __APP_H
 
+#include "mb_backend.h"
+
 typedef struct _mbapp mbaf_t;
 struct _mbapp {
-    void *rt;
+    mb_rt_t *rt;
     redraw_man_t *rdman;
     mb_sprite_t *rootsprite;
     mb_obj_t *root;
@@ -12,7 +16,7 @@
 };
 mbaf_t *mbaf_init(const char *module, const char *module_dir);
 void mbaf_set_data(mbaf_t *app,void *data);
-mb_tman_t *mbaf_get_timer(mbaf_t *app);
+mb_timer_man_t *mbaf_get_timer(mbaf_t *app);
 void mbaf_loop(mbaf_t *app);
 #define MBAF_DATA(app,type) ((type *) ((app)->private))
 #define MBAF_RDMAN(app) (((mbaf_t *) app)->rdman)
--- a/include/mb_ani_menu.h	Mon Jul 19 15:44:49 2010 +0800
+++ b/include/mb_ani_menu.h	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #ifndef __ANIMATED_MENU_H
 #define __ANIMATED_MENU_H
 typedef struct _mb_animated_menu {
@@ -19,9 +21,9 @@
 	X_kb_event_t pending_keys[16];
     int pending_pos, pending_last;
 } mb_animated_menu_t;
-/** \brief Create an instace of animated menu. 
+/** \brief Create an instace of animated menu.
  *
- *   The objectnames is used to extract symbols from the SVG file. 
+ *   The objectnames is used to extract symbols from the SVG file.
  *         ${objectnames}0 - ${objectnames}8 is the text object.
  *         ${objectnames}_lightbar is the lightbar.
  *
--- a/include/mb_animate.h	Mon Jul 19 15:44:49 2010 +0800
+++ b/include/mb_animate.h	Wed Dec 01 12:25:56 2010 +0800
@@ -1,9 +1,11 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #ifndef __ANIMATE_H_
 #define __ANIMATE_H_
 
 #include "mb_types.h"
-#include "mb_timer.h"
 #include "mb_paint.h"
+#include "mb_backend.h"
 
 /*! \page def_action How to Define An Action?
  *
@@ -40,7 +42,7 @@
 extern mb_word_t *mb_progm_next_word(mb_progm_t *progm,
 				     const mb_timeval_t *start,
 				     const mb_timeval_t *playing);
-extern void mb_progm_start(mb_progm_t *progm, mb_tman_t *tman,
+extern void mb_progm_start(mb_progm_t *progm, mb_timer_man_t *timer_man,
 			   mb_timeval_t *now);
 extern void mb_progm_abort(mb_progm_t *progm);
 extern subject_t *mb_progm_get_complete(mb_progm_t *progm);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/mb_backend.h	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,196 @@
+#ifndef __MB_BACKEND_H_
+#define __MB_BACKEND_H_
+
+#include "mb_redraw_man.h"
+#include "mb_timer.h"
+#include "mb_observer.h"
+#include "mb_img_ldr.h"
+
+#include "mb_config.h"
+
+#ifdef X_BACKEND
+#include "mb_X_supp.h"
+#endif
+
+#ifdef DFB_BACKEND
+#inclde "mb_dfb_supp.h"
+#endif
+
+struct _mb_rt;
+typedef struct _mb_rt mb_rt_t;
+
+struct _mb_timer_man;
+struct _mb_timer_factory;
+struct _mb_IO_man;
+struct _mb_IO_factory;
+
+/*! \brief Type of IO that registered with an IO manager.
+ */
+enum _MB_IO_TYPE {MB_IO_DUMMY, MB_IO_R, MB_IO_W, MB_IO_RW};
+
+typedef struct _mb_timer_man mb_timer_man_t;
+typedef struct _mb_timer_factory mb_timer_factory_t;
+typedef struct _mb_IO_man mb_IO_man_t;
+typedef struct _mb_IO_factory mb_IO_factory_t;
+typedef enum _MB_IO_TYPE MB_IO_TYPE;
+
+/*! \brief Function signature of callback functions for IO requests.
+ */
+typedef void (*mb_IO_cb_t)(int hdl, int fd, MB_IO_TYPE type, void *data);
+
+/*! \brief The backend engine mb_backend_t is used to define the
+ *         interface to realize the MB.
+ *
+ * A backend is used to receive events from the system. The MB does
+ * not define the backend by itself.  Instead, it define an interface
+ * which allow the lower layer to implement the event system. Each
+ * backend need to provides the following events.
+ *
+ * - keyboard event
+ * - timer event
+ * - image loader(?)
+ * - render manager(?)
+ */
+typedef struct {
+    mb_rt_t *(*rt_new)(const char *display, int w,int h);
+    mb_rt_t *(*rt_new_with_win)(MB_DISPLAY display, MB_WINDOW win);
+    
+    void (*rt_free)(mb_rt_t *rt);
+    void (*rt_free_keep_win)(mb_rt_t *rt);
+    /*! \brief Request the backend to start monitoring a file descriptor.
+     *
+     * This is used only when the backend is responsible for event loop.
+     */
+    int (*add_event)(mb_rt_t *rt, int fd, MB_IO_TYPE type,
+		     mb_IO_cb_t f,void *arg);
+    /*! \brief Request the backend to stop monitoring a file descriptor.
+     *
+     * This is used only when the backend is responsible for event loop.
+     */
+    void (*remove_event)(mb_rt_t *rt, int hdl);
+    /*! \brief Event Loop
+     *
+     * This is called when main application does not handle event
+     * loop.  Or, it should register an IO factory (i.e \ref
+     * mb_IO_factory_t) with the backend.
+     */
+    void (*event_loop)(mb_rt_t *rt);
+
+    /*! \brief Flush requests to screen server if existed */
+    int (*flush)(mb_rt_t *rt);
+    
+    subject_t *(*kbevents)(mb_rt_t *rt);
+    redraw_man_t *(*rdman)(mb_rt_t *rt);
+    mb_timer_man_t *(*timer_man)(mb_rt_t *rt);
+    observer_factory_t *(*observer_factory)(mb_rt_t *rt);
+    mb_img_ldr_t *(*loader)(mb_rt_t *rt);
+    
+    /*
+     * Following two methods are used to integrate a backend to
+     * event loop of main application.
+     */
+    void (*reg_IO_factory)(mb_IO_factory_t *io_man);
+    void (*reg_timer_factory)(mb_timer_factory_t *tm_man);
+} mb_backend_t;
+
+#define mb_runtime_new(disp, w, h)	\
+    mb_dfl_backend.rt_new((disp), (w), (h))
+#define mb_runtime_new_with_win(disp, win)	\
+    mb_dfl_backend.rt_new_with_win((disp), (win))
+#define mb_reg_IO_factory(io_fact)	\
+    mb_dfl_backend.reg_IO_factory(io_fact)
+#define mb_reg_timer_factory(tm_fact)	\
+    mb_dfl_backend.reg_timer_factory(tm_fact)
+
+/*
+ * This is defined by backend implementations.  For example, X_supp.c
+ * or dfb_supp.c should defined a backend.
+ */
+extern mb_backend_t mb_dfl_backend;
+
+#define mb_runtime_free(rt)			\
+    mb_dfl_backend.rt_free(rt)
+#define mb_runtime_free_keep_win(rt)		\
+    mb_dfl_backend.rt_free_keep_win(rt)
+#define mb_runtime_add_event(rt, fd, type, cb, arg)		\
+    mb_dfl_backend.add_event((rt), (fd), (type), (cb), (arg))
+#define mb_runtime_remove_event(hdl)		\
+    mb_dfl_backend.remove_event((rt), (hdl))
+#define mb_runtime_event_loop(rt)		\
+    mb_dfl_backend.event_loop(rt)
+#define mb_runtime_flush(rt)			\
+    mb_dfl_backend.flush(rt)
+#define mb_runtime_kbevents(rt)			\
+    mb_dfl_backend.kbevents(rt)
+#define mb_runtime_rdman(rt)			\
+    mb_dfl_backend.rdman(rt)
+#define mb_runtime_timer_man(rt)		\
+    mb_dfl_backend.timer_man(rt)
+#define mb_runtime_observer_factory(rt)		\
+    mb_dfl_backend.observer_factory(rt)
+#define mb_runtime_loader(rt)			\
+    mb_dfl_backend.loader(rt)
+
+
+/*! \brief IO Manager
+ */
+struct _mb_IO_man {
+    int (*reg)(struct _mb_IO_man *io_man,
+	       int fd, MB_IO_TYPE type, mb_IO_cb_t cb, void *data);
+    void (*unreg)(struct _mb_IO_man *io_man,
+		  int io_hdl);
+};
+
+/*! \brief Factory of IO managers.
+ */
+struct _mb_IO_factory {
+    mb_IO_man_t *(*io_man_new)(void);
+    void (*io_man_free)(mb_IO_man_t *io_man);
+};
+
+#define mb_io_man_reg(io_man, fd, type, cb, data)	\
+    (io_man)->reg(io_man, fd, type, cb, data)
+#define mb_io_man_unreg(io_man, io_hdl)		\
+    (io_man)->unreg(io_man, io_hdl)
+#define mb_io_man_new(io_fact) (io_fact)->io_man_new()
+#define mb_io_man_free(io_fact, io_man) (io_fact)->io_man_free(io_man)
+
+/*! \brief Function signature of callback functions for timers.
+ */
+typedef void (*mb_timer_cb_t)(int hdl,
+			      const mb_timeval_t *tmo,
+			      const mb_timeval_t *now,
+			      void *data);
+
+/*! \brief Timer manager
+ */
+struct _mb_timer_man {
+    /*! \brief Setup a timeout callback.
+     *
+     * \return -1 for error.
+     */
+    int (*timeout)(struct _mb_timer_man *tm_man,
+		   mb_timeval_t *tmout, /* tiemout (wall time) */
+		   mb_timer_cb_t cb, void *data);
+    /*! \brief Remove a timeout request.
+     *
+     * \param tm_hdl is the handle returned by _mb_timer_man::timeout.
+     */
+    void (*remove)(struct _mb_timer_man *tm_man, int tm_hdl);
+};
+
+/*! \brief Factory of timer manager.
+ */
+struct _mb_timer_factory {
+    mb_timer_man_t *(*timer_man_new)(void);
+    void (*timer_man_free)(mb_timer_man_t *timer_man);
+};
+
+#define mb_timer_man_timeout(tm_man, tmout, cb, data)	\
+    (tm_man)->timeout((tm_man), (tmout), (cb), (data))
+#define mb_timer_man_remove(tm_man, tm_hdl)	\
+    (tm_man)->remove((tm_man), (tm_hdl))
+#define mb_timer_man_new(tm_fact) (tm_fact)->timer_man_new()
+#define mb_timer_man_free(tm_fact, tm_man) (tm_fact)->timer_man_free(tm_man)
+
+#endif /* __MB_BACKEND_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/mb_backend_utils.h	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,12 @@
+#ifndef __MB_BACKEND_UTILS_H_
+#define __MB_BACKEND_UTILS_H_
+
+#include "mb_backend.h"
+#include "mb_timer.h"
+
+/*! \brief A facotry of timer manager implemented with mb_tman_t.
+ */
+extern mb_timer_factory_t tman_timer_factory;
+extern mb_tman_t *tman_timer_man_get_tman(mb_timer_man_t *tm_man);
+
+#endif /* __MB_BACKEND_UTILS_H_ */
--- a/include/mb_basic_types.h	Mon Jul 19 15:44:49 2010 +0800
+++ b/include/mb_basic_types.h	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #ifndef __MB_BASIC_TYPES_H_
 #define __MB_BASIC_TYPES_H_
 
@@ -7,9 +9,5 @@
     co_aix offset;
     co_comp_t r, g, b, a;
 } grad_stop_t;
-typedef struct _area {
-    co_aix x, y;
-    co_aix w, h;
-} area_t;
 
 #endif /* __MB_BASIC_TYPES_H_ */
--- a/include/mb_config.h.in	Mon Jul 19 15:44:49 2010 +0800
+++ b/include/mb_config.h.in	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #ifndef __MB_CONFIG_H_
 #define __MB_CONFIG_H_
 
@@ -10,6 +12,15 @@
 /* Enable OpenVG Graphic Engine */
 #undef OPENVG_GRAPH_ENGINE
 
+/* Enable DirectFB Graphic Engine */
+#undef DFB_GRAPH_ENGINE
+
+/* Enable Cairo Image Loader */
+#undef CAIRO_IMG_LOADER
+
+/* Enable Imlib2 Image Loader */
+#undef IMLIB2_IMG_LOADER
+
 /* Enable sh_text */
 #undef SH_TEXT
 
@@ -17,6 +28,9 @@
 #undef SH_STEXT
 
 /* Enable X backend */
-#undef X_SUPP
+#undef X_BACKEND
+
+/* Enable DirectFB backend */
+#undef DFB_BACKEND
 
 #endif /* __MB_CONFIG_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/mb_dfb_supp.h	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,38 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
+#ifndef __DFB_SUPP_H_
+#define __DFB_SUPP_H_
+
+#include "mb_types.h"
+#include "mb_timer.h"
+#include "mb_redraw_man.h"
+#include "mb_img_ldr.h"
+
+/*! \ingroup xkb
+ * @{
+ */
+typedef struct _X_kb_info X_kb_info_t;
+
+struct _X_kb_event {
+    event_t event;
+    int keycode;
+    int sym;
+};
+typedef struct _X_kb_event X_kb_event_t;
+
+/* @} */
+
+typedef struct _X_MB_runtime X_MB_runtime_t;
+
+extern void X_MB_handle_connection(void *rt);
+extern void *X_MB_new(const char *display_name, int w, int h);
+extern void X_MB_free(void *xmb_rt);
+extern void X_MB_free_keep_win(void *rt);
+
+extern subject_t *X_MB_kbevents(void *xmb_rt);
+extern redraw_man_t *X_MB_rdman(void *xmb_rt);
+extern mb_tman_t *X_MB_tman(void *xmb_rt);
+extern observer_factory_t *X_MB_observer_factory(void *xmb_rt);
+extern mb_img_ldr_t *X_MB_img_ldr(void *xmb_rt);
+
+#endif
--- a/include/mb_graph_engine.h	Mon Jul 19 15:44:49 2010 +0800
+++ b/include/mb_graph_engine.h	Wed Dec 01 12:25:56 2010 +0800
@@ -10,6 +10,8 @@
  * include/mb_graph_engine.h to include correct header for the graphic
  * engine enabled by the user.
  */
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #ifndef __MBE_H_
 #define __MBE_H_
 #include <mb_config.h>
--- a/include/mb_graph_engine_cairo.h	Mon Jul 19 15:44:49 2010 +0800
+++ b/include/mb_graph_engine_cairo.h	Wed Dec 01 12:25:56 2010 +0800
@@ -1,19 +1,26 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #ifndef __MB_GE_CAIRO_H_
 #define __MB_GE_CAIRO_H_
 
 #include <stdio.h>
 #include <cairo.h>
-#include <cairo-xlib.h>
 #include "mb_basic_types.h"
 #include "mb_img_ldr.h"
 
 /*! \defgroup mb_ge_cairo MadButterfly Graphic Engine with Cairo
  * @{
  */
+#define MBE_OPERATOR_CLEAR CAIRO_OPERATOR_CLEAR
+#define MBE_OPERATOR_SOURCE CAIRO_OPERATOR_SOURCE
+#define MBE_STATUS_SUCCESS CAIRO_STATUS_SUCCESS
+
+#define mbe_image_surface_create_from_png cairo_image_surface_create_from_png
 #define mbe_pattern_create_for_surface cairo_pattern_create_for_surface
 #define mbe_scaled_font_text_extents cairo_scaled_font_text_extents
 #define mbe_image_surface_get_stride cairo_image_surface_get_stride
 #define mbe_image_surface_get_height cairo_image_surface_get_height
+#define mbe_directfb_surface_create cairo_directfb_surface_create
 #define mbe_image_surface_get_width cairo_image_surface_get_width
 #define mbe_image_surface_get_data cairo_image_surface_get_data
 #define mbe_scaled_font_reference cairo_scaled_font_reference
@@ -32,7 +39,7 @@
 #define mbe_get_font_face cairo_get_font_face
 #define mbe_fill_preserve cairo_fill_preserve
 #define mbe_set_source cairo_set_source
-#define mbe_reset_scissoring cairo_reset_clip
+#define mbe_reset_clip cairo_reset_clip
 #define mbe_get_target cairo_get_target
 #define mbe_close_path cairo_close_path
 #define mbe_text_path cairo_text_path
@@ -50,7 +57,7 @@
 #define mbe_paint cairo_paint
 #define mbe_save cairo_save
 #define mbe_fill cairo_fill
-#define mbe_init()
+#define mbe_clip cairo_clip
 
 typedef cairo_text_extents_t mbe_text_extents_t;
 typedef cairo_scaled_font_t mbe_scaled_font_t;
@@ -82,8 +89,6 @@
 						co_aix x1, co_aix y1,
 						grad_stop_t *stops,
 						int stop_cnt);
-extern mbe_pattern_t *mbe_pattern_create_image(mb_img_data_t *img);
-extern void mbe_scissoring(mbe_t *canvas, int n_areas, area_t **areas);
 
 
 static void mbe_pattern_set_matrix(mbe_pattern_t *ptn,
@@ -96,7 +101,7 @@
 
 static void mbe_clear(mbe_t *canvas) {
     cairo_operator_t old_op;
-    
+
     old_op = cairo_get_operator(canvas);
     cairo_set_operator(canvas, CAIRO_OPERATOR_CLEAR);
     cairo_paint(canvas);
@@ -107,10 +112,10 @@
     cairo_operator_t saved_op;
     cairo_surface_t *surf;
     cairo_pattern_t *ptn;
-    
+
     surf = cairo_get_target(src);
     ptn = cairo_pattern_create_for_surface(surf);
-    cairo_set_source(dst, ptn);
+    cairo_set_source(src, ptn);
     cairo_pattern_destroy(ptn);
     saved_op = cairo_get_operator(dst);
     cairo_set_operator(dst, CAIRO_OPERATOR_SOURCE);
@@ -128,16 +133,43 @@
     options = cairo_font_options_create();
     if(options == NULL)
 	return NULL;
-    
+
     MB_MATRIX_2_CAIRO(cfnt_mtx, fnt_mtx);
     MB_MATRIX_2_CAIRO(cctm, ctm);
     scaled = cairo_scaled_font_create(face, &cfnt_mtx, &cctm, options);
 
     cairo_font_options_destroy(options);
-    
+
     return scaled;
 }
 
+static mbe_surface_t *
+mbe_image_surface_create_for_data(unsigned char *data,
+				  mb_img_fmt_t fmt,
+				  int width, int height,
+				  int stride) {
+    cairo_format_t _fmt;
+
+    switch(fmt) {
+    case MB_IFMT_ARGB32:
+	_fmt = CAIRO_FORMAT_ARGB32;
+	break;
+    case MB_IFMT_RGB24:
+	_fmt = CAIRO_FORMAT_RGB24;
+	break;
+    case MB_IFMT_A8:
+	_fmt = CAIRO_FORMAT_A8;
+	break;
+    case MB_IFMT_A1:
+	_fmt = CAIRO_FORMAT_A1;
+	break;
+    default:
+	return NULL;
+    }
+    return cairo_image_surface_create_for_data(data, _fmt,
+					       width, height, stride);
+}
+
 static mb_img_fmt_t
 mbe_image_surface_get_format(mbe_surface_t *surface) {
     cairo_format_t _fmt;
@@ -168,7 +200,7 @@
 static mbe_surface_t *
 mbe_image_surface_create(mb_img_fmt_t fmt, int width, int height) {
     cairo_format_t _fmt;
-    
+
     switch(fmt) {
     case MB_IFMT_ARGB32:
 	_fmt = CAIRO_FORMAT_ARGB32;
@@ -185,7 +217,7 @@
     default:
 	return NULL;
     }
-    
+
     return cairo_image_surface_create(_fmt, width, height);
 }
 
--- a/include/mb_graph_engine_skia.h	Mon Jul 19 15:44:49 2010 +0800
+++ b/include/mb_graph_engine_skia.h	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #ifndef __MB_GE_SKIA_H_
 #define __MB_GE_SKIA_H_
 
@@ -8,6 +10,10 @@
 /*! \defgroup mb_ge_skia MadButterfly Graphic Engine with Skia
  * @{
  */
+#define MBE_OPERATOR_CLEAR 2
+#define MBE_OPERATOR_SOURCE 1
+#define MBE_STATUS_SUCCESS 0
+
 struct _mbe_text_extents_t {
     co_aix x_bearing;
     co_aix y_bearing;
@@ -40,7 +46,6 @@
 						co_aix x1, co_aix y1,
 						grad_stop_t *stops,
 						int stop_cnt);
-extern mbe_pattern_t *mbe_pattern_create_image(mb_img_data_t *img);
 extern void mbe_pattern_set_matrix(mbe_pattern_t *ptn,
 				   const co_aix matrix[6]);
 extern void mbe_pattern_destroy(mbe_pattern_t *ptn);
@@ -49,6 +54,12 @@
 extern int mbe_image_surface_get_height(mbe_surface_t *surface);
 extern int mbe_image_surface_get_width(mbe_surface_t *surface);
 extern unsigned char *mbe_image_surface_get_data(mbe_surface_t *surface);
+extern mbe_surface_t *mbe_image_surface_create_from_png(const char *filename);
+extern mbe_surface_t *
+mbe_image_surface_create_for_data(unsigned char *data,
+				  mb_img_fmt_t fmt,
+				  int width, int height,
+				  int stride);
 extern mb_img_fmt_t mbe_image_surface_get_format(mbe_surface_t *surface);
 extern mbe_surface_t *
 mbe_image_surface_create(mb_img_fmt_t fmt, int width, int height);
@@ -76,7 +87,7 @@
 extern mbe_font_face_t *mbe_get_font_face(mbe_t *canvas);
 extern void mbe_fill_preserve(mbe_t *canvas);
 extern void mbe_set_source(mbe_t *canvas, mbe_pattern_t *source);
-extern void mbe_reset_scissoring(mbe_t *canvas);
+extern void mbe_reset_clip(mbe_t *canvas);
 extern mbe_surface_t *mbe_get_target(mbe_t *canvas);
 extern void mbe_close_path(mbe_t *canvas);
 extern void mbe_text_path(mbe_t *canvas, const char *txt);
@@ -97,7 +108,7 @@
 extern void mbe_paint(mbe_t *canvas);
 extern void mbe_save(mbe_t *canvas);
 extern void mbe_fill(mbe_t *canvas);
-extern void mbe_scissoring(mbe_t *canvas, int n_areas, area_t **areas);
+extern void mbe_clip(mbe_t *canvas);
 
 extern mbe_font_face_t * mbe_query_font_face(const char *family,
 					     int slant, int weight);
@@ -108,7 +119,6 @@
 extern void mbe_transform(mbe_t *mbe, co_aix matrix[6]);
 extern void mbe_arc(mbe_t *mbe, co_aix x, co_aix y, co_aix radius,
 		    co_aix angle_start, co_aix angle_stop);
-#define mbe_init()
 /* @} */
 
 #endif /* __MB_GE_SKIA_H_ */
--- a/include/mb_img_ldr.h	Mon Jul 19 15:44:49 2010 +0800
+++ b/include/mb_img_ldr.h	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #ifndef __MB_IMG_LDR_H_
 #define __MB_IMG_LDR_H_
 
--- a/include/mb_obj.h	Mon Jul 19 15:44:49 2010 +0800
+++ b/include/mb_obj.h	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #ifndef __MBOBJ_H
 #define __MBOBJ_H
 #include "mb_shapes.h"
--- a/include/mb_observer.h	Mon Jul 19 15:44:49 2010 +0800
+++ b/include/mb_observer.h	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #ifndef __OBSERVER_H_
 #define __OBSERVER_H_
 
@@ -8,7 +10,7 @@
 typedef struct _subject subject_t;
 typedef struct _mouse_event mouse_event_t;
 typedef struct _monitor_event monitor_event_t;
-typedef struct _ob_factory ob_factory_t;
+typedef struct _observer_factory observer_factory_t;
 typedef void (*evt_handler)(event_t *event, void *arg);
 
 struct _event {
@@ -53,7 +55,7 @@
     int flags;
     subject_t *monitor_sub;	/*!< \brief Monitor adding/removing
 				 *          obervers on this subject. */
-    ob_factory_t *factory;
+    observer_factory_t *factory;
     STAILQ(observer_t) observers;
 };
 /*! \brief Flag that make a subject to stop propagate events to parents. */
@@ -85,13 +87,13 @@
  * It provides functions for allocation of subject and observer objects,
  * and strategy function for getting the subject of parent coord object.
  */
-struct _ob_factory {
-    subject_t *(*subject_alloc)(ob_factory_t *factory);
-    void (*subject_free)(ob_factory_t *factory, subject_t *subject);
-    observer_t *(*observer_alloc)(ob_factory_t *factory);
-    void (*observer_free)(ob_factory_t *factory, observer_t *observer);
+struct _observer_factory {
+    subject_t *(*subject_alloc)(observer_factory_t *factory);
+    void (*subject_free)(observer_factory_t *factory, subject_t *subject);
+    observer_t *(*observer_alloc)(observer_factory_t *factory);
+    void (*observer_free)(observer_factory_t *factory, observer_t *observer);
     /*! This is a strategy function to get subjects of parents. */
-    subject_t *(*get_parent_subject)(ob_factory_t *factory,
+    subject_t *(*get_parent_subject)(observer_factory_t *factory,
 				     subject_t *cur_subject);
 };
 
@@ -103,7 +105,7 @@
       EVT_MOUSE_MOVE_RAW
 };
 
-extern subject_t *subject_new(ob_factory_t *factory,
+extern subject_t *subject_new(observer_factory_t *factory,
 			      void *obj, int obj_type);
 extern void subject_free(subject_t *subject);
 extern void subject_notify(subject_t *subject, event_t *evt);
--- a/include/mb_paint.h	Mon Jul 19 15:44:49 2010 +0800
+++ b/include/mb_paint.h	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #ifndef __PAINT_H_
 #define __PAINT_H_
 
--- a/include/mb_prop.h	Mon Jul 19 15:44:49 2010 +0800
+++ b/include/mb_prop.h	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #ifndef __MB_PROP_H_
 #define __MB_PROP_H_
 
@@ -32,6 +34,7 @@
     PROP_DUMMY,
     PROP_MEVT_OB_CNT,
     PROP_MEVT_OBSERVER,
+    PROP_JSOBJ,
     PROP_LAST
 };
 
--- a/include/mb_redraw_man.h	Mon Jul 19 15:44:49 2010 +0800
+++ b/include/mb_redraw_man.h	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #ifndef __REDRAW_MAN_H_
 #define __REDRAW_MAN_H_
 
@@ -6,10 +8,15 @@
 #include "mb_types.h"
 #include "mb_observer.h"
 #include "mb_img_ldr.h"
-#include "mb_timer.h"
 
+/*! \defgroup rdman Redraw Manager
+ * @{
+ */
 typedef struct _redraw_man redraw_man_t;
 
+/*! \defgroup rdman_private Private Types of Redraw Manager
+ * @{
+ */
 typedef void (*free_func_t)(redraw_man_t *rdman, void *obj);
 struct _free_obj {
     void *obj;
@@ -24,6 +31,7 @@
 
 DARRAY(coords, coord_t *);
 DARRAY(geos, geo_t *);
+/* @} */
 
 /*! \brief Manage redrawing of shapes (graphic elements).
  *
@@ -48,14 +56,18 @@
     elmpool_t *geo_pool;
     elmpool_t *coord_pool;
     elmpool_t *shnode_pool;
+    elmpool_t *sh_path_pool;
+    elmpool_t *sh_rect_pool;
     elmpool_t *observer_pool;
     elmpool_t *subject_pool;
     elmpool_t *paint_color_pool;
+    elmpool_t *paint_linear_pool;
+    elmpool_t *paint_radial_pool;
+    elmpool_t *paint_image_pool;
     elmpool_t *pent_pool;
     elmpool_t *coord_canvas_pool;
 
     coords_t dirty_coords;
-    coords_t dirty_pcache_area_coords;
     geos_t dirty_geos;
     int n_dirty_areas;		/*!< \brief Number of all dirty areas. */
 
@@ -70,7 +82,7 @@
     mbe_t *cr;
     mbe_t *backend;
 
-    ob_factory_t ob_factory;
+    observer_factory_t observer_factory;
 
     subject_t *redraw;		/*!< \brief Notified after redrawing. */
     subject_t *addrm_monitor;	/*!< \brief Monitor adding/removing observers
@@ -78,8 +90,8 @@
 				 *	    \see addrm_monitor_hdlr()
 				 */
     mb_obj_t *last_mouse_over;
-    void *rt;                  /*!< \brief This is a pointer to the current 
-                                *          graphic backend. 
+    void *rt;                  /*!< \brief This is a pointer to the current
+                                *          graphic backend.
 				*          \see rdman_attach_backend()
 				*/
     mb_prop_store_t props;
@@ -100,13 +112,15 @@
 extern int rdman_add_shape(redraw_man_t *rdman,
 			   shape_t *shape, coord_t *coord);
 /*! \brief Make a shape been managed by a redraw manager. */
-#define rdman_shape_man(rdman, shape)					\
+#define rdman_man_shape(rdman, shape)					\
     do {								\
 	mb_prop_store_init(&((mb_obj_t *)(shape))->props,		\
 			   (rdman)->pent_pool);				\
 	STAILQ_INS_TAIL(rdman->shapes, shape_t, sh_next, shape);	\
 	if(rdman->last_mouse_over == (mb_obj_t *)(shape))		\
 	    rdman->last_mouse_over = NULL;				\
+	mb_prop_store_init(&((mb_obj_t *)(shape))->props,		\
+			   (rdman)->pent_pool);				\
     } while(0)
 extern int rdman_shape_free(redraw_man_t *rdman, shape_t *shape);
 
@@ -140,6 +154,10 @@
 	}							\
 	shnode_free(rdman, __last);				\
     } while(0)
+
+/*! \defgroup rdman_paints Paints Supporting of Redraw Manger
+ * @{
+ */
 #define _rdman_paint_child(rdman, paint, shape)		\
     do {						\
 	shnode_t *__node;				\
@@ -183,14 +201,23 @@
 	(shape)->stroke = paint;				\
     } while(0)
 extern int rdman_paint_changed(redraw_man_t *rdman, paint_t *paint);
+/* @} */
 
+/*! \defgroup rdman_pos Position/Overlay Detection for Managed Objects
+ * @{
+ */
 extern shape_t *find_shape_at_pos(redraw_man_t *rdman,
 				  co_aix x, co_aix y, int *in_stroke);
 extern int mb_obj_pos_is_in(redraw_man_t *rdman, mb_obj_t *obj,
 			    co_aix x, co_aix y, int *in_stroke);
 extern int mb_objs_are_overlay(redraw_man_t *rdman,
 			       mb_obj_t *obj1, mb_obj_t *obj2);
-#define rdman_get_ob_factory(rdman) (&(rdman)->ob_factory)
+/* @} */
+
+/*! \defgroup rdman_accessors Accessors of Redraw Manager
+ * @{
+ */
+#define rdman_get_observer_factory(rdman) (&(rdman)->observer_factory)
 #define rdman_get_redraw_subject(rdman) ((rdman)->redraw)
 #define rdman_get_root(rdman) ((rdman)->root_coord)
 #define rdman_get_cr(rdman) ((rdman)->cr)
@@ -204,62 +231,24 @@
     rdman_get_gen_geos(rdman)->num
 #define rdman_clear_shape_gl(rdman)		\
     DARRAY_CLEAN(rdman_get_gen_geos(rdman))
+#define _coord_get_canvas(coord) ((coord)->canvas_info->canvas)
+#define _coord_set_canvas(coord, _canvas)		\
+    do {						\
+	(coord)->canvas_info->canvas = _canvas;		\
+    } while(0)
 #define rdman_prop_store(rdman) ((rdman)->props)
 #define rdman_img_ldr(rdman) ((rdman)->img_ldr)
 #define rdman_set_img_ldr(rdman, ldr)		\
     do { (rdman)->img_ldr = ldr; } while(0)
+/* @} */
 
 /*! \brief Attach backend to the redraw manager so that we can hide the backend from the users.
  *
  */
 #define rdman_attach_backend(rdman,backend) (((rdman)->rt)=(backend))
-/*! \brief Load sprite dymanicly from the shared object module. 
- *  
- * The search path can be changed by sprite_set_search_path. The name
- * can have a relative path in the front of it.
- * This function will search the object in the current working directory
- * and then search the system search path.
- */
-extern mb_sprite_t *sprite_load(const char *name, redraw_man_t *rdman,
-				coord_t *root);
-
-/*! \brief Set the search path of dymanic object loading.
- *
- */
-extern void sprite_set_search_path(char *path);
 
 extern paint_t *rdman_img_ldr_load_paint(redraw_man_t *rdman,
 					 const char *img_id);
-
-typedef void (*mb_eventcb_t )(int fd,void *arg);
-#define MONITOR_READ   1
-#define MONITOR_WRITE  2
-
-/*! \brief The backend engine mb_backend_t is used to define the interface to realize the MB.
- *
- * A backend is used to receive events from the system. The MB does not define the backend by itself.
- * Instead, it define an interface which allow the lower layer to implement the event system. Each
- * backend need to provides the following events.
- *
- * - keyboard event
- * - timer event
- * - image loader(?)
- * - render manager(?)
- */
-typedef struct {
-    
-    void *(*init)(char *display,int w,int h);
-    void (*free)(void *be);
-    void (*add_event)(void *be,int type, int fd, mb_eventcb_t f,void *arg);
-    void (*remove_event)(void *be,int type, int fd);
-    void (*loop)(void *be);
-    subject_t *(*kbevents)(void *be);
-    redraw_man_t *(*rdman)(void *be);
-    mb_tman_t *(*tman)(void *be);
-    ob_factory_t *(*factory)(void *be);
-    mb_img_ldr_t *(*loader)(void *be);
-} mb_backend_t;
-
-extern mb_backend_t backend;
+/* @} */
 
 #endif /* __REDRAW_MAN_H_ */
--- a/include/mb_shapes.h	Mon Jul 19 15:44:49 2010 +0800
+++ b/include/mb_shapes.h	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 /*! \file
  * \brief Declare interfaces of shapes.
  *
@@ -26,7 +28,7 @@
  *     - mb_obj_init() to initialize shape_t::obj.
  *   - assign *_free() to \ref shape_t::free.
  *   - make new object been managed by a redraw manager.
- *     - call rdman_shape_man()
+ *     - call rdman_man_shape()
  * - *_free()
  *   - assigned to \ref shape_t::free.
  * - *_transform()
@@ -34,7 +36,7 @@
  *   - *_draw() is responsive to define shape.  How the shape is filled
  *     or stroked is defined by paint.
  * - first member variable of a shape type must be a shape_t.
- * 
+ *
  * Must modify
  * - event.c::draw_shape_path()
  * - redraw_man.c::clean_shape()
@@ -48,14 +50,14 @@
  * update geo_t.
  *
  * \section rdman_man_shape Make a redraw manager managing a shape.
- * 
+ *
  * It means managing life cycle of a shape to make a redraw manager
  * managing a shape.  Shapes are referenced by a redraw manager to
  * update output device.  So, a redraw manager must make sure all
  * shapes are valid when they are needed to update output.  To make sure
  * lifecycle of shapes are synchronized with status of a redraw manager,
  * lifecycle of shapes are managed by associated redraw manager.
- * 
+ *
  */
 
 /*! \defgroup shapes Shapes
@@ -85,12 +87,12 @@
 				     co_aix font_size,
 				     mbe_font_face_t *face,PangoAttrList *attrs);
 /*! \brief Change the content of the text element.
- *  In the SVG, the content of a text tag can be composed of several tspan inside it. The Madbutterfly parser will collect all content of a 
+ *  In the SVG, the content of a text tag can be composed of several tspan inside it. The Madbutterfly parser will collect all content of a
  *  text segement as a single string. The attribute of these characters are saved in a seperate data structure. In the program level, we will
- *  not keep the SVG text tree. Instead, all attributes will be expanded into a list. 
+ *  not keep the SVG text tree. Instead, all attributes will be expanded into a list.
  *
- *  When you change the content of a text element, please remember that the attributes will not be removed by the way. You need to change 
- *  them seperately. 
+ *  When you change the content of a text element, please remember that the attributes will not be removed by the way. You need to change
+ *  them seperately.
  *
  */
 extern void sh_text_set_text(shape_t *shape, const char *txt);
@@ -142,34 +144,34 @@
 
 /*! \brief Change the style of the text.
  *
- *  This function can add a couple of attributes to a segment of text or the whole text field. If the @end is -1, the attributes 
+ *  This function can add a couple of attributes to a segment of text or the whole text field. If the @end is -1, the attributes
  *  will be applied to the whole text field. The @style should be initialized by using the mb_textstyle_xxx functions. All attributes
  *  which is not initialized will not be changed. It means that the @style will be added into all existing style instead of reaplcing
  *  it.
  */
 extern void sh_text_set_style(shape_t *shape,int begin,int end,mb_textstyle_t *format);
 /*! \brief Change the color of the text field
- *  Change the color of the whole text field. This will removed all existing color attribute. If you want to change part of the text 
+ *  Change the color of the whole text field. This will removed all existing color attribute. If you want to change part of the text
  *  field only, please use the sh_text_set_style instead.
  */
 extern void sh_text_set_color(shape_t *shape, unsigned color);
 /*! \brief Turn on/off the bold attribute.
- *  Turn on/off the font weight of the whole text field. This will removed all existing bold setting. If you want to change part of the text 
+ *  Turn on/off the font weight of the whole text field. This will removed all existing bold setting. If you want to change part of the text
  *  field only, please use the sh_text_set_style instead.
  */
 extern void sh_text_set_bold(shape_t *shape, int bold);
 /*! \brief Turn on/off the italic attribute.
- *  Turn on/off the italic of the whole text field. This will removed all existing italic setting. If you want to change part of the text 
+ *  Turn on/off the italic of the whole text field. This will removed all existing italic setting. If you want to change part of the text
  *  field only, please use the sh_text_set_style instead.
  */
 extern void sh_text_set_italic(shape_t *shape, int italic);
 /*! \brief Turn on/off the underline attribute.
- *  Turn on/off the underline of the whole text field. This will removed all existing underline setting. If you want to change part of the text 
+ *  Turn on/off the underline of the whole text field. This will removed all existing underline setting. If you want to change part of the text
  *  field only, please use the sh_text_set_style instead.
  */
 extern void sh_text_set_underline(shape_t *shape, int underline);
 /*! \brief Change the font of the text field.
- *  Change the font of the whole text field. This will removed all existing underline setting. If you want to change part of the text 
+ *  Change the font of the whole text field. This will removed all existing underline setting. If you want to change part of the text
  *  field only, please use the sh_text_set_style instead.
  */
 extern void sh_text_set_font(shape_t *shape, char *family);
@@ -195,7 +197,7 @@
         return NULL;
 }
 extern void mb_textstyle_set_bold(mb_textstyle_t *style, int bold);
-static inline int mb_textstyle_get_bold(mb_textstyle_t *style) 
+static inline int mb_textstyle_get_bold(mb_textstyle_t *style)
 {
     return style->property & TEXTSTYLE_BOLD;
 }
--- a/include/mb_so.h	Mon Jul 19 15:44:49 2010 +0800
+++ b/include/mb_so.h	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 /*! \file
  * \brief Define types for sprite shared objects.
  *
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/mb_sprite.h	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,69 @@
+#ifndef __MB_SPRITE_H_
+#define __MB_SPRITE_H_
+
+#include "mb_types.h"
+#include "mb_redraw_man.h"
+
+/*! \defgroup mb_sprite  Implement sprites for animation.
+ * @{
+ */
+/*! \brief A sprite is a set of graphics that being an object in animation.
+ *
+ * A sprite include graphics comprise an object.  For example, a tank, in
+ * example tank, is comprised a set of graphics that is represented as a
+ * sprite.
+ */
+struct _mb_sprite {
+    void (*free)(mb_sprite_t *);
+    mb_obj_t *(*get_obj_with_name)(mb_sprite_t *sprite, const char *id);
+    /*! Return non-zero for error. */
+    int (*goto_scene)(mb_sprite_t *sprite, int scene_no);
+};
+
+#define MB_SPRITE_FREE(sprite) ((mb_sprite_t *)(sprite))->free(sprite)
+#define MB_SPRITE_GET_OBJ(sprite, name)					\
+    ((mb_sprite_t *)(sprite))->get_obj_with_name((mb_sprite_t *)(sprite), \
+						 (name))
+#define MB_SPRITE_GOTO_SCENE(sprite, scene_no)				\
+    ((mb_sprite_t *)(sprite))->goto_scene((mb_sprite_t *)(sprite), scene_no)
+
+
+/*! \brief Load sprite dymanicly from the shared object module.
+ *
+ * The search path can be changed by sprite_set_search_path. The name
+ * can have a relative path in the front of it.
+ * This function will search the object in the current working directory
+ * and then search the system search path.
+ */
+extern mb_sprite_t *sprite_load(const char *name, redraw_man_t *rdman,
+				coord_t *root);
+
+/*! \brief Set the search path of dymanic object loading.
+ *
+ */
+extern void sprite_set_search_path(const char *path);
+
+/*! \defgroup mb_sprite_lsym Sprite with linear symbol table.
+ * @{
+ */
+struct _mb_sprite_lsym_entry {
+    const char *sym;
+    const int offset;
+};
+typedef struct _mb_sprite_lsym_entry mb_sprite_lsym_entry_t;
+
+/*! \brief A sub-type of mb_sprite_t with linear symbol table.
+ *
+ * This type of sprite search symbols with linear/or binary searching.
+ */
+struct _mb_sprite_lsym {
+    mb_sprite_t sprite;
+    int num_entries;
+    mb_sprite_lsym_entry_t *entries;
+};
+typedef struct _mb_sprite_lsym mb_sprite_lsym_t;
+/* @} */
+/* @} */
+
+#endif /* __MB_SPRITE_H_ */
+
--- a/include/mb_timer.h	Mon Jul 19 15:44:49 2010 +0800
+++ b/include/mb_timer.h	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #ifndef __MB_TIMER_H_
 #define __MB_TIMER_H_
 
--- a/include/mb_tools.h	Mon Jul 19 15:44:49 2010 +0800
+++ b/include/mb_tools.h	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #ifndef __TOOLS_H_
 #define __TOOLS_H_
 
--- a/include/mb_types.h	Mon Jul 19 15:44:49 2010 +0800
+++ b/include/mb_types.h	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #ifndef __MB_TYPES_H_
 #define __MB_TYPES_H_
 
@@ -8,6 +10,7 @@
 
 typedef struct _shape shape_t;
 typedef struct _geo geo_t;
+typedef struct _area area_t;
 typedef struct _shnode shnode_t;
 typedef struct _paint paint_t;
 typedef struct _mb_obj mb_obj_t;
@@ -30,9 +33,11 @@
  * mb_obj_t should be initialized with mb_obj_init() and destroied with
  * mb_obj_destroy().
  *
- * We have defined a set of convienent API which will wrap the coord_t or shape_t API accoridng to its type.
- * Please refer to http://www.assembla.com/wiki/show/dFrSMOtDer3BZUab7jnrAJ/MBAF_Object for the details. This
- * API is designed for regular programmers which can be used to change some common properties of objects without
+ * We have defined a set of convienent API which will wrap the coord_t
+ * or shape_t API accoridng to its type.  Please refer to
+ * http://www.assembla.com/wiki/show/dFrSMOtDer3BZUab7jnrAJ/MBAF_Object
+ * for the details. This API is designed for regular programmers which
+ * can be used to change some common properties of objects without
  * checking its type.
  */
 struct _mb_obj {
@@ -79,7 +84,7 @@
 struct _paint {
     int pnt_type;
     int flags;
-    void (*prepare)(paint_t *paint, mbe_t *cr);
+    void (*prepare)(paint_t *paint, mbe_t *cr, shape_t *sh);
     void (*free)(struct _redraw_man *rdman, paint_t *paint);
     STAILQ(shnode_t) members;
     paint_t *pnt_next;		/*!< \brief Collect all paints of a rdman. */
@@ -98,6 +103,11 @@
     shnode_t *next;
 };
 
+struct _area {
+    co_aix x, y;
+    co_aix w, h;
+};
+
 /*! \brief Geometry data of a shape or a group of shape.
  */
 struct _geo {
@@ -118,6 +128,13 @@
 #define GEF_FREE 0x4
 #define GEF_OV_DRAW 0x8		/*!< To flag drawed for a overlay testing. */
 #define GEF_SWAP 0x10
+#define GEF_NOT_SHOWED 0x20	/*!< This geo is not showed.
+				 *
+				 * A geo is not showed if it is hidden
+				 * or one of its ancestors is hidden.
+				 * Redraw manager uses this flag to
+				 * determine who is not showed.
+				 */
 
 extern int areas_are_overlay(area_t *r1, area_t *r2);
 extern void area_init(area_t *area, int n_pos, co_aix pos[][2]);
@@ -160,6 +177,12 @@
                                  *   cached. */
     area_t *pcache_cur_area;	/*!< Current area for parent cached. */
     area_t *pcache_last_area;	/*!< Last area for parent cached. */
+    co_aix cache_2_pdev[6];	/*!< Transfrom matrix from space of
+				 * cached one to its parent. */
+    co_aix cache_2_pdev_rev[6];	/*!< Reverse of cache_2_pdev. */
+    co_aix aggr_2_pdev[6];	/*!< Aggregation of cache_2_pdev from root  */
+    co_aix aggr_2_pdev_rev[6];	/*!< Aggregation of cache_2_pdev_rev
+				 * from root  */
 } coord_canvas_info_t;
 
 /*! \brief A coordination system.
@@ -214,7 +237,7 @@
 #define COF_DIRTY 0x1
 #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.
@@ -238,15 +261,19 @@
 				 */
 #define COF_TEMP_MARK 0x400	/*!< \brief Temporary mark a coord. */
 #define COF_JUST_ZERO 0x800	/*!< \brief The coord is real peformed zeroing.
-				 * 
+				 *
 				 * It's canvas is changed by zeroing.
 				 */
-#define COF_DIRTY_PCACHE_AREA 0x1000 /*!< \brief pcache_area shoud be
-                                      *  updated.
-				      */
+/*! \brief pcache_area shoud be updated.
+ *
+ * A coord is marked with COF_DIRTY_PCACHE_AREA means its pcache_area
+ * must be re-computed when zeroing.
+ */
+#define COF_DIRTY_PCACHE_AREA 0x1000 
 #define COF_SKIP_ZERO 0x2000	/*!< \brief The coord just skip zeroing.
 				 * No real zeroing was performed.
 				 */
+#define COF_ALWAYS_CACHE 0x4000	/*!< \brief The coord always own a canvas */
 /* @} */
 
 extern void matrix_mul(co_aix *m1, co_aix *m2, co_aix *dst);
@@ -284,21 +311,9 @@
 	(co)->flags &= ~COF_CACHE_MASK;				\
     } while(0)
 #define coord_is_root(co) ((co)->parent == NULL)
-#define coord_is_cached(co) ((co)->flags & COF_OWN_CANVAS)
-#define coord_is_fast_cached(co) ((co)->flags & COF_FAST_MASK)
-#define coord_is_precise_cached(co) ((co)->flags & COF_PRECISE_MASK)
-#define coord_is_zeroing(co) ((co)->flags & COF_MUST_ZEROING)
-#define coord_set_zeroing(co) \
-    do { (co)->flags |= COF_MUST_ZEROING; } while(0)
-#define coord_clear_zeroing(co) \
-    do { (co)->flags &= ~COF_MUST_ZEROING; } while(0)
-#define coord_set_flags(co, _flags)		\
-    do { (co)->flags |= (_flags); } while(0)
-#define coord_get_parent(co) ((co)->parent)
-#define coord_get_flags(co, _flags) ((co)->flags & (_flags))
-#define coord_clear_flags(co, _flags)		\
-    do { (co)->flags &= ~(_flags); } while(0)
 #define coord_get_mouse_event(coord) ((coord)->mouse_event)
+#define coord_get_opacity(coord) ((coord)->opacity)
+#define coord_set_opacity(coord, v) do { (coord)->opacity = v; } while(0)
 #define coord_get_aggr_matrix(coord) ((coord)->aggr_matrix)
 #define coord_get_matrix(coord) ((coord)->matrix)
 #define FOR_COORDS_POSTORDER(coord, cur)			\
@@ -333,18 +348,6 @@
 					       sh_get_geo(shape))))
 #define coord_get_area(coord) ((coord)->cur_area)
 #define coord_get_last_area(coord) ((coord)->last_area)
-#define coord_get_pcache_area(coord) ((coord)->canvas_info->pcache_cur_area)
-#define coord_get_pcache_last_area(coord)	\
-    ((coord)->canvas_info->pcache_last_area)
-#define coord_get_cached(coord) ((coord)->canvas_info->owner)
-#define _coord_get_canvas(coord) ((coord)->canvas_info->canvas)
-#define _coord_set_canvas(coord, _canvas)		\
-    do {						\
-	(coord)->canvas_info->canvas = _canvas;		\
-    } while(0)
-#define _coord_get_dirty_areas(coord) (&(coord)->canvas_info->dirty_areas)
-#define _coord_get_aggr_dirty_areas(coord)	\
-    ((coord)->canvas_info->aggr_dirty_areas)
 
 /* @} */
 
@@ -396,48 +399,4 @@
 #define sh_set_stroke_width(sh, v) do { (sh)->stroke_width = (v); } while(0)
 #define sh_get_stroke_width(sh) (sh)->stroke_width
 
-
-/*! \brief A sprite is a set of graphics that being an object in animation.
- *
- * A sprite include graphics comprise an object.  For example, a tank, in
- * example tank, is comprised a set of graphics that is represented as a
- * sprite.
- */
-struct _mb_sprite {
-    void (*free)(mb_sprite_t *);
-    mb_obj_t *(*get_obj_with_name)(mb_sprite_t *sprite, const char *id);
-    /*! Return non-zero for error. */
-    int (*goto_scene)(mb_sprite_t *sprite, int scene_no);
-};
-
-#define MB_SPRITE_FREE(sprite) ((mb_sprite_t *)(sprite))->free(sprite)
-#define MB_SPRITE_GET_OBJ(sprite, name)					\
-    ((mb_sprite_t *)(sprite))->get_obj_with_name((mb_sprite_t *)(sprite), \
-						 (name))
-#define MB_SPRITE_GOTO_SCENE(sprite, scene_no)				\
-    ((mb_sprite_t *)(sprite))->goto_scene((mb_sprite_t *)(sprite), scene_no)
-
-
-/*! \defgroup mb_sprite_lsym Sprite with linear symbol table.
- * @{
- */ 
-struct _mb_sprite_lsym_entry {
-    const char *sym;
-    const int offset;
-};
-typedef struct _mb_sprite_lsym_entry mb_sprite_lsym_entry_t;
-
-/*! \brief A sub-type of mb_sprite_t with linear symbol table.
- *
- * This type of sprite search symbols with linear/or binary searching.
- */
-struct _mb_sprite_lsym {
-    mb_sprite_t sprite;
-    int num_entries;
-    mb_sprite_lsym_entry_t *entries;
-};
-typedef struct _mb_sprite_lsym mb_sprite_lsym_t;
-
-/* @} */
-
 #endif /* __MB_TYPES_H_ */
--- a/include/mbbutton.h	Mon Jul 19 15:44:49 2010 +0800
+++ b/include/mbbutton.h	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #ifndef __MBBUTTON_H
 #define __MBBUTTON_H
 typedef struct _mb_button {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/INSTALL.txt	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,24 @@
+svg.js: 
+===============
+
+The sample code to load an SVG file. It requires the libxmljs, which is
+available at http://github.com/polotek/libxmljs/tree/refactor.  Please
+copy the libxmljs.node to the objs/default if you want to test the
+libxmljs+mbfly without installing them into the system. The path can be
+defined by the NODE_PATH environment variable.
+
+(1) Compile MadButterfly with nodejs support
+ ~user/MadButterfly$ ./configure --enable-nodejs; make
+
+(2) Compile libxmljs
+ ~user/MadButterfly/nodejs$ git clone http://github.com/polotek/libxmljs.git
+ ~user/MadButterfly/nodejs$ cd libxmljs; make; cd ..
+
+(3) Copy the libxmljs.node to the nodejs build directory
+ ~user/MadButterfly/nodejs$ cp libxmljs/libjsxml.node objs/default
+(4) Set the path
+ ~user/MadButterfly/nodejs$ export NODE_PATH=objs/default
+
+(5) Execute testsvg.js
+ ~user/MadButterfly/nodejs$ ./run examples/testsvg/testsvg.js
+
--- a/nodejs/Makefile.am	Mon Jul 19 15:44:49 2010 +0800
+++ b/nodejs/Makefile.am	Wed Dec 01 12:25:56 2010 +0800
@@ -1,5 +1,7 @@
+# -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+# vim: sw=4:ts=8:sts=4
 
-mbfly_node_SRCS = mbfly_njs.cc X_supp_njs.c coord.cc shapes.cc paints.cc \
+mbfly_node_SRCS = mbfly_njs.cc njs_mb_supp.c coord.cc shapes.cc paints.cc \
 		font.cc
 mbfly_node_CFLAGS= -I$(abs_top_builddir)/include \
 	-I$(abs_top_srcdir)/include \
@@ -7,6 +9,10 @@
 	@pangocairo_CFLAGS@ $(CFLAGS)
 mbfly_node_LDFLAGS = -L$(abs_top_builddir)/src/.libs @pangocairo_LIBS@
 
+if XSHM
+mbfly_node_LDFLAGS += -lXext
+endif
+
 all: mbfly.node
 clean: clean-mbfly-node
 
@@ -17,9 +23,10 @@
 		LDFLAGS="$(mbfly_node_LDFLAGS)" \
 		TOP_BUILDDIR="$(abs_top_builddir)" \
 		WAFLOCK=$(abs_builddir)/objs/.lock-wscript \
-		$(NODE_WAF) configure build --blddir=$(abs_builddir)/objs
+		$(NODE_WAF) configure build --srcdir=$(abs_srcdir) \
+		--blddir=$(abs_builddir)/objs
 
 clean-mbfly-node:
-	cd $(srcdir); \
-	WAFLOCK=$(abs_builddir)/.lock-wscript \
+	-cd $(srcdir); \
+	WAFLOCK=$(abs_builddir)/objs/.lock-wscript \
 		$(NODE_WAF) clean
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/README.h	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,11 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
+/*! \defgroup xnjsmb MadButterfly JS binding for V8 and nodejs
+ *
+ * This implementation of JS binding heavily relies on
+ * tools/gen_v8_binding.m4, a set of m4 macros.  We defines binding in
+ * nodejs/*.m4 files and generate respective code, nodejs/*-inc.h,
+ * with macros defined in tools/gen_v8_binding.m4.  *-inc.h files are
+ * included by respective *.cc files they implement functions needed
+ * by generated code.
+ */
--- a/nodejs/X_supp_njs.c	Mon Jul 19 15:44:49 2010 +0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,151 +0,0 @@
-/*! \brief Implement X11 backend for nodejs plugin.
- *
- * Since nodejs use libev to handle event loops, part of X11 backend
- * code can not be used directly.  The part of code should be rewrote.
- * The part is about
- */
-#include <stdio.h>
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
-#include <ev.h>
-#include "mb_X_supp.h"
-#include "mb_tools.h"
-#include "X_supp_njs.h"
-
-#ifndef ASSERT
-#define ASSERT(x)
-#endif
-
-static void timer_cb(EV_P_ ev_timer *tmwatcher, int revent);
-
-/*! \brief Register next timeout with libev.
- */
-static void
-set_next_timeout(njs_runtime_t *rt) {
-    mb_tman_t *tman;
-    mb_timeval_t now, tmo;
-    ev_tstamp tout;
-    int r;
-    
-    tman = X_MB_tman(rt->xrt);
-    get_now(&now);
-    r = mb_tman_next_timeout(tman, &now, &tmo);
-    if(r == 0) {
-	MB_TIMEVAL_DIFF(&tmo, &now);
-	tout = (ev_tstamp)MB_TIMEVAL_SEC(&tmo) +
-	    (ev_tstamp)MB_TIMEVAL_USEC(&tmo) / 1000000;
-	ev_timer_init(&rt->tmwatcher, timer_cb, tout, 0);
-	ev_timer_start(&rt->tmwatcher);
-	rt->enable_timer = 1;
-    } else
-	rt->enable_timer = 0;
-}
-
-static void
-x_conn_cb(EV_P_ ev_io *iowatcher, int revent) {
-    njs_runtime_t *rt = MEM2OBJ(iowatcher, njs_runtime_t, iowatcher);
-    redraw_man_t *rdman;
-    extern void _X_MB_handle_x_event_for_nodejs(void *rt);
-
-    rdman = X_MB_rdman(rt->xrt);
-    _X_MB_handle_x_event_for_nodejs(rt->xrt);
-    rdman_redraw_changed(rdman);
-    
-    if(rt->enable_timer == 0) /* no installed timeout */
-	set_next_timeout(rt);
-}
-
-static void
-timer_cb(EV_P_ ev_timer *tmwatcher, int revent) {
-    njs_runtime_t *rt = MEM2OBJ(tmwatcher, njs_runtime_t, tmwatcher);
-    mb_tman_t *tman;
-    redraw_man_t *rdman;
-    mb_timeval_t now;
-    extern int _X_MB_flush_x_conn_for_nodejs(void *rt);
-    
-    tman = X_MB_tman(rt->xrt);
-    get_now(&now);
-    mb_tman_handle_timeout(tman, &now);
-
-    rdman = X_MB_rdman(rt->xrt);
-    rdman_redraw_changed(rdman);
-    _X_MB_flush_x_conn_for_nodejs(rt->xrt);
-    
-    set_next_timeout(rt);
-}
-
-/*! \brief Handle connection coming data and timeout of timers.
- *
- * \param rt is a runtime object for X.
- */
-void
-X_njs_MB_init_handle_connection(njs_runtime_t *rt) {
-    void *xrt = rt->xrt;
-    mb_tman_t *tman;
-    mb_timeval_t now, tmo;
-    ev_tstamp tout;
-    int fd;
-    int r;
-    extern int _X_MB_get_x_conn_for_nodejs(void *rt);
-
-    /*
-     * Setup watcher for X connection.
-     */
-    fd = _X_MB_get_x_conn_for_nodejs(xrt);
-    ev_io_init(&rt->iowatcher, x_conn_cb, fd, EV_READ);
-    ev_io_start(&rt->iowatcher);
-    rt->enable_io = 1;
-
-    set_next_timeout(rt);
-}
-
-/*! \brief Free njs_runtime_t.
- */
-void
-X_njs_MB_free(njs_runtime_t *rt) {
-    /*
-     * stop IO and timer watcher
-     */
-    if(rt->enable_io)
-	ev_io_stop(&rt->iowatcher);
-    if(rt->enable_timer)
-	ev_timer_stop(&rt->tmwatcher);
-    
-    X_MB_free(rt->xrt);
-    free(rt);
-}
-
-int
-X_njs_MB_flush(njs_runtime_t *rt) {
-    void *xrt = rt->xrt;
-    int r;
-    extern int _X_MB_flush_x_conn_for_nodejs(void *rt);
-
-    _X_MB_flush_x_conn_for_nodejs(xrt);
-    
-    return r;
-}
-
-njs_runtime_t *
-X_njs_MB_new(char *display_name, int w, int h) {
-    njs_runtime_t *rt;
-    void *xrt;
-    
-    rt = (njs_runtime_t *)malloc(sizeof(njs_runtime_t));
-    ASSERT(rt != NULL);
-
-    xrt = X_MB_new(display_name, w, h);
-
-    rt->xrt = xrt;
-    rt->enable_io = 0;
-    rt->enable_timer = 0;	/* no timer, now */
-    
-    return rt;
-}
-
-/*! \brief Get X runtime that is backend of this njs runtime.
- */
-void *
-_X_njs_MB_get_X_runtime(njs_runtime_t *rt) {
-    return rt->xrt;
-}
--- a/nodejs/X_supp_njs.h	Mon Jul 19 15:44:49 2010 +0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,26 +0,0 @@
-#ifndef __X_SUPP_NJS_H_
-#define __X_SUPP_NJS_H_
-
-#include <ev.h>
-
-typedef struct _njs_runtime {
-    ev_io iowatcher;
-    ev_timer tmwatcher;
-    int enable_io;
-    int enable_timer;
-    void *xrt;
-} njs_runtime_t;
-
-extern void X_njs_MB_init_handle_connection(njs_runtime_t *rt);
-extern void X_njs_MB_free(njs_runtime_t *rt);
-extern njs_runtime_t *X_njs_MB_new(char *display_name, int w, int h);
-extern int X_njs_MB_flush(njs_runtime_t *rt);
-extern void *_X_njs_MB_get_X_runtime(njs_runtime_t *rt);
-
-#define X_njs_MB_kbevents(rt) X_MB_kbevents((rt)->xrt)
-#define X_njs_MB_rdman(rt) X_MB_rdman((rt)->xrt)
-#define X_njs_MB_tman(rt) X_MB_tman((rt)->xrt)
-#define X_njs_MB_ob_factory(rt) X_MB_ob_factory((rt)->xrt)
-#define X_njs_MB_img_ldr(rt) X_MB_img_ldr((rt)->xrt)
-
-#endif /* __X_SUPP_NJS_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/animate.js	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,358 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
+var sys=require("sys");
+
+/*
+ * This is configuration for animate module.  For slower or speeder
+ * machines, ffs can be decreased or increased respective.
+ */
+var ffs = 12;
+var frame_interval = 1000 / ffs;
+
+function shift_draw(percent) {
+    var x, y;
+    
+    x = (this.targetx - this.startposx) * percent + this.startposx;
+    y = (this.targety - this.startposy) * percent + this.startposy;
+    this.obj.center.move(x, y);
+    this._app.refresh();
+}
+
+function shift(app,obj,shiftx,shifty) {
+    obj.animated_shift = this;
+    this._app = app;
+    this.obj = obj;
+    this.end = 0;
+    this.targetx = shiftx + obj.center.x;
+    this.targety = shifty + obj.center.y;
+    this.startposx = obj.center.x;
+    this.startposy = obj.center.y;
+}
+
+exports.shift = shift;
+shift.prototype.draw = shift_draw;
+
+/* ------------------------------------------------------------ */
+function rotate(app, obj, ang, duration) {
+    this._app = app;
+    this._obj = obj;
+    this._ang = ang;
+    this._start_mtx = [obj[0], obj[1], obj[2], obj[3], obj[4], obj[5]];
+}
+
+function rotate_draw(percent) {
+    var percent;
+    var ang;
+    var sv, cv;
+    var obj = this._obj;
+    var mtx, shift;
+
+
+    ang = percent * this._ang;
+    sv = Math.sin(ang);
+    cv = Math.cos(ang);
+    mtx = [cv, -sv, 0, sv, cv, 0];
+    shift = [1, 0, -obj.center.x, 0, 1, -obj.center.y];
+    mtx = multiply(mtx, shift);
+    shift = [1, 0, obj.center.x, 0, 1, obj.center.y];
+    mtx = multiply(shift, mtx);
+    mtx = multiply(mtx, this._start_mtx);
+
+    obj[0] = mtx[0];
+    obj[1] = mtx[1];
+    obj[2] = mtx[2];
+    obj[3] = mtx[3];
+    obj[4] = mtx[4];
+    obj[5] = mtx[5];
+
+    this._app.refresh();
+}
+
+rotate.prototype.draw = rotate_draw;
+exports.rotate = rotate;
+
+function multiply(s,d) {
+    var m=[];
+    m[0] = s[0]*d[0]+s[1]*d[3];
+    m[1] = s[0]*d[1]+s[1]*d[4];
+    m[2] = s[0]*d[2]+s[1]*d[5]+s[2];
+    m[3] = s[3]*d[0]+s[4]*d[3];
+    m[4] = s[3]*d[1]+s[4]*d[4];
+    m[5] = s[3]*d[2]+s[4]*d[5]+s[5];
+    return m;
+}
+
+
+function scale_draw(percent) {
+    if (this.end==1) {
+        percent = 1;
+    }
+    var sx = 1 + (this.totalsx - 1) * percent;
+    var sy = 1 + (this.totalsy - 1) * percent;
+    var sh1 = [1, 0, -this.center_x, 0, 1, -this.center_y];
+    var sh2 = [1, 0, this.center_x, 0, 1, this.center_y];
+    sys.puts("sc="+sx+" sy="+sy);
+    var scale = [sx, 0, 0, 0, sy, 0];
+    var obj = this.obj;
+    var mtx;
+
+    mtx = multiply(scale, sh1);
+    mtx = multiply(sh2, mtx);
+    mtx = multiply(this.orig_mtx, mtx);
+    obj[0] = mtx[0];
+    obj[1] = mtx[1];
+    obj[2] = mtx[2];
+    obj[3] = mtx[3];
+    obj[4] = mtx[4];
+    obj[5] = mtx[5];
+
+    this.app.refresh();
+}
+
+function scale(app, obj, fact_x, fact_y, duration) {
+    var bbox;
+    
+    try {
+        if (obj.animated_scale) {
+	    obj.animated_scale.end = 1;
+	}
+    } catch(e) {
+	    
+    }
+
+    bbox = obj.bbox;
+    bbox.update();
+    obj.animated_scale = this;
+    this.app = app;
+    this.obj = obj;
+    this.end = 0;
+    this.starttime = Date.now();
+    this.totalsx = fact_x * bbox.orig.width / bbox.width;
+    this.totalsy = fact_y * bbox.orig.height / bbox.height;
+    this.duration = duration*1000;
+    this.center_x = obj.center.rel.x;
+    this.center_y = obj.center.rel.y;
+    this.orig_mtx = [obj[0], obj[1], obj[2], obj[3], obj[4], obj[5]];
+}
+
+
+exports.scale = scale;
+scale.prototype.draw = scale_draw;
+
+function holder(app, coord) {
+    var mtx = [coord[0], coord[1], coord[2], coord[3], coord[4], coord[5]];
+    
+    this._mtx = mtx;
+    this._coord = coord;
+    this._app = app;
+}
+
+holder.prototype = {
+    go: function(pos) {
+	var sx, sy;
+
+	sx = pos.x - this._coord.center.x;
+	sy = pos.y - this._coord.center.y;
+	this.shift(sx, sy);
+    },
+
+    go_center: function(o) {
+	this.go(o.center);
+    },
+    
+    home: function() {
+	this._coord[2] = this._mtx[2];
+	this._coord[5] = this._mtx[5];
+	this._app.refresh();
+    },
+
+    shift: function(sx, sy) {
+	this._coord[2] = this._mtx[2] + sx;
+	this._coord[5] = this._mtx[5] + sy;
+	this._app.refresh();
+    }
+};
+
+exports.holder = holder;
+
+
+
+function alpha_draw(percent) {
+
+    if (this.end == 1) return;
+    var sx = (this.targetalpha-this.startalpha)*percent+this.startalpha;
+    this.obj.opacity=sx;
+    this.app.refresh();
+    this.obj.animated_alpha = null;
+}
+
+function alpha(app,obj,alpha, duration) {
+    try {
+        if (obj.animated_alpha) {
+	    obj.animated_alpha.end = 1;
+	}
+    } catch(e) {
+	    
+    }
+    obj.animated_alpha = this;
+    this.app = app;
+    this.obj = obj;
+    this.end = 0;
+    this.starttime = Date.now();
+    this.startalpha = obj.opacity;
+    this.targetalpha = alpha;
+    this.duration = duration*1000;
+}
+
+alpha.prototype.draw = alpha_draw;
+exports.alpha = alpha;
+
+function linear_update()
+{
+    var now = Date.now();
+    var i;
+    
+    if (now >= this.end) {
+        this.timer.stop();
+	now = this.end;
+        if (this.callback_end) {
+            this.callback_end();
+    	    this.callback_end=null;
+        }
+    }
+    if (now < this.startmove) return;
+    var per = (now-this.startmove)/this.duration/1000;
+    if (per > 1) per = 1;
+    this.action.draw(per);
+}
+
+function linear_start()
+{
+    var self = this;
+    if (this.timer)
+        this.timer.stop();
+    this.timer = setInterval(function() { self.update();}, frame_interval);
+    this.startmove = Date.now()+this.starttime*1000;
+    this.end = this.startmove+this.duration*1000;
+}
+function linear_stop() 
+{
+    if (this.timer) {
+        this.timer.stop();
+	this.timer = null;
+    }
+}
+
+function linear_finish()
+{
+    this.action.draw(1);
+    if (this.callback_end) {
+        this.callback_end();
+	this.callback_end=null;
+    }
+}
+function linear(action,start, duration) 
+{
+    this.action = action;
+    this.duration = duration;
+    this.starttime = start;
+    this.callback_end = null;
+    this.timer=null;
+}
+
+function linear_callback(cb)
+{
+    this.callback_end = cb;
+}
+
+linear.prototype.update = linear_update;
+linear.prototype.start = linear_start;
+linear.prototype.stop = linear_stop;
+linear.prototype.finish = linear_finish;
+linear.prototype.callbackAtEnd = linear_callback;
+exports.linear = linear;
+
+
+function multilinear_update()
+{
+}
+
+function multilinear_start()
+{
+}
+
+function multilinear_stop()
+{
+}
+
+function multilinear_finish()
+{
+}
+function multilinear(action,start,stages) {
+    sys.puts("Multilinear word is not implemented yet");
+}
+
+exports.multilinear = multilinear;
+multilinear.prototype.update = multilinear_update;
+multilinear.prototype.start = multilinear_start;
+multilinear.prototype.stop = multilinear_stop;
+multilinear.prototype.finish = multilinear_finish;
+function exponential_update()
+{
+}
+function exponential_start()
+{
+}
+function exponential_stop()
+{
+}
+function exponential_finish()
+{
+}
+function exponential(action,start,stages) {
+    sys.puts("exponential word is not implemented yet");
+}
+
+exports.exponential = exponential;
+exponential.prototype.update = exponential_update;
+exponential.prototype.start = exponential_start;
+exponential.prototype.stop = exponential_stop;
+exponential.prototype.finish = exponential_finish;
+
+function program(words)
+{
+    this.words = wrods;
+}
+
+program.prototype.start=function() {
+    for(w in this.words) {
+        w.start();
+    }
+}
+
+program.prototype.step=function(s) {
+    sys.puts("This function is not implemented yet");
+}
+program.prototype.stop=function() {
+    for(w in this.words) {
+        w.stop();
+    }
+}
+program.prototype.finish=function() {
+    for(w in this.words) {
+        w.finish();
+    }
+}
+
+exports.run = function(actions,start,duration,cb) {
+    var li;
+    for(a in actions) {
+        li = new linear(actions[a],start,duration);
+	li.start();
+    }
+    li.callbackAtEnd(cb);
+}
+exports.runexp=function(actions,start,exp) {
+    sys.puts("This function is not implemented yet");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/canvas.js	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,109 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
+var mbfly = require("mbfly");
+var sys=require("sys");
+
+
+function canvas(app,root) {
+    this.mb_rt = app.mb_rt;
+	this.parent = root;
+    this.root = this.mb_rt.coord_new(root);
+	this.bg_r = 0;
+	this.bg_g = 0;
+	this.bg_b = 0;
+	this.bg_a = 0;
+	this.stroke_r = 0;
+	this.stroke_g = 0;
+	this.stroke_b = 0;
+	this.stroke_a = 0;
+	this.stroke_w = 1;
+}
+
+canvas.prototype.background=function(r,g,b,a) {
+    this.bg_r = r;
+	this.bg_g = g;
+	this.bg_b = b;
+	this.bg_a = a;
+}
+
+canvas.prototype.rect=function(x,y,w,h) {
+    var rect = this.mb_rt.rect_new(x,y,w,h,0,0);
+    var paint = this.mb_rt.paint_color_new(this.bg_r,this.bg_g,this.bg_b,this.bg_a);
+	paint.fill(rect);
+	this.root.add_shape(rect);
+}
+
+canvas.prototype.stroke=function(r,g,b,a) {
+    this.stroke_r = r;
+    this.stroke_g = g;
+    this.stroke_b = b;
+    this.stroke_a = a;
+}
+
+
+canvas.prototype.line=function(x1,y1,x2,y2) {
+    var s = "M "+x1+","+y1+" L "+x2+","+y2;
+    //sys.puts(s);
+
+    var p = this.mb_rt.path_new(s);
+	this.root.add_shape(p);
+	var paint = this.mb_rt.paint_color_new(this.stroke_r,this.stroke_g,this.stroke_b,this.stroke_a);
+	paint.stroke(p);
+	p.stroke_width = this.stroke_w;
+}
+
+canvas.prototype.strokeWeight=function(w) {
+    this.stroke_w = w;
+}
+
+canvas.prototype.alpha=function(c) {
+}
+
+canvas.prototype.red=function(c) {
+}
+canvas.prototype.green=function(c) {
+}
+canvas.prototype.blue=function(c) {
+}
+
+
+canvas.prototype.clear=function() {
+    this.root.remove();
+    this.root = this.mb_rt.coord_new(this.parent);
+}
+
+canvas.prototype.arc=function(x,y,w,h,start,end) {
+}
+
+canvas.prototype.ellipse=function(x, y, width, height) {
+}
+
+canvas.prototype.point=function(x,y) {
+}
+
+canvas.prototype.quad=function(x1,y1,x2,y2,x3,y3,x4,y4) {
+}
+
+canvas.prototype.triangle=function(x1,y1,x2,y2,x3,y3) {
+}
+
+canvas.prototype.bezier=function(x1, y1, cx1, cy1, cx2, cy2, x2, y2) {
+}
+
+canvas.prototype.curveTightness=function(squishy) {
+}
+
+canvas.prototype.colorMode=function() {
+}
+
+canvas.prototype.fill=function(color) {
+}
+
+canvas.prototype.noFill=function() {
+}
+
+canvas.prototype.noStroke=function() {
+}
+
+
+exports.canvas = canvas;
--- a/nodejs/coord.cc	Mon Jul 19 15:44:49 2010 +0800
+++ b/nodejs/coord.cc	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include <stdio.h>
 #include <v8.h>
 
@@ -5,7 +7,7 @@
 #include "mb.h"
 #include "mb_X_supp.h"
 #include "mb_tools.h"
-#include "X_supp_njs.h"
+#include "njs_mb_supp.h"
 }
 
 #include "mbfly_njs.h"
@@ -14,163 +16,346 @@
 #define ASSERT(x)
 #endif
 
+#define OK 0
+
+/*! \page jsgc How to Manage Life-cycle of Objects for Javascript.
+ *
+ * The life-cycle of MadButterfly ojects are simple.  A object is live
+ * when it is created and dead when it is free.  When a coord or shape
+ * is free, it is also removed from the tree.  There is not way to
+ * remove a coord or a shape without freeing it.  So, if you want to
+ * remove a coord or a shape object from the tree, you can only free
+ * it.
+ *
+ * Javascript, in conventional, does not free an object.  It has GC,
+ * the engine, being used, will free an object if it is no more
+ * referenced.  So, we had better provide a removing function, but
+ * actually free an object.  In idea situation, a new MB object would
+ * be created for and attached on the JS object, when an object added
+ * back to the tree.  But, it means we need to keep states of an
+ * object and create a new one with the same states later.  It is
+ * complicated.  So, once an object is removed, it is invalidated.
+ *
+ * I hope someone would implement a higher abstract layer, in JS, to
+ * implement the idea model that recreate a new object when an
+ * invalidated JS object being added back.
+ *
+ * An invalid object is the one with NULL internal field and obj.valid
+ * == false.  The binding of MadButterfly hold a reference to every
+ * object added to the tree of a mbrt (runtime object), and remove the
+ * reference and invalidate it when it being removed.
+ *
+ * For coords, they are always attached to the tree when it is valid.
+ * So, binding hold a persistent reference to it.  The reference is
+ * purged when a coord being removed from the tree and being
+ * invalidated.
+ *
+ * For any shape, it is not attached to the tree at begining, but is
+ * attached to a tree laterly, or is collected by GC.  The binding
+ * hold a weak reference for a new shape, and upgrade to a strong
+ * reference when the shape being added to the tree.
+ */
+
 using namespace v8;
 
-static Handle<Value>
-xnjsmb_coord_get_index(uint32_t index, const AccessorInfo &info) {
-    HandleScope scope;
-    Handle<Object> self;
-    coord_t *coord;
-    co_aix v;
+/*! \defgroup xnjsmb_coord JS binding for coord objects.
+ * \ingroup xnjsmb
+ *
+ * @{
+ */
+/*! \brief Invalidate JS objects for coords and shapes in a subtree.
+ *
+ * \param self is the object of the root of subtree.
+ *
+ * \sa \ref jsgc
+ */
+static void
+xnjsmb_coord_invalidate_subtree(coord_t *coord) {
+    Persistent<Object> *child_hdl;
+    Persistent<Object> *mem_hdl;
+    coord_t *child;
+    shape_t *mem;
+    Handle<Value> _false = Boolean::New(0);
 
-    if(index < 0 || index >= 6)
-	THROW("Invalid index");
-    
-    self = info.This();
-    coord = (coord_t *)UNWRAP(self);
-    v = coord_get_matrix(coord)[index];
+    /* Invalidate all coords in the subtree */
+    FOR_COORDS_PREORDER(coord, child) {
+	child_hdl = (Persistent<Object> *)mb_prop_get(&child->obj.props,
+						      PROP_JSOBJ);
+	SET(*child_hdl, "valid", _false);
+	WRAP(*child_hdl, NULL);
+	child_hdl->Dispose();
+	delete child_hdl;
 
-    return Number::New(v);
+	/* Invalidate members of a coord */
+	FOR_COORD_SHAPES(child, mem) {
+	    mem_hdl = (Persistent<Object> *)mb_prop_get(&mem->obj.props,
+							PROP_JSOBJ);
+	    SET(*mem_hdl, "valid", _false);
+	    WRAP(*mem_hdl, NULL);
+	    mem_hdl->Dispose();
+	    delete mem_hdl;
+	}
+    }
 }
 
-static Handle<Value>
-xnjsmb_coord_set_index(uint32_t index, Local<Value> value,
-		       const AccessorInfo &info) {
-    
-    HandleScope scope;
-    Handle<Object> self;
+/*! \brief Free C objects for coords and shapes in a subtree.
+ *
+ * \param self is the object of the root of subtree.
+ *
+ * \sa \ref jsgc
+ */
+static void
+xnjsmb_coord_free_subtree(redraw_man_t *rdman, coord_t *coord) {
+    coord_t *child, *last_child;
+    shape_t *mem, *last_mem;
+    int r;
+
+    rdman_coord_changed(rdman, coord);
+
+    last_child = NULL;
+    FOR_COORDS_POSTORDER(coord, child) {
+	if(last_child != NULL) {
+	    r = rdman_coord_free(rdman, last_child);
+	    if(r != OK)
+		THROW_noret("Unknown error");
+	}
+
+	/* Free members of a coord */
+	last_mem = NULL;
+	FOR_COORD_SHAPES(child, mem) {
+	    if(last_mem != NULL) {
+		r = rdman_shape_free(rdman, last_mem);
+		if(r != OK)
+		    THROW_noret("Unknown error");
+	    }
+
+	    last_mem = mem;
+	}
+	if(last_mem != NULL) {
+	    r = rdman_shape_free(rdman, last_mem);
+	    if(r != OK)
+		THROW_noret("Unknown error");
+	}
+
+	last_child = child;
+    }
+    if(last_child != NULL) {
+	r = rdman_coord_free(rdman, last_child);
+	if(r != OK)
+	    THROW_noret("Unknown error");
+    }
+}
+
+static void
+xnjsmb_coord_mod(Handle<Object> self, coord_t *coord) {
+    Persistent<Object> *self_hdl;
+    subject_t *subject;
+    Handle<Value> subject_o;
+
+    /* Keep associated js object in property store for retrieving,
+     * later, without create new js object.
+     */
+    self_hdl = new Persistent<Object>();
+    *self_hdl = Persistent<Object>::New(self);
+    mb_prop_set(&coord->obj.props, PROP_JSOBJ, self_hdl);
+
+    subject = coord->mouse_event;
+    subject_o = export_xnjsmb_auto_subject_new(subject);
+    SET(self, "mouse_event", subject_o);
+    SET(self, "valid", Boolean::New(1));
+}
+
+static float
+coord_get_index(coord_t *coord, Handle<Object> self, int idx,
+		const char **err) {
+    if(idx < 0 || idx >= 6) {
+        *err = "Invalid index: out of range";
+        return 0;
+    }
+
+    return coord_get_matrix(coord)[idx];
+}
+
+static float
+coord_set_index(coord_t *coord, Handle<Object> self,
+		int idx, float v, const char **err) {
     Handle<Object> js_rt;
     redraw_man_t *rdman;
-    coord_t *coord;
-    co_aix v;
 
-    if(index < 0 || index >= 6)
-	THROW("Invalid Index");
-    if(!value->IsNumber())
-	THROW("Invalid value");
+    if(idx < 0 || idx >= 6) {
+        *err = "Invalid index: out of range";
+        return 0;
+    }
 
-    self = info.This();
-    coord = (coord_t *)UNWRAP(self);
-    v = value->NumberValue();
-    coord_get_matrix(coord)[index] = v;
+    coord_get_matrix(coord)[idx] = v;
 
     js_rt = GET(self, "mbrt")->ToObject();
     rdman = xnjsmb_rt_rdman(js_rt);
     rdman_coord_changed(rdman, coord);
 
-    return value;
+    return v;
 }
 
-/*! \brief Callback functio to add a shape to a coord in Javascript.
- *
- * coord.add_shape(shape)
- */
-static Handle<Value>
-xnjsmb_coord_add_shape(const Arguments &args) {
-    int argc = args.Length();
-    Handle<Object> self = args.This();
-    Handle<Object> shape_obj;
-    Handle<Object> rt_obj;
-    Handle<Value> rt_val;
+static void
+xnjsmb_coord_add_shape(coord_t *coord, Handle<Object> self,
+			shape_t *shape, const char **err) {
+    Handle<Object> js_rt;
+    Persistent<Object> *shape_hdl;
     redraw_man_t *rdman;
-    coord_t *coord;
-    shape_t *sh;
     int r;
 
-    if(argc != 1)
-	THROW("Invalid number of arguments (!= 1)");
-    
-    shape_obj = args[0]->ToObject();
-    sh = (shape_t *)UNWRAP(shape_obj);
-    ASSERT(sh != NULL);
-    
-    coord = (coord_t *)UNWRAP(self);
-    ASSERT(coord != NULL);
+    js_rt = GET(self, "mbrt")->ToObject();
+    rdman = xnjsmb_rt_rdman(js_rt);
+    r = rdman_add_shape(rdman, shape, coord);
+    if(r != 0)
+	*err = "Unknown error";
+
+    /* see \ref jsgc */
+    shape_hdl = (Persistent<Object> *)mb_prop_get(&shape->obj.props,
+						  PROP_JSOBJ);
+    shape_hdl->ClearWeak();
+    rdman_shape_changed(rdman, shape);
+}
 
-    rt_val = GET(self, "mbrt");
-    rt_obj = rt_val->ToObject();
-    rdman = xnjsmb_rt_rdman(rt_obj);
-    
-    r = rdman_add_shape(rdman, sh, coord);
-    if(r != 0)
-	THROW("Unknown error");
+static void
+xnjsmb_coord_remove(coord_t *coord, Handle<Object> self) {
+    Handle<Object> js_rt;
+    redraw_man_t *rdman;
 
-    return Null();
+    if(!GET(self, "valid")->ToBoolean()->Value()) /* Invalidated object */
+	THROW_noret("Invalid object");
+
+    js_rt = GET(self, "mbrt")->ToObject();
+    rdman = xnjsmb_rt_rdman(js_rt);
+
+    xnjsmb_coord_invalidate_subtree(coord);
+    xnjsmb_coord_free_subtree(rdman, coord);
 }
 
-static Persistent<ObjectTemplate> coord_obj_temp;
+static void
+xnjsmb_coord_show(coord_t *coord, Handle<Object> self) {
+    Handle<Object> js_rt;
+    redraw_man_t *rdman;
+
+    js_rt = GET(self, "mbrt")->ToObject();
+    ASSERT(js_rt != NULL);
+    rdman = xnjsmb_rt_rdman(js_rt);
+
+    coord_show(coord);
+    rdman_coord_changed(rdman, coord);
+}
 
 static void
-xnjsmb_init_temp(void) {
-    Handle<FunctionTemplate> add_shape_temp;
+xnjsmb_coord_hide(coord_t *coord, Handle<Object> self) {
+    Handle<Object> js_rt;
+    redraw_man_t *rdman;
+
+    js_rt = GET(self, "mbrt")->ToObject();
+    ASSERT(js_rt != NULL);
+    rdman = xnjsmb_rt_rdman(js_rt);
+
+    coord_hide(coord);
+    rdman_coord_changed(rdman, coord);
+}
+
+static void
+xnjsmb_coord_set_opacity(Handle<Object> self, coord_t *coord, Handle<Value> value, const char **str)
+{
+    Handle<Object> js_rt;
+    redraw_man_t *rdman;
     
-    coord_obj_temp = Persistent<ObjectTemplate>::New(ObjectTemplate::New());
-    coord_obj_temp->SetIndexedPropertyHandler(xnjsmb_coord_get_index,
-					      xnjsmb_coord_set_index);
-    coord_obj_temp->SetInternalFieldCount(1);
+    js_rt = GET(self, "mbrt")->ToObject();
+    ASSERT(js_rt != NULL);
+    rdman = xnjsmb_rt_rdman(js_rt);
 
-    add_shape_temp = FunctionTemplate::New(xnjsmb_coord_add_shape);
-    SET(coord_obj_temp, "add_shape", add_shape_temp);
+    
+    coord_set_opacity(coord, value->NumberValue());
+    rdman_coord_changed(rdman, coord);
 }
 
-/*! \brief Create and initialize a Javascript object for a coord.
- */
-static Handle<Object>
-xnjsmb_coord_new_jsobj(coord_t *coord, Handle<Object> parent_obj,
-		       Handle<Object> js_rt) {
-    Handle<Object> coord_obj;
-    static int init_temp = 0;
-    
-    if(!init_temp) {
-	xnjsmb_init_temp();
-	init_temp = 1;
-    }
+static Handle<Value>
+xnjsmb_coord_get_opacity(Handle<Object> self, coord_t *coord,
+			      const char **err) {
+    float opacity;
+
+    opacity = coord_get_opacity(coord);
+    return Number::New(opacity);
+}
 
-    coord_obj = coord_obj_temp->NewInstance();
-    ASSERT(coord_obj != NULL);
-    WRAP(coord_obj, coord);
+#define cc(i) (coord_get_matrix(coord)[i])
+static void
+xnjsmb_coord_set_y(Handle<Object> self, coord_t *coord, Handle<Value> value, const char **str)
+{
+    Handle<Object> js_rt;
+    redraw_man_t *rdman;
+    co_aix y,ty;
+    co_aix xx,yy;
 
-    if(!parent_obj.IsEmpty())
-	SET(coord_obj, "parent", parent_obj);
-    SET(coord_obj, "mbrt", js_rt);
+    js_rt = GET(self, "mbrt")->ToObject();
+    ASSERT(js_rt != NULL);
+    rdman = xnjsmb_rt_rdman(js_rt);
+
 
-    return coord_obj;
+    ty = value->NumberValue();
+    xx = GET(self,"_x")->ToNumber()->NumberValue();
+    yy = GET(self,"_y")->ToNumber()->NumberValue();
+    y = ty-cc(3)*xx-cc(4)*yy;
+    coord_get_matrix(coord)[5] = y;
+    rdman_coord_changed(rdman, coord);
 }
 
-/*! \brief Create a coord object associated with the rdman of the runtime.
- *
- * Two internal fields, coord and rdman.
- */
-Handle<Value>
-xnjsmb_coord_new(const Arguments &args) {
-    HandleScope scope;
-    Handle<Object> js_rt;
-    Handle<Object> coord_obj, parent_obj;
-    njs_runtime_t *rt;
-    redraw_man_t *rdman;
-    coord_t *coord, *parent = NULL;
-    int argc;
+static Handle<Value>
+xnjsmb_coord_get_y(Handle<Object> self, coord_t *coord,
+			      const char **err) {
+    co_aix y;
+    co_aix xx,yy;
+    
+    xx = GET(self,"_x")->ToNumber()->NumberValue();
+    yy = GET(self,"_y")->ToNumber()->NumberValue();
 
-    argc = args.Length();
-    if(argc > 1)
-	THROW("Too many arguments (> 1)");
-
-    js_rt = args.This();
-    rt = (njs_runtime_t *)UNWRAP(js_rt);
-    rdman = X_njs_MB_rdman(rt);
+    y = cc(3)*xx+cc(4)*yy+cc(5);
+    return Number::New(y);
+}
+static void
+xnjsmb_coord_set_x(Handle<Object> self, coord_t *coord, Handle<Value> value, const char **str)
+{
+    Handle<Object> js_rt;
+    redraw_man_t *rdman;
+    co_aix x,tx;
+    co_aix xx,yy;
+    
+    xx = GET(self,"_x")->ToNumber()->NumberValue();
+    yy = GET(self,"_y")->ToNumber()->NumberValue();
+    js_rt = GET(self, "mbrt")->ToObject();
+    ASSERT(js_rt != NULL);
+    rdman = xnjsmb_rt_rdman(js_rt);
+    
+    tx = value->NumberValue();
+    x = tx-cc(0)*xx-cc(1)*yy;
+    coord_get_matrix(coord)[2] = x;
+    rdman_coord_changed(rdman, coord);
+}
 
-    if(argc == 1) {
-	parent_obj = args[0]->ToObject();
-	parent = (coord_t *)UNWRAP(parent_obj);
-    }
+static Handle<Value>
+xnjsmb_coord_get_x(Handle<Object> self, coord_t *coord,
+			      const char **err) {
+    co_aix x;
+    co_aix xx,yy;
     
-    coord = rdman_coord_new(rdman, parent);
-    ASSERT(coord != NULL);
-    coord_obj = xnjsmb_coord_new_jsobj(coord, parent_obj, js_rt);
+    xx = GET(self,"_x")->ToNumber()->NumberValue();
+    yy = GET(self,"_y")->ToNumber()->NumberValue();
 
-    scope.Close(coord_obj);
-    
-    return coord_obj;
+    x = cc(0)*xx+cc(1)*yy+cc(2);
+    return Number::New(x);
+}
+#undef m
+
+#include "coord-inc.h"
+
+/*! \brief This function used by \ref xnjsmb_mb_rt to wrap coord object.
+ */
+Handle<Value> export_xnjsmb_auto_coord_new(coord_t *coord) {
+    return xnjsmb_auto_coord_new(coord);
 }
 
 /*! \brief Initialize Javascript object for root coord of a runtime.
@@ -185,10 +370,19 @@
     redraw_man_t *rdman;
     coord_t *root;
     Handle<Object> obj;
-    
+    static int init_flag = 0;
+
+    if(!init_flag) {
+	xnjsmb_auto_coord_init();
+	init_flag = 1;
+    }
+
     rdman = xnjsmb_rt_rdman(js_rt);
     root = rdman_get_root(rdman);
-    obj = xnjsmb_coord_new_jsobj(root, Handle<Object>(NULL), js_rt);
+    obj = xnjsmb_auto_coord_new(root).As<Object>();
+    SET(obj, "mbrt", js_rt);
 
     SET(js_rt, "root", obj);
 }
+
+/* @} */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/coord.m4	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,15 @@
+define([PROJ_PREFIX], [xnjsmb_auto_])dnl
+STRUCT([coord], [coord_t], 
+        [
+	ACCESSOR([opacity], [xnjsmb_coord_get_opacity],[xnjsmb_coord_set_opacity]),
+	ACCESSOR([x], [xnjsmb_coord_get_x],[xnjsmb_coord_set_x]),
+	ACCESSOR([y], [xnjsmb_coord_get_y],[xnjsmb_coord_set_y]),
+	],
+	[METHOD([add_shape], [xnjsmb_coord_add_shape],
+		(SELF, OBJ([shape], [shape], [shape_t]), ERR), 1, []),
+	 METHOD([remove], [xnjsmb_coord_remove], (SELF), 0, []),
+	 METHOD([show], [xnjsmb_coord_show], (SELF), 0, []),
+	 METHOD([hide], [xnjsmb_coord_hide], (SELF), 0, [])],
+	((GET_INDEX, (coord_get_index, NUMBER)),
+	 (SET_INDEX, (coord_set_index, NUMBER)),
+	 ([STMOD], [xnjsmb_coord_mod])))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/examples/desktop/desktop.svg	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,507 @@
+<?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:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="744.09448"
+   height="480"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.47 r22583"
+   sodipodi:docname="desktop.svg">
+  <defs
+     id="defs4">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 240 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 240 : 1"
+       inkscape:persp3d-origin="372.04724 : 160 : 1"
+       id="perspective21" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3706">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop3708" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop3710" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3706"
+       id="linearGradient3712"
+       x1="0"
+       y1="240"
+       x2="747.27411"
+       y2="240"
+       gradientUnits="userSpaceOnUse" />
+    <inkscape:perspective
+       id="perspective3711"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective2953"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective2981"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3009"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3037"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3065"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3093"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective2960"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective2988"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3016"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3016-0"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3016-6"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3016-7"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3016-5"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.56875"
+     inkscape:cx="207.9"
+     inkscape:cy="290.99601"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1440"
+     inkscape:window-height="900"
+     inkscape:window-x="-1"
+     inkscape:window-y="-1"
+     inkscape:window-maximized="0" />
+  <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" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer3"
+     inkscape:label="background"
+     sodipodi:insensitive="true"
+     inkscape:transform-center-x="0"
+     inkscape:transform-center-y="0"
+     inkscape:bbox-x="0"
+     inkscape:bbox-y="0"
+     inkscape:bbox-width="747.27411"
+     inkscape:bbox-height="480">
+    <rect
+       style="fill:url(#linearGradient3712);fill-opacity:1"
+       id="rect2928"
+       width="747.27411"
+       height="480"
+       x="0"
+       y="0"
+       ry="3.0304577"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="0"
+       inkscape:bbox-y="0"
+       inkscape:bbox-width="747.27411"
+       inkscape:bbox-height="480" />
+  </g>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     inkscape:transform-center-x="0"
+     inkscape:transform-center-y="0"
+     inkscape:bbox-x="35.355339"
+     inkscape:bbox-y="249.90203"
+     inkscape:bbox-width="641.44684"
+     inkscape:bbox-height="219.19541"
+     style="display:inline">
+    <g
+       id="g2917"
+       transform="translate(-27.274116,-798.02051)"
+       mbname="video"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="42.294399"
+       inkscape:bbox-x="35.992421"
+       inkscape:bbox-y="383.87928"
+       inkscape:bbox-width="85.598946"
+       inkscape:bbox-height="84.588799">
+      <image
+         sodipodi:absref="/usr/home/thinker/progm/MadButterfly/nodejs/video.png"
+         xlink:href="video.png"
+         width="85.598946"
+         height="84.588799"
+         id="image2983"
+         x="63.266537"
+         y="809.55243"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         inkscape:bbox-x="35.992421"
+         inkscape:bbox-y="383.87928"
+         inkscape:bbox-width="85.598946"
+         inkscape:bbox-height="84.588799" />
+    </g>
+    <g
+       id="g2917-8"
+       transform="translate(126.77414,-798.02053)"
+       mbname="audio"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="38.464287"
+       inkscape:bbox-x="189.7836"
+       inkscape:bbox-y="386.37278"
+       inkscape:bbox-width="86.5"
+       inkscape:bbox-height="76.928574">
+      <image
+         sodipodi:absref="/usr/home/thinker/progm/MadButterfly/nodejs/music.png"
+         xlink:href="music.png"
+         width="86.5"
+         height="76.928574"
+         id="image3048"
+         x="63.00946"
+         y="814.71918"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         inkscape:bbox-x="189.7836"
+         inkscape:bbox-y="386.37278"
+         inkscape:bbox-width="86.5"
+         inkscape:bbox-height="76.928574" />
+    </g>
+    <g
+       id="g2917-88"
+       transform="translate(290.67139,-799.03067)"
+       mbname="picture"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="38.07143"
+       inkscape:bbox-x="354.2955"
+       inkscape:bbox-y="392.95458"
+       inkscape:bbox-width="84"
+       inkscape:bbox-height="76.14286">
+      <image
+         sodipodi:absref="/usr/home/thinker/progm/MadButterfly/nodejs/photo.png"
+         xlink:href="photo.png"
+         width="84"
+         height="76.14286"
+         id="image3049"
+         x="63.624115"
+         y="809.93323"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         inkscape:bbox-x="354.2955"
+         inkscape:bbox-y="392.95458"
+         inkscape:bbox-width="84"
+         inkscape:bbox-height="76.14286" />
+    </g>
+    <g
+       id="g2917-8-6"
+       transform="translate(462.9024,-794.99007)"
+       mbname="setting"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="37.57143"
+       inkscape:bbox-x="526.42387"
+       inkscape:bbox-y="389.0568"
+       inkscape:bbox-width="84.571426"
+       inkscape:bbox-height="75.14286">
+      <image
+         sodipodi:absref="/usr/home/thinker/progm/MadButterfly/nodejs/tool.png"
+         xlink:href="tool.png"
+         width="84.571426"
+         height="75.14286"
+         id="image3168"
+         x="63.521473"
+         y="810.79041"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         inkscape:bbox-x="526.42387"
+         inkscape:bbox-y="389.0568"
+         inkscape:bbox-width="84.571426"
+         inkscape:bbox-height="75.14286" />
+    </g>
+    <rect
+       style="fill:#dfdf49;fill-opacity:1"
+       id="rect2929"
+       width="641.44684"
+       height="71.720833"
+       x="35.355339"
+       y="158.37714"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       rx="10"
+       ry="10"
+       inkscape:bbox-x="35.355339"
+       inkscape:bbox-y="249.90203"
+       inkscape:bbox-width="641.44684"
+       inkscape:bbox-height="71.720833"
+       mbname="lightbar" />
+    <text
+       xml:space="preserve"
+       style="font-size:40px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="124.96794"
+       y="185.925"
+       id="text2931"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="126.28044"
+       inkscape:bbox-y="271.84843"
+       inkscape:bbox-width="488.15625"
+       inkscape:bbox-height="32.382812"
+       mbname="line1"><tspan
+         sodipodi:role="line"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         x="124.96794"
+         y="185.925"
+         id="tspan2935"
+         style="font-size:16px"
+         inkscape:bbox-x="126.53825"
+         inkscape:bbox-y="288.74687"
+         inkscape:bbox-width="487.89844"
+         inkscape:bbox-height="15.484375">Lim JeongHee (J.Lim) &amp; JoKwon (2AM) - The Road to Break Up</tspan><tspan
+         sodipodi:role="line"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         x="124.96794"
+         y="205.925"
+         style="font-size:16px"
+         inkscape:bbox-x="126.28044"
+         inkscape:bbox-y="271.84843"
+         inkscape:bbox-width="75.335938"
+         inkscape:bbox-height="12.101562"
+         id="tspan2941">7:00-7:30</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:40px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans"
+       x="124.96794"
+       y="247.75766"
+       id="text2931-20"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="126.28044"
+       inkscape:bbox-y="210.01578"
+       inkscape:bbox-width="488.15625"
+       inkscape:bbox-height="32.382812"
+       mbname="line2"><tspan
+         sodipodi:role="line"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         x="124.96794"
+         y="247.75766"
+         id="tspan2935-1"
+         style="font-size:16px"
+         inkscape:bbox-x="126.53825"
+         inkscape:bbox-y="226.91422"
+         inkscape:bbox-width="487.89844"
+         inkscape:bbox-height="15.484375">Lim JeongHee (J.Lim) &amp; JoKwon (2AM) - The Road to Break Up</tspan><tspan
+         sodipodi:role="line"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         x="124.96794"
+         y="267.75766"
+         style="font-size:16px"
+         inkscape:bbox-x="126.28044"
+         inkscape:bbox-y="210.01578"
+         inkscape:bbox-width="75.335938"
+         inkscape:bbox-height="12.101562"
+         id="tspan2941-9">7:30-8:30</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:40px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans"
+       x="124.96794"
+       y="309.59033"
+       id="text2931-21"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="126.05388"
+       inkscape:bbox-y="152.18311"
+       inkscape:bbox-width="488.38281"
+       inkscape:bbox-height="32.382812"
+       mbname="line3"><tspan
+         sodipodi:role="line"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         x="124.96794"
+         y="309.59033"
+         id="tspan2935-2"
+         style="font-size:16px"
+         inkscape:bbox-x="126.53825"
+         inkscape:bbox-y="169.08154"
+         inkscape:bbox-width="487.89844"
+         inkscape:bbox-height="15.484375">Lim JeongHee (J.Lim) &amp; JoKwon (2AM) - The Road to Break Up</tspan><tspan
+         sodipodi:role="line"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         x="124.96794"
+         y="329.59033"
+         style="font-size:16px"
+         inkscape:bbox-x="126.05388"
+         inkscape:bbox-y="152.18311"
+         inkscape:bbox-width="75.5625"
+         inkscape:bbox-height="12.101562"
+         id="tspan2941-0">8:30-9:30</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:40px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans"
+       x="124.96794"
+       y="371.423"
+       id="text2931-3"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="125.97575"
+       inkscape:bbox-y="90.350433"
+       inkscape:bbox-width="488.46094"
+       inkscape:bbox-height="32.382812"
+       mbname="line4"><tspan
+         sodipodi:role="line"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         x="124.96794"
+         y="371.423"
+         id="tspan2935-11"
+         style="font-size:16px"
+         inkscape:bbox-x="126.53825"
+         inkscape:bbox-y="107.24887"
+         inkscape:bbox-width="487.89844"
+         inkscape:bbox-height="15.484375">Lim JeongHee (J.Lim) &amp; JoKwon (2AM) - The Road to Break Up</tspan><tspan
+         sodipodi:role="line"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         x="124.96794"
+         y="391.423"
+         style="font-size:16px"
+         inkscape:bbox-x="125.97575"
+         inkscape:bbox-y="90.350433"
+         inkscape:bbox-width="85.828125"
+         inkscape:bbox-height="12.101562"
+         id="tspan2941-90">9:30-10:30</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:40px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans"
+       x="124.96794"
+       y="433.25568"
+       id="text2931-6"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="126.53825"
+       inkscape:bbox-y="28.517761"
+       inkscape:bbox-width="487.89844"
+       inkscape:bbox-height="32.382812"
+       mbname="line5"><tspan
+         sodipodi:role="line"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         x="124.96794"
+         y="433.25568"
+         id="tspan2935-7"
+         style="font-size:16px"
+         inkscape:bbox-x="126.53825"
+         inkscape:bbox-y="45.416199"
+         inkscape:bbox-width="487.89844"
+         inkscape:bbox-height="15.484375">Lim JeongHee (J.Lim) &amp; JoKwon (2AM) - The Road to Break Up</tspan><tspan
+         sodipodi:role="line"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         x="124.96794"
+         y="453.25568"
+         style="font-size:16px"
+         inkscape:bbox-x="126.72575"
+         inkscape:bbox-y="28.517761"
+         inkscape:bbox-width="95.265625"
+         inkscape:bbox-height="12.101562"
+         id="tspan2941-7">10:30-11:30</tspan></text>
+  </g>
+</svg>
Binary file nodejs/examples/desktop/music.png has changed
Binary file nodejs/examples/desktop/photo.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/examples/desktop/testdesktop.js	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,126 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
+var svg = require("svg");
+var mbapp = require("mbapp");
+var sys=require("sys");
+var animate=require("animate");
+var fs = require("fs");
+
+app = new mbapp.app();
+app.loadSVG("desktop.svg");
+
+video = app.get("video");
+//var an = new animate.alpha(app,video,0,1);
+//an.start();
+audio = app.get("audio");
+picture = app.get("picture");
+setting = app.get("setting");
+
+lightbar = app.get("lightbar");
+lines = [];
+for(i = 0; i < 5; i++) {
+    line = app.get("line" + (i + 1));
+    lines.push(line);
+}
+line=0;
+
+items=[video, audio, picture, setting];
+
+item = 0;
+
+animate.run([new animate.scale(app,items[item], 1, 1.5)], 0, 0.1);
+app.refresh();
+
+app.addKeyListener(mbapp.KEY_LEFT, function() {
+    var old = items[item];
+    item = item - 1;
+    if (item == -1) {
+	item = 0;
+	return;
+    }
+    
+    var target = items[item];
+
+    old.bbox.update();
+    target.bbox.update();
+    
+    var an = new animate.scale(app, old, 1, 1);
+    animate.run([an], 0, 0.1);
+    an = new animate.scale(app, target, 1, 1.5);
+    animate.run([an], 0, 0.3);
+});
+
+app.addKeyListener(mbapp.KEY_RIGHT, function() {
+    var old = items[item];
+    item = item + 1;
+    if (item == items.length) {
+	item = item - 1;
+	return;
+    }
+    
+    var target = items[item];
+
+    old.bbox.update();
+    target.bbox.update();
+    
+    var an = new animate.scale(app, old, 1, 1);
+    animate.run([an], 0, 0.1);
+    an = new animate.scale(app, target, 1, 1.5);
+    animate.run([an], 0, 0.3);
+});
+
+app.addKeyListener(mbapp.KEY_UP, function() {
+    var old = lines[line];
+    line = line - 1;
+    if (line == -1) {
+	line = 0;
+	return;
+    }
+    var target = lines[line];
+    var sy = target.center.y - lightbar.center.y;
+    sys.puts(sy);
+    var an = new animate.shift(app, lightbar, 0, sy);
+    animate.run([an], 0, 0.3);
+});
+app.addKeyListener(mbapp.KEY_DOWN, function() {
+    var old = lines[line];
+    line = line + 1;
+    if (line == lines.length) {
+	line = line - 1; 
+	return;
+    }
+    var target = lines[line];
+    var sy = target.center.y - lightbar.center.y;
+    sys.puts("line="+line);
+    sys.puts("sy="+sy);
+    sys.puts("target.y="+target.center.y);
+    sys.puts("lightbar.y="+lightbar.center.y);
+    var an = new animate.shift(app, lightbar, 0, sy);
+    animate.run([an], 0, 0.3);
+});
+
+app.addKeyListener(mbapp.KEY_ENTER, function() {
+    var target = items[item];
+    var sx = 500 - target.center.x;
+    var sy = 220 - target.center.y;
+    sys.puts("target "+sx+','+sy);
+    var an = new animate.shift(app,target,sx,sy,1);
+    an.start();
+    for(i=0;i<items.length;i++) {
+	if (i == item) continue;
+	var x = Math.random();
+	var y = Math.random();
+	if (x > 0.5) x = 900;
+	else x = -500;
+	if (y > 0.5) y = 900;
+	else y = -500;
+	sx = x - items[i].center.x;
+	sy = y - items[i].center.y;
+	an = new animate.shift(app,items[i], sx, sy);
+	animate.run([an], 0, 2);
+	alpha = new animate.alpha(app,items[i], 0);
+	animate.run([an], 0, 1);
+    }
+});
+
+app.loop();
Binary file nodejs/examples/desktop/tool.png has changed
Binary file nodejs/examples/desktop/video.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/examples/mce/README	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,10 @@
+Scene 1: Main category
+
+1. Start to load all pictures and save them in the file system.
+2. Load the main screen and move all pictures to the correct positions.
+3. Show the cursor and do animation to scale it.
+4. After the animation, start to listent to keys and move the cursor.
+5. If the key is enter, load the next scene.
+
+
+Scene 2:
Binary file nodejs/examples/mce/background.png has changed
Binary file nodejs/examples/mce/background2.jpg has changed
Binary file nodejs/examples/mce/background2.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/examples/mce/browser.svg	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,98 @@
+<?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:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="744.09448819"
+   height="1052.3622047"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.47+devel r9312"
+   sodipodi:docname="browser.svg">
+  <defs
+     id="defs4">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3684">
+      <stop
+         style="stop-color:#0000d5;stop-opacity:1;"
+         offset="0"
+         id="stop3686" />
+      <stop
+         style="stop-color:#0000d5;stop-opacity:0;"
+         offset="1"
+         id="stop3688" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3684"
+       id="linearGradient3690"
+       x1="400"
+       y1="123.79076"
+       x2="394.28571"
+       y2="529.50507"
+       gradientUnits="userSpaceOnUse" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.35"
+     inkscape:cx="-99.285714"
+     inkscape:cy="634.28571"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1680"
+     inkscape:window-height="976"
+     inkscape:window-x="0"
+     inkscape:window-y="25"
+     inkscape:window-maximized="1" />
+  <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" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <rect
+       style="fill:url(#linearGradient3690);fill-opacity:1"
+       id="rect2914"
+       width="722.85712"
+       height="537.14288"
+       x="11.428572"
+       y="23.790754"
+       rx="3.8392856"
+       ry="4.8275862" />
+    <text
+       xml:space="preserve"
+       style="font-size:133.75950622999999950px;font-style:normal;font-weight:normal;fill:#f2f2f2;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="41.756718"
+       y="139.44588"
+       id="text2915"
+       transform="scale(0.74827402,1.3364088)"><tspan
+         sodipodi:role="line"
+         id="tspan2917"
+         x="41.756718"
+         y="139.44588">Loading</tspan></text>
+  </g>
+</svg>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/examples/mce/desktop.svg	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,507 @@
+<?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:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="744.09448"
+   height="480"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.47 r22583"
+   sodipodi:docname="desktop.svg">
+  <defs
+     id="defs4">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 240 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 240 : 1"
+       inkscape:persp3d-origin="372.04724 : 160 : 1"
+       id="perspective21" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3706">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop3708" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop3710" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3706"
+       id="linearGradient3712"
+       x1="0"
+       y1="240"
+       x2="747.27411"
+       y2="240"
+       gradientUnits="userSpaceOnUse" />
+    <inkscape:perspective
+       id="perspective3711"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective2953"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective2981"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3009"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3037"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3065"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3093"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective2960"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective2988"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3016"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3016-0"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3016-6"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3016-7"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3016-5"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.56875"
+     inkscape:cx="207.9"
+     inkscape:cy="290.99601"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1440"
+     inkscape:window-height="900"
+     inkscape:window-x="-1"
+     inkscape:window-y="-1"
+     inkscape:window-maximized="0" />
+  <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" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer3"
+     inkscape:label="background"
+     sodipodi:insensitive="true"
+     inkscape:transform-center-x="0"
+     inkscape:transform-center-y="0"
+     inkscape:bbox-x="0"
+     inkscape:bbox-y="0"
+     inkscape:bbox-width="747.27411"
+     inkscape:bbox-height="480">
+    <rect
+       style="fill:url(#linearGradient3712);fill-opacity:1"
+       id="rect2928"
+       width="747.27411"
+       height="480"
+       x="0"
+       y="0"
+       ry="3.0304577"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="0"
+       inkscape:bbox-y="0"
+       inkscape:bbox-width="747.27411"
+       inkscape:bbox-height="480" />
+  </g>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     inkscape:transform-center-x="0"
+     inkscape:transform-center-y="0"
+     inkscape:bbox-x="35.355339"
+     inkscape:bbox-y="249.90203"
+     inkscape:bbox-width="641.44684"
+     inkscape:bbox-height="219.19541"
+     style="display:inline">
+    <g
+       id="g2917"
+       transform="translate(-27.274116,-798.02051)"
+       mbname="video"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="42.294399"
+       inkscape:bbox-x="35.992421"
+       inkscape:bbox-y="383.87928"
+       inkscape:bbox-width="85.598946"
+       inkscape:bbox-height="84.588799">
+      <image
+         sodipodi:absref="/usr/home/thinker/progm/MadButterfly/nodejs/video.png"
+         xlink:href="video.png"
+         width="85.598946"
+         height="84.588799"
+         id="image2983"
+         x="63.266537"
+         y="809.55243"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         inkscape:bbox-x="35.992421"
+         inkscape:bbox-y="383.87928"
+         inkscape:bbox-width="85.598946"
+         inkscape:bbox-height="84.588799" />
+    </g>
+    <g
+       id="g2917-8"
+       transform="translate(126.77414,-798.02053)"
+       mbname="audio"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="38.464287"
+       inkscape:bbox-x="189.7836"
+       inkscape:bbox-y="386.37278"
+       inkscape:bbox-width="86.5"
+       inkscape:bbox-height="76.928574">
+      <image
+         sodipodi:absref="/usr/home/thinker/progm/MadButterfly/nodejs/music.png"
+         xlink:href="music.png"
+         width="86.5"
+         height="76.928574"
+         id="image3048"
+         x="63.00946"
+         y="814.71918"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         inkscape:bbox-x="189.7836"
+         inkscape:bbox-y="386.37278"
+         inkscape:bbox-width="86.5"
+         inkscape:bbox-height="76.928574" />
+    </g>
+    <g
+       id="g2917-88"
+       transform="translate(290.67139,-799.03067)"
+       mbname="picture"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="38.07143"
+       inkscape:bbox-x="354.2955"
+       inkscape:bbox-y="392.95458"
+       inkscape:bbox-width="84"
+       inkscape:bbox-height="76.14286">
+      <image
+         sodipodi:absref="/usr/home/thinker/progm/MadButterfly/nodejs/photo.png"
+         xlink:href="photo.png"
+         width="84"
+         height="76.14286"
+         id="image3049"
+         x="63.624115"
+         y="809.93323"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         inkscape:bbox-x="354.2955"
+         inkscape:bbox-y="392.95458"
+         inkscape:bbox-width="84"
+         inkscape:bbox-height="76.14286" />
+    </g>
+    <g
+       id="g2917-8-6"
+       transform="translate(462.9024,-794.99007)"
+       mbname="setting"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="37.57143"
+       inkscape:bbox-x="526.42387"
+       inkscape:bbox-y="389.0568"
+       inkscape:bbox-width="84.571426"
+       inkscape:bbox-height="75.14286">
+      <image
+         sodipodi:absref="/usr/home/thinker/progm/MadButterfly/nodejs/tool.png"
+         xlink:href="tool.png"
+         width="84.571426"
+         height="75.14286"
+         id="image3168"
+         x="63.521473"
+         y="810.79041"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         inkscape:bbox-x="526.42387"
+         inkscape:bbox-y="389.0568"
+         inkscape:bbox-width="84.571426"
+         inkscape:bbox-height="75.14286" />
+    </g>
+    <rect
+       style="fill:#dfdf49;fill-opacity:1"
+       id="rect2929"
+       width="641.44684"
+       height="71.720833"
+       x="35.355339"
+       y="158.37714"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       rx="10"
+       ry="10"
+       inkscape:bbox-x="35.355339"
+       inkscape:bbox-y="249.90203"
+       inkscape:bbox-width="641.44684"
+       inkscape:bbox-height="71.720833"
+       mbname="lightbar" />
+    <text
+       xml:space="preserve"
+       style="font-size:40px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="124.96794"
+       y="185.925"
+       id="text2931"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="126.28044"
+       inkscape:bbox-y="271.84843"
+       inkscape:bbox-width="488.15625"
+       inkscape:bbox-height="32.382812"
+       mbname="line1"><tspan
+         sodipodi:role="line"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         x="124.96794"
+         y="185.925"
+         id="tspan2935"
+         style="font-size:16px"
+         inkscape:bbox-x="126.53825"
+         inkscape:bbox-y="288.74687"
+         inkscape:bbox-width="487.89844"
+         inkscape:bbox-height="15.484375">Lim JeongHee (J.Lim) &amp; JoKwon (2AM) - The Road to Break Up</tspan><tspan
+         sodipodi:role="line"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         x="124.96794"
+         y="205.925"
+         style="font-size:16px"
+         inkscape:bbox-x="126.28044"
+         inkscape:bbox-y="271.84843"
+         inkscape:bbox-width="75.335938"
+         inkscape:bbox-height="12.101562"
+         id="tspan2941">7:00-7:30</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:40px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans"
+       x="124.96794"
+       y="247.75766"
+       id="text2931-20"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="126.28044"
+       inkscape:bbox-y="210.01578"
+       inkscape:bbox-width="488.15625"
+       inkscape:bbox-height="32.382812"
+       mbname="line2"><tspan
+         sodipodi:role="line"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         x="124.96794"
+         y="247.75766"
+         id="tspan2935-1"
+         style="font-size:16px"
+         inkscape:bbox-x="126.53825"
+         inkscape:bbox-y="226.91422"
+         inkscape:bbox-width="487.89844"
+         inkscape:bbox-height="15.484375">Lim JeongHee (J.Lim) &amp; JoKwon (2AM) - The Road to Break Up</tspan><tspan
+         sodipodi:role="line"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         x="124.96794"
+         y="267.75766"
+         style="font-size:16px"
+         inkscape:bbox-x="126.28044"
+         inkscape:bbox-y="210.01578"
+         inkscape:bbox-width="75.335938"
+         inkscape:bbox-height="12.101562"
+         id="tspan2941-9">7:30-8:30</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:40px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans"
+       x="124.96794"
+       y="309.59033"
+       id="text2931-21"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="126.05388"
+       inkscape:bbox-y="152.18311"
+       inkscape:bbox-width="488.38281"
+       inkscape:bbox-height="32.382812"
+       mbname="line3"><tspan
+         sodipodi:role="line"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         x="124.96794"
+         y="309.59033"
+         id="tspan2935-2"
+         style="font-size:16px"
+         inkscape:bbox-x="126.53825"
+         inkscape:bbox-y="169.08154"
+         inkscape:bbox-width="487.89844"
+         inkscape:bbox-height="15.484375">Lim JeongHee (J.Lim) &amp; JoKwon (2AM) - The Road to Break Up</tspan><tspan
+         sodipodi:role="line"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         x="124.96794"
+         y="329.59033"
+         style="font-size:16px"
+         inkscape:bbox-x="126.05388"
+         inkscape:bbox-y="152.18311"
+         inkscape:bbox-width="75.5625"
+         inkscape:bbox-height="12.101562"
+         id="tspan2941-0">8:30-9:30</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:40px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans"
+       x="124.96794"
+       y="371.423"
+       id="text2931-3"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="125.97575"
+       inkscape:bbox-y="90.350433"
+       inkscape:bbox-width="488.46094"
+       inkscape:bbox-height="32.382812"
+       mbname="line4"><tspan
+         sodipodi:role="line"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         x="124.96794"
+         y="371.423"
+         id="tspan2935-11"
+         style="font-size:16px"
+         inkscape:bbox-x="126.53825"
+         inkscape:bbox-y="107.24887"
+         inkscape:bbox-width="487.89844"
+         inkscape:bbox-height="15.484375">Lim JeongHee (J.Lim) &amp; JoKwon (2AM) - The Road to Break Up</tspan><tspan
+         sodipodi:role="line"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         x="124.96794"
+         y="391.423"
+         style="font-size:16px"
+         inkscape:bbox-x="125.97575"
+         inkscape:bbox-y="90.350433"
+         inkscape:bbox-width="85.828125"
+         inkscape:bbox-height="12.101562"
+         id="tspan2941-90">9:30-10:30</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:40px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans"
+       x="124.96794"
+       y="433.25568"
+       id="text2931-6"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="126.53825"
+       inkscape:bbox-y="28.517761"
+       inkscape:bbox-width="487.89844"
+       inkscape:bbox-height="32.382812"
+       mbname="line5"><tspan
+         sodipodi:role="line"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         x="124.96794"
+         y="433.25568"
+         id="tspan2935-7"
+         style="font-size:16px"
+         inkscape:bbox-x="126.53825"
+         inkscape:bbox-y="45.416199"
+         inkscape:bbox-width="487.89844"
+         inkscape:bbox-height="15.484375">Lim JeongHee (J.Lim) &amp; JoKwon (2AM) - The Road to Break Up</tspan><tspan
+         sodipodi:role="line"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         x="124.96794"
+         y="453.25568"
+         style="font-size:16px"
+         inkscape:bbox-x="126.72575"
+         inkscape:bbox-y="28.517761"
+         inkscape:bbox-width="95.265625"
+         inkscape:bbox-height="12.101562"
+         id="tspan2941-7">10:30-11:30</tspan></text>
+  </g>
+</svg>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/examples/mce/epg.js	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,194 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
+var http = require('http');
+var URL = require('url');
+var sys = require("sys");
+var fs = require("fs");
+var os = require("child_process");
+function EPG() 
+{
+    var epgsrv = http.createClient(8080, '211.23.50.144');
+    var cmd = '{"Protocol":"EPG-CSP","Command":"SearchRequest","ProgramCat":"MainCat"}';
+    var headers={
+        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
+        'Host':'211.23.50.144:8080',
+        'User-Agent':'MadButterfly',
+        'Content-Type':'application/x-www-form-urlencoded'
+    };
+    headers['Content-Length'] = cmd.length;
+    var request = epgsrv.request('POST', '/IPTV_EPG/EPGService.do?timestamp='+new Date().getTime(),headers);
+    var self = this;
+    sys.puts("aaaa");
+    var js = '';
+    request.write(cmd);
+    request.end();
+    request.on('response', function(res) {
+        sys.puts("connected");
+ 	res.on('data',function (data) {
+		js = js + data;
+	});
+	res.on('end', function () {
+		res = JSON.parse(js);
+		sys.puts("parsed");
+		self.onLoad(res);
+
+	});
+    });
+}
+
+
+/**
+ *   Check if the file has been cached. Create a symbolic to link if it is cached already.
+ *
+ */
+function isCached(cachepath,file,obj) {
+    var fields = cachepath.split('.');
+    try {
+        var ext = fields.pop();
+	var pngfile = cachepath;
+	sys.puts("ext="+ext);
+	if (ext != 'png') {
+	    fields.push('png');
+	    pngfile = fields.join('.');
+	}
+        var st = fs.statSync(pngfile);
+	try {
+	    fs.unlinkSync(file);
+	} catch(e) {
+	}
+	fs.linkSync(pngfile, file);
+        obj.pend = obj.pend - 1;
+        if (obj.pend == 0) {
+            obj.onInitDone();
+        }
+	return 1;
+    } catch(e) {
+	sys.puts(e);
+    }
+    return 0;
+}
+
+/**
+ *  Implement the mkdir -p to create the directory 
+ */
+function CreateDirectory(cachepath)
+{
+    var fields = cachepath.split('/');
+    var p='';
+    for(i=0;i<fields.length-1;i++) {
+        p = p + fields[i]+'/';
+	try {
+            fs.mkdirSync(p,0777);
+	} catch(e) {
+	}
+    }
+}
+/*
+ *  We will check the cache directory. If the file is available, we will create a symbolic link only. Otherwise, 
+ *  we will fetch it before create the symbolic link.
+ */
+function httpGetFile(url,file,obj)
+{
+    sys.puts("fetch "+ file);
+    var u = URL.parse(url);
+    var cachepath =  'cache/'+u.pathname;
+    if (isCached(cachepath,file,obj)) return;
+    CreateDirectory(cachepath);
+
+    // Fetch file from the server and convert it tyo PNG if it is not PNG format.
+    var f = fs.openSync(cachepath,'w');
+    sys.puts("f="+f);
+    var headers={
+        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
+        'Host':'211.23.50.144:8080',
+        'User-Agent':'MadButterfly',
+        'Content-Type':'application/x-www-form-urlencoded'
+    };
+    sys.puts("host="+u.host+' '+u.port+' '+u.pathname);
+    for(k in u) {
+        sys.puts(k+"--->"+u[k]);
+    }
+    var c = http.createClient(8080,'211.23.50.144');
+    var req = c.request('GET',u.pathname,headers);
+    req.end();
+    req.on('response', function(res) {
+        res.on('data',function(data) {
+            fs.writeSync(f,data,0,data.length);
+	});
+	res.on('end',function() {
+	    fs.close(f);
+	    var fields = cachepath.split('.');
+	    var ext = fields.pop();
+	    if (ext != "png") {
+	        fields.push("png");
+	        newf = fields.join(".");
+		sys.puts("cachepath="+cachepath+" newf="+newf);
+	        os.spawn("convert",[cachepath,newf]);
+	    } else {
+	        newf = cachepath;
+	    }
+	    try {
+	        fs.unlinkSync(file);
+	    } catch(e) {
+	    }
+	    sys.puts("end of "+cachepath+" to "+file);
+	    fs.symlinkSync(newf, file);
+	    obj.pend = obj.pend - 1;
+	    if (obj.pend == 0) {
+	        obj.onInitDone();
+	    }
+
+	});
+
+    });
+}
+
+EPG.prototype.onLoad = function(res) {
+    cats = res['ProgramCat'];
+    this.pend = cats.length;
+    this.maincat = cats;
+    for (i in cats) {
+	c = cats[i];
+	httpGetFile(c['ProgramPIC'],'cat'+i+'.png',this);
+    }
+}
+
+EPG.prototype.getList=function(item,func) {
+    var epgsrv = http.createClient(8080, '211.23.50.144');
+    var cmd = '{"Protocol":"EPG-CSP","Command":"SearchRequest","ProgramSub":"'+item.Category+'"}';
+    var headers={
+        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
+        'Host':'211.23.50.144:8080',
+        'User-Agent':'MadButterfly',
+        'Content-Type':'application/x-www-form-urlencoded'
+    };
+    headers['Content-Length'] = cmd.length;
+    var request = epgsrv.request('POST', '/IPTV_EPG/EPGService.do?timestamp='+new Date().getTime(),headers);
+    var self = this;
+    sys.puts("aaaa");
+    var js = '';
+    request.write(cmd);
+    request.end();
+    request.on('response', function(res) {
+        sys.puts("connected");
+ 	res.on('data',function (data) {
+		js = js + data;
+	});
+	res.on('end', function () {
+		res = JSON.parse(js);
+		sys.puts("parsed");
+		func();
+
+	});
+    });
+}
+EPG.prototype.onInitDone=function() {
+    if (this.loadCallback)
+        this.loadCallback();
+}
+
+EPG.prototype.registerInitDone=function(cb) {
+    this.loadCallback = cb;
+}
+
+exports.EPG = EPG;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/examples/mce/main.svg	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,376 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:ns0="http://madbutterfly.sourceforge.net/DTD/madbutterfly.dtd"
+   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:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="744.09448819"
+   height="1052.3622047"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48+devel r9764 custom"
+   sodipodi:docname="main.svg">
+  <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="perspective13" />
+    <inkscape:perspective
+       id="perspective2973"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective2975"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective2993"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.35"
+     inkscape:cx="206.98185"
+     inkscape:cy="483.73232"
+     inkscape:document-units="px"
+     inkscape:current-layer="Layer 1s235"
+     showgrid="false"
+     inkscape:window-width="1680"
+     inkscape:window-height="976"
+     inkscape:window-x="0"
+     inkscape:window-y="25"
+     inkscape:window-maximized="1" />
+  <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" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+    <ns0:scenes>
+      <ns0:scene
+         start="1"
+         ref="Layer 1s1841" />
+      <ns0:scene
+         start="2"
+         ref="Layer 1s235" />
+    </ns0:scenes>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer2"
+     inkscape:label="Background">
+    <image
+       y="-27.637817"
+       x="0"
+       inkscape:bbox-height="1080"
+       inkscape:bbox-width="1920"
+       inkscape:bbox-y="0"
+       inkscape:bbox-x="0"
+       id="image2977"
+       height="1080"
+       width="1920"
+       xlink:href="file:///home/wycc/devel/md6/MadButterfly/nodejs/examples/mce/background.png" />
+  </g>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <g
+       id="Layer 1s1841"
+       inkscape:groupmode="layer"
+       style="display:none">
+      <g
+         transform="translate(7.14163,8.5714057)"
+         id="g2999"
+         inkscape:label="lightbar"
+         inkscape:bbox-x="107.14287"
+         inkscape:bbox-y="49.999969"
+         inkscape:bbox-width="228.57144"
+         inkscape:bbox-height="865.71429">
+        <image
+           xlink:href="file:///home/wycc/devel/md6/MadButterfly/nodejs/examples/mce/vod1-select.png"
+           width="228.57144"
+           height="865.71429"
+           id="image2995"
+           inkscape:bbox-x="107.14287"
+           inkscape:bbox-y="49.999969"
+           inkscape:bbox-width="228.57144"
+           inkscape:bbox-height="865.71429"
+           x="107.14287"
+           y="136.64792" />
+      </g>
+      <g
+         transform="translate(7.14163,8.5714057)"
+         id="g2922"
+         inkscape:label="cat0"
+         inkscape:bbox-x="117.64285"
+         inkscape:bbox-y="462.7648"
+         inkscape:bbox-width="200"
+         inkscape:bbox-height="286.7168">
+        <image
+           sodipodi:absref="/home/wycc/devel/md7/MadButterfly/nodejs/examples/mce/./cat0.png"
+           xlink:href="./cat0.png"
+           y="302.88058"
+           x="117.64285"
+           id="image3058"
+           height="286.7168"
+           width="200"
+           inkscape:label=""
+           inkscape:bbox-x="117.64285"
+           inkscape:bbox-y="462.7648"
+           inkscape:bbox-width="200"
+           inkscape:bbox-height="286.7168" />
+      </g>
+      <g
+         transform="translate(7.14163,8.5714057)"
+         id="g2923"
+         inkscape:label="cat1"
+         inkscape:bbox-x="359.21432"
+         inkscape:bbox-y="455.42856"
+         inkscape:bbox-width="200"
+         inkscape:bbox-height="291.09415">
+        <image
+           sodipodi:absref="/home/wycc/devel/md7/MadButterfly/nodejs/examples/mce/./cat1.png"
+           xlink:href="./cat1.png"
+           y="305.83948"
+           x="359.21432"
+           id="image3116"
+           height="291.09415"
+           width="200"
+           inkscape:label=""
+           inkscape:bbox-x="359.21432"
+           inkscape:bbox-y="455.42856"
+           inkscape:bbox-width="200"
+           inkscape:bbox-height="291.09415" />
+      </g>
+      <g
+         transform="translate(7.14163,8.571406)"
+         id="g2926"
+         inkscape:label="cat2"
+         inkscape:bbox-x="599.07141"
+         inkscape:bbox-y="458.28571"
+         inkscape:bbox-width="200"
+         inkscape:bbox-height="286.7168">
+        <image
+           sodipodi:absref="/home/wycc/devel/md7/MadButterfly/nodejs/examples/mce/./cat2.png"
+           xlink:href="./cat2.png"
+           y="307.35968"
+           x="599.07141"
+           id="image3174"
+           height="286.7168"
+           width="200"
+           inkscape:label=""
+           inkscape:bbox-x="599.07141"
+           inkscape:bbox-y="458.28571"
+           inkscape:bbox-width="200"
+           inkscape:bbox-height="286.7168" />
+      </g>
+      <g
+         transform="translate(7.14163,8.5714057)"
+         id="g2929"
+         inkscape:label="cat3"
+         inkscape:bbox-x="821.35712"
+         inkscape:bbox-y="456.71426"
+         inkscape:bbox-width="200"
+         inkscape:bbox-height="289.10892">
+        <image
+           sodipodi:absref="/home/wycc/devel/md7/MadButterfly/nodejs/examples/mce/./cat3.png"
+           xlink:href="./cat3.png"
+           y="306.539"
+           x="821.35712"
+           id="image3232"
+           height="289.10892"
+           width="200"
+           inkscape:label=""
+           inkscape:bbox-x="821.35712"
+           inkscape:bbox-y="456.71426"
+           inkscape:bbox-width="200"
+           inkscape:bbox-height="289.10892" />
+      </g>
+      <path
+         sodipodi:type="star"
+         style="fill:#00c280;fill-opacity:1"
+         id="path3235"
+         sodipodi:sides="3"
+         sodipodi:cx="-171.42857"
+         sodipodi:cy="186.6479"
+         sodipodi:r1="48.760139"
+         sodipodi:r2="24.38007"
+         sodipodi:arg1="1.0153026"
+         sodipodi:arg2="2.0625001"
+         inkscape:flatsided="false"
+         inkscape:rounded="0"
+         inkscape:randomized="0"
+         d="m -145.71429,228.07648 -37.22481,-19.93682 -37.22481,-19.93682 35.87819,-22.26922 35.8782,-22.26923 1.34662,42.20604 z"
+         inkscape:transform-center-x="11.746635"
+         inkscape:transform-center-y="-0.50892793"
+         transform="matrix(0.9999393,0.01101845,-0.01101845,0.9999393,246.33065,273.32876)"
+         inkscape:label="leftarrow"
+         inkscape:bbox-x="16.964767"
+         inkscape:bbox-y="561.14775"
+         inkscape:bbox-width="74.005762"
+         inkscape:bbox-height="84.436636" />
+      <path
+         transform="matrix(-0.99874276,-0.05012887,0.05012887,-0.99874276,1666.5721,621.61046)"
+         inkscape:transform-center-y="-0.445031"
+         inkscape:transform-center-x="-11.802598"
+         d="m -145.71429,228.07648 -37.22481,-19.93682 -37.22481,-19.93682 35.87819,-22.26922 35.8782,-22.26923 1.34662,42.20604 z"
+         inkscape:randomized="0"
+         inkscape:rounded="0"
+         inkscape:flatsided="false"
+         sodipodi:arg2="2.0625001"
+         sodipodi:arg1="1.0153026"
+         sodipodi:r2="24.38007"
+         sodipodi:r1="48.760139"
+         sodipodi:cy="186.6479"
+         sodipodi:cx="-171.42857"
+         sodipodi:sides="3"
+         id="path4005"
+         style="fill:#00c280;fill-opacity:1"
+         sodipodi:type="star"
+         inkscape:label="rightarrow"
+         inkscape:bbox-x="1814.8532"
+         inkscape:bbox-y="575.3674"
+         inkscape:bbox-width="73.898841"
+         inkscape:bbox-height="84.440967" />
+      <g
+         inkscape:bbox-height="289.10892"
+         inkscape:bbox-width="200"
+         inkscape:bbox-y="456.71426"
+         inkscape:bbox-x="1035.3571"
+         inkscape:label="cat4"
+         id="g2931"
+         transform="translate(221.14163,8.5714057)">
+        <image
+           sodipodi:absref="/home/wycc/devel/md7/MadButterfly/nodejs/examples/mce/./cat4.png"
+           xlink:href="./cat4.png"
+           inkscape:bbox-height="289.10892"
+           inkscape:bbox-width="200"
+           inkscape:bbox-y="456.71426"
+           inkscape:bbox-x="1035.3571"
+           inkscape:label=""
+           width="200"
+           height="289.10892"
+           id="image2933"
+           x="821.35712"
+           y="306.539" />
+      </g>
+      <g
+         transform="translate(445.14163,8.5714057)"
+         id="g2935"
+         inkscape:label="cat5"
+         inkscape:bbox-x="1259.3571"
+         inkscape:bbox-y="456.71426"
+         inkscape:bbox-width="200"
+         inkscape:bbox-height="289.10892">
+        <image
+           sodipodi:absref="/home/wycc/devel/md7/MadButterfly/nodejs/examples/mce/./cat5.png"
+           xlink:href="./cat5.png"
+           y="306.539"
+           x="821.35712"
+           id="image2937"
+           height="289.10892"
+           width="200"
+           inkscape:label=""
+           inkscape:bbox-x="1259.3571"
+           inkscape:bbox-y="456.71426"
+           inkscape:bbox-width="200"
+           inkscape:bbox-height="289.10892" />
+      </g>
+      <g
+         inkscape:bbox-height="289.10892"
+         inkscape:bbox-width="200"
+         inkscape:bbox-y="456.71426"
+         inkscape:bbox-x="1475.3571"
+         inkscape:label="cat6"
+         id="g2939"
+         transform="translate(661.14163,8.5714057)">
+        <image
+           sodipodi:absref="/home/wycc/devel/md7/MadButterfly/nodejs/examples/mce/./cat6.png"
+           xlink:href="./cat6.png"
+           inkscape:bbox-height="289.10892"
+           inkscape:bbox-width="200"
+           inkscape:bbox-y="456.71426"
+           inkscape:bbox-x="1475.3571"
+           inkscape:label=""
+           width="200"
+           height="289.10892"
+           id="image2941"
+           x="821.35712"
+           y="306.539" />
+      </g>
+    </g>
+    <g
+       id="Layer 1s235"
+       inkscape:groupmode="layer"
+       style="display:inline">
+      <rect
+         x="0"
+         y="0"
+         width="100"
+         height="100"
+         id="rect3181" />
+      <image
+         sodipodi:absref="/home/wycc/devel/md7/MadButterfly/nodejs/examples/mce/./background2.png"
+         xlink:href="./background2.png"
+         width="1920"
+         height="1080"
+         id="image3245"
+         x="0"
+         y="-27.637817" />
+      <g
+         transform="matrix(1.1857143,0,0,1.2748252,-274.497,-403.36247)"
+         id="g29230"
+         inkscape:label="current"
+         inkscape:bbox-x="359.21432"
+         inkscape:bbox-y="455.42856"
+         inkscape:bbox-width="200"
+         inkscape:bbox-height="291.09415">
+        <image
+           sodipodi:absref="/home/wycc/devel/md7/MadButterfly/nodejs/examples/mce/./cat1.png"
+           xlink:href="./cat1.png"
+           inkscape:bbox-height="291.09415"
+           inkscape:bbox-width="200"
+           inkscape:bbox-y="455.42856"
+           inkscape:bbox-x="359.21432"
+           inkscape:label=""
+           width="200"
+           height="291.09415"
+           id="image3116-4"
+           x="330.29868"
+           y="458.24133" />
+      </g>
+    </g>
+  </g>
+</svg>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/examples/mce/mainmenu.js	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,163 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
+var svg = require("svg");
+var mbapp = require("mbapp");
+var sys=require("sys");
+var animate=require("animate");
+var fs = require("fs");
+var EPG = require('./epg');
+/**
+ *   We will fetch the EPG file from the server and fetch all images required for the main category from it.
+ *   If these files are cached, we will not fetch it again. Otherwise, we will fetch them. The EPG class is
+ *   responsible for the cache management.
+ */
+function MainMenu(app) 
+{
+    var self = this;
+    var epg = new EPG.EPG();
+    epg.registerInitDone(function() { self.init();});
+    app.epg = epg;
+    //self.init();
+}
+MainMenu.prototype.init=function()
+{
+    app.loadSVG("main.svg");
+    app.changeScene(1);
+
+    var i;
+    var self = this;
+    this.items=[];
+    for(i=0;i<8;i++) {
+	this.items.push(app.get("cat"+i));
+    }
+    this.app = app;
+
+    this.lightbar = app.get("lightbar");
+    this.lines = [];
+    for(i = 0; i < 5; i++) {
+        var line = app.get("line" + (i + 1));
+        this.lines.push(line);
+    }
+    this.line=0;
+    this.item = 0;
+
+    animate.run([new animate.scale(app,this.items[this.item], 1, 1.5)], 0, 0.1);
+    app.refresh();
+
+    app.addKeyListener(mbapp.KEY_LEFT, function() { self.key_left();});
+    app.addKeyListener(mbapp.KEY_RIGHT, function() { self.key_right();});
+    app.addKeyListener(mbapp.KEY_UP, function() {self.key_up();});
+    app.addKeyListener(mbapp.KEY_DOWN, function() {self.key_down();});
+    app.addKeyListener(mbapp.KEY_ENTER, function() {self.key_enter();});
+}
+
+MainMenu.prototype.key_left=function () 
+{
+    var old = this.items[this.item];
+    this.item = this.item - 1;
+    if (this.item == -1) {
+	this.item = 0;
+	return;
+    }
+    
+    var target = this.items[this.item];
+
+    old.bbox.update();
+    target.bbox.update();
+    
+    var an = new animate.scale(this.app, old, 1/1.1, 1/1.5);
+    animate.run([an], 0, 0.1);
+    an = new animate.scale(this.app, target, 1.1, 1.5);
+    animate.run([an], 0, 0.3);
+    var sx = target.center.x - this.lightbar.center.x;
+    var an = new animate.shift(this.app, this.lightbar, sx, 0);
+    animate.run([an], 0, 0.3);
+}
+
+MainMenu.prototype.key_right=function() 
+{
+    var old = this.items[this.item];
+    this.item = this.item + 1;
+    if (this.item == this.items.length) {
+	this.item = this.item - 1;
+	return;
+    }
+    
+    var target = this.items[this.item];
+
+    old.bbox.update();
+    target.bbox.update();
+    
+    var an = new animate.scale(this.app, old, 1/1.1, 1/1.5);
+    animate.run([an], 0, 0.1);
+    an = new animate.scale(this.app, target, 1.1, 1.5);
+    animate.run([an], 0, 0.3);
+    var sx = target.center.x - this.lightbar.center.x;
+    var an = new animate.shift(this.app, this.lightbar, sx, 0);
+    animate.run([an], 0, 0.3);
+}
+
+MainMenu.prototype.key_up=function() 
+{
+    var old = this.lines[this.line];
+    this.line = this.line - 1;
+    if (this.line == -1) {
+	this.line = 0;
+	return;
+    }
+    var target = this.lines[this.line];
+    var sy = target.center.y - this.lightbar.center.y;
+    var an = new animate.shift(this.app, this.lightbar, 0, sy);
+    animate.run([an], 0, 0.3);
+}
+
+
+MainMenu.prototype.key_down=function () 
+{
+    var old = this.lines[this.line];
+    this.line = this.line + 1;
+    if (this.line == this.lines.length) {
+	this.line = this.line - 1; 
+	return;
+    }
+    var target = this.lines[this.line];
+    var sy = target.center.y - this.lightbar.center.y;
+    var an = new animate.shift(this.app, this.lightbar, 0, sy);
+    animate.run([an], 0, 0.3);
+}
+
+MainMenu.prototype.key_enter=function() 
+{
+    var self = this;
+    var target = this.items[this.item];
+    var an = new animate.scale(this.app, target, 1/1.1, 1/1.5);
+    animate.run([an], 0, 0.3,function() {
+        var sx = 259 - target.center.x;
+        var sy = 355 - target.center.y;
+	var an1 = new animate.shift(self.app,target,sx,sy);
+	animate.run([an1],0,1,function() {self.changePage(self.item);});
+    });
+    for(i=0;i<this.items.length;i++) {
+	if (i == this.item) continue;
+	if (i > this.item) {
+	    sx = 1920*2 - this.items[i].center.x;
+	    sy = 0;
+	} else {
+	    sx =  -this.items[i].center.x*2;
+	    sy = 0;
+	}
+	an = new animate.shift(this.app,this.items[i], sx, sy);
+	animate.run([an], 0, 2);
+	alpha = new animate.alpha(this.app,this.items[i], 0);
+	animate.run([an], 0, 2);
+    }
+}
+
+MainMenu.prototype.onNextPage=function() {
+    this.app.changeScene(2);
+}
+MainMenu.prototype.changePage=function(item) {
+    this.epg.getList(item,self.onNextPage());
+}
+
+exports.MainMenu=MainMenu;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/examples/mce/mbmce.js	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,11 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
+var svg = require("svg");
+var mbapp = require("mbapp");
+var sys=require("sys");
+var animate=require("animate");
+var fs = require("fs");
+var main=require("./mainmenu");
+app = new mbapp.app(":0.0",1920,1080);
+scene=new main.MainMenu(app);
+app.loop();
Binary file nodejs/examples/mce/music.png has changed
Binary file nodejs/examples/mce/photo.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/examples/mce/preview.svg	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,89 @@
+<?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:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="1920"
+   height="1080"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.47 r22583"
+   inkscape:transform-center-x="0"
+   inkscape:transform-center-y="0"
+   sodipodi:docname="New document 1">
+  <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" />
+    <inkscape:perspective
+       id="perspective2987"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.35"
+     inkscape:cx="375"
+     inkscape:cy="520"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1680"
+     inkscape:window-height="976"
+     inkscape:window-x="0"
+     inkscape:window-y="25"
+     inkscape:window-maximized="1" />
+  <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" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     inkscape:transform-center-x="0"
+     inkscape:transform-center-y="0"
+     transform="translate(0,27.637817)">
+    <image
+       y="-27.637817"
+       x="0"
+       inkscape:bbox-height="1080"
+       inkscape:bbox-width="1920"
+       inkscape:bbox-y="3.8281246e-07"
+       inkscape:bbox-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:transform-center-x="0"
+       id="image2989"
+       height="1080"
+       width="1920"
+       xlink:href="file:///home/wycc/devel/md6/MadButterfly/nodejs/examples/mce/background2.png" />
+  </g>
+</svg>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/examples/mce/test	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+export NODE_PATH=${PWD}/../../objs/default:${PWD}/../..
+node mbmce.js
Binary file nodejs/examples/mce/tool.png has changed
Binary file nodejs/examples/mce/video.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/examples/phone/phone.js	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,61 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
+var mbapp = require("mbapp");
+var sys=require("sys");
+var animate=require("animate");
+
+app = new mbapp.app(":0.0", 320, 480);
+app.loadSVG("phone_ui.svg");
+
+var icons = [];
+var r, c;
+var mbname;
+for(c = 0; c < 4; c++) {	// 4 columns
+    for(r = 0; r < 5; r++) {	// 5 rows
+	mbname = "icon" + c + "" + r;
+	icons.push(app.get(mbname));
+    }
+}
+
+var overhint, presshint;
+var overholder, pressholder;
+overhint = app.get("overhint");
+presshint = app.get("presshint");
+overholder = new animate.holder(app, overhint);
+pressholder = new animate.holder(app, presshint);
+
+var dock;
+var dockholder;
+dock = app.get("dock");
+dockholder = new animate.holder(app, dock);
+
+var i;
+var icon;
+for(i = 0; i < icons.length; i++) {
+    icon = icons[i];
+    icon.mouse_event.add_event_observer(1, function(evt) {
+	    overholder.go_center(evt.cur_tgt);
+	});
+    icon.mouse_event.add_event_observer(2, function(evt) {
+	    overholder.home();
+	});
+    icon.mouse_event.add_event_observer(4, function(evt) {
+	    pressholder.go_center(evt.cur_tgt);
+	    var rotate = new animate.rotate(app, evt.cur_tgt, 2 * 3.1415);
+	    animate.run([rotate], 0, 0.7);
+	});
+}
+
+var sw = 0;
+var dock_up = new animate.shift(app, dock, 0, -300);
+var dock_down = new animate.shift(app, dock, 0, 0);
+dock.mouse_event.add_event_observer(4, function(evt) {
+	if(sw == 0) {
+	    animate.run([dock_up], 0, 0.5);
+	} else {
+	    animate.run([dock_down], 0, 0.2);
+	}
+	sw = sw ^ 1;
+    });
+
+app.loop();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/examples/phone/phone_ui.svg	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,491 @@
+<?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="320"
+   height="480"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48+devel r9732"
+   sodipodi:docname="phone_ui.svg">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.90416667"
+     inkscape:cx="312.60279"
+     inkscape:cy="186.36119"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     width="320px"
+     inkscape:window-width="888"
+     inkscape:window-height="684"
+     inkscape:window-x="333"
+     inkscape:window-y="132"
+     inkscape:window-maximized="0" />
+  <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"
+     inkscape:bbox-x="-1.1059908"
+     inkscape:bbox-y="-860.32248"
+     inkscape:bbox-width="415.19362"
+     inkscape:bbox-height="773.49026">
+    <rect
+       mbname="icon00"
+       style="fill:#ffe8e8;fill-opacity:1;stroke:#800000;stroke-width:2.07682943;stroke-miterlimit:4;stroke-dasharray:none"
+       id="rect3599"
+       width="56.328701"
+       height="44.162804"
+       x="31.642097"
+       y="60.326893"
+       inkscape:bbox-x="30.603683"
+       inkscape:bbox-y="374.47189"
+       inkscape:bbox-width="58.40553"
+       inkscape:bbox-height="46.239633"
+       rx="11.071856"
+       ry="9.7391548"
+       inkscape:tile-cx="82.396311"
+       inkscape:tile-cy="75.207361"
+       inkscape:tile-w="56.328701"
+       inkscape:tile-h="44.162804"
+       inkscape:tile-x0="54.23196"
+       inkscape:tile-y0="53.125959" />
+    <rect
+       mbname="icon01"
+       inkscape:tile-y0="53.125959"
+       inkscape:tile-x0="54.23196"
+       ry="9.7391548"
+       rx="11.071856"
+       inkscape:bbox-height="46.239633"
+       inkscape:bbox-width="58.40553"
+       inkscape:bbox-y="312.64394"
+       inkscape:bbox-x="30.603683"
+       y="122.15484"
+       x="31.642097"
+       height="44.162804"
+       width="56.328701"
+       id="use3685"
+       style="fill:#ffe8e8;fill-opacity:1;stroke:#800000;stroke-width:2.07682943;stroke-miterlimit:4;stroke-dasharray:none" />
+    <rect
+       mbname="icon02"
+       inkscape:tile-y0="53.125959"
+       inkscape:tile-x0="54.23196"
+       ry="9.7391548"
+       rx="11.071856"
+       inkscape:bbox-height="46.239633"
+       inkscape:bbox-width="58.40553"
+       inkscape:bbox-y="250.81607"
+       inkscape:bbox-x="30.603683"
+       y="183.98271"
+       x="31.642097"
+       height="44.162804"
+       width="56.328701"
+       id="use3687"
+       style="fill:#ffe8e8;fill-opacity:1;stroke:#800000;stroke-width:2.07682943;stroke-miterlimit:4;stroke-dasharray:none" />
+    <rect
+       mbname="icon03"
+       inkscape:tile-y0="53.125959"
+       inkscape:tile-x0="54.23196"
+       ry="9.7391548"
+       rx="11.071856"
+       inkscape:bbox-height="46.239633"
+       inkscape:bbox-width="58.40553"
+       inkscape:bbox-y="188.98813"
+       inkscape:bbox-x="30.603683"
+       y="245.81065"
+       x="31.642097"
+       height="44.162804"
+       width="56.328701"
+       id="use3689"
+       style="fill:#ffe8e8;fill-opacity:1;stroke:#800000;stroke-width:2.07682943;stroke-miterlimit:4;stroke-dasharray:none" />
+    <rect
+       mbname="icon04"
+       inkscape:tile-y0="53.125959"
+       inkscape:tile-x0="54.23196"
+       ry="9.7391548"
+       rx="11.071856"
+       inkscape:bbox-height="46.239633"
+       inkscape:bbox-width="58.40553"
+       inkscape:bbox-y="127.16017"
+       inkscape:bbox-x="30.603683"
+       y="307.63861"
+       x="31.642097"
+       height="44.162804"
+       width="56.328701"
+       id="use3691"
+       style="fill:#ffe8e8;fill-opacity:1;stroke:#800000;stroke-width:2.07682943;stroke-miterlimit:4;stroke-dasharray:none" />
+    <rect
+       mbname="icon10"
+       inkscape:tile-y0="53.125959"
+       inkscape:tile-x0="54.23196"
+       ry="9.7391548"
+       rx="11.071856"
+       inkscape:bbox-height="46.239633"
+       inkscape:bbox-width="58.40553"
+       inkscape:bbox-y="374.47189"
+       inkscape:bbox-x="98.198119"
+       y="60.326893"
+       x="99.236534"
+       height="44.162804"
+       width="56.328701"
+       id="use3693"
+       style="fill:#ffe8e8;fill-opacity:1;stroke:#800000;stroke-width:2.07682943;stroke-miterlimit:4;stroke-dasharray:none" />
+    <rect
+       mbname="icon11"
+       inkscape:tile-y0="53.125959"
+       inkscape:tile-x0="54.23196"
+       ry="9.7391548"
+       rx="11.071856"
+       inkscape:bbox-height="46.239633"
+       inkscape:bbox-width="58.40553"
+       inkscape:bbox-y="312.64394"
+       inkscape:bbox-x="98.198119"
+       y="122.15484"
+       x="99.236534"
+       height="44.162804"
+       width="56.328701"
+       id="use3695"
+       style="fill:#ffe8e8;fill-opacity:1;stroke:#800000;stroke-width:2.07682943;stroke-miterlimit:4;stroke-dasharray:none" />
+    <rect
+       mbname="icon12"
+       inkscape:tile-y0="53.125959"
+       inkscape:tile-x0="54.23196"
+       ry="9.7391548"
+       rx="11.071856"
+       inkscape:bbox-height="46.239633"
+       inkscape:bbox-width="58.40553"
+       inkscape:bbox-y="250.81607"
+       inkscape:bbox-x="98.198119"
+       y="183.98271"
+       x="99.236534"
+       height="44.162804"
+       width="56.328701"
+       id="use3697"
+       style="fill:#ffe8e8;fill-opacity:1;stroke:#800000;stroke-width:2.07682943;stroke-miterlimit:4;stroke-dasharray:none" />
+    <rect
+       mbname="icon13"
+       inkscape:tile-y0="53.125959"
+       inkscape:tile-x0="54.23196"
+       ry="9.7391548"
+       rx="11.071856"
+       inkscape:bbox-height="46.239633"
+       inkscape:bbox-width="58.40553"
+       inkscape:bbox-y="188.98813"
+       inkscape:bbox-x="98.198119"
+       y="245.81065"
+       x="99.236534"
+       height="44.162804"
+       width="56.328701"
+       id="use3699"
+       style="fill:#ffe8e8;fill-opacity:1;stroke:#800000;stroke-width:2.07682943;stroke-miterlimit:4;stroke-dasharray:none" />
+    <rect
+       mbname="icon14"
+       inkscape:tile-y0="53.125959"
+       inkscape:tile-x0="54.23196"
+       ry="9.7391548"
+       rx="11.071856"
+       inkscape:bbox-height="46.239633"
+       inkscape:bbox-width="58.40553"
+       inkscape:bbox-y="127.16017"
+       inkscape:bbox-x="98.198119"
+       y="307.63861"
+       x="99.236534"
+       height="44.162804"
+       width="56.328701"
+       id="use3701"
+       style="fill:#ffe8e8;fill-opacity:1;stroke:#800000;stroke-width:2.07682943;stroke-miterlimit:4;stroke-dasharray:none" />
+    <rect
+       mbname="icon20"
+       inkscape:tile-y0="53.125959"
+       inkscape:tile-x0="54.23196"
+       ry="9.7391548"
+       rx="11.071856"
+       inkscape:bbox-height="46.239633"
+       inkscape:bbox-width="58.40553"
+       inkscape:bbox-y="374.47189"
+       inkscape:bbox-x="165.79256"
+       y="60.326893"
+       x="166.83098"
+       height="44.162804"
+       width="56.328701"
+       id="use3703"
+       style="fill:#ffe8e8;fill-opacity:1;stroke:#800000;stroke-width:2.07682943;stroke-miterlimit:4;stroke-dasharray:none" />
+    <rect
+       mbname="icon21"
+       inkscape:tile-y0="53.125959"
+       inkscape:tile-x0="54.23196"
+       ry="9.7391548"
+       rx="11.071856"
+       inkscape:bbox-height="46.239633"
+       inkscape:bbox-width="58.40553"
+       inkscape:bbox-y="312.64394"
+       inkscape:bbox-x="165.79256"
+       y="122.15484"
+       x="166.83098"
+       height="44.162804"
+       width="56.328701"
+       id="use3705"
+       style="fill:#ffe8e8;fill-opacity:1;stroke:#800000;stroke-width:2.07682943;stroke-miterlimit:4;stroke-dasharray:none" />
+    <rect
+       mbname="icon22"
+       inkscape:tile-y0="53.125959"
+       inkscape:tile-x0="54.23196"
+       ry="9.7391548"
+       rx="11.071856"
+       inkscape:bbox-height="46.239633"
+       inkscape:bbox-width="58.40553"
+       inkscape:bbox-y="250.81607"
+       inkscape:bbox-x="165.79256"
+       y="183.98271"
+       x="166.83098"
+       height="44.162804"
+       width="56.328701"
+       id="use3707"
+       style="fill:#ffe8e8;fill-opacity:1;stroke:#800000;stroke-width:2.07682943;stroke-miterlimit:4;stroke-dasharray:none" />
+    <rect
+       mbname="icon23"
+       inkscape:tile-y0="53.125959"
+       inkscape:tile-x0="54.23196"
+       ry="9.7391548"
+       rx="11.071856"
+       inkscape:bbox-height="46.239633"
+       inkscape:bbox-width="58.40553"
+       inkscape:bbox-y="188.98813"
+       inkscape:bbox-x="165.79256"
+       y="245.81065"
+       x="166.83098"
+       height="44.162804"
+       width="56.328701"
+       id="use3709"
+       style="fill:#ffe8e8;fill-opacity:1;stroke:#800000;stroke-width:2.07682943;stroke-miterlimit:4;stroke-dasharray:none" />
+    <rect
+       mbname="icon24"
+       inkscape:tile-y0="53.125959"
+       inkscape:tile-x0="54.23196"
+       ry="9.7391548"
+       rx="11.071856"
+       inkscape:bbox-height="46.239633"
+       inkscape:bbox-width="58.40553"
+       inkscape:bbox-y="127.16017"
+       inkscape:bbox-x="165.79256"
+       y="307.63861"
+       x="166.83098"
+       height="44.162804"
+       width="56.328701"
+       id="use3711"
+       style="fill:#ffe8e8;fill-opacity:1;stroke:#800000;stroke-width:2.07682943;stroke-miterlimit:4;stroke-dasharray:none" />
+    <rect
+       mbname="icon30"
+       inkscape:tile-y0="53.125959"
+       inkscape:tile-x0="54.23196"
+       ry="9.7391548"
+       rx="11.071856"
+       inkscape:bbox-height="46.239633"
+       inkscape:bbox-width="58.40553"
+       inkscape:bbox-y="374.47189"
+       inkscape:bbox-x="233.387"
+       y="60.326893"
+       x="234.42542"
+       height="44.162804"
+       width="56.328701"
+       id="use3713"
+       style="fill:#ffe8e8;fill-opacity:1;stroke:#800000;stroke-width:2.07682943;stroke-miterlimit:4;stroke-dasharray:none" />
+    <rect
+       mbname="icon31"
+       inkscape:tile-y0="53.125959"
+       inkscape:tile-x0="54.23196"
+       ry="9.7391548"
+       rx="11.071856"
+       inkscape:bbox-height="46.239633"
+       inkscape:bbox-width="58.40553"
+       inkscape:bbox-y="312.64394"
+       inkscape:bbox-x="233.387"
+       y="122.15484"
+       x="234.42542"
+       height="44.162804"
+       width="56.328701"
+       id="use3715"
+       style="fill:#ffe8e8;fill-opacity:1;stroke:#800000;stroke-width:2.07682943;stroke-miterlimit:4;stroke-dasharray:none" />
+    <rect
+       mbname="icon32"
+       inkscape:tile-y0="53.125959"
+       inkscape:tile-x0="54.23196"
+       ry="9.7391548"
+       rx="11.071856"
+       inkscape:bbox-height="46.239633"
+       inkscape:bbox-width="58.40553"
+       inkscape:bbox-y="250.81607"
+       inkscape:bbox-x="233.387"
+       y="183.98271"
+       x="234.42542"
+       height="44.162804"
+       width="56.328701"
+       id="use3717"
+       style="fill:#ffe8e8;fill-opacity:1;stroke:#800000;stroke-width:2.07682943;stroke-miterlimit:4;stroke-dasharray:none" />
+    <rect
+       mbname="icon33"
+       inkscape:tile-y0="53.125959"
+       inkscape:tile-x0="54.23196"
+       ry="9.7391548"
+       rx="11.071856"
+       inkscape:bbox-height="46.239633"
+       inkscape:bbox-width="58.40553"
+       inkscape:bbox-y="188.98813"
+       inkscape:bbox-x="233.387"
+       y="245.81065"
+       x="234.42542"
+       height="44.162804"
+       width="56.328701"
+       id="use3719"
+       style="fill:#ffe8e8;fill-opacity:1;stroke:#800000;stroke-width:2.07682943;stroke-miterlimit:4;stroke-dasharray:none" />
+    <rect
+       mbname="icon34"
+       inkscape:tile-y0="53.125959"
+       inkscape:tile-x0="54.23196"
+       ry="9.7391548"
+       rx="11.071856"
+       inkscape:bbox-height="46.239633"
+       inkscape:bbox-width="58.40553"
+       inkscape:bbox-y="127.16017"
+       inkscape:bbox-x="233.387"
+       y="307.63861"
+       x="234.42542"
+       height="44.162804"
+       width="56.328701"
+       id="use3721"
+       style="fill:#ffe8e8;fill-opacity:1;stroke:#800000;stroke-width:2.07682943;stroke-miterlimit:4;stroke-dasharray:none" />
+    <g
+       id="g3781"
+       inkscape:bbox-x="-2.6747894e-07"
+       inkscape:bbox-y="440.72538"
+       inkscape:bbox-width="321.84332"
+       inkscape:bbox-height="43.133648"
+       transform="translate(1.1059905,-570.69124)">
+      <rect
+         inkscape:bbox-height="35.391705"
+         inkscape:bbox-width="320.73734"
+         inkscape:bbox-y="440.72538"
+         inkscape:bbox-x="-2.7016535e-07"
+         y="574.57416"
+         x="-1.1059908"
+         height="35.391705"
+         width="320.73734"
+         id="rect3761"
+         style="fill:#f2f2f2;fill-opacity:1;stroke:none" />
+      <rect
+         inkscape:bbox-height="35.391705"
+         inkscape:bbox-width="320.73734"
+         inkscape:bbox-y="448.46732"
+         inkscape:bbox-x="1.1059882"
+         y="566.83221"
+         x="-2.250144e-06"
+         height="35.391705"
+         width="320.73734"
+         id="rect3761-7"
+         style="fill:#ececec;fill-opacity:1;stroke:none" />
+    </g>
+    <rect
+       inkscape:tile-y0="53.125959"
+       inkscape:tile-x0="54.23196"
+       inkscape:tile-h="44.162804"
+       inkscape:tile-w="56.328701"
+       inkscape:tile-cy="75.207361"
+       inkscape:tile-cx="82.396311"
+       ry="9.7391548"
+       rx="11.071856"
+       inkscape:bbox-height="46.239804"
+       inkscape:bbox-width="58.405701"
+       inkscape:bbox-y="370.70221"
+       inkscape:bbox-x="356.04143"
+       y="64.096489"
+       x="357.07993"
+       height="44.162804"
+       width="56.328701"
+       id="rect3599-9"
+       style="fill:none;stroke:#ff1010;stroke-width:2.0769999;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       mbname="overhint" />
+    <rect
+       inkscape:tile-y0="53.125959"
+       inkscape:tile-x0="54.23196"
+       inkscape:tile-h="44.162804"
+       inkscape:tile-w="56.328701"
+       inkscape:tile-cy="75.207361"
+       inkscape:tile-cx="82.396311"
+       ry="9.7391548"
+       rx="11.071856"
+       inkscape:bbox-height="46.239804"
+       inkscape:bbox-width="58.405701"
+       inkscape:bbox-y="318.06628"
+       inkscape:bbox-x="356.78795"
+       y="116.73241"
+       x="357.82645"
+       height="44.162804"
+       width="56.328701"
+       id="rect3599-9-3"
+       style="fill:none;stroke:#000080;stroke-width:2.07699990000000012;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       mbname="presshint" />
+    <g
+       id="g3849"
+       inkscape:bbox-x="2.2255181"
+       inkscape:bbox-y="-289.63124"
+       inkscape:bbox-width="317.40625"
+       inkscape:bbox-height="359.25593"
+       mbname="dock"
+       transform="translate(1.1059905,-570.69124)">
+      <path
+         inkscape:bbox-height="76.31255"
+         inkscape:bbox-width="317.40625"
+         inkscape:bbox-y="-6.68786"
+         inkscape:bbox-x="2.2255181"
+         inkscape:connector-curvature="0"
+         id="rect2817"
+         d="m 163.68203,981.06655 c -63.1198,0 -120.411185,7.92804 -162.5625024,20.84375 l 0,55.4688 317.4062524,0 0,-57.7188 C 277.22165,988.08365 223.03485,981.06655 163.68203,981.06655 z"
+         style="fill:#4494c2;fill-opacity:0.59174314;stroke:none" />
+      <path
+         style="fill:#6083ff;fill-opacity:1;stroke:#1033ff;stroke-width:2.0769999;stroke-miterlimit:4;stroke-opacity:1"
+         d="m 182.48848,1003.6866 c 0,8.2461 -9.16063,14.9309 -20.46083,14.9309 -11.3002,0 -20.46083,-6.6848 -20.46083,-14.9309 0,-8.24608 9.16063,-14.93086 20.46083,-14.93086 11.3002,0 20.46083,6.68478 20.46083,14.93086 z"
+         id="path3843"
+         inkscape:connector-curvature="0"
+         inkscape:bbox-x="141.63431"
+         inkscape:bbox-y="31.03524"
+         inkscape:bbox-width="42.99866"
+         inkscape:bbox-height="31.93876" />
+      <rect
+         style="fill:#ececec;fill-opacity:1;stroke:#c7cfff;stroke-width:2.03777313;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+         id="rect2838"
+         width="310.82263"
+         height="282.06689"
+         x="4.4043493"
+         y="1057.2367"
+         inkscape:bbox-x="4.4914533"
+         inkscape:bbox-y="-289.63124"
+         inkscape:bbox-width="312.8604"
+         inkscape:bbox-height="284.10467" />
+    </g>
+  </g>
+</svg>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/examples/scene/mainmenu.js	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,60 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
+var svg = require("svg");
+var mbapp = require("mbapp");
+var sys=require("sys");
+var animate=require("animate");
+var fs = require("fs");
+/**
+ *   We will fetch the EPG file from the server and fetch all images required for the main category from it.
+ *   If these files are cached, we will not fetch it again. Otherwise, we will fetch them. The EPG class is
+ *   responsible for the cache management.
+ */
+function MainMenu(app) 
+{
+    var self = this;
+    this.n = 1;
+    this.app = app;
+    self.init(app);
+}
+MainMenu.prototype.init=function(app)
+{
+    var self = this;
+    app.loadSVG("mbtest.svg");
+
+    app.addKeyListener(mbapp.KEY_LEFT, function() { self.key_left();});
+    app.addKeyListener(mbapp.KEY_RIGHT, function() { self.key_right();});
+    app.addKeyListener(mbapp.KEY_UP, function() {self.key_up();});
+    app.addKeyListener(mbapp.KEY_DOWN, function() {self.key_down();});
+    app.addKeyListener(mbapp.KEY_ENTER, function() {self.key_enter();});
+    app.changeScene(this.n);
+}
+
+MainMenu.prototype.key_left=function () 
+{
+   this.n = this.n - 1;
+   this.app.changeScene(this.n);
+   sys.puts("scene "+this.n);
+}
+
+MainMenu.prototype.key_right=function() 
+{
+   this.n = this.n + 1;
+   this.app.changeScene(this.n);
+   sys.puts("scene "+this.n);
+}
+
+MainMenu.prototype.key_up=function() 
+{
+}
+
+
+MainMenu.prototype.key_down=function () 
+{
+}
+
+MainMenu.prototype.key_enter=function() 
+{
+}
+
+exports.MainMenu=MainMenu;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/examples/scene/mbtest.svg	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,374 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:ns0="http://madbutterfly.sourceforge.net/DTD/madbutterfly.dtd"
+   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:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="640px"
+   height="480px"
+   id="svg2383"
+   sodipodi:version="0.32"
+   inkscape:version="0.48+devel r9764 custom"
+   sodipodi:docname="mbtest.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.1">
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.6029106"
+     inkscape:cx="69.206233"
+     inkscape:cy="290.40921"
+     inkscape:current-layer="layer2"
+     inkscape:document-units="px"
+     showgrid="false"
+     inkscape:window-width="1400"
+     inkscape:window-height="974"
+     inkscape:window-x="271"
+     inkscape:window-y="25"
+     inkscape:window-maximized="0" />
+  <defs
+     id="defs2385">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3211">
+      <stop
+         style="stop-color:#001dff;stop-opacity:1;"
+         offset="0"
+         id="stop3213" />
+      <stop
+         style="stop-color:#001dff;stop-opacity:0;"
+         offset="1"
+         id="stop3215" />
+    </linearGradient>
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 240 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="640 : 240 : 1"
+       inkscape:persp3d-origin="320 : 160 : 1"
+       id="perspective2391" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3211"
+       id="linearGradient3217"
+       x1="31.940987"
+       y1="28.009715"
+       x2="104.68548"
+       y2="28.009715"
+       gradientUnits="userSpaceOnUse" />
+    <filter
+       inkscape:collect="always"
+       id="filter3295">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.67110109"
+         id="feGaussianBlur3297" />
+    </filter>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3211"
+       id="linearGradient3316"
+       gradientUnits="userSpaceOnUse"
+       x1="31.940987"
+       y1="28.009715"
+       x2="104.68548"
+       y2="28.009715" />
+    <linearGradient
+       y2="28.009715"
+       x2="104.68548"
+       y1="28.009715"
+       x1="31.940987"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3542"
+       xlink:href="#linearGradient3211"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="28.009715"
+       x2="104.68548"
+       y1="28.009715"
+       x1="31.940987"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3544"
+       xlink:href="#linearGradient3211"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3211-0">
+      <stop
+         style="stop-color:#001dff;stop-opacity:1;"
+         offset="0"
+         id="stop3213-3" />
+      <stop
+         style="stop-color:#001dff;stop-opacity:0;"
+         offset="1"
+         id="stop3215-9" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3211-0"
+       id="linearGradient3316-4"
+       gradientUnits="userSpaceOnUse"
+       x1="31.940987"
+       y1="28.009714"
+       x2="104.68548"
+       y2="28.009714" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3211-0"
+       id="linearGradient3217-6"
+       x1="31.940987"
+       y1="28.009714"
+       x2="104.68548"
+       y2="28.009714"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3211-0-5"
+       id="linearGradient3316-4-8"
+       gradientUnits="userSpaceOnUse"
+       x1="31.940987"
+       y1="28.009714"
+       x2="104.68548"
+       y2="28.009714" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3211-0-5">
+      <stop
+         style="stop-color:#001dff;stop-opacity:1;"
+         offset="0"
+         id="stop3213-3-8" />
+      <stop
+         style="stop-color:#001dff;stop-opacity:0;"
+         offset="1"
+         id="stop3215-9-4" />
+    </linearGradient>
+    <filter
+       color-interpolation-filters="sRGB"
+       inkscape:collect="always"
+       id="filter3295-1-3">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.67110109"
+         id="feGaussianBlur3297-9-7" />
+    </filter>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3211-0-5"
+       id="linearGradient3217-6-1"
+       x1="31.940987"
+       y1="28.009714"
+       x2="104.68548"
+       y2="28.009714"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3211-0-5"
+       id="linearGradient3237"
+       gradientUnits="userSpaceOnUse"
+       x1="31.940987"
+       y1="28.009714"
+       x2="104.68548"
+       y2="28.009714" />
+  </defs>
+  <metadata
+     id="metadata2388">
+    <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>
+    <ns0:scenes>
+      <ns0:scene
+         start="1"
+         ref="g3189"
+         end="15" />
+      <ns0:scene
+         start="1"
+         ref="s4427" />
+      <ns0:scene
+         start="2"
+         ref="s4159" />
+      <ns0:scene
+         start="5"
+         ref="s9524"
+         end="10" />
+      <ns0:scene
+         start="15"
+         ref="s6546" />
+    </ns0:scenes>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer2"
+     inkscape:label="Background"
+     style="display:inline">
+    <g
+       id="g3189"
+       style="">
+      <rect
+         style="fill:#00ffff;fill-opacity:1;stroke:#000000;stroke-opacity:1;display:inline"
+         id="rect2437"
+         width="641.95721"
+         height="481.62387"
+         x="0.93578684"
+         y="-10.98185" />
+      <rect
+         style="fill:#ffcc1d;fill-opacity:1;stroke:none"
+         id="rect3698"
+         width="624.48901"
+         height="46.789886"
+         x="6.8625164"
+         y="5.8625031"
+         rx="10"
+         ry="10" />
+      <g
+         style="display:inline"
+         transform="translate(-11.385541,2.6514388)"
+         id="g3303">
+        <rect
+           y="15.22048"
+           x="32.440987"
+           height="25.57847"
+           width="71.744492"
+           id="rect2439"
+           style="fill:url(#linearGradient3237);fill-opacity:1;stroke:none;filter:url(#filter3295-1-3)" />
+        <text
+           id="text3299"
+           y="33.312569"
+           x="39.927368"
+           style="font-size:24px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+           xml:space="preserve"><tspan
+             style="font-size:16px"
+             y="33.312569"
+             x="39.927368"
+             id="tspan3301"
+             sodipodi:role="line">Action</tspan></text>
+      </g>
+      <g
+         style="display:inline"
+         id="g3308"
+         transform="translate(76.891374,2.9633707)">
+        <rect
+           y="15.22048"
+           x="32.440987"
+           height="25.57847"
+           width="71.744492"
+           id="rect3310"
+           style="fill:url(#linearGradient3316-4-8);fill-opacity:1;stroke:none;filter:url(#filter3295-1-3)" />
+        <text
+           id="text3312"
+           y="33.312569"
+           x="39.927368"
+           style="font-size:24px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+           xml:space="preserve"><tspan
+             style="font-size:16px"
+             y="33.312569"
+             x="39.927368"
+             id="tspan3314"
+             sodipodi:role="line">Select</tspan></text>
+      </g>
+    </g>
+    <g
+       id="s4393"
+       style="" />
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer3"
+     inkscape:label="Buton"
+     style="display:inline">
+    <g
+       id="s4427"
+       style="display:none" />
+    <g
+       id="s4159"
+       transform="translate(170.31517,0.62386544)"
+       style="display:none">
+      <g
+         transform="translate(4.9909171,0.3119319)"
+         id="g3370">
+        <rect
+           style="fill:url(#linearGradient3542);fill-opacity:1;stroke:none;filter:url(#filter3295)"
+           id="rect3372"
+           width="71.744492"
+           height="25.57847"
+           x="32.440987"
+           y="15.22048" />
+        <text
+           xml:space="preserve"
+           style="font-size:24px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+           x="35.927368"
+           y="33.312569"
+           id="text3374"><tspan
+             sodipodi:role="line"
+             id="tspan3376"
+             x="35.927368"
+             y="33.312569"
+             style="font-size:16px">GNOME</tspan></text>
+      </g>
+      <g
+         transform="translate(102.93775,-0.9357981)"
+         id="g3380">
+        <rect
+           style="fill:url(#linearGradient3544);fill-opacity:1;stroke:none;filter:url(#filter3295)"
+           id="rect3382"
+           width="71.744492"
+           height="25.57847"
+           x="32.440987"
+           y="15.22048" />
+        <text
+           xml:space="preserve"
+           style="font-size:24px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+           x="39.927368"
+           y="33.312569"
+           id="text3384"><tspan
+             sodipodi:role="line"
+             id="tspan3386"
+             x="39.927368"
+             y="33.312569"
+             style="font-size:16px">AAA</tspan></text>
+      </g>
+    </g>
+    <g
+       id="s9524"
+       transform="translate(90.460441,64.258106)"
+       style="display:none">
+      <rect
+         x="0"
+         y="0"
+         width="100"
+         height="100"
+         id="rect3118" />
+    </g>
+    <g
+       id="s6546"
+       style="display:none">
+      <rect
+         x="0"
+         y="0"
+         width="100"
+         height="100"
+         style="fill:#ff00"
+         id="rect3116" />
+    </g>
+  </g>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     style="display:inline" />
+</svg>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/examples/scene/scene.js	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,11 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
+var svg = require("svg");
+var mbapp = require("mbapp");
+var sys=require("sys");
+var animate=require("animate");
+var fs = require("fs");
+var main=require("./mainmenu");
+app = new mbapp.app(":0.0",800,600);
+scene=new main.MainMenu(app);
+app.loop();
Binary file nodejs/examples/simple/sample.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/examples/simple/testcanvas.js	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,31 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
+var mbapp = require("mbapp");
+var sys=require("sys");
+var canvas=require("canvas");
+
+app = new mbapp.app();
+
+app.canvas = new canvas.canvas(app,app.mb_rt.root);
+
+app.canvas.background(0,0,0,1);
+app.canvas.rect(0,0,400,400);
+app.canvas.strokeWeight(8);
+width=200;
+height=200;
+
+setInterval(function() {
+    app.canvas.clear();
+
+    for(i=0;i<width;i++) {
+        x = Math.random()*255;
+   	    y = Math.random()*200;
+	    cr = Math.random()
+	    cg = Math.random()
+	    cb = Math.random()
+	    app.canvas.stroke(cr,cg,cb,1);
+	    app.canvas.line(i,0,x,height);
+	}
+	app.update();
+},33);
+app.loop();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/examples/simple/testcase.js	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,169 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
+var mbfly = require("mbfly");
+var r = mbfly.Hello(" test");
+var sys = require("sys");
+sys.puts(r);
+
+var display_name = ":0.0";
+if(process.argv.length == 3)
+   display_name = process.argv[2];
+
+var mb_rt = new mbfly.mb_rt(display_name, 300, 200);
+var root = mb_rt.root;
+sys.puts("root matrix: " +
+	 [root[0], root[1], root[2], root[3], root[4], root[5]]);
+var coord = mb_rt.coord_new(root);
+sys.puts("coord matrix: " + 
+	 [coord[0], coord[1], coord[2], coord[3], coord[4], coord[5]]);
+
+/* Testcase for image shapes */
+var img = mb_rt.image_new(10, 10, 50, 50);
+var ldr = mbfly.img_ldr_new(".");
+var img_data = ldr.load("sample.png");
+var paint = mb_rt.paint_image_new(img_data);
+var img_coord = mb_rt.coord_new(root);
+paint.fill(img);
+img_coord.add_shape(img);
+
+/* test linear paint and rectangle */
+var rect = mb_rt.rect_new(100, 100, 50, 50, 10, 10);
+sys.puts(mb_rt.paint_linear_new);
+var paint = mb_rt.paint_linear_new(100, 100, 150, 150);
+paint.set_stops([[0, 0, 1, 0, 1], [1, 0, 0, 1, 1]]);
+paint.fill(rect);
+root.add_shape(rect);
+
+/* test radial paint and rectangle */
+var rect = mb_rt.rect_new(150, 100, 50, 50, 10, 10);
+sys.puts(mb_rt.paint_radial_new);
+var paint = mb_rt.paint_radial_new(175, 125, 25);
+paint.set_stops([[0, 0, 1, 0, 1], [1, 0, 0, 1, 1]]);
+paint.fill(rect);
+root.add_shape(rect);
+
+/* test alpha blending and rectangle */
+var rect = mb_rt.rect_new(40, 40, 100, 100, 10, 10);
+sys.puts(mb_rt.paint_color_new);
+var paint = mb_rt.paint_color_new(1, 0.5, 0.5, 0.5);
+paint.fill(rect);
+root.add_shape(rect);
+
+/* test hide of shapes */
+var sw = 1;
+setInterval(function() {
+	if(sw) {
+	    rect.hide();
+	    sw = 0;
+	} else {
+	    rect.show();
+	    sw = 1;
+	}
+    }, 1000);
+
+/* test hide of coord */
+var cw = 1;
+setInterval(function() {
+	if(sw) {
+	    coord.hide();
+	    cw = 0;
+	} else {
+	    coord.show();
+	    cw = 1;
+	}
+    }, 3000);
+
+/* test removing a coord */
+var rm_coord = mb_rt.coord_new(root);
+var rm_rect1 = mb_rt.rect_new(150, 150, 50, 50, 10, 10);
+paint.fill(rm_rect1);
+rm_coord.add_shape(rm_rect1);
+var rm_rect2 = mb_rt.rect_new(100, 150, 50, 50, 10, 10);
+paint.fill(rm_rect2);
+rm_coord.add_shape(rm_rect2);
+setTimeout(function() {
+	rm_coord.remove();
+	mb_rt.redraw_changed();
+	mb_rt.flush();
+    }, 3000);
+
+/* test removing a shape */
+setTimeout(function() {
+	rm_rect1.remove();
+	mb_rt.redraw_changed();
+	mb_rt.flush();
+    }, 2000);
+
+/* Moving a path */
+sys.puts(mb_rt.path_new);
+var path = mb_rt.path_new("m 100,50 L 120,50 L 200,150 L 180,150 z");
+sys.puts(path);
+sys.puts(coord.add_shape);
+coord.add_shape(path);
+
+sys.puts(mb_rt.paint_color_new);
+var paint = mb_rt.paint_color_new(1, 1, 1, 1);
+sys.puts(paint);
+paint.stroke(path);
+
+sys.puts(path.stroke_width);
+path.stroke_width = 2;
+sys.puts(path.stroke_width);
+
+var face = mb_rt.font_face_query("courier", 2, 100);
+var blks = [[5, face, 20]];
+var stext = mb_rt.stext_new("Hello", 100, 50);
+stext.set_style(blks);
+paint.fill(stext);
+coord.add_shape(stext);
+
+mb_rt.redraw_all();
+mb_rt.flush();
+
+var i = 0;
+setInterval(function() {
+	var deg = (i++) * 0.1;
+	coord[2] = (i % 40) * 5;
+	mb_rt.redraw_changed();
+	mb_rt.flush();
+    }, 20);
+setTimeout(function() { sys.puts("timeout"); }, 1000);
+
+sys.puts(root.mouse_event);
+var observer;
+/* Mouse button pressed */
+observer = root.mouse_event.add_event_observer(4, function(evt) {
+	var c = 1 - (i % 40) / 40;
+	sys.puts(c);
+
+	sys.puts("mouse " + evt.x + " " + evt.y);
+	sys.puts(c);
+	sys.puts(paint.set_color);
+	paint.set_color(c, 1, 1, 1);
+	mb_rt.redraw_changed();
+	mb_rt.flush();
+    });
+
+var resize_sw = 0;
+setInterval(function() {
+	var sz;
+	
+	resize_sw++;
+	
+	sz = (resize_sw % 20) - 10;
+	if(sz < 0)
+	    sz = -sz;
+	sz = 2 - sz / 10;
+	
+	img_coord[0] = sz;
+	img_coord[4] = sz;
+	mb_rt.redraw_changed();
+	mb_rt.flush();
+    }, 50);
+
+var kbobserver;
+/* Keyboard event */
+kbobserver = mb_rt.kbevents.add_event_observer(6, function(evt) {
+	sys.puts("keycode = " + evt.keycode);
+	sys.puts("sym = " + evt.sym);
+    });
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/examples/simple/testimage.js	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,19 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
+var svg = require("svg");
+var mbapp = require("mbapp");
+var sys=require("sys");
+var animate=require("animate");
+var fs = require("fs");
+
+app = new mbapp.app();
+
+coord = app.mb_rt.coord_new(app.mb_rt.root);
+data=mbapp.ldr.load("sample.png");
+paint = app.mb_rt.paint_image_new(data);
+img = app.mb_rt.image_new(10,10,50,50);
+paint.fill(img);
+coord.opacity = 0.9;
+coord.add_shape(img);
+
+app.loop();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/examples/simple/testleak.js	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,217 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
+var mbfly = require("mbfly");
+var sys = require("sys");
+var cproc = require("child_process");
+
+function testcase1() {
+    var mbrt = new mbfly.mb_rt(":32.0", 300, 200);
+    var root = mbrt.root;
+    var coords = [];
+    var coord;
+    var i;
+
+    while(true) {
+	for(i = 0; i < 200; i++) {
+	    coords.push(mbrt.coord_new(root));
+	}
+	mbrt.redraw_changed();
+	mbrt.flush();
+	
+	while(coords.length > 0) {
+	    coord = coords.pop();
+	    coord.remove();
+	}
+    }
+}
+
+function testcase2() {
+    var mbrt = new mbfly.mb_rt(":32.0", 300, 200);
+    var root = mbrt.root;
+    var coords = [];
+    var coord;
+    var i, j;
+
+    while(true) {
+	for(i = 0; i < 200; i++) {
+	    coords.push(mbrt.coord_new(root));
+	    for(j = 0; j < 10; j++)
+		coord = mbrt.coord_new(coords[i]);
+	}
+	mbrt.redraw_changed();
+	mbrt.flush();
+	
+	while(coords.length > 0) {
+	    coord = coords.pop();
+	    coord.remove();
+	}
+    }
+}
+
+function testcase3() {
+    var mbrt = new mbfly.mb_rt(":32.0", 300, 200);
+    var root = mbrt.root;
+    var coords = [];
+    var coord;
+    var shape;
+    var i, j;
+
+    while(true) {
+	for(i = 0; i < 200; i++) {
+	    coords.push(mbrt.coord_new(root));
+	    for(j = 0; j < 10; j++) {
+		coord = mbrt.coord_new(coords[i]);
+		shape = mbrt.rect_new(15, 15, 20, 20, 0, 0);
+		coord.add_shape(shape);
+	    }
+	}
+	mbrt.redraw_changed();
+	mbrt.flush();
+	
+	while(coords.length > 0) {
+	    coord = coords.pop();
+	    coord.remove();
+	}
+    }
+}
+
+function testcase4() {
+    var mbrt = new mbfly.mb_rt(":32.0", 300, 200);
+    var root = mbrt.root;
+    var coords = [];
+    var coord;
+    var shape;
+    var i, j;
+
+    while(true) {
+	for(i = 0; i < 200; i++) {
+	    coords.push(mbrt.coord_new(root));
+	    for(j = 0; j < 10; j++) {
+		coord = mbrt.coord_new(coords[i]);
+		shape = mbrt.path_new("m 10,10 l 55,27 l -30,-3 z");
+		coord.add_shape(shape);
+	    }
+	}
+	mbrt.redraw_changed();
+	mbrt.flush();
+	
+	while(coords.length > 0) {
+	    coord = coords.pop();
+	    coord.remove();
+	}
+    }
+}
+
+function testcase5() {
+    var mbrt = new mbfly.mb_rt(":32.0", 300, 200);
+    var root = mbrt.root;
+    var paint;
+
+    while(true) {
+	paint = mbrt.paint_color_new(0.5, 0.5, 0.5, 1);
+    }
+}
+
+function testcase6() {
+    var mbrt = new mbfly.mb_rt(":32.0", 300, 200);
+    var root = mbrt.root;
+    var coords = [];
+    var coord;
+    var shape;
+    var paint;
+    var i, j;
+
+    while(true) {
+	for(i = 0; i < 200; i++) {
+	    coords.push(mbrt.coord_new(root));
+	    /* Paint is free, but rdman is dirty */
+	    paint = mbrt.paint_color_new(0.5, 0.5, 0.5, 1);
+	    for(j = 0; j < 10; j++) {
+		coord = mbrt.coord_new(coords[i]);
+		shape = mbrt.rect_new(5, 5, 20, 20, 0, 0);
+		coord.add_shape(shape);
+	    }
+	}
+	mbrt.redraw_changed();
+	mbrt.flush();
+	
+	while(coords.length > 0) {
+	    coord = coords.pop();
+	    coord.remove();
+	}
+    }
+}
+
+function testcase7() {
+    var mbrt = new mbfly.mb_rt(":32.0", 300, 200);
+    var root = mbrt.root;
+    var coords = [];
+    var coord;
+    var shape;
+    var paint;
+    var i, j;
+
+    while(true) {
+	for(i = 0; i < 200; i++) {
+	    coords.push(mbrt.coord_new(root));
+	    for(j = 0; j < 10; j++) {
+		/* paint is not free, and rdman is dirty */
+		paint = mbrt.paint_color_new(0.5, 0.5, 0.5, 1);
+		coord = mbrt.coord_new(coords[i]);
+		shape = mbrt.path_new("M 100,100 L 30,30 L 0,30 z");
+		coord.add_shape(shape);
+		paint.stroke(shape);
+	    }
+	}
+	mbrt.redraw_changed();
+	mbrt.flush();
+	
+	while(coords.length > 0) {
+	    coord = coords.pop();
+	    coord.remove();
+	}
+    }
+}
+
+cproc.exec("killall -9 Xvfb; Xvfb :32 -screen 0 800x600x24");
+setTimeout(function() {
+	if(process.argv.length == 1)
+	    testcase1();
+	else if(process.argv.length == 3) {
+	    sys.puts("testcase " + process.argv[2]);
+	    switch(process.argv[2]) {
+	    case "1":
+		testcase1();
+		break;
+		
+	    case "2":
+		testcase2();
+		break;
+		
+	    case "3":
+		testcase3();
+		break;
+		
+	    case "4":
+		testcase4();
+		break;
+		
+	    case "5":
+		testcase5();
+		break;
+		
+	    case "6":
+		testcase6();
+		break;
+		
+	    case "7":
+		testcase7();
+		break;
+		
+	    default:
+		sys.puts("Usage: node testleak.js [1|2|3...7]");
+	    }
+	} else
+	    sys.puts("Usage: node testleak.js [1|2|3...7]");
+	cproc.exec("killall -9 Xvfb");
+    }, 1500);
Binary file nodejs/examples/testsvg/logo.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/examples/testsvg/svgviewer.js	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,8 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
+var svg = require("svg");
+var mbapp = require("mbapp");
+
+app = new mbapp.app();
+app.loadSVG("test.svg");
+app.loop();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/examples/testsvg/test.svg	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,538 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:ns0="http://madbutterfly.sourceforge.net/DTD/madbutterfly.dtd"
+   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:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="720"
+   height="480"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.47 r22583"
+   sodipodi:docname="test.svg"
+   version="1.0"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <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="1.0347223"
+     inkscape:cx="151.05815"
+     inkscape:cy="228.90269"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1347"
+     inkscape:window-height="894"
+     inkscape:window-x="0"
+     inkscape:window-y="25"
+     inkscape:window-maximized="0" />
+  <defs
+     id="defs4">
+    <linearGradient
+       id="linearGradient3183">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop3185" />
+      <stop
+         style="stop-color:#505050;stop-opacity:1;"
+         offset="1"
+         id="stop3187" />
+    </linearGradient>
+    <inkscape:perspective
+       id="perspective10"
+       inkscape:persp3d-origin="804.3173 : 267.38473 : 1"
+       inkscape:vp_z="1630.7262 : 376.00761 : 1"
+       inkscape:vp_y="0 : 619.30892 : 0"
+       inkscape:vp_x="-22.091594 : 376.00761 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3183"
+       id="linearGradient3189"
+       x1="0"
+       y1="239.5"
+       x2="719.99998"
+       y2="239.5"
+       gradientUnits="userSpaceOnUse" />
+  </defs>
+  <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>
+    <ns0:scenes
+       current="1" />
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer2"
+     inkscape:label="Background"
+     sodipodi:insensitive="true">
+    <rect
+       style="opacity:1;fill:url(#linearGradient3189);fill-opacity:1;stroke:#000000;stroke-width:0.9993065;stroke-opacity:1"
+       id="rect3181"
+       width="719.00067"
+       height="480.0007"
+       x="0.49965325"
+       y="-0.50034672" />
+  </g>
+  <g
+     id="layer1"
+     inkscape:groupmode="layer"
+     inkscape:label="Layer 1">
+    <g
+       transform="translate(144.04213,-2.85715)"
+       id="item1"
+       mbname="item1"
+       style="fill-opacity:1"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="303.20061"
+       inkscape:bbox-y="422.51731"
+       inkscape:bbox-width="74.0625"
+       inkscape:bbox-height="18.574219">
+      <text
+         id="text2395"
+         y="60"
+         x="157.14285"
+         style="font-size:24px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold"
+         xml:space="preserve"
+         sodipodi:linespacing="125%"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         inkscape:bbox-x="303.20061"
+         inkscape:bbox-y="422.51731"
+         inkscape:bbox-width="74.0625"
+         inkscape:bbox-height="18.574219"><tspan
+           mbname="item1text"
+           y="60"
+           x="157.14285"
+           id="tspan2397"
+           sodipodi:role="line"
+           inkscape:transform-center-x="0"
+           inkscape:transform-center-y="0"
+           inkscape:bbox-x="303.20061"
+           inkscape:bbox-y="422.51731"
+           inkscape:bbox-width="74.0625"
+           inkscape:bbox-height="18.574219">item1</tspan></text>
+    </g>
+    <g
+       transform="translate(144.04213,39.689836)"
+       id="item2"
+       mbname="item2"
+       style="fill-opacity:1"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="303.20061"
+       inkscape:bbox-y="379.97032"
+       inkscape:bbox-width="74.0625"
+       inkscape:bbox-height="18.574219">
+      <text
+         id="text2421"
+         y="60"
+         x="157.14285"
+         style="font-size:24px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold"
+         xml:space="preserve"
+         sodipodi:linespacing="125%"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         inkscape:bbox-x="303.20061"
+         inkscape:bbox-y="379.97032"
+         inkscape:bbox-width="74.0625"
+         inkscape:bbox-height="18.574219"><tspan
+           mbname="item2text"
+           y="60"
+           x="157.14285"
+           id="tspan2423"
+           sodipodi:role="line"
+           inkscape:transform-center-x="0"
+           inkscape:transform-center-y="0"
+           inkscape:bbox-x="303.20061"
+           inkscape:bbox-y="379.97032"
+           inkscape:bbox-width="74.0625"
+           inkscape:bbox-height="18.574219">item1</tspan></text>
+    </g>
+    <g
+       transform="translate(144.04213,82.236798)"
+       id="item3"
+       mbname="item3"
+       style="fill-opacity:1"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="303.20061"
+       inkscape:bbox-y="337.42336"
+       inkscape:bbox-width="74.0625"
+       inkscape:bbox-height="18.574219">
+      <text
+         id="item3text"
+         y="60"
+         x="157.14285"
+         style="font-size:24px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold"
+         xml:space="preserve"
+         sodipodi:linespacing="125%"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         inkscape:bbox-x="303.20061"
+         inkscape:bbox-y="337.42336"
+         inkscape:bbox-width="74.0625"
+         inkscape:bbox-height="18.574219"><tspan
+           mbname="item3text"
+           y="60"
+           x="157.14285"
+           id="tspan2429"
+           sodipodi:role="line"
+           inkscape:transform-center-x="0"
+           inkscape:transform-center-y="0"
+           inkscape:bbox-x="303.20061"
+           inkscape:bbox-y="337.42336"
+           inkscape:bbox-width="74.0625"
+           inkscape:bbox-height="18.574219">item1</tspan></text>
+    </g>
+    <g
+       transform="translate(144.04213,124.7838)"
+       id="item4"
+       mbname="item4"
+       style="fill-opacity:1"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="303.20061"
+       inkscape:bbox-y="294.87636"
+       inkscape:bbox-width="74.0625"
+       inkscape:bbox-height="18.574219">
+      <text
+         id="item4text"
+         y="60"
+         x="157.14285"
+         style="font-size:24px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold"
+         xml:space="preserve"
+         sodipodi:linespacing="125%"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         inkscape:bbox-x="303.20061"
+         inkscape:bbox-y="294.87636"
+         inkscape:bbox-width="74.0625"
+         inkscape:bbox-height="18.574219"><tspan
+           mbname="item4text"
+           y="60"
+           x="157.14285"
+           id="tspan2435"
+           sodipodi:role="line"
+           inkscape:transform-center-x="0"
+           inkscape:transform-center-y="0"
+           inkscape:bbox-x="303.20061"
+           inkscape:bbox-y="294.87636"
+           inkscape:bbox-width="74.0625"
+           inkscape:bbox-height="18.574219">item1</tspan></text>
+    </g>
+    <g
+       transform="translate(144.04213,167.33077)"
+       id="item5"
+       mbname="item5"
+       style="fill-opacity:1"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="303.20061"
+       inkscape:bbox-y="252.32939"
+       inkscape:bbox-width="74.0625"
+       inkscape:bbox-height="18.574219">
+      <text
+         id="item5text"
+         y="60"
+         x="157.14285"
+         style="font-size:24px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold"
+         xml:space="preserve"
+         sodipodi:linespacing="125%"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         inkscape:bbox-x="303.20061"
+         inkscape:bbox-y="252.32939"
+         inkscape:bbox-width="74.0625"
+         inkscape:bbox-height="18.574219"><tspan
+           mbname="item5text"
+           y="60"
+           x="157.14285"
+           id="tspan2441"
+           sodipodi:role="line"
+           inkscape:transform-center-x="0"
+           inkscape:transform-center-y="0"
+           inkscape:bbox-x="303.20061"
+           inkscape:bbox-y="252.32939"
+           inkscape:bbox-width="74.0625"
+           inkscape:bbox-height="18.574219">item1</tspan></text>
+    </g>
+    <g
+       transform="translate(144.04213,209.87776)"
+       id="item6"
+       mbname="item6"
+       style="fill-opacity:1"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="303.20061"
+       inkscape:bbox-y="209.7824"
+       inkscape:bbox-width="74.0625"
+       inkscape:bbox-height="18.574219">
+      <text
+         id="item6text"
+         y="60"
+         x="157.14285"
+         style="font-size:24px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold"
+         xml:space="preserve"
+         sodipodi:linespacing="125%"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         inkscape:bbox-x="303.20061"
+         inkscape:bbox-y="209.7824"
+         inkscape:bbox-width="74.0625"
+         inkscape:bbox-height="18.574219"><tspan
+           mbname="item6text"
+           y="60"
+           x="157.14285"
+           id="tspan2447"
+           sodipodi:role="line"
+           inkscape:transform-center-x="0"
+           inkscape:transform-center-y="0"
+           inkscape:bbox-x="303.20061"
+           inkscape:bbox-y="209.7824"
+           inkscape:bbox-width="74.0625"
+           inkscape:bbox-height="18.574219">item1</tspan></text>
+    </g>
+    <g
+       transform="translate(145.21401,252.42474)"
+       id="item7"
+       mbname="item7"
+       style="fill-opacity:1"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="303.20061"
+       inkscape:bbox-y="167.23542"
+       inkscape:bbox-width="65.027344"
+       inkscape:bbox-height="18.574219">
+      <text
+         id="item7text"
+         y="60"
+         x="157.14285"
+         style="font-size:24px;font-style:oblique;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Oblique"
+         xml:space="preserve"
+         sodipodi:linespacing="125%"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         inkscape:bbox-x="303.20061"
+         inkscape:bbox-y="167.23542"
+         inkscape:bbox-width="65.027344"
+         inkscape:bbox-height="18.574219"><tspan
+           mbname="item7text"
+           y="60"
+           x="157.14285"
+           id="tspan2453"
+           sodipodi:role="line"
+           inkscape:transform-center-x="0"
+           inkscape:transform-center-y="0"
+           inkscape:bbox-x="303.20061"
+           inkscape:bbox-y="167.23542"
+           inkscape:bbox-width="65.027344"
+           inkscape:bbox-height="18.574219">item1</tspan></text>
+    </g>
+    <g
+       transform="translate(143.79604,294.97172)"
+       id="item8"
+       mbname="item8"
+       style="fill-opacity:1"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="303.20061"
+       inkscape:bbox-y="124.68844"
+       inkscape:bbox-width="65.027344"
+       inkscape:bbox-height="18.574219">
+      <text
+         id="item8text"
+         y="60"
+         x="157.14285"
+         style="font-size:24px;font-style:normal;font-weight:normal;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+         xml:space="preserve"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         inkscape:bbox-x="303.20061"
+         inkscape:bbox-y="124.68844"
+         inkscape:bbox-width="65.027344"
+         inkscape:bbox-height="18.574219"><tspan
+           mbname="item8text"
+           y="60"
+           x="157.14285"
+           id="tspan2459"
+           sodipodi:role="line"
+           inkscape:transform-center-x="0"
+           inkscape:transform-center-y="0"
+           inkscape:bbox-x="303.20061"
+           inkscape:bbox-y="124.68844"
+           inkscape:bbox-width="65.027344"
+           inkscape:bbox-height="18.574219">item1</tspan></text>
+    </g>
+    <g
+       transform="translate(143.79604,337.5187)"
+       id="item9"
+       mbname="item9"
+       style="fill-opacity:1"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="303.20061"
+       inkscape:bbox-y="82.141456"
+       inkscape:bbox-width="65.027344"
+       inkscape:bbox-height="18.574219">
+      <text
+         mbname=""
+         id="text2407"
+         y="60"
+         x="157.14285"
+         style="font-size:24px;font-style:normal;font-weight:normal;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+         xml:space="preserve"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         inkscape:bbox-x="303.20061"
+         inkscape:bbox-y="82.141456"
+         inkscape:bbox-width="65.027344"
+         inkscape:bbox-height="18.574219"><tspan
+           mbname="item9text"
+           y="60"
+           x="157.14285"
+           id="tspan2409"
+           sodipodi:role="line"
+           inkscape:transform-center-x="0"
+           inkscape:transform-center-y="0"
+           inkscape:bbox-x="303.20061"
+           inkscape:bbox-y="82.141456"
+           inkscape:bbox-width="65.027344"
+           inkscape:bbox-height="18.574219">item1</tspan></text>
+    </g>
+    <text
+       id="text2416"
+       y="44.383541"
+       x="24.177626"
+       style="font-size:24px;font-style:normal;font-weight:normal;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+       xml:space="preserve"><tspan
+         mbname="title"
+         y="44.383541"
+         x="24.177626"
+         id="tspan2418"
+         sodipodi:role="line">Menu test</tspan></text>
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer4"
+     inkscape:label="lightbar">
+    <g
+       mbname="item_lightbar"
+       transform="matrix(0.9148913,0,0,1,168.41407,93.684101)"
+       id="item_lightbar"
+       style="stroke:none"
+       inkscape:transform-center-x="0"
+       inkscape:transform-center-y="0"
+       inkscape:bbox-x="164.66374"
+       inkscape:bbox-y="326.01386"
+       inkscape:bbox-width="409.10203"
+       inkscape:bbox-height="33.623234">
+      <rect
+         style="opacity:0.3669725;fill:#001f41;fill-opacity:1;stroke:none"
+         id="rect3191"
+         width="442.8623"
+         height="29.190758"
+         x="0.19951171"
+         y="31.140766"
+         transform="matrix(0.9999958,-0.00289533,0,1,0,0)"
+         rx="10.000001"
+         ry="10"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         inkscape:bbox-x="168.5966"
+         inkscape:bbox-y="326.01386"
+         inkscape:bbox-width="405.16917"
+         inkscape:bbox-height="30.41517" />
+      <rect
+         style="opacity:0.3669725;fill:#eafbf3;fill-opacity:1;stroke:none"
+         id="rect2405"
+         width="442.86224"
+         height="29.190758"
+         x="-4.099226"
+         y="27.920256"
+         transform="matrix(0.9999958,-0.00289533,0,1,0,0)"
+         rx="10"
+         ry="10"
+         inkscape:transform-center-x="0"
+         inkscape:transform-center-y="0"
+         inkscape:bbox-x="164.66374"
+         inkscape:bbox-y="329.22193"
+         inkscape:bbox-width="405.16911"
+         inkscape:bbox-height="30.41517" />
+    </g>
+    <image
+       y="78.788635"
+       x="34.986565"
+       id="image3033"
+       xlink:href="file:logo.png"
+       height="37"
+       width="46" />
+    <g
+       sodipodi:type="inkscape:box3d"
+       style="fill:none;stroke:none"
+       id="g2953"
+       inkscape:perspectiveID="#perspective10"
+       inkscape:corner0="11.418244 : -1.091331 : 0 : 1"
+       inkscape:corner7="6.2635014 : -2.542367 : 0.25 : 1">
+      <path
+         sodipodi:type="inkscape:box3dside"
+         id="path2963"
+         style="fill:#afafde;fill-rule:evenodd;stroke:none"
+         inkscape:box3dsidetype="13"
+         d="M 44.456373,239.52954 91.68396,335.71719 142.89318,328.00691 75.760432,236.8548 z" />
+      <path
+         sodipodi:type="inkscape:box3dside"
+         id="path2955"
+         style="fill:#353564;fill-rule:evenodd;stroke:none"
+         inkscape:box3dsidetype="6"
+         d="m 44.456373,167.16508 0,72.36446 31.304059,-2.67474 0,-70.93639 z" />
+      <path
+         sodipodi:type="inkscape:box3dside"
+         id="path2965"
+         style="fill:#e9e9ff;fill-rule:evenodd;stroke:none"
+         inkscape:box3dsidetype="11"
+         d="m 75.760432,165.91841 67.132748,42.48521 0,119.60329 -67.132748,-91.15211 z" />
+      <path
+         sodipodi:type="inkscape:box3dside"
+         id="path2957"
+         style="fill:#4d4d9f;fill-rule:evenodd;stroke:none"
+         inkscape:box3dsidetype="5"
+         d="m 44.456373,167.16508 47.227587,44.83223 51.20922,-3.59369 -67.132748,-42.48521 z" />
+      <path
+         sodipodi:type="inkscape:box3dside"
+         id="path2961"
+         style="fill:#d7d7ff;fill-rule:evenodd;stroke:none"
+         inkscape:box3dsidetype="14"
+         d="m 91.68396,211.99731 0,123.71988 51.20922,-7.71028 0,-119.60329 z" />
+      <path
+         sodipodi:type="inkscape:box3dside"
+         id="path2959"
+         style="fill:#8686bf;fill-rule:evenodd;stroke:none"
+         inkscape:box3dsidetype="3"
+         d="m 44.456373,167.16508 47.227587,44.83223 0,123.71988 -47.227587,-96.18765 z" />
+    </g>
+  </g>
+</svg>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/examples/testsvg/testsvg.js	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,53 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
+var svg = require("svg");
+var mbapp = require("mbapp");
+var sys=require("sys");
+var animate=require("animate");
+var fs = require("fs");
+
+app = new mbapp.app();
+app.loadSVG("test.svg");
+lightbar = app.get("item_lightbar");
+item=1;
+var target = app.get("item" + item);
+lightbar.center.move_pnt(target.center);
+
+app.files=fs.readdirSync("/tmp/");
+for(i=1;i<10;i++) {
+    var o = app.get("item"+i+"text");
+    o.set_text(app.files[i]);
+}
+
+
+
+
+app.addKeyListener(mbapp.KEY_UP, function() {
+    item = item - 1;
+    if (item == 0)
+	item = 1;
+    else {
+	var target = app.get("item"+item);
+	var shx = target.center.x - lightbar.center.x;
+	var shy = target.center.y - lightbar.center.y;
+	var action = new animate.shift(app, lightbar, shx, shy);
+	var an = new animate.linear(action, 0, 0.3);
+	an.start();
+    }
+});
+
+app.addKeyListener(mbapp.KEY_DOWN, function() {
+    item = item + 1;
+    if (item == 10) {
+	item = 9;
+    } else {
+	var target = app.get("item"+item);
+	var shx = target.center.x - lightbar.center.x;
+	var shy = target.center.y - lightbar.center.y;
+	var action = new animate.shift(app, lightbar, shx, shy);
+	var an = new animate.linear(action, 0, 0.3);
+	an.start();
+    }
+});
+
+app.loop();
--- a/nodejs/font.cc	Mon Jul 19 15:44:49 2010 +0800
+++ b/nodejs/font.cc	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include <v8.h>
 #include "mbfly_njs.h"
 
@@ -62,10 +64,10 @@
 
     temp = FunctionTemplate::New();
     temp->SetClassName(String::New("font_face"));
-    
+
     inst_temp = temp->InstanceTemplate();
     inst_temp->SetInternalFieldCount(1);
-    
+
     xnjsmb_font_face_temp = Persistent<FunctionTemplate>::New(temp);
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/image_ldr.cc	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,149 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
+/*! \file
+ * This file implements Javascript binding for img_ldr_t of MadButterfly.
+ */
+#include <v8.h>
+
+extern "C" {
+#include "mb.h"
+}
+
+#include "mbfly_njs.h"
+
+using namespace v8;
+
+#ifndef ASSERT
+#define ASSERT(x)
+#endif
+
+/*! \defgroup xnjsmb_img_ldr_js Javascript binding for image loader.
+ * \ingroup xnjsmb
+ *
+ * @{
+ */
+
+static Persistent<ObjectTemplate> img_data_temp;
+
+/*! \brief load() method of img_ldr Javascript objects.
+ */
+static Handle<Value>
+xnjsmb_img_ldr_load(const Arguments &args) {
+    HandleScope scope;
+    int argc = args.Length();
+    Handle<Object> self = args.This();
+    char *img_id;
+    mb_img_ldr_t *img_ldr;
+    mb_img_data_t *img_data;
+    Handle<Object> img_data_obj;
+
+    if(argc != 1)
+	THROW("Invalid number of arguments (!= 1)");
+    if(!args[0]->IsString())
+	THROW("Invalid argument type");
+
+    String::Utf8Value img_id_utf8(args[0]->ToString());
+    img_id = *img_id_utf8;
+    img_ldr = (mb_img_ldr_t *)UNWRAP(self);
+
+    img_data_obj = img_data_temp->NewInstance();
+    ASSERT(img_data_obj);
+
+    img_data = MB_IMG_LDR_LOAD(img_ldr, img_id);
+    if(img_data == NULL)
+	THROW("Can not load an image");
+    WRAP(img_data_obj, img_data);
+
+    scope.Close(img_data_obj);
+
+    return img_data_obj;
+}
+
+/*! \brief Constructor function of img_ldr Javascript objects.
+ */
+static Handle<Value>
+xnjsmb_img_ldr(const Arguments &args) {
+    HandleScope scope;
+    int argc = args.Length();
+    Handle<Object> self = args.This();
+    char *path;
+    mb_img_ldr_t *img_ldr;
+
+    if(argc != 1)
+	THROW("Invalid number of arguments (!= 1)");
+    if(!args[0]->IsString())
+	THROW("Invalid argument type");
+
+    String::Utf8Value pathutf8(args[0]->ToString());
+    path = *pathutf8;
+
+    img_ldr = simple_mb_img_ldr_new(path);
+    if(img_ldr == NULL)
+	THROW("Can not create an image loader");
+    WRAP(self, img_ldr);
+
+    return Null();
+}
+
+static Persistent<FunctionTemplate> xnjsmb_img_ldr_temp;
+
+static Handle<Value>
+xnjsmb_img_ldr_new(const Arguments &args) {
+    HandleScope scope;
+    int argc = args.Length();
+    Handle<Value> il_args[1];
+    Handle<Object> img_ldr;
+    Handle<Function> func;
+
+    if(argc != 1)
+	THROW("Invalid number of arguments (!= 1)");
+    if(!args[0]->IsString())
+	THROW("Invalid argument type");
+
+    il_args[0] = args[0];
+    func = xnjsmb_img_ldr_temp->GetFunction();
+    img_ldr = func->NewInstance(1, il_args);
+
+    scope.Close(img_ldr);
+    return img_ldr;
+}
+
+/* @} */
+
+/*! \brief Initialize image loader.
+ *
+ * This function is called by init() in mbfly_njs.cc when the module
+ * being loaded.
+ */
+void
+xnjsmb_img_ldr_init_mb_rt_temp(Handle<Object> rt_temp) {
+    HandleScope scope;
+    Handle<FunctionTemplate> img_ldr_temp;
+    Handle<FunctionTemplate> img_ldr_new_temp;
+    Handle<ObjectTemplate> ldr_inst_temp;
+    Handle<ObjectTemplate> ldr_proto_temp;
+    Handle<FunctionTemplate> img_ldr_load_temp;
+    Handle<ObjectTemplate> _img_data_temp;
+
+    /* Setup object template for img_data_t object for Javascript */
+    _img_data_temp = ObjectTemplate::New();
+    _img_data_temp->SetInternalFieldCount(1);
+    img_data_temp = Persistent<ObjectTemplate>::New(_img_data_temp);
+
+    /* Setup img_ldr class */
+    img_ldr_temp = FunctionTemplate::New(xnjsmb_img_ldr);
+    img_ldr_temp->SetClassName(String::New("img_ldr"));
+    ldr_inst_temp = img_ldr_temp->InstanceTemplate();
+    ldr_inst_temp->SetInternalFieldCount(1);
+
+    /* Set method load() for img_ldr */
+    ldr_proto_temp = img_ldr_temp->PrototypeTemplate();
+    img_ldr_load_temp = FunctionTemplate::New(xnjsmb_img_ldr_load);
+    SET(ldr_proto_temp, "load", img_ldr_load_temp);
+
+    xnjsmb_img_ldr_temp = Persistent<FunctionTemplate>::New(img_ldr_temp);
+
+    /* Initialize img_ldr_new function */
+    img_ldr_new_temp = FunctionTemplate::New(xnjsmb_img_ldr_new);
+    SET(rt_temp, "img_ldr_new", img_ldr_new_temp->GetFunction());
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/mbapp.js	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,224 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
+var mbfly = require("mbfly");
+var svg = require("./svg");
+var sys = require("sys");
+var ldr = mbfly.img_ldr_new(".");
+
+function _reverse(m1) {
+    var rev = new Array(1, 0, 0, 0, 1, 0);
+    var m = new Array(m1[0], m1[1], m1[2], m1[3], m1[4], m1[5]);
+
+    rev[3] = -m[3] / m[0];
+    m[3] = 0;
+    m[4] += rev[3] * m[1];
+    m[5] += rev[3] * m[2];
+    
+    rev[1] = -m[1] / m[4];
+    rev[0] += rev[1] * rev[3];
+    m[1] = 0;
+    m[2] += rev[1] * m[5];
+    
+    rev[2] = -m[2];
+    rev[5] = -m[5];
+    
+    rev[0] = rev[0] / m[0];
+    rev[1] = rev[1] / m[0];
+    rev[2] = rev[2] / m[0];
+    
+    rev[3] = rev[3] / m[4];
+    rev[4] = rev[4] / m[4];
+    rev[5] = rev[5] / m[4];
+
+    return rev;
+}
+
+function _decorate_mb_rt(mb_rt) {
+    var coord;
+    
+    mb_rt._mbapp_saved_coord_new = mb_rt.coord_new;
+    mb_rt.coord_new = function(parent) {
+	var coord;
+	
+	coord = this._mbapp_saved_coord_new(parent);
+	coord.type = "coord";
+	coord.children = [];
+	coord._mbapp_saved_mtx = [coord[0], coord[1], coord[2],
+				  coord[3], coord[4], coord[5]];
+	coord._mbapp_saved_rev_mtx = _reverse(coord._mbapp_saved_mtx);
+	coord.parent = parent;
+	coord._mbapp_saved_add_shape = coord.add_shape;
+	coord.add_shape = function(shape) {
+	    var coord;
+	    
+	    this._mbapp_saved_add_shape(shape);
+	    shape.parent = this;
+	    this.children.push(shape);
+	}
+
+	parent.children.push(coord);
+	
+	return coord;
+    };
+
+    /*
+     * Decorate root coord
+     */
+    coord = mb_rt.root;
+    coord.type = "coord";
+    coord.children = [];
+    coord._mbapp_saved_mtx = [coord[0], coord[1], coord[2],
+			      coord[3], coord[4], coord[5]];
+	coord._mbapp_saved_rev_mtx = _reverse(coord._mbapp_saved_mtx);
+    coord._mbapp_saved_add_shape = coord.add_shape;
+    coord.add_shape = function(shape) {
+	var coord;
+	
+	this._mbapp_saved_add_shape(shape);
+	shape.parent = this;
+    }
+}
+
+app=function(display, w, h) {
+    var self = this;
+    var mb_rt;
+
+    if(typeof display == "undefined")
+	display = ":0.0";
+    if(typeof w == "undefined")
+	w = 720;
+    if(typeof h == "undefined")
+	h = 480;
+    
+    mb_rt = this.mb_rt = new mbfly.mb_rt(display, w, h);
+    _decorate_mb_rt(mb_rt);
+    var background = mb_rt.rect_new(0, 0, 720, 480, 0, 0);
+    var paint = mb_rt.paint_color_new(1, 1, 1, 1);
+    paint.fill(background);
+    mb_rt.root.add_shape(background);
+
+    this.mb_rt.kbevents.add_event_observer(exports.EVT_KB_PRESS, function(evt) { self.KeyPress(evt);});
+    this.keymap={};
+    this.onKeyPress = null;
+    this.svg = new svg.loadSVG(this.mb_rt,this.mb_rt.root,null);
+}
+app.prototype.loadSVG=function(fname) {
+    this.svg.load(this.mb_rt,this.mb_rt.root,fname)
+}
+
+app.prototype.KeyPress = function(evt) {
+    if (this.onKeyPress) this.onKeyPress(evt.sym);
+	if (evt.sym in this.keymap) {
+	    this.keymap[evt.sym]();
+	}
+}
+
+app.prototype.loop=function() {
+    this.mb_rt.redraw_all();
+    this.mb_rt.flush();
+}
+app.prototype.update=function() {
+    this.mb_rt.redraw_all();
+    this.mb_rt.flush();
+}
+app.prototype.get=function(name) {
+    return this.mb_rt.mbnames[name];
+}
+app.prototype.addKeyboardListener=function(type,f) {
+    return this.mb_rt.kbevents.add_event_observer(type,f);    
+}
+app.prototype.refresh=function() {
+    this.mb_rt.redraw_changed();
+    this.mb_rt.flush();
+}
+app.prototype.dump=function() {
+    sys.puts(this.onKeyPress);
+}
+
+app.prototype.addKeyListener=function(key,f) {
+    if (typeof(key) == 'number')
+        this.keymap[key] = f;
+    else {
+        for(k in key) {
+	    this.keymap[k] = f;
+	}
+    }
+}
+
+
+app.prototype.changeScene=function(s) {
+    var nth;
+    if (typeof(s)=='number') {
+        var i;
+	nth = s;
+    } else {
+        nth = this.svg.getFrameNumber(s);
+	if (nth == -1) return;
+    }
+    var scenes = this.svg.scenes;
+    for(i=0;i<scenes.length;i++) {
+        try {
+            if (nth >=scenes[i].start && nth <=scenes[i].end) {
+	        this.get(scenes[i].ref).show();
+	    } else {
+	        this.get(scenes[i].ref).hide();
+	    }
+	} catch(e) {
+	    sys.puts(e);
+	    sys.puts(scenes[i].ref);
+	}
+    }
+}
+app.prototype.addSceneListener=function(n, cb) {
+    sys.puts("This is not implemented yet")
+}
+
+var app_with_win = function(display, win) {
+    var self = this;
+    var mb_rt;
+    var background;
+    var paint;
+
+    if(typeof display == "undefined" || typeof win == "undefined")
+	throw "Invalid argument";
+    
+    mb_rt = this.mb_rt = new mbfly.mb_rt_with_win(display, win);
+    _decorate_mb_rt(mb_rt);
+    background = mb_rt.rect_new(0, 0, 720, 480, 0, 0);
+    paint = mb_rt.paint_color_new(1, 1, 1, 1);
+    paint.fill(background);
+    mb_rt.root.add_shape(background);
+
+    this.mb_rt.kbevents.
+	add_event_observer(exports.EVT_KB_PRESS,
+			   function(evt) { self.KeyPress(evt); });
+    this.keymap = {};
+    this.onKeyPress = null;
+}
+
+app_with_win.prototype = app.prototype;
+
+exports.app=app;
+exports.app_with_win = app_with_win;
+
+// Put all key definition here
+exports.KEY_LEFT = 0xff51;
+exports.KEY_UP = 0xff52;
+exports.KEY_RIGHT = 0xff53;
+exports.KEY_DOWN = 0xff54;
+exports.KEY_ENTER = 0xff0d;
+exports.EVT_ANY=0;
+exports.EVT_MOUSE_OVER=1;
+exports.EVT_MOUSE_OUT=2;
+exports.EVT_MOUSE_MOVE=3;
+exports.EVT_MOUSE_BUT_PRESS4;
+exports.EVT_MOUSE_BUT_RELEASE=5
+exports.EVT_KB_PRESS=6;
+exports.EVT_KB_RELEASE=7;
+exports.EVT_PROGM_COMPLETE=8;
+exports.EVT_RDMAN_REDRAW=9;
+exports.EVT_MONITOR_ADD=10;
+exports.EVT_MONITOR_REMOVE=11;
+exports.EVT_MONITOR_FREE=12;
+exports.EVT_MOUSE_MOVE_RAW=13;
+exports.ldr = ldr;
--- a/nodejs/mbfly_njs.cc	Mon Jul 19 15:44:49 2010 +0800
+++ b/nodejs/mbfly_njs.cc	Wed Dec 01 12:25:56 2010 +0800
@@ -1,95 +1,136 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include <stdio.h>
+#include <string.h>
 #include <v8.h>
 
 extern "C" {
-#include "X_supp_njs.h"
+#include "njs_mb_supp.h"
 }
 
 #include "mbfly_njs.h"
 
 using namespace v8;
 
+/*! \defgroup xnjsmb_mb_rt JS binding for MB runtime.
+ * \ingroup xnjsmb
+ *
+ * @{
+ */
+static coord_t *
+xnjsmb_coord_new(njs_runtime_t *rt, coord_t *parent, const char **err) {
+    coord_t *coord;
+    redraw_man_t *rdman;
+
+    rdman = njs_mb_rdman(rt);
+    coord = rdman_coord_new(rdman, parent);
+    if(coord == NULL) {
+        *err = "Can not allocate a redraw_man_t";
+	return NULL;
+    }
+    rdman_coord_changed(rdman, coord);
+
+    return coord;
+}
+
+static void
+xnjsmb_mb_rt_objs_mod(Handle<Object> mbrt, Handle<Value> ret) {
+    Handle<Object> ret_obj = ret->ToObject();
+
+    SET(ret_obj, "mbrt", mbrt);
+}
+
+#define xnjsmb_auto_coord_new export_xnjsmb_auto_coord_new
+
+static void
+xnjsmb_redraw_changed(njs_runtime_t *rt) {
+    redraw_man_t *rdman;
+
+    rdman = njs_mb_rdman(rt);
+    rdman_redraw_changed(rdman);
+}
+
+static void
+xnjsmb_redraw_all(njs_runtime_t *rt) {
+    redraw_man_t *rdman;
+
+    rdman = njs_mb_rdman(rt);
+    rdman_redraw_all(rdman);
+}
+
+static void
+xnjsmb_handle_single_event(njs_runtime_t *rt, void *evt) {
+    njs_mb_handle_single_event(rt, evt);
+}
+
+static void
+xnjsmb_no_more_event(njs_runtime_t *rt) {
+    njs_mb_no_more_event(rt);
+}
+
+static njs_runtime_t *
+_njs_mb_new(Handle<Object> self, char *display_name,
+	      int width, int height) {
+    njs_runtime_t *obj;
+    subject_t *subject;
+    Handle<Value> subject_o;
+
+    obj = njs_mb_new(display_name, width, height);
+    WRAP(self, obj);		/* mkroot need a wrapped object, but
+				 * it is wrapped after returning of
+				 * this function.  So, we wrap it
+				 * here. */
+    xnjsmb_coord_mkroot(self);
+
+    subject = njs_mb_kbevents(obj);
+    subject_o = export_xnjsmb_auto_subject_new(subject);
+    SET(self, "kbevents", subject_o);
+
+    return obj;
+}
+
+static njs_runtime_t *
+_njs_mb_new_with_win(Handle<Object> self, void *display,
+		       long win) {
+    njs_runtime_t *obj;
+    subject_t *subject;
+    Handle<Value> subject_o;
+
+    obj = njs_mb_new_with_win(display, win);
+    WRAP(self, obj);		/* mkroot need a wrapped object, but
+				 * it is wrapped after returning of
+				 * this function.  So, we wrap it
+				 * here. */
+    xnjsmb_coord_mkroot(self);
+
+    subject = njs_mb_kbevents(obj);
+    subject_o = export_xnjsmb_auto_subject_new(subject);
+    SET(self, "kbevents", subject_o);
+
+    return obj;
+}
+
 /*! \defgroup njs_template_cb Callback functions for v8 engine and nodejs.
  *
  * @{
  */
 
-/*! \brief to Create a njs runtime object for MadButterfly.
- *
- * Three arguments are requried.  They are
- *   - display name,
- *   - width, and
- *   - height.
+/*
+ * Redirect following function to respective exported version from
+ * other modules.  Since gen_v8_binding.m4 make all functions static,
+ * we need a exported version to call them indrectly from other
+ * modules.
  */
-static Handle<Value>
-xnjsmb_new(const Arguments &args) {
-    HandleScope scope;
-    int argc;
-    Handle<Value> exc;
-    njs_runtime_t *rt;
-    char *display_name;
-    int width, height;
-    Handle<Object> self;
-
-    argc = args.Length();
-    if(argc != 3) {
-	exc = Exception::Error(String::New("Need 3 arguments."));
-	return ThrowException(exc);
-    }
-
-    if(!args[0]->IsString() || !args[1]->IsInt32() || !args[2]->IsInt32()) {
-	exc = Exception::Error(String::New("Invalid argument type."));
-	return ThrowException(exc);
-    }
-    
-    String::Utf8Value disp_utf8(args[0]->ToString());
-    display_name = *disp_utf8;
-    width = args[1]->Int32Value();
-    height = args[2]->Int32Value();
-    rt = X_njs_MB_new(display_name, width, height);
+#define xnjsmb_auto_path_new export_xnjsmb_auto_path_new
+#define xnjsmb_auto_stext_new export_xnjsmb_auto_stext_new
+#define xnjsmb_auto_image_new export_xnjsmb_auto_image_new
+#define xnjsmb_auto_rect_new export_xnjsmb_auto_rect_new
+#define xnjsmb_auto_paint_color_new export_xnjsmb_auto_paint_color_new
+#define xnjsmb_auto_paint_image_new export_xnjsmb_auto_paint_image_new
+#define xnjsmb_auto_paint_linear_new export_xnjsmb_auto_paint_linear_new
+#define xnjsmb_auto_paint_radial_new export_xnjsmb_auto_paint_radial_new
 
-    self = args.This();
-    WRAP(self, rt);
-    xnjsmb_coord_mkroot(self);
-    
-    X_njs_MB_init_handle_connection(rt);
-
-    return Null();
-}
-
-static Handle<Value>
-xnjsmb_handle_connection(const Arguments &args) {
-}
-
-static Handle<Value>
-xnjsmb_rt_redraw_changed(const Arguments &args) {
-    Handle<Object> self = args.This();
-    njs_runtime_t *rt;
-    redraw_man_t *rdman;
-    
-    rdman = xnjsmb_rt_rdman(self);
-    rdman_redraw_changed(rdman);
-    
-    rt = (njs_runtime_t *)UNWRAP(self);
-    X_njs_MB_flush(rt);
-    
-    return Null();
-}
-
-static Handle<Value>
-xnjsmb_rt_redraw_all(const Arguments &args) {
-    Handle<Object> self = args.This();
-    njs_runtime_t *rt;
-    redraw_man_t *rdman;
-    
-    rdman = xnjsmb_rt_rdman(self);
-    rdman_redraw_all(rdman);
-    
-    rt = (njs_runtime_t *)UNWRAP(self);
-    X_njs_MB_flush(rt);
-    
-    return Null();
-}
+#include "mbfly_njs-inc.h"
 
 /* @} */
 
@@ -102,8 +143,8 @@
     redraw_man_t *rdman;
 
     rt = (njs_runtime_t *)UNWRAP(mbrt);
-    rdman = X_njs_MB_rdman(rt);
-    
+    rdman = njs_mb_rdman(rt);
+
     return rdman;
 }
 
@@ -126,28 +167,25 @@
     /*
      * Initialize template for MadButterfly runtime objects.
      */
-    mb_rt_func = FunctionTemplate::New(xnjsmb_new);
-    mb_rt_func->SetClassName(String::New("mb_rt"));
-    
-    rt_instance_temp = mb_rt_func->InstanceTemplate();
-    rt_instance_temp->SetInternalFieldCount(1);
-    
-    rt_proto_temp = mb_rt_func->PrototypeTemplate();
-    func = FunctionTemplate::New(xnjsmb_coord_new);
-    SET(rt_proto_temp, "coord_new", func);
-
-    func = FunctionTemplate::New(xnjsmb_rt_redraw_changed);
-    SET(rt_proto_temp, "redraw_changed", func);
-
-    func = FunctionTemplate::New(xnjsmb_rt_redraw_all);
-    SET(rt_proto_temp, "redraw_all", func);
+    xnjsmb_auto_mb_rt_init();
+    xnjsmb_auto_mb_rt_display_init();
+    xnjsmb_auto_mb_rt_with_win_init();
+    njs_mb_reg_timer_man();
+    njs_mb_reg_IO_man();
 
     /*
      * Add properties to mb_rt templates for other modules.
      */
-    xnjsmb_shapes_init_mb_rt_temp(mb_rt_func);
-    xnjsmb_paints_init_mb_rt_temp(mb_rt_func);
-    xnjsmb_font_init_mb_rt_temp(mb_rt_func);
-    
-    target->Set(String::New("mb_rt"), mb_rt_func->GetFunction());    
+    xnjsmb_shapes_init_mb_rt_temp(xnjsmb_auto_mb_rt_temp);
+    xnjsmb_paints_init_mb_rt_temp(xnjsmb_auto_mb_rt_temp);
+    xnjsmb_font_init_mb_rt_temp(xnjsmb_auto_mb_rt_temp);
+    xnjsmb_img_ldr_init_mb_rt_temp(target);
+    xnjsmb_observer_init();
+
+    target->Set(String::New("mb_rt"),
+		xnjsmb_auto_mb_rt_temp->GetFunction());
+    target->Set(String::New("mb_rt_with_win"),
+		xnjsmb_auto_mb_rt_with_win_temp->GetFunction());
 }
+
+/* @} */
--- a/nodejs/mbfly_njs.h	Mon Jul 19 15:44:49 2010 +0800
+++ b/nodejs/mbfly_njs.h	Wed Dec 01 12:25:56 2010 +0800
@@ -1,9 +1,12 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #ifndef __MBFLY_NJS_H_
 #define __MBFLY_NJS_H_
 
 #include <v8.h>
 extern "C" {
 #include <mb.h>
+#include "njs_mb_supp.h"
 }
 
 #define THROW(x)						\
@@ -12,6 +15,13 @@
 	exc = v8::Exception::Error(v8::String::New(x));		\
 	return v8::ThrowException(exc);				\
     } while(0)
+#define THROW_noret(x)						\
+    do {							\
+	v8::Handle<v8::Value> exc;				\
+	exc = v8::Exception::Error(v8::String::New(x));		\
+	v8::ThrowException(exc);				\
+	return;							\
+    } while(0)
 #define UNWRAP(o) v8::External::Unwrap((o)->GetInternalField(0))
 #define WRAP(o, v) (o)->SetInternalField(0, v8::External::Wrap(v))
 #define SET(o, n, v) (o)->Set(v8::String::New(n), v)
@@ -20,16 +30,51 @@
 redraw_man_t *xnjsmb_rt_rdman(v8::Handle<v8::Object> mbrt);
 
 /* From coord.cc */
-v8::Handle<v8::Value> xnjsmb_coord_new(const v8::Arguments &args);
 void xnjsmb_coord_mkroot(v8::Handle<v8::Object> js_rt);
+v8::Handle<v8::Value> export_xnjsmb_auto_coord_new(coord_t *coord);
 
 /* From shapes.cc */
 void xnjsmb_shapes_init_mb_rt_temp(v8::Handle<v8::FunctionTemplate> rt_temp);
+shape_t *xnjsmb_path_new(njs_runtime_t *rt, const char *d);
+shape_t *xnjsmb_stext_new(njs_runtime_t *rt, const char *txt,
+			  float x, float y);
+shape_t *xnjsmb_image_new(njs_runtime_t *rt, float x, float y,
+			  float w, float h);
+shape_t *xnjsmb_rect_new(njs_runtime_t *rt, float x, float y,
+			 float w, float h,
+			 float rx, float ry, const char **err);
+v8::Handle<v8::Value> export_xnjsmb_auto_path_new(shape_t *sh);
+v8::Handle<v8::Value> export_xnjsmb_auto_stext_new(shape_t *sh);
+v8::Handle<v8::Value> export_xnjsmb_auto_image_new(shape_t *sh);
+v8::Handle<v8::Value> export_xnjsmb_auto_rect_new(shape_t *sh);
 
 /* From paints.cc */
 void xnjsmb_paints_init_mb_rt_temp(v8::Handle<v8::FunctionTemplate> rt_temp);
+paint_t *xnjsmb_paint_color_new(njs_runtime_t *rt,
+				float r, float g, float b, float a,
+				const char **err);
+paint_t *xnjsmb_paint_image_new(njs_runtime_t *rt, mb_img_data_t *img,
+				const char **err);
+paint_t *xnjsmb_paint_linear_new(njs_runtime_t *rt,
+				 float x1, float y1, float x2, float y2,
+				 const char **err);
+paint_t *xnjsmb_paint_radial_new(njs_runtime_t *rt,
+				 float cx, float cy, float r,
+				 const char **err);
+v8::Handle<v8::Value> export_xnjsmb_auto_paint_color_new(paint_t *paint);
+v8::Handle<v8::Value> export_xnjsmb_auto_paint_image_new(paint_t *paint);
+v8::Handle<v8::Value> export_xnjsmb_auto_paint_linear_new(paint_t *sh);
+v8::Handle<v8::Value> export_xnjsmb_auto_paint_radial_new(paint_t *sh);
 
 /* From font.cc */
 void xnjsmb_font_init_mb_rt_temp(v8::Handle<v8::FunctionTemplate> mb_rt_temp);
 
+/* From image_ldr.cc */
+void
+xnjsmb_img_ldr_init_mb_rt_temp(v8::Handle<v8::Object> mb_rt_temp);
+
+/* From observer.cc */
+v8::Handle<v8::Value> export_xnjsmb_auto_subject_new(subject_t *subject);
+void xnjsmb_observer_init(void);
+
 #endif /* __MBFLY_NJS_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/mbfly_njs.m4	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,65 @@
+dnl
+define([PROJ_PREFIX], [xnjsmb_auto_])dnl
+dnl
+STRUCT([mb_rt], [njs_runtime_t], [],
+       [METHOD([coord_new], [xnjsmb_coord_new],
+		 (OBJ([parent], [coord], [coord_t]), ERR), 1,
+		 [OBJ([coord], [coord_t])],
+		 (([MOD], [xnjsmb_mb_rt_objs_mod]))),
+        METHOD([redraw_changed], [xnjsmb_redraw_changed], (), 0, []),
+	METHOD([redraw_all], [xnjsmb_redraw_all], (), 0, []),
+	METHOD([flush], [njs_mb_flush], (), 0, []),
+	METHOD([path_new], [xnjsmb_path_new], (STR(txt)), 1,
+	       [OBJ([path], [shape_t])], (([MOD], [xnjsmb_mb_rt_objs_mod]))),
+	METHOD([stext_new], [xnjsmb_stext_new],
+	       (STR(txt), NUMBER(x), NUMBER(y)), 3,
+	       [OBJ([stext], [shape_t])],
+	       (([MOD], [xnjsmb_mb_rt_objs_mod]))),
+	METHOD([image_new], [xnjsmb_image_new],
+	       (NUMBER(x), NUMBER(y), NUMBER(w), NUMBER(h)), 4,
+	       [OBJ([image], [shape_t])],
+	       (([MOD], [xnjsmb_mb_rt_objs_mod]))),
+	METHOD([rect_new], [xnjsmb_rect_new],
+	       (NUMBER(x), NUMBER(y), NUMBER(w), NUMBER(h),
+	        NUMBER(rx), NUMBER(ry), ERR), 6,
+	       [OBJ([rect], [shape_t])],
+	       (([MOD], [xnjsmb_mb_rt_objs_mod]))),
+	METHOD([paint_color_new], [xnjsmb_paint_color_new],
+	       (NUMBER(r), NUMBER(g), NUMBER(b), NUMBER(a), ERR), 4,
+	       [OBJ([paint_color], [paint_t])],
+	       (([MOD], [xnjsmb_mb_rt_objs_mod]))),
+	METHOD([paint_image_new], [xnjsmb_paint_image_new],
+	       (OBJ([img], [img_data], [mb_img_data_t]), ERR), 1,
+	       [OBJ([paint_image], [paint_t])],
+	       (([MOD], [xnjsmb_mb_rt_objs_mod]))),
+	METHOD([paint_linear_new], [xnjsmb_paint_linear_new],
+	       (NUMBER(x1), NUMBER(y1), NUMBER(x2), NUMBER(y2), ERR), 4,
+	       [OBJ([paint_linear], [paint_t])],
+	       (([MOD], [xnjsmb_mb_rt_objs_mod]))),
+	METHOD([paint_radial_new], [xnjsmb_paint_radial_new],
+	       (NUMBER(cx), NUMBER(cy), NUMBER(r), ERR), 3,
+	       [OBJ([paint_radial], [paint_t])],
+	       (([MOD], [xnjsmb_mb_rt_objs_mod]))),
+	METHOD([handle_single_event], [xnjsmb_handle_single_event],
+	       (OBJ([evt], [event], [void])), 1, []),
+	METHOD([no_more_event], [xnjsmb_no_more_event],
+	       (), 0, [])],
+	((CTOR, ([_njs_mb_new], (SELF, STR(display_name), INT(width), INT(height)), 3)))dnl
+)
+dnl
+dnl
+dnl
+STRUCT([mb_rt_display], [void], [],
+       [],
+       ())dnl
+dnl
+dnl Function to create mb_rt for an existed window.
+dnl
+STRUCT([mb_rt_with_win], [njs_runtime_t], [],
+       [],
+       ((CTOR, ([_njs_mb_new_with_win],dnl
+       	        (SELF, OBJ([display], [mb_rt_display], [void]),dnl
+		 INT([window])),dnl
+		 2)),dnl
+        ([INHERIT], [mb_rt]))dnl
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/njs_mb_supp.c	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,317 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
+/*! \brief Implement X11 backend for nodejs plugin.
+ *
+ * Since nodejs use libev to handle event loops, part of X11 backend
+ * code can not be used directly.  The part of code should be rewrote.
+ * The part is about
+ */
+#include <stdio.h>
+#include <string.h>
+#include <ev.h>
+#include "mb_tools.h"
+#include <mb_backend.h>
+#include "njs_mb_supp.h"
+
+#ifndef ASSERT
+#define ASSERT(x)
+#endif
+
+#define OK 0
+#define ERR -1
+
+
+/*! \defgroup njs_timer_man Timer manager for nodejs.
+ * @{
+ */
+struct _njs_timer_timeout {
+    ev_timer tmwatcher;
+    mb_timer_cb_t cb;
+    mb_timeval_t *timeout;
+    void *data;
+};
+
+static int njs_timer_man_timeout(mb_timer_man_t *tm_man,
+				 mb_timeval_t *tm_out,
+				 mb_timer_cb_t cb, void *data);
+static void njs_timer_man_remove(mb_timer_man_t *tm_man, int tm_hdl);
+static mb_timer_man_t *njs_timer_man_new(void);
+static void njs_timer_man_free(mb_timer_man_t *timer_man);
+
+static mb_timer_man_t njs_timer_man = {
+    njs_timer_man_timeout,
+    njs_timer_man_remove
+};
+
+static mb_timer_factory_t njs_timer_factory = {
+    njs_timer_man_new,
+    njs_timer_man_free
+};
+
+static void
+njs_timer_man_cb(EV_P_ ev_timer *tmwatcher, int revent) {
+    struct _njs_timer_timeout *timer_timeout =
+	MEM2OBJ(tmwatcher, struct _njs_timer_timeout, tmwatcher);
+    mb_timeval_t now;
+
+    get_now(&now);
+    timer_timeout->cb((int)timer_timeout, timer_timeout->timeout, &now,
+		      timer_timeout->data);
+}
+
+static int
+njs_timer_man_timeout(mb_timer_man_t *tm_man,
+		      mb_timeval_t *timeout,
+		      mb_timer_cb_t cb, void *data) {
+    struct _njs_timer_timeout *timer_timeout;
+    mb_timeval_t now, timeout_diff;
+    ev_tstamp timeout_stamp;
+
+    timer_timeout = O_ALLOC(struct _njs_timer_timeout);
+    if(timer_timeout == NULL)
+	return ERR;
+    
+    timer_timeout->cb = cb;
+    timer_timeout->timeout = timeout;
+    
+    get_now(&now);
+    
+    memcpy(&timeout_diff, timeout, sizeof(mb_timeval_t));
+    MB_TIMEVAL_DIFF(&timeout_diff, &now);
+    timeout_stamp = (ev_tstamp)MB_TIMEVAL_SEC(&timeout_diff) +
+	(ev_tstamp)MB_TIMEVAL_USEC(&timeout_diff) / 1000000;
+    ev_timer_init(&timer_timeout->tmwatcher, njs_timer_man_cb,
+		  timeout_stamp, 0);
+    ev_timer_start(&timer_timeout->tmwatcher);
+
+    return (int)timer_timeout;
+}
+
+static void
+njs_timer_man_remove(struct _mb_timer_man *tm_man, int tm_hdl) {
+    struct _njs_timer_timeout *timer_timeout =
+	(struct _njs_timer_timeout *)tm_hdl;
+
+    ev_timer_stop(&timer_timeout->tmwatcher);
+    free(timer_timeout);
+}
+
+static mb_timer_man_t *
+njs_timer_man_new(void) {
+    return &njs_timer_man;
+}
+
+static void
+njs_timer_man_free(mb_timer_man_t *timer_man) {
+}
+
+void
+njs_mb_reg_timer_man(void) {
+    mb_reg_timer_factory(&njs_timer_factory);
+}
+
+/* @} */
+
+
+/*! \defgroup njs_io_man IO manager for nodejs.
+ * @{
+ */
+struct _njs_io_reg {
+    ev_io iowatcher;
+    int fd;
+    mb_IO_cb_t cb;
+    void *data;
+};
+
+static int njs_io_man_reg(struct _mb_IO_man *io_man,
+			  int fd, MB_IO_TYPE type, mb_IO_cb_t cb, void *data);
+static void njs_io_man_unreg(struct _mb_IO_man *io_man, int io_hdl);
+static mb_IO_man_t *njs_io_man_new(void);
+static void njs_io_man_free(mb_IO_man_t *io_man);
+
+static mb_IO_man_t njs_io_man = {
+    njs_io_man_reg,
+    njs_io_man_unreg
+};
+
+/*! \brief IO factory to integrate MadButterfly to event loop of nodejs.
+ */
+static mb_IO_factory_t njs_io_factory = {
+    njs_io_man_new,
+    njs_io_man_free
+};
+
+/*! \brief Bridge libev callback to IO manager callback.
+ */
+static void
+njs_io_man_cb(EV_P_ ev_io *iowatcher, int revent) {
+    struct _njs_io_reg *io_reg =
+	MEM2OBJ(iowatcher, struct _njs_io_reg, iowatcher);
+    MB_IO_TYPE type;
+
+    switch(revent & (EV_READ | EV_WRITE)) {
+    case EV_READ:
+	type = MB_IO_R;
+	break;
+    case EV_WRITE:
+	type = MB_IO_W;
+	break;
+    case EV_READ | EV_WRITE:
+	type = MB_IO_RW;
+	break;
+    }
+    
+    io_reg->cb((int)io_reg, io_reg->fd, type, io_reg->data);
+}
+
+static int
+njs_io_man_reg(struct _mb_IO_man *io_man,
+	       int fd, MB_IO_TYPE type, mb_IO_cb_t cb, void *data) {
+    int _type;
+    struct _njs_io_reg *io_reg;
+
+    if(type == MB_IO_R)
+	_type = EV_READ;
+    else if(type == MB_IO_W)
+	_type = EV_WRITE;
+    else if(type == MB_IO_RW)
+	_type = EV_READ | EV_WRITE;
+    else
+	return ERR;
+    
+    io_reg = O_ALLOC(struct _njs_io_reg);
+    if(io_reg == NULL)
+	return ERR;
+    
+    io_reg->fd = fd;
+    io_reg->cb = cb;
+    io_reg->data = data;
+
+    ev_io_init(&io_reg->iowatcher, njs_io_man_cb, fd, _type);
+    ev_io_start(&io_reg->iowatcher);
+    
+    return (int)io_reg;
+}
+
+static void
+njs_io_man_unreg(struct _mb_IO_man *io_man, int io_hdl) {
+    struct _njs_io_reg *io_reg = (struct _njs_io_reg *)io_hdl;
+
+    ev_io_stop(&io_reg->iowatcher);
+    free(io_reg);
+}
+
+static mb_IO_man_t *
+njs_io_man_new(void) {
+    return &njs_io_man;
+}
+
+static void
+njs_io_man_free(mb_IO_man_t *io_man) {
+}
+
+/*! \brief Register an IO factory with MadButterfly backend.
+ */
+void
+njs_mb_reg_IO_man(void) {
+    mb_reg_IO_factory(&njs_io_factory);
+}
+
+/* @} */
+
+/*! \brief Free njs_runtime_t.
+ */
+void
+njs_mb_free(njs_runtime_t *rt) {
+    /*!
+     * TODO: Release all IO and timer request.
+     */
+    mb_runtime_free(rt->mb_rt);
+    free(rt);
+}
+
+/*! \brief Free njs_runtime_t.
+ */
+void
+njs_mb_free_keep_win(njs_runtime_t *rt) {
+    /*
+     * TODO: Release all IO and timer request.
+     */
+    mb_runtime_free_keep_win(rt->mb_rt);
+    free(rt);
+}
+
+int
+njs_mb_flush(njs_runtime_t *rt) {
+    mb_rt_t *mb_rt = rt->mb_rt;
+    int r;
+
+    r = mb_runtime_flush(mb_rt);
+    return r;
+}
+
+njs_runtime_t *
+njs_mb_new(char *display_name, int w, int h) {
+    njs_runtime_t *rt;
+    mb_rt_t *mb_rt;
+
+    rt = (njs_runtime_t *)malloc(sizeof(njs_runtime_t));
+    ASSERT(rt != NULL);
+
+    mb_rt = mb_runtime_new(display_name, w, h);
+
+    rt->mb_rt = mb_rt;
+
+    return rt;
+}
+
+/*! \brief Create a njs_runtime_t for an existed window.
+ *
+ * The njs_runtime_t created by this function must be free by
+ * njs_mb_free_keep_win().
+ */
+njs_runtime_t *
+njs_mb_new_with_win(void *display, long win) {
+    njs_runtime_t *rt;
+    mb_rt_t *mb_rt;
+
+    rt = (njs_runtime_t *)malloc(sizeof(njs_runtime_t));
+    ASSERT(rt != NULL);
+
+    mb_rt = mb_runtime_new_with_win((Display *)display, win);
+
+    rt->mb_rt = mb_rt;
+
+    return rt;
+}
+
+/*! \brief Pass a X event to X runtime.
+ */
+void
+njs_mb_handle_single_event(njs_runtime_t *rt, void *evt) {
+#if 0
+    void *mb_rt = rt->mb_rt;
+    extern void _X_MB_handle_single_event(void *rt, void *evt);
+
+    _X_MB_handle_single_event(mb_rt, evt);
+#endif
+}
+
+/*! \brief Called at end of an iteration of event loop.
+ */
+void
+njs_mb_no_more_event(njs_runtime_t *rt) {
+#if 0
+    mb_rt_t *mb_rt = rt->mb_rt;
+    extern void _X_MB_no_more_event(mb_rt_t *rt);
+
+    _X_MB_no_more_event(mb_rt);
+#endif
+}
+
+/*! \brief Get X runtime that is backend of this njs runtime.
+ */
+mb_rt_t *
+_njs_mb_get_runtime(njs_runtime_t *rt) {
+    return rt->mb_rt;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/njs_mb_supp.h	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,31 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
+#ifndef __NJS_MB_SUPP_H_
+#define __NJS_MB_SUPP_H_
+
+#include <ev.h>
+#include <mb_backend.h>
+
+typedef struct _njs_runtime {
+    mb_rt_t *mb_rt;
+} njs_runtime_t;
+
+extern void njs_mb_reg_timer_man(void);
+extern void njs_mb_reg_IO_man(void);
+/* extern void njs_mb_init_handle_connection(njs_runtime_t *rt); */
+extern void njs_mb_free(njs_runtime_t *rt);
+extern njs_runtime_t *njs_mb_new(char *display_name, int w, int h);
+extern void njs_mb_free_keep_win(njs_runtime_t *rt);
+extern njs_runtime_t *njs_mb_new_with_win(void *display, long win);
+extern int njs_mb_flush(njs_runtime_t *rt);
+extern void njs_mb_handle_single_event(njs_runtime_t *rt, void *evt);
+extern void njs_mb_no_more_event(njs_runtime_t *rt);
+extern mb_rt_t *_njs_mb_get_runtime(njs_runtime_t *rt);
+
+#define njs_mb_kbevents(rt) mb_runtime_kbevents((rt)->mb_rt)
+#define njs_mb_rdman(rt) mb_runtime_rdman((rt)->mb_rt)
+#define njs_mb_timer_man(rt) mb_runtime_timer_man((rt)->mb_rt)
+#define njs_mb_observer_factory(rt) mb_runtime_observer_factory((rt)->mb_rt)
+#define njs_mb_img_ldr(rt) mb_runtime_img_ldr((rt)->mb_rt)
+
+#endif /* __NJS_MB_SUPP_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/observer.cc	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,169 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
+#include <v8.h>
+#include "mbfly_njs.h"
+
+extern "C" {
+#include <mb.h>
+#include <string.h>
+}
+
+#ifndef ASSERT
+#define ASSERT(x)
+#endif
+
+using namespace v8;
+
+/*! \defgroup xnjsmb_observer JS binding for observer, subject and events
+ * \ingroup xnjsmb
+ *
+ * @{
+ */
+
+struct xnjsmb_observer_data {
+    Persistent<Function> func;
+    Persistent<Context> ctx;
+};
+
+static void
+event_handler(event_t *evt, void *arg);
+
+static void
+xnjsmb_event_mod(Handle<Object> self, event_t *evt) {
+    mouse_event_t *mevt;
+    X_kb_event_t *xkbevt;
+
+    switch(evt->type) {
+    case EVT_ANY:
+    case EVT_MOUSE_OVER:
+    case EVT_MOUSE_OUT:
+    case EVT_MOUSE_MOVE:
+    case EVT_MOUSE_BUT_PRESS:
+    case EVT_MOUSE_BUT_RELEASE:
+	mevt = (mouse_event_t *)evt;
+	SET(self, "x", Integer::New(mevt->x));
+	SET(self, "y", Integer::New(mevt->y));
+	SET(self, "but_state", Integer::New(mevt->but_state));
+	SET(self, "button", Integer::New(mevt->button));
+	break;
+
+    case EVT_KB_PRESS:
+    case EVT_KB_RELEASE:
+	xkbevt = (X_kb_event_t *)evt;
+	SET(self, "keycode", Integer::New(xkbevt->keycode));
+	SET(self, "sym", Integer::New(xkbevt->sym));
+	break;
+
+    case EVT_PROGM_COMPLETE:
+    case EVT_RDMAN_REDRAW:
+    case EVT_MONITOR_ADD:
+    case EVT_MONITOR_REMOVE:
+    case EVT_MONITOR_FREE:
+    case EVT_MOUSE_MOVE_RAW:
+    default:
+	/* Not implemented.  Do nothing. */
+	break;
+    }
+}
+
+static observer_t *
+_subject_add_event_observer(subject_t *subject, int type,
+                            Handle<Function> func) {
+    observer_t *observer;
+    xnjsmb_observer_data *data;
+    Handle<Context> ctx;
+
+    data = new xnjsmb_observer_data;
+    if(data == NULL)
+	return NULL;
+    data->func = Persistent<Function>::New(func);
+    ctx = Context::GetCurrent();
+    data->ctx = Persistent<Context>::New(ctx);
+    observer = subject_add_event_observer(subject, type,
+					  event_handler,
+    	       				  data);
+
+    return observer;
+}
+
+static void
+_subject_remove_observer(subject_t *subject, observer_t *observer) {
+    xnjsmb_observer_data *data;
+
+    subject_remove_observer(subject, observer);
+    data = (xnjsmb_observer_data *)observer->arg;
+    delete data;
+}
+
+static Handle<Value>
+xnjsmb_event_tgt_getter(Handle<Object> self, event_t *evt, const char **err) {
+    Persistent<Object> *hdl;
+
+    hdl = (Persistent<Object> *)
+	mb_prop_get(&((mb_obj_t *)evt->tgt->obj)->props,
+		    PROP_JSOBJ);
+    return Local<Object>::New(*hdl);
+}
+
+static void
+xnjsmb_event_tgt_setter(Handle<Object> self, event_t *evt,
+			Handle<Value> value, const char **err) {
+    *err = "Not implemented";
+}
+
+static Handle<Value>
+xnjsmb_event_cur_tgt_getter(Handle<Object> self, event_t *evt,
+			    const char **err) {
+    Persistent<Object> *hdl;
+
+    hdl = (Persistent<Object> *)
+	mb_prop_get(&((mb_obj_t *)evt->cur_tgt->obj)->props,
+		    PROP_JSOBJ);
+    return Local<Object>::New(*hdl);
+}
+
+static void
+xnjsmb_event_cur_tgt_setter(Handle<Object> self, event_t *evt,
+			    Handle<Value> value, const char **err) {
+    *err = "Not implemented";
+}
+
+/* This is the part of the code generated by gen_v8_binding.m4 */
+#include "observer-inc.h"
+
+static void
+event_handler(event_t *evt, void *arg) {
+    xnjsmb_observer_data *data = (xnjsmb_observer_data *)arg;
+    Handle<Value> evt_obj;
+    Handle<Value> func_args[1];
+    Handle<Value> r;
+    Context::Scope context_scope(data->ctx);
+    TryCatch trycatch;
+
+    evt_obj = xnjsmb_auto_event_new(evt);
+    ASSERT(!evt_obj.IsEmpty());
+    func_args[0] = evt_obj;
+    r = data->func->Call(data->ctx->Global(), 1, func_args);
+    if(r.IsEmpty()) {
+	Handle<Value> exception = trycatch.Exception();
+	String::AsciiValue exc_str(exception);
+	fprintf(stderr, "Exception: %s\n", *exc_str);
+    }
+}
+
+Handle<Value>
+export_xnjsmb_auto_subject_new(subject_t *subject) {
+    Handle<Value> val;
+
+    val = xnjsmb_auto_subject_new(subject);
+    return val;
+}
+
+void
+xnjsmb_observer_init(void) {
+    xnjsmb_auto_observer_init();
+    xnjsmb_auto_subject_init();
+    xnjsmb_auto_event_init();
+}
+
+/* @} */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/observer.m4	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,18 @@
+define([PROJ_PREFIX], [xnjsmb_auto_])dnl
+dnl
+STRUCT([observer], [observer_t],
+       [INT([type])], [])
+
+STRUCT([subject], [subject_t], [],
+       [METHOD([add_event_observer], [_subject_add_event_observer],
+       	       (INT([type]), FUNC([handler])), 2,
+	       [OBJ([observer], [observer_t])]),
+        METHOD([remove_observer], [_subject_remove_observer],
+	       (OBJ([observer], [observer], [observer_t])), 1, [])])
+
+STRUCT([event], [event_t],
+       [INT([type]),
+        ACCESSOR([tgt], [xnjsmb_event_tgt_getter], [xnjsmb_event_tgt_setter]),
+        ACCESSOR([cur_tgt], [xnjsmb_event_cur_tgt_getter],
+	    [xnjsmb_event_cur_tgt_setter]),
+	INT([flags])], [], (([STMOD], [xnjsmb_event_mod])))
--- a/nodejs/paints.cc	Mon Jul 19 15:44:49 2010 +0800
+++ b/nodejs/paints.cc	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include <v8.h>
 
 extern "C" {
@@ -12,197 +14,311 @@
 #define ASSERT(x)
 #endif
 
-
-/*! \brief Fill a shape with the paint.
- */
-static Handle<Value>
-xnjsmb_paint_fill(const Arguments &args) {
-    int argc = args.Length();
-    Handle<Object> self = args.This();
-    Handle<Object> sh_obj;
-    Handle<Object> rt;
-    Handle<Value> rt_val;
-    paint_t *paint;
-    shape_t *sh;
-    redraw_man_t *rdman;
-
-    if(argc != 1)
-	THROW("Invalid number of arguments (!= 1)");
-    if(!args[0]->IsObject())
-	THROW("Invalid argument type (shape)");
-
-    paint = (paint_t *)UNWRAP(self);
-    
-    sh_obj = args[0]->ToObject();
-    sh = (shape_t *)UNWRAP(sh_obj);
-
-    rt_val = GET(self, "mbrt");
-    rt = rt_val->ToObject();
-    rdman = xnjsmb_rt_rdman(rt);
-    
-    rdman_paint_fill(rdman, paint, sh);
-    
-    if(sh_get_coord(sh))
-	rdman_shape_changed(rdman, sh);
-    
-    return Null();
-}
-
-/*! \brief Stroke a shape with the paint.
+/*! \defgroup xnjsmb_paints JS binding for paints
+ * \ingroup xnjsmb
+ *
+ * @{
  */
-static Handle<Value>
-xnjsmb_paint_stroke(const Arguments &args) {
-    int argc = args.Length();
-    Handle<Object> self = args.This();
-    Handle<Object> sh_obj;
-    Handle<Object> rt;
-    Handle<Value> rt_val, sh_val;
+
+/*! \page paint_gc How to manage life-cycle of paints?
+ *
+ * A paint is used to fill and stroke shapes.  It should keep live
+ * before all shapes being stroked/or filled by it are dead or
+ * filled/stroked by other paints.  So, every shape that uses a paint
+ * should keep a reference to the paint.  The reference are removed
+ * when a shape is filled/or stroked by other paints.
+ *
+ * A paint should keep a weak reference to itself.  It is used to free
+ * resource before it being collected.
+ */
+static void
+xnjsmb_paint_recycle(Persistent<Value> obj, void *parameter) {
+    Persistent<Object> *paint_hdl = (Persistent<Object> *)parameter;
     paint_t *paint;
-    shape_t *sh;
-    redraw_man_t *rdman;
-
-    if(argc != 1)
-	THROW("Invalid number of arguments (!= 1)");
-    if(!args[0]->IsObject())
-	THROW("Invalid argument type (shape)");
-
-    paint = (paint_t *)UNWRAP(self);
-    
-    sh_val = args[0];
-    sh_obj = sh_val->ToObject();
-    sh = (shape_t *)UNWRAP(sh_obj);
-
-    rt_val = GET(self, "mbrt");
-    rt = rt_val->ToObject();
-    rdman = xnjsmb_rt_rdman(rt);
-    
-    rdman_paint_stroke(rdman, paint, sh);
-    
-    if(sh_get_coord(sh))
-	rdman_shape_changed(rdman, sh);
-    
-    return Null();
-}
-
-/*! \brief Constructor of color paint_color_t object for Javascript.
- */
-static Handle<Value>
-xnjsmb_paint_color(const Arguments &args) {
-    int argc = args.Length();
-    Handle<Object> self = args.This();
     Handle<Object> rt;
     redraw_man_t *rdman;
-    paint_t *paint;
-    float r, g, b, a;
+    int r;
+
+    paint = (paint_t *)UNWRAP(*paint_hdl);
+    rt = GET(*paint_hdl, "mbrt")->ToObject();
+    rdman = xnjsmb_rt_rdman(rt);
+
+    r = rdman_paint_free(rdman, paint);
+    ASSERT(r == 0);
 
-    if(argc != 5)
-	THROW("Invalid number of arguments (!= 5)");
-    if(!args[0]->IsObject() || !args[1]->IsNumber() ||
-       !args[2]->IsNumber() || !args[3]->IsNumber() ||
-       !args[4]->IsNumber())
-	THROW("Invalid argument type");
+    paint_hdl->Dispose();
+    delete paint_hdl;
+}
+
+static void
+xnjsmb_paint_mod(Handle<Object> self, void *paint) {
+    Persistent<Object> *paint_hdl;
+
+    paint_hdl = new Persistent<Object>();
+    *paint_hdl = Persistent<Object>::New(self);
 
-    rt = args[0]->ToObject();
-    r = args[1]->ToNumber()->Value();
-    g = args[2]->ToNumber()->Value();
-    b = args[3]->ToNumber()->Value();
-    a = args[4]->ToNumber()->Value();
+    paint_hdl->MakeWeak(paint_hdl, xnjsmb_paint_recycle);
+}
+
+static void
+xnjsmb_paint_fill(paint_t *paint, Handle<Object> self, shape_t *sh) {
+    Handle<Value> rt_v;
+    Handle<Object> rt_o;
+    Handle<Object> sh_o;
+    redraw_man_t *rdman;
 
-    rdman = xnjsmb_rt_rdman(rt);
-    paint = rdman_paint_color_new(rdman, r, g, b, a);
-    ASSERT(sh != NULL);
+    rt_v = GET(self, "mbrt");
+    rt_o = rt_v->ToObject();
+    rdman = xnjsmb_rt_rdman(rt_o);
+
+    rdman_paint_fill(rdman, paint, sh);
 
-    WRAP(self, paint);
-    SET(self, "mbrt", rt);
+    if(sh_get_coord(sh))
+	rdman_shape_changed(rdman, sh);
 
-    return Null();
+    sh_o = *(Persistent<Object> *)mb_prop_get(&sh->obj.props, PROP_JSOBJ);
+    SET(sh_o, "_fill_by", self);
 }
 
-static Persistent<FunctionTemplate> xnjsmb_paint_temp;
-static Persistent<FunctionTemplate> xnjsmb_paint_color_temp;
+static void
+xnjsmb_paint_stroke(paint_t *paint, Handle<Object> self, shape_t *sh) {
+    Handle<Value> rt_v;
+    Handle<Object> rt_o;
+    Handle<Object> sh_o;
+    redraw_man_t *rdman;
+
+    rt_v = GET(self, "mbrt");
+    rt_o = rt_v->ToObject();
+    rdman = xnjsmb_rt_rdman(rt_o);
+
+    rdman_paint_stroke(rdman, paint, sh);
+
+    if(sh_get_coord(sh))
+	rdman_shape_changed(rdman, sh);
 
-/*! \brief Create and return a paint_color object.
+    sh_o = *(Persistent<Object> *)mb_prop_get(&sh->obj.props, PROP_JSOBJ);
+    SET(sh_o, "_stroke_by", self);
+}
+
+static void
+xnjsmb_paint_color_set_color(paint_t *paint, Handle<Object> self,
+			     float r, float g, float b, float a) {
+    Handle<Value> rt_v;
+    Handle<Object> rt_o;
+    redraw_man_t *rdman;
+
+    rt_v = GET(self, "mbrt");
+    rt_o = rt_v->ToObject();
+    rdman = xnjsmb_rt_rdman(rt_o);
+
+    paint_color_set(paint, r, g, b, a);
+
+    rdman_paint_changed(rdman, paint);
+}
+
+/*! \brief Set stops for linear paint for Javascript code.
  */
-static Handle<Value>
-xnjsmb_paint_color_new(const Arguments &args) {
-    HandleScope scope;
-    Handle<Object> rt = args.This();
-    Handle<Object> paint_color_obj;
-    Handle<Function> paint_color_func;
-    Handle<Value> pc_args[5];
-    int argc;
+static void
+xnjsmb_paint_linear_set_stops(paint_t *paint, Handle<Value> stops) {
+    Array *stops_o;
+    Array *stop_o;
+    int nstops;
+    grad_stop_t *grad_stops, *old_grad_stops;
+    int i;
+
+    stops_o = Array::Cast(*stops);
+    nstops = stops_o->Length();
+    grad_stops = (grad_stop_t *)malloc(sizeof(grad_stop_t) * nstops);
+    ASSERT(grad_stops != NULL);
+
+    for(i = 0; i < nstops; i++) {
+	stop_o = Array::Cast(*stops_o->Get(i));
+	ASSERT(stop_o->Length() == 5);
+	grad_stop_init(grad_stops + i,
+		       stop_o->Get(0)->ToNumber()->Value(),  /* off */
+		       stop_o->Get(1)->ToNumber()->Value(),  /* r */
+		       stop_o->Get(2)->ToNumber()->Value(),  /* g */
+		       stop_o->Get(3)->ToNumber()->Value(),  /* b */
+		       stop_o->Get(4)->ToNumber()->Value()); /* a */
+    }
+
+    old_grad_stops = paint_linear_stops(paint, nstops, grad_stops);
+    if(old_grad_stops)
+	free(old_grad_stops);	/* The stops, here, were allocated for
+				 * previous calling of this
+				 * function. */
+}
+
+/*! \brief Set stops for radial paint for Javascript code.
+ */
+static void
+xnjsmb_paint_radial_set_stops(paint_t *paint, Handle<Value> stops) {
+    Array *stops_o;
+    Array *stop_o;
+    int nstops;
+    grad_stop_t *grad_stops, *old_grad_stops;
     int i;
 
-    argc = args.Length();
-    if(argc != 4)
-	THROW("Invalid number of arguments (r, g, b, a)");
-    for(i = 0; i < 4; i++)
-	if(!args[i]->IsNumber())
-	    THROW("Invalid argument type");
-    
-    pc_args[0] = rt;
-    pc_args[1] = args[0];	// r
-    pc_args[2] = args[1];	// g
-    pc_args[3] = args[2];	// b
-    pc_args[4] = args[3];	// a
-    paint_color_func = xnjsmb_paint_color_temp->GetFunction();
-    paint_color_obj = paint_color_func->NewInstance(5, pc_args);
+    stops_o = Array::Cast(*stops);
+    nstops = stops_o->Length();
+    grad_stops = (grad_stop_t *)malloc(sizeof(grad_stop_t) * nstops);
+    ASSERT(grad_stops != NULL);
+
+    for(i = 0; i < nstops; i++) {
+	stop_o = Array::Cast(*stops_o->Get(i));
+	ASSERT(stop_o->Length() == 5);
+	grad_stop_init(grad_stops + i,
+		       stop_o->Get(0)->ToNumber()->Value(),  /* off */
+		       stop_o->Get(1)->ToNumber()->Value(),  /* r */
+		       stop_o->Get(2)->ToNumber()->Value(),  /* g */
+		       stop_o->Get(3)->ToNumber()->Value(),  /* b */
+		       stop_o->Get(4)->ToNumber()->Value()); /* a */
+    }
+
+    old_grad_stops = paint_radial_stops(paint, nstops, grad_stops);
+    if(old_grad_stops)
+	free(old_grad_stops);	/* The stops, here, were allocated for
+				 * previous calling of this
+				 * function. */
+}
+
+#include "paints-inc.h"
 
-    scope.Close(paint_color_obj);
-    return paint_color_obj;
+/*! \defgroup xnjsmb_paints_cons Constructor of paints
+ *
+ * @{
+ */
+paint_t *
+xnjsmb_paint_color_new(njs_runtime_t *rt,
+		       float r, float g, float b, float a,
+		       const char **err) {
+    paint_t *paint;
+    redraw_man_t *rdman;
+
+    rdman = njs_mb_rdman(rt);
+    paint = rdman_paint_color_new(rdman, r, g, b, a);
+    if(paint == NULL) {
+	*err = "can not allocate a paint_color_t";
+	return NULL;
+    }
+
+    return paint;
+}
+
+paint_t *
+xnjsmb_paint_image_new(njs_runtime_t *rt, mb_img_data_t *img,
+		       const char **err) {
+    paint_t *paint;
+    redraw_man_t *rdman;
+
+    rdman = njs_mb_rdman(rt);
+    paint = rdman_paint_image_new(rdman, img);
+    if(paint == NULL) {
+	*err = "can not allocate a paint_image_t";
+	return NULL;
+    }
+
+    return paint;
 }
 
-static Persistent<FunctionTemplate> xnjsmb_paint_color_new_temp;
+paint_t *
+xnjsmb_paint_linear_new(njs_runtime_t *rt,
+			float x1, float y1, float x2, float y2,
+			const char **err) {
+    paint_t *paint;
+    redraw_man_t *rdman;
+
+    rdman = njs_mb_rdman(rt);
+    paint = rdman_paint_linear_new(rdman, x1, y1, x2, y2);
+    if(paint == NULL) {
+	*err = "can not allocate a paint_linear_t";
+	return NULL;
+    }
 
-/*! \brief Create templates for paint types.
- *
- * This function is only called one time for every execution.
- */
-static void
-xnjsmb_init_paints(void) {
-    Handle<FunctionTemplate> temp, meth;
-    Handle<ObjectTemplate> inst_temp;
-    Handle<ObjectTemplate> proto_temp;
-    
-    /*
-     * Base type of paint types.
-     */
-    temp = FunctionTemplate::New();
-    xnjsmb_paint_temp = Persistent<FunctionTemplate>::New(temp);
-    xnjsmb_paint_temp->SetClassName(String::New("paint"));
-    
-    meth = FunctionTemplate::New(xnjsmb_paint_fill);
-    proto_temp = xnjsmb_paint_temp->PrototypeTemplate();
-    SET(proto_temp, "fill", meth);
+    return paint;
+}
 
-    meth = FunctionTemplate::New(xnjsmb_paint_stroke);
-    proto_temp = xnjsmb_paint_temp->PrototypeTemplate();
-    SET(proto_temp, "stroke", meth);
+paint_t *
+xnjsmb_paint_radial_new(njs_runtime_t *rt,
+			float cx, float cy, float r,
+			const char **err) {
+    paint_t *paint;
+    redraw_man_t *rdman;
 
-    /*
-     * Paint color
-     */
-    temp = FunctionTemplate::New(xnjsmb_paint_color);
-    xnjsmb_paint_color_temp = Persistent<FunctionTemplate>::New(temp);
-    xnjsmb_paint_color_temp->SetClassName(String::New("paint_color"));
-    xnjsmb_paint_color_temp->Inherit(xnjsmb_paint_temp);
-    
-    inst_temp = xnjsmb_paint_color_temp->InstanceTemplate();
-    inst_temp->SetInternalFieldCount(1);
+    rdman = njs_mb_rdman(rt);
+    paint = rdman_paint_radial_new(rdman, cx, cy, r);
+    if(paint == NULL) {
+	*err = "can not allocate a paint_radial_t";
+	return NULL;
+    }
 
-    temp = FunctionTemplate::New(xnjsmb_paint_color_new);
-    xnjsmb_paint_color_new_temp = Persistent<FunctionTemplate>::New(temp);
+    return paint;
 }
 
+/* @} */
+
+/*! \defgroup xnjsmb_paints_export Exported wrapper maker for paints
+ *
+ * These functions are used by MB runtime to wrap C paints to JS
+ * objects.
+ *
+ * @{
+ */
+Handle<Value>
+export_xnjsmb_auto_paint_color_new(paint_t *paint) {
+    Handle<Value> ret;
+
+    ret = xnjsmb_auto_paint_color_new(paint);
+
+    return ret;
+}
+
+Handle<Value>
+export_xnjsmb_auto_paint_image_new(paint_t *paint) {
+    Handle<Value> ret;
+
+    ret = xnjsmb_auto_paint_image_new(paint);
+
+    return ret;
+}
+
+Handle<Value>
+export_xnjsmb_auto_paint_linear_new(paint_t *paint) {
+    Handle<Value> ret;
+
+    ret = xnjsmb_auto_paint_linear_new(paint);
+
+    return ret;
+}
+
+Handle<Value>
+export_xnjsmb_auto_paint_radial_new(paint_t *paint) {
+    Handle<Value> ret;
+
+    ret = xnjsmb_auto_paint_radial_new(paint);
+
+    return ret;
+}
+/* @} */
+
+/*! \brief Initialize paints for mbfly.
+ *
+ * This function is called by init() in mbfly_njs.cc when the module
+ * being loaded.
+ */
 void xnjsmb_paints_init_mb_rt_temp(Handle<FunctionTemplate> rt_temp) {
     static int init_flag = 0;
     Handle<ObjectTemplate> rt_proto_temp;
 
     if(!init_flag) {
-	xnjsmb_init_paints();
+	xnjsmb_auto_paint_init();
+	xnjsmb_auto_paint_color_init();
+	xnjsmb_auto_paint_image_init();
+	xnjsmb_auto_paint_linear_init();
+	xnjsmb_auto_paint_radial_init();
+
 	init_flag = 1;
     }
+}
 
-    rt_proto_temp = rt_temp->PrototypeTemplate();
-    SET(rt_proto_temp, "paint_color_new", xnjsmb_paint_color_new_temp);
-}
+/* @} */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/paints.m4	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,31 @@
+define([PROJ_PREFIX], [xnjsmb_auto_])dnl
+dnl
+STRUCT([paint], [paint_t], [],
+       [METHOD([fill], [xnjsmb_paint_fill],
+               (SELF, OBJ([sh], [shape], [shape_t])), 1, []),
+        METHOD([stroke], [xnjsmb_paint_stroke],
+	       (SELF, OBJ([sh], [shape], [shape_t])), 1, [])])
+
+STRUCT([paint_color], [paint_t], [],
+       [METHOD([set_color], [xnjsmb_paint_color_set_color],
+       	       (SELF, NUMBER([r]), NUMBER([g]), NUMBER([b]), NUMBER([a])),
+	       4, [])],
+       (([INHERIT], [paint]),
+        ([STMOD], [xnjsmb_paint_mod])))
+
+STRUCT([paint_image], [paint_t], [],
+       [],
+       (([INHERIT], [paint]),
+       ([STMOD], [xnjsmb_paint_mod])))
+
+STRUCT([paint_linear], [paint_t], [],
+       [METHOD([set_stops], [xnjsmb_paint_linear_set_stops],
+       (ARRAY([stops])), 1, [])],
+       (([INHERIT], [paint]),
+        ([STMOD], [xnjsmb_paint_mod])))
+
+STRUCT([paint_radial], [paint_t], [],
+       [METHOD([set_stops], [xnjsmb_paint_radial_set_stops],
+       (ARRAY([stops])), 1, [])],
+       (([INHERIT], [paint]),
+        ([STMOD], [xnjsmb_paint_mod])))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/run.sh	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+ABS=`realpath $0`
+BASE=`dirname $ABS`
+NODE_PATH=$BASE:$BASE/objs/default:$NODE_PATH
+export NODE_PATH
+
+DIR=`dirname $1`
+FNAME=`basename $1`
+
+cd $DIR; node $FNAME
--- a/nodejs/shapes.cc	Mon Jul 19 15:44:49 2010 +0800
+++ b/nodejs/shapes.cc	Wed Dec 01 12:25:56 2010 +0800
@@ -1,6 +1,10 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include <v8.h>
 #include "mbfly_njs.h"
 
+#include <string.h>
+
 extern "C" {
 #include <mb.h>
 }
@@ -9,73 +13,148 @@
 #define ASSERT(x)
 #endif
 
+#define OK 0
+
 using namespace v8;
 
-/*! \defgroup shape_temp Templates for shape and derivations.
+/*! \defgroup xnjsmb_shapes JS binding for shapes.
+ * \ingroup xnjsmb
  *
  * @{
  */
-static Persistent<FunctionTemplate> xnjsmb_shape_temp;
+/*! \brief This function is called when GC collecting a shape.
+ *
+ * It was installed by Persistent<Object>::MakeWeak().
+ */
+static void
+xnjsmb_shape_recycled(Persistent<Value> obj, void *parameter) {
+    Persistent<Object> *self_hdl = (Persistent<Object> *)parameter;
+    Handle<Object> js_rt;
+    redraw_man_t *rdman;
+    shape_t *shape;
+
+    shape = (shape_t *)UNWRAP(*self_hdl);
+    if(shape == NULL)
+	return;
+
+    WRAP(*self_hdl, NULL);
+
+    js_rt = GET(*self_hdl, "mbrt")->ToObject();
+    rdman = xnjsmb_rt_rdman(js_rt);
+    rdman_shape_changed(rdman, shape);
+    rdman_shape_free(rdman, shape);
+
+    self_hdl->Dispose();
+    delete self_hdl;
+}
+
+static void
+xnjsmb_shape_mod(Handle<Object> self, shape_t *sh) {
+    Persistent<Object> *self_hdl;
+    static int count = 0;
+
+    /* Keep associated js object in property store for retrieving,
+     * later, without create new js object.
+     */
+    self_hdl = new Persistent<Object>();
+    *self_hdl = Persistent<Object>::New(self);
+    mb_prop_set(&sh->obj.props, PROP_JSOBJ, self_hdl);
+
+    self_hdl->MakeWeak(self_hdl, xnjsmb_shape_recycled);
+
+    /* XXX: should be remove.  It is for trace recycle of shape */
+    count++;
+    if(count > 10000) {
+	V8::LowMemoryNotification();
+	count = 0;
+    }
+}
 
-static Handle<Value>
-xnjsmb_shape_show(const Arguments &args) {
-    shape_t *sh;
-    Handle<Object> self;
+static void
+xnjsmb_sh_stext_set_text(shape_t *sh, Handle<Object> self,
+			 const char *txt) {
+    Handle<Object> rt;
+    redraw_man_t *rdman;
+    
+    sh_stext_set_text(sh, txt);
+    
+    /*
+     * Mark changed.
+     */
+    rt = GET(self, "mbrt")->ToObject();
+    ASSERT(rt != NULL);
+    rdman = xnjsmb_rt_rdman(rt);
+
+    if(sh_get_coord(sh))
+	rdman_shape_changed(rdman, sh);
+}
 
-    self = args.This();
-    sh = (shape_t *)UNWRAP(self);
-    sh_show(sh);
-    
-    return Null();
+/*! \brief Set style blocks for a stext object from JS.
+ *
+ * A style block is style setting of a chip of text.  It is a 3-tuple,
+ * includes number of charaters, a font face, and font size.  This
+ * function need a list of 3-tuples to set style of text chips of the
+ * stext.
+ */
+static void
+xnjsmb_sh_stext_set_style(shape_t *sh, Handle<Object> self,
+			  Handle<Value> blks, const char **err) {
+    Array *blksobj;
+    Array *blkobj;
+    mb_style_blk_t *mb_blks;
+    int nblks;
+    Handle<Object> rt;
+    redraw_man_t *rdman;
+    int r;
+    int i;
+
+    blksobj = Array::Cast(*blks);
+    nblks = blksobj->Length();
+    mb_blks = new mb_style_blk_t[nblks];
+    for(i = 0; i < nblks; i++) {
+	blkobj = Array::Cast(*blksobj->Get(i));
+	mb_blks[i].n_chars = blkobj->Get(0)->ToInt32()->Value();
+	mb_blks[i].face = (mb_font_face_t *)UNWRAP(blkobj->Get(1)->ToObject());
+	mb_blks[i].font_sz = blkobj->Get(2)->ToNumber()->Value();
+    }
+
+    r = sh_stext_set_style(sh, mb_blks, nblks);
+    if(r != 0) {
+	*err = "Unknown error";
+	return;
+    }
+
+    /*
+     * Mark changed.
+     */
+    rt = GET(self, "mbrt")->ToObject();
+    ASSERT(rt != NULL);
+    rdman = xnjsmb_rt_rdman(rt);
+
+    if(sh_get_coord(sh))
+	rdman_shape_changed(rdman, sh);
+
+    delete mb_blks;
 }
 
 static Handle<Value>
-xnjsmb_shape_hide(const Arguments &args) {
-    shape_t *sh;
-    Handle<Object> self;
+xnjsmb_shape_stroke_width_get(Handle<Object> self, shape_t *sh,
+			      const char **err) {
+    float stroke_width;
 
-    self = args.This();
-    sh = (shape_t *)UNWRAP(self);
-    sh_hide(sh);
-    
-    return Null();
+    stroke_width = sh_get_stroke_width(sh);
+    return Number::New(stroke_width);
 }
 
-/*! \brief Get stroke width of a shape.
- */
-static Handle<Value>
-xnjsmb_shape_stroke_width_getter(Local<String> property,
-				 const AccessorInfo &info) {
-    Handle<Object> self = info.This();
-    shape_t *sh;
-    float w;
-    Handle<Value> w_val;
-    
-    sh = (shape_t *)UNWRAP(self);
-    w = sh_get_stroke_width(sh);
-    w_val = Number::New(w);
-    
-    return w_val;
-}
+static void
+xnjsmb_shape_stroke_width_set(Handle<Object> self, shape_t *sh,
+			      Handle<Value> value, const char **err) {
+    float stroke_width;
+    Handle<Object> rt;
+    redraw_man_t *rdman;
 
-/*! \brief Set stroke width of a shape.
- */
-static void
-xnjsmb_shape_stroke_width_setter(Local<String> property,
-				 Local<Value> value,
-				 const AccessorInfo &info) {
-    Handle<Object> self = info.This();
-    Handle<Object> rt;
-    shape_t *sh;
-    redraw_man_t *rdman;
-    float w;
-    Handle<Number> w_num;
-    
-    sh = (shape_t *)UNWRAP(self);
-    w_num = value->ToNumber();
-    w = w_num->Value();
-
-    sh_set_stroke_width(sh, w);
+    stroke_width = value->Int32Value();
+    sh_set_stroke_width(sh, stroke_width);
 
     /*
      * Mark changed.
@@ -83,280 +162,194 @@
     rt = GET(self, "mbrt")->ToObject();
     ASSERT(rt != NULL);
     rdman = xnjsmb_rt_rdman(rt);
-    
+
     if(sh_get_coord(sh))
 	rdman_shape_changed(rdman, sh);
 }
 
 static void
-xnjsmb_init_shape_temp(void) {
-    Handle<FunctionTemplate> temp;
-    Handle<ObjectTemplate> proto_temp;
-    Handle<FunctionTemplate> method_temp;
+xnjsmb_shape_show(shape_t *sh, Handle<Object> self) {
+    Handle<Object> js_rt;
+    redraw_man_t *rdman;
+
+    js_rt = GET(self, "mbrt")->ToObject();
+    ASSERT(js_rt != NULL);
+    rdman = xnjsmb_rt_rdman(js_rt);
+
+    sh_show(sh);
+    rdman_shape_changed(rdman, sh);
+}
 
-    temp = FunctionTemplate::New();
-    temp->SetClassName(String::New("shape"));
-    xnjsmb_shape_temp = Persistent<FunctionTemplate>::New(temp);
-    proto_temp = temp->PrototypeTemplate();
-    
-    method_temp = FunctionTemplate::New(xnjsmb_shape_show);
-    SET(proto_temp, "show", method_temp);
-    method_temp = FunctionTemplate::New(xnjsmb_shape_hide);
-    SET(proto_temp, "hide", method_temp);
+static void
+xnjsmb_shape_hide(shape_t *sh, Handle<Object> self) {
+    Handle<Object> js_rt;
+    redraw_man_t *rdman;
 
-    proto_temp->SetAccessor(String::New("stroke_width"),
-			    xnjsmb_shape_stroke_width_getter,
-			    xnjsmb_shape_stroke_width_setter);
+    js_rt = GET(self, "mbrt")->ToObject();
+    ASSERT(js_rt != NULL);
+    rdman = xnjsmb_rt_rdman(js_rt);
+
+    sh_hide(sh);
+    rdman_shape_changed(rdman, sh);
 }
 
-/* @} */
-
-/*! \defgroup path_temp Templates for path objects.
- *
- * @{
- */
-static Persistent<FunctionTemplate> xnjsmb_path_temp;
+static void
+xnjsmb_shape_remove(shape_t *sh, Handle<Object> self) {
+    Handle<Object> js_rt;
+    redraw_man_t *rdman;
+    Persistent<Object> *self_hdl;
+    int r;
 
-/*! \brief Callback of constructor of path objects for Javascript.
- */
-static Handle<Value>
-xnjsmb_shape_path(const Arguments &args) {
-    shape_t *sh;
-    redraw_man_t *rdman;
-    Handle<Object> self = args.This(); // path object
-    Handle<Object> rt;
-    char *dstr;
-    int argc;
+    self_hdl = (Persistent<Object> *)mb_prop_get(&sh->obj.props,
+						 PROP_JSOBJ);
 
-    argc = args.Length();
-    if(argc != 2)
-	THROW("Invalid number of arugments (!= 1)");
-    if(!args[0]->IsString())
-	THROW("Invalid argument type (should be a string)");
-    if(!args[1]->IsObject())
-	THROW("Invalid argument type (should be an object)");
+    SET(*self_hdl, "valid", Boolean::New(0));
+    WRAP(*self_hdl, NULL);
+
+    js_rt = GET(*self_hdl, "mbrt")->ToObject();
+    ASSERT(js_rt != NULL);
+    rdman = xnjsmb_rt_rdman(js_rt);
 
-    String::Utf8Value dutf8(args[0]->ToString());
-    dstr = *dutf8;
+    rdman_shape_changed(rdman, sh);
+    r = rdman_shape_free(rdman, sh);
+    if(r != OK)
+	THROW_noret("Can not free a shape for unknown reason");
 
-    rt = args[1]->ToObject();
-    rdman = xnjsmb_rt_rdman(rt);
-    sh = rdman_shape_path_new(rdman, dstr);
-
-    WRAP(self, sh);
-    SET(self, "mbrt", rt);
-
-    return Null();
+    self_hdl->Dispose();
+    delete self_hdl;
 }
 
-/*! \brief Initial function template for constructor of path objects.
- */
 static void
-xnjsmb_init_path_temp(void) {
-    Handle<FunctionTemplate> temp;
-    Handle<ObjectTemplate> inst_temp;
+xnjsmb_sh_rect_set(shape_t *sh, Handle<Object> self, float x, float y,
+		   float w, float h, float rx, float ry) {
+    Handle<Object> rt;
+    redraw_man_t *rdman;
 
-    temp = FunctionTemplate::New(xnjsmb_shape_path);
-    temp->Inherit(xnjsmb_shape_temp);
-    temp->SetClassName(String::New("path"));
-
-    inst_temp = temp->InstanceTemplate();
-    inst_temp->SetInternalFieldCount(1);
-
-    xnjsmb_path_temp = Persistent<FunctionTemplate>::New(temp);
-}
+    sh_rect_set(sh, x, y, w, h, rx, ry);
 
-/*! \brief Callback function of mb_rt.path_new().
- */
-static Handle<Value>
-xnjsmb_shape_path_new(const Arguments &args) {
-    HandleScope scope;
-    Handle<Object> self = args.This(); // runtime object
-    Handle<Object> path_obj;
-    Handle<Value> path_args[2];
-    int argc;
+    /*
+     * Mark changed.
+     */
+    rt = GET(self, "mbrt")->ToObject();
+    ASSERT(rt != NULL);
+    rdman = xnjsmb_rt_rdman(rt);
 
-    argc = args.Length();
-    if(argc != 1)
-	THROW("Invalid number of arugments (!= 1)");
-    if(!args[0]->IsString())
-	THROW("Invalid argument type (shoud be a string)");
-
-    path_args[0] = args[0];
-    path_args[1] = self;
-    
-    path_obj = xnjsmb_path_temp->GetFunction()->NewInstance(2, path_args);
-    
-    scope.Close(path_obj);
-    return path_obj;
+    if(sh_get_coord(sh))
+	rdman_shape_changed(rdman, sh);
 }
 
 /* @} */
 
-/*! \defgroup stext_path Template for stext objects.
+#include "shapes-inc.h"
+
+/*! \defgroup xnjsmb_shapes_wraps Exported wrapper makers for shapes
+ * \ingroup xnjsmb_shapes
+ *
+ * These functions are used by methods of mb_rt to wrap shape objects
+ * as Javascript objects.
+ *
+ * @{
+ */
+Handle<Value>
+export_xnjsmb_auto_path_new(shape_t *sh) {
+    return xnjsmb_auto_path_new(sh);
+}
+
+Handle<Value>
+export_xnjsmb_auto_stext_new(shape_t *sh) {
+    return xnjsmb_auto_stext_new(sh);
+}
+
+Handle<Value>
+export_xnjsmb_auto_image_new(shape_t *sh) {
+    return xnjsmb_auto_image_new(sh);
+}
+
+Handle<Value>
+export_xnjsmb_auto_rect_new(shape_t *sh) {
+    return xnjsmb_auto_rect_new(sh);
+}
+
+/* @} */
+
+/*! \defgroup xnjsmb_shapes_cons Constructor of shapes
+ * \ingroup xnjsmb_shapes
  *
  * @{
  */
-
-/*! \brief Constructor for stext objects.
- *
- * 4 arguments
- * \param rt is a runtime object.
- * \param data is a text to be showed.
- * \param x is postion in x-axis.
- * \param y is position in y-axis.
- */
-static Handle<Value>
-xnjsmb_shape_stext(const Arguments &args) {
-    int argc = args.Length();
-    Handle<Object> self = args.This();
-    Handle<Object> rt;
-    float x, y;
-    char *data;
+shape_t *
+xnjsmb_path_new(njs_runtime_t *rt, const char *d) {
     redraw_man_t *rdman;
-    shape_t *stext;
-    
-    if(argc != 4)
-	THROW("Invalid number of arguments (!= 4)");
-    if(!args[0]->IsObject() || !args[1]->IsString() ||
-       !args[2]->IsNumber() || !args[3]->IsNumber())
-	THROW("Invalid argument type");
+    shape_t *sh;
+
+    rdman = njs_mb_rdman(rt);
+    sh = rdman_shape_path_new(rdman, d);
+    /* Code generator supposes that callee should free the memory */
+    free((void *)d);
+
+    return sh;
+}
 
-    rt = args[0]->ToObject();
-    String::Utf8Value data_utf8(args[1]);
-    data = *data_utf8;
-    x = args[2]->ToNumber()->Value();
-    y = args[3]->ToNumber()->Value();
+shape_t *
+xnjsmb_stext_new(njs_runtime_t *rt, const char *txt, float x, float y) {
+    redraw_man_t *rdman;
+    shape_t *sh;
 
-    rdman = xnjsmb_rt_rdman(rt);
-    ASSERT(rdman != NULL);
-
-    stext = rdman_shape_stext_new(rdman, data, x, y);
+    rdman = njs_mb_rdman(rt);
+    sh = rdman_shape_stext_new(rdman, txt, x, y);
+    /* Code generator supposes that callee should free the memory */
+    free((void *)txt);
 
-    WRAP(self, stext);
-    SET(self, "mbrt", rt);
-
-    return Null();
+    return sh;
 }
 
-static Persistent<FunctionTemplate> xnjsmb_shape_stext_temp;
-
-/*! \brief Create a stext and return it.
- */
-static Handle<Value>
-xnjsmb_shape_stext_new(const Arguments &args) {
-    HandleScope scope;
-    int argc = args.Length();
-    Handle<Object> self = args.This();
-    Handle<Value> stext_args[4];
-    Handle<Object> stext_obj;
-    Handle<Function> func;
+shape_t *
+xnjsmb_image_new(njs_runtime_t *rt, float x, float y, float w, float h) {
+    redraw_man_t *rdman;
+    shape_t *sh;
 
-    if(argc != 3)
-	THROW("Invalid number of arguments (!= 3)");
+    rdman = njs_mb_rdman(rt);
+    sh = rdman_shape_image_new(rdman, x, y, w, h);
 
-    stext_args[0] = self;
-    stext_args[1] = args[0];
-    stext_args[2] = args[1];
-    stext_args[3] = args[2];
-
-    func = xnjsmb_shape_stext_temp->GetFunction();
-    stext_obj = func->NewInstance(4, stext_args);
-    ASSERT(stext_obj != NULL);
-
-    return stext_obj;
+    return sh;
 }
 
-/*! \brief Setup style blocks for a stext.
- *
- * It defines font style and size for blocks of text message.
- *
- * \param blks is a list (n_char, face, font size) tuples.
- */
-static Handle<Value>
-xnjsmb_shape_stext_set_style(const Arguments &args) {
-    HandleScope scope;
-    int argc = args.Length();
-    Handle<Object> self = args.This();
+shape_t *
+xnjsmb_rect_new(njs_runtime_t *rt, float x, float y, float w, float h,
+		float rx, float ry, const char **err) {
+    redraw_man_t *rdman;
     shape_t *sh;
-    Array *blksobj;
-    Array *blkobj;
-    mb_style_blk_t *blks;
-    int nblks;
-    int i;
-    int r;
 
-    if(argc != 1)
-	THROW("Invalid number of arguments (!= 1)");
-    if(!args[0]->IsArray())
-	THROW("Invalid type of the argument");
-
-    blksobj = Array::Cast(*args[0]);
-    nblks = blksobj->Length();
-    blks = new mb_style_blk_t[nblks];
-    for(i = 0; i < nblks; i++) {
-	blkobj = Array::Cast(*blksobj->Get(i));
-	blks[i].n_chars = blkobj->Get(0)->ToInt32()->Value();
-	blks[i].face = (mb_font_face_t *)UNWRAP(blkobj->Get(1)->ToObject());
-	blks[i].font_sz = blkobj->Get(2)->ToNumber()->Value();
+    rdman = njs_mb_rdman(rt);
+    sh = rdman_shape_rect_new(rdman, x, y, w, h, rx, ry);
+    if(sh == NULL) {
+	*err = "Can not create a sh_rect_t";
+	return NULL;
     }
-    
-    sh = (shape_t *)UNWRAP(self);
-    r = sh_stext_set_style(sh, blks, nblks);
-    if(r != 0)
-	THROW("Unknown error");
-    
-    delete blks;
-    
-    return Null();
-}
 
-/*! \brief Initialize function template for stext objects.
- */
-void
-xnjsmb_init_stext_temp(void) {
-    Handle<FunctionTemplate> func_temp;
-    Handle<FunctionTemplate> meth_temp;
-    Handle<ObjectTemplate> inst_temp;
-    Handle<ObjectTemplate> proto_temp;
-    
-    func_temp = FunctionTemplate::New(xnjsmb_shape_stext);
-    func_temp->Inherit(xnjsmb_shape_temp);
-    func_temp->SetClassName(String::New("stext"));
-    
-    inst_temp = func_temp->InstanceTemplate();
-    inst_temp->SetInternalFieldCount(1);
-
-    proto_temp = func_temp->PrototypeTemplate();
-    meth_temp = FunctionTemplate::New(xnjsmb_shape_stext_set_style);
-    SET(proto_temp, "set_style", meth_temp);
-    
-    xnjsmb_shape_stext_temp = Persistent<FunctionTemplate>::New(func_temp);
+    return sh;
 }
 
 /* @} */
 
 /*! \brief Set properties of template of mb_rt.
+ * \ingroup xnjsmb_shapes
  */
 void
 xnjsmb_shapes_init_mb_rt_temp(Handle<FunctionTemplate> rt_temp) {
     HandleScope scope;
     Handle<FunctionTemplate> path_new_temp, stext_new_temp;
+    Handle<FunctionTemplate> image_new_temp;
     Handle<ObjectTemplate> rt_proto_temp;
     static int temp_init_flag = 0;
 
     if(temp_init_flag == 0) {
-	xnjsmb_init_shape_temp();
-	xnjsmb_init_path_temp();
-	xnjsmb_init_stext_temp();
+	xnjsmb_auto_shape_init();
+	xnjsmb_auto_path_init();
+	xnjsmb_auto_stext_init();
+	xnjsmb_auto_image_init();
+	xnjsmb_auto_rect_init();
 	temp_init_flag = 1;
     }
-
-    rt_proto_temp = rt_temp->PrototypeTemplate();
-    
-    path_new_temp = FunctionTemplate::New(xnjsmb_shape_path_new);
-    SET(rt_proto_temp, "path_new", path_new_temp);
-
-    stext_new_temp = FunctionTemplate::New(xnjsmb_shape_stext_new);
-    SET(rt_proto_temp, "stext_new", stext_new_temp);
+    return;
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/shapes.m4	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,28 @@
+define([PROJ_PREFIX], [xnjsmb_auto_])dnl
+dnl
+STRUCT([shape], [shape_t],
+       [ACCESSOR([stroke_width],
+		 [xnjsmb_shape_stroke_width_get],
+		 [xnjsmb_shape_stroke_width_set])],
+       [METHOD([show], [xnjsmb_shape_show], (SELF), 0, []),
+        METHOD([hide], [xnjsmb_shape_hide], (SELF), 0, []),
+	METHOD([remove], [xnjsmb_shape_remove], (SELF), 0, [])])
+
+STRUCT([path], [shape_t], [], [],
+       (([INHERIT], [shape]), ([STMOD], [xnjsmb_shape_mod])))
+
+STRUCT([stext], [shape_t], [],
+       [METHOD([set_text], [xnjsmb_sh_stext_set_text],
+       			    (SELF, STR([txt])), 1, []),
+        METHOD([set_style], [xnjsmb_sh_stext_set_style],
+	       (SELF, ARRAY([blks]), ERR), 1, [])],
+       (([INHERIT], [shape]), ([STMOD], [xnjsmb_shape_mod])))
+
+STRUCT([image], [shape_t], [], [],
+       (([INHERIT], [shape]), ([STMOD], [xnjsmb_shape_mod])))
+
+STRUCT([rect], [shape_t], [],
+       [METHOD([set], [xnjsmb_sh_rect_set],
+	       (SELF, NUMBER(x), NUMBER(y), NUMBER(w), NUMBER(h),
+	        NUMBER(rx), NUMBER(ry)), 6, [])],
+       (([INHERIT], [shape]), ([STMOD], [xnjsmb_shape_mod])))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs/svg.js	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,1258 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
+var libxml = require('libxmljs');
+var sys=require('sys');
+var mbfly = require("mbfly");
+var ldr = mbfly.img_ldr_new(".");
+
+
+var _std_colors = {
+    "white": [1, 1, 1],
+    "black": [0, 0, 0],
+    "red": [1, 0, 0]
+};
+
+function parse_color(color) {
+    var r, g, b;
+    var c;
+    
+    if (color[0] == "#") {
+	r = parseInt(color.substring(1, 3), 16) / 255;
+	g = parseInt(color.substring(3, 5), 16) / 255;
+	b = parseInt(color.substring(5, 7), 16) / 255;
+    } else if(_std_colors[color]) {
+	c = _std_colors[color];
+	r = c[0];
+	g = c[1];
+	b = c[2];
+    } else {
+	r = g = b = 0;
+    }
+    
+    return [r, g, b];
+}
+
+exports.loadSVG=loadSVG;
+
+function loadSVG(mb_rt, root, filename) {
+    this.pgstack=[];
+    if (filename)
+        this.load(mb_rt,root,filename);
+}
+
+loadSVG.prototype.load=function(mb_rt, root, filename) {
+    var doc = libxml.parseXmlFile(filename);
+    var _root = doc.root();
+    var nodes = _root.childNodes();
+    var coord = mb_rt.coord_new(root);
+    var k;
+    var accu=[1,0,0,0,1,0];
+    this.mb_rt = mb_rt;
+    this.stop_ref={};
+    this.gradients={};
+    this.radials = {};
+    coord.center=new Object();
+    coord.center.x = 10000;
+    coord.center.y = 10000;
+    if (this.pgstack.length > 0)
+        this.pgstack[this.pgstack.length-1].hide();
+    this.pgstack.push(coord);
+
+    
+    if(_root.attr("width")) {
+	k = _root.attr("width").value();
+	this.width = parseFloat(k);
+    }
+    if(_root.attr("height")) {
+	k = _root.attr("height").value();
+	this.height = parseFloat(k);
+    }
+    
+    for(k in nodes) {
+	var n = nodes[k].name();
+        if (n == "defs") {
+            this.parseDefs(coord,nodes[k]);
+	} else if (n == "metadata") {
+	    this.parseMetadata(coord,nodes[k]);
+        } else if (n == "g") {
+            this.parseGroup(accu,coord,'root_coord',nodes[k]);
+        } 
+    }
+}
+
+
+
+loadSVG.prototype.leaveSVG=function()
+{
+    var p = this.pgstack.pop();
+    p.hide();
+    if (this.pgstack.length > 0)
+	this.pgstack[this.pgstack.length-1].show();
+}
+
+function make_mbnames(mb_rt, n, obj) {
+    var mbname;
+    var name;
+    
+    if(!mb_rt.mbnames)
+	mb_rt.mbnames = {};
+    
+    mbname = n.attr("mbname");
+    if(mbname) {
+	name = mbname.value();
+	mb_rt.mbnames[name] = obj;
+	return;
+    }
+    mbname = n.attr("label");
+    if(mbname&&(mbname.value()!="")) {
+	name = mbname.value();
+	mb_rt.mbnames[name] = obj;
+    }
+    try {
+        var gname = n.attr('id').value();
+        sys.puts(gname);
+        mb_rt.mbnames[gname] = obj;
+    } catch(e) {
+    }
+}
+
+function getInteger(n,name)
+{
+    if (n == null) return 0;
+    var a = n.attr(name);
+    if (a==null) return 0;
+    return parseInt(a.value());
+}
+
+function parsePointSize(s)
+{
+    var fs=0;
+    var i;
+
+    for(i=0;i<s.length;i++) {
+        if (s[i]<'0' || s[i] > '9') break;
+        fs = fs*10 + (s[i]-'0');
+    }
+    return fs;
+	
+}
+
+function parse_style(node) {
+    var style_attr;
+    var style;
+    var parts, part;
+    var kv, key, value;
+    var content = {};
+    var i;
+    
+    style_attr = node.attr('style');
+    if(!style_attr)
+	return content;
+
+    style = style_attr.value();
+    parts = style.split(';');
+    for(i = 0; i < parts.length; i++) {
+	part = parts[i].trim();
+	if(part) {
+	    kv = part.split(':');
+	    key = kv[0].trim();
+	    value = kv[1].trim();
+	    content[key] = value;
+	}
+    }
+
+    return content;
+}
+
+function parseColor(c)
+{
+    if (c[0] == '#') {
+        return parseInt(c.substring(1,3),16)<<16 | parseInt(c.substring(3,5),16)<<8 | parseInt(c.substring(5,7),16);
+    }
+}
+
+function parseTextStyle(style,n)
+{
+    var attr;
+    if (n) {
+        attr = n.attr('style');
+    } else {
+        attr = null;
+    }
+    if (attr == null) {
+        return;
+    }
+    var f = attr.value().split(';');
+
+    for(i in f) {
+        var kv = f[i].split(':');
+        if (kv[0] == 'font-size') {
+            style.fs = parsePointSize(kv[1]);
+        } else if (kv[0] == "font-style") {
+        } else if (kv[0] == "font-weight") {
+        } else if (kv[0] == "fill") {
+            style.color = parseColor(kv[1]);
+        } else if (kv[0] == "fill-opacity") {
+        } else if (kv[0] == "stroke-opacity") {
+        } else if (kv[0] == "stroke") {
+        } else if (kv[0] == "stroke-width") {
+        } else if (kv[0] == "stroke-linecap") {
+        } else if (kv[0] == "stroke-linejoin") {
+        } else if (kv[0] == "stroke-lineopacity") {
+        } else if (kv[0] == "font-family") {
+            style.family = kv[1];
+        } else if (kv[0] == "font-stretch") {
+        } else if (kv[0] == "font-variant") {
+        } else if (kv[0] == "text-anchor") {
+        } else if (kv[0] == "text-align") {
+        } else if (kv[0] == "writing-mode") {
+        } else if (kv[0] == "line-height") {
+        } else {
+            sys.puts("Unknown style: "+kv[0]);
+        }
+    }
+}
+function tspan_set_text(text)
+{
+    this.text.set_text(text); 
+}
+
+function _parse_font_size(fn_sz_str) {
+    var pos;
+
+    pos = fn_sz_str.search("px");
+    if(pos >= 0)
+	fn_sz_str = fn_sz_str.substring(0, pos);
+    pos = fn_sz_str.search("pt");
+    if(pos >= 0)
+	fn_sz_str = fn_sz_str.substring(0, pos);
+
+    return parseFloat(fn_sz_str);
+}
+
+loadSVG.prototype.parseTSpan = function(coord, n, pstyle) {
+    var x = getInteger(n,'x');
+    var y = getInteger(n,'y');
+    var tcoord = this.mb_rt.coord_new(coord);
+    var style;
+    var family = "Courier";
+    var weight = 80;
+    var slant = 0;
+    var sz = 10;
+    var face;
+    var k;
+    var obj = this.mb_rt.stext_new(n.text(),x,y);
+    
+    style = parse_style(n);
+    for(k in pstyle) {
+	if(k in style)
+	    continue;
+	style[k] = pstyle[k];
+    }
+
+    if("font-family" in style)
+	family = style["font-family"];
+    if("font-size" in style)
+	sz = _parse_font_size(style["font-size"]);
+    if("font-weight" in style) {
+	if(style["font-weight"] == "bold")
+	    weight = 200;
+    }
+    if("font-style" in style) {
+	if(style["font-style"] == "oblique")
+	    slant = 110;
+    }
+
+    face = this.mb_rt.font_face_query(family, slant, weight);
+    obj.set_style([[n.text().length, face, sz]]);
+
+    tcoord.add_shape(obj);
+    tcoord.set_text = tspan_set_text;
+    tcoord.text = obj;
+    
+    this._set_paint_style(style, obj);
+    this._set_bbox(n, obj);
+    
+    make_mbnames(this.mb_rt, n, tcoord);
+};
+
+loadSVG.prototype._prepare_paint_color = function(color, alpha) {
+    var paint;
+    var c;
+    
+    if (color[0]=='#') {
+	var r,g,b;
+	r = parseInt(color.substring(1,3),16)/255;
+	g = parseInt(color.substring(3,5),16)/255;
+	b = parseInt(color.substring(5,7),16)/255;
+	paint = this.mb_rt.paint_color_new(r, g, b, alpha);
+    } else if(_std_colors[color]) {
+	c = _std_colors[color];
+	paint = this.mb_rt.paint_color_new(c[0], c[1], c[2], alpha);
+    } else if (color.substring(0,3) == 'url') {
+	var id = color.substring(5, color.length-1);
+	if(id in this.gradients) {
+	    var gr = this.gradients[id];
+	    paint = this.mb_rt.paint_linear_new(gr[0],gr[1],gr[2],gr[3]);
+	} else {
+	    var radial = this.radials[id];
+	    paint = this.mb_rt.paint_radial_new(radial[0],
+						radial[1],
+						radial[2]);
+	}
+	paint.set_stops(this.stop_ref[id]);
+    } else {
+	paint = this.mb_rt.paint_color_new(0,0,0,1);
+    }
+    return paint;
+};
+	
+function guessPathBoundingBox(coord,d)
+{
+    return;
+    var items = d.split(' ');
+    var len = items.length;
+    var pair;
+    var i;
+    var minx,miny;
+
+    minx = 10000;
+    miny = 10000;
+
+    for(i=0;i<len;i++) {
+	var type = items[i].toLowerCase();
+	x = minx;y = miny;
+	switch(type) {
+	case 'm':
+	case 'l':
+	case 'a':
+	case 'x':
+	    pair = items[i+1].split(',');
+	    if (pair.length==2) {
+		x = parseFloat(pair[0]);
+		y = parseFloat(pair[1]);
+		i++;
+	    } else {
+		x = parseFloat(items[i+1]);
+		y = parseFloat(items[i+2]);
+		i+=2;
+	    }
+	    break;
+	case 'q':
+	    // Implement this latter
+	    break;
+	case 'c':
+	    // Implement this latter
+	    break;
+	case 's':
+	    // Implement this latter
+	    break;
+	case 'h':
+	    x = parseFloat(items[i+1]);
+	    break;
+	case 'v':
+	    y = parseFloat(items[i+1]);
+	    break;
+	default:
+	    continue;
+	}
+	if (x < minx) minx = x;
+	if (y < miny) miny = y;
+    }
+    if (coord.center.x >  minx)
+	coord.center.x = minx;
+    if (coord.center.y >  miny)
+	coord.center.y = miny;
+};
+
+function _mul(m1, m2) {
+    var res = new Array();
+
+    res.push(m1[0] * m2[0] + m1[1] * m2[3]);
+    res.push(m1[0] * m2[1] + m1[1] * m2[4]);
+    res.push(m1[0] * m2[2] + m1[1] * m2[5] + m1[2]);
+    res.push(m1[3] * m2[0] + m1[4] * m2[3]);
+    res.push(m1[3] * m2[1] + m1[4] * m2[4]);
+    res.push(m1[3] * m2[2] + m1[4] * m2[5] + m1[5]);
+
+    return res;
+}
+
+function _pnt_transform(x, y, matrix) {
+    var rx, ry;
+
+    rx = x * matrix[0] + y * matrix[1] + matrix[2];
+    ry = x * matrix[3] + y * matrix[4] + matrix[5];
+    return new Array(rx, ry);
+}
+
+function _shift_transform(x, y, matrix) {
+    var rx, ry;
+
+    rx = x * matrix[0] + y * matrix[1];
+    ry = x * matrix[3] + y * matrix[4];
+    return new Array(rx, ry);
+}
+
+function _transform_bbox(bbox, matrix) {
+    var min_x, min_y, max_x, max_y;
+    var x, y;
+    var pnt;
+    var pnts = new Array();
+    var i;
+
+    pnt = _pnt_transform(bbox.x, bbox.y, matrix);
+    pnts.push(pnt);
+    pnt = _pnt_transform(bbox.x + bbox.width, bbox.y, matrix);
+    pnts.push(pnt);
+    pnt = _pnt_transform(bbox.x, bbox.y + bbox.height, matrix);
+    pnts.push(pnt);
+    pnt = _pnt_transform(bbox.x + bbox.width, bbox.y + bbox.height, matrix);
+    pnts.push(pnt);
+
+    min_x = max_x = pnts[0][0];
+    min_y = max_y = pnts[0][1];
+    for(i = 1; i < pnts.length; i++) {
+	pnt = pnts[i];
+	if(pnt[0] < min_x)
+	    min_x = pnt[0];
+	if(pnt[1] < min_y)
+	    min_y = pnt[1];
+	if(pnt[0] > max_x)
+	    max_x = pnt[0];
+	if(pnt[1] > max_y)
+	    max_y = pnt[1];
+    }
+
+    bbox.x = min_x;
+    bbox.y = min_y;
+    bbox.width = max_x - min_x;
+    bbox.height = max_y - min_y;
+}
+
+function _reverse(m1) {
+    var rev = new Array(1, 0, 0, 0, 1, 0);
+    var m = new Array(m1[0], m1[1], m1[2], m1[3], m1[4], m1[5]);
+
+    rev[3] = -m[3] / m[0];
+    m[3] = 0;
+    m[4] += rev[3] * m[1];
+    m[5] += rev[3] * m[2];
+    
+    rev[1] = -m[1] / m[4];
+    rev[0] += rev[1] * rev[3];
+    m[1] = 0;
+    m[2] += rev[1] * m[5];
+    
+    rev[2] = -m[2];
+    rev[5] = -m[5];
+    
+    rev[0] = rev[0] / m[0];
+    rev[1] = rev[1] / m[0];
+    rev[2] = rev[2] / m[0];
+    
+    rev[3] = rev[3] / m[4];
+    rev[4] = rev[4] / m[4];
+    rev[5] = rev[5] / m[4];
+
+    return rev;
+}
+
+var _bbox_proto = {
+    _get_ac_saved_rev: function() {
+	var c = this.owner;
+	var mtx;
+	
+	c = c.parent;
+	
+	mtx = c._mbapp_saved_rev_mtx;
+	while(c.parent && typeof c.parent != "undefined") {
+	    c = c.parent;
+	    mtx = _mul(mtx, c._mbapp_saved_rev_mtx);
+	}
+
+	return mtx;
+    },
+    
+    _get_ac_mtx: function() {
+	var c = this.owner;
+	var mtx;
+	
+	c = c.parent;
+
+	mtx = [c[0], c[1], c[2], c[3], c[4], c[5]];
+	while(c.parent) {
+	    c = c.parent;
+	    mtx = _mul(c, mtx);
+	}
+
+	return mtx;
+    },
+
+    _saved_to_current: function() {
+	var r;
+	
+	r = _mul(this._get_ac_mtx(), this._get_ac_saved_rev());
+	
+	return r;
+    },
+
+    /*! \brief Update x, y, width, and height of the bbox.
+     */
+    update: function() {
+	var mtx;
+
+	this.x = this.orig.x;
+	this.y = this.orig.y;
+	this.width = this.orig.width;
+	this.height = this.orig.height;
+	
+	mtx = this._saved_to_current();
+	_transform_bbox(this, mtx);
+    },    
+};
+
+var _center_proto = {
+    _get_ac_saved_rev: function() {
+	var c = this.owner;
+	var mtx;
+	
+	c = c.parent;
+	
+	mtx = c._mbapp_saved_rev_mtx;
+	while(c.parent && typeof c.parent != "undefined") {
+	    c = c.parent;
+	    mtx = _mul(mtx, c._mbapp_saved_rev_mtx);
+	}
+
+	return mtx;
+    },
+    
+    _get_ac_mtx: function() {
+	var c = this.owner;
+	var mtx;
+	
+	c = c.parent;
+
+	mtx = [c[0], c[1], c[2], c[3], c[4], c[5]];
+	while(c.parent) {
+	    c = c.parent;
+	    mtx = _mul(c, mtx);
+	}
+
+	return mtx;
+    },
+
+    _get_ac_rev: function() {
+	var c = this.owner;
+	var mtx;
+	
+	if(c.type != "coord")
+	    c = c.parent;	// is a shape!
+
+	mtx = _reverse([c[0], c[1], c[2], c[3], c[4], c[5]]);
+	while(c.parent) {
+	    c = c.parent;
+	    mtx = _mul(mtx, _reverse([c[0], c[1], c[2], c[3], c[4], c[5]]));
+	}
+
+	return mtx;
+    },
+
+    _saved_to_current: function() {
+	var r;
+	
+	r = _mul(this._get_ac_mtx(), this._get_ac_saved_rev());
+	
+	return r;
+    },
+
+    /*! \brief Update x, y of center point of an object.
+     */
+    update: function() {
+	var mtx;
+	var xy;
+
+	mtx = this._saved_to_current();
+	xy = _pnt_transform(this.orig.x, this.orig.y, mtx);
+
+	this._x = xy[0];
+	this._y = xy[1];
+    },
+
+    /*! \brief Move owner object to make center at (x, y).
+     */
+    move: function(x, y) {
+	var mtx;
+	var xdiff = x - this._x;
+	var ydiff = y - this._y;
+	var shiftxy;
+	var c;
+
+	mtx = this._get_ac_rev();
+	shiftxy = _shift_transform(xdiff, ydiff, mtx);
+
+	c = this.owner;
+	if(c.type != "coord")
+	    c = c.parent;
+
+	c[2] += shiftxy[0];
+	c[5] += shiftxy[1];
+
+	this._x = x;
+	this._y = y;
+    },
+
+    /*! \brief Move owner object to make center at position specified by pnt.
+     */
+    move_pnt: function(pnt) {
+	this.move(pnt.x, pnt.y);
+    },
+
+    /*! \brief Prevent user to modify value.
+     */
+    get x() { return this._x; },
+    
+    /*! \brief Prevent user to modify value.
+     */
+    get y() { return this._y; },
+
+    /*! \brief Center position in the relative space defined by parent.
+     */
+    get rel() {
+	var rev;
+	var xy;
+	
+	rev = this._get_ac_rev();
+	xy = _pnt_transform(this.orig.x, this.orig.y, rev);
+
+	return {x: xy[0], y: xy[1]};
+    },
+};
+
+loadSVG.prototype._set_bbox = function(node, tgt) {
+    var a;
+    var vstr;
+    var bbox, center;
+    var orig;
+    
+    a = node.attr("bbox-x");
+    if(!a)
+	return 0;
+    
+    /* bbox.orig is initial values of bbox.  The bbox is recomputed
+     * according bbox.orig and current matrix.  See bbox.update().
+     */
+    tgt.bbox = bbox = new Object();
+    bbox.orig = orig = new Object();
+    bbox.owner = tgt;
+    bbox.__proto__ = _bbox_proto;
+    vstr = a.value();
+    orig.x = parseFloat(vstr);
+
+    a = node.attr("bbox-y");
+    vstr = a.value();
+    orig.y = this.height - parseFloat(vstr);
+
+    a = node.attr("bbox-width");
+    vstr = a.value();
+    orig.width = parseFloat(vstr);
+
+    a = node.attr("bbox-height");
+    vstr = a.value();
+    orig.height = parseFloat(vstr);
+    orig.y -= orig.height;
+    
+    bbox.update();
+
+    /* center.orig is initial values of center.  The center is
+     * recomputed according center.orig and current matrix.  See
+     * center.update().
+     */
+    tgt.center = center = new Object();
+    center.orig = orig = new Object();
+    
+    orig.x = bbox.orig.width / 2 + bbox.orig.x;
+    orig.y = bbox.orig.height / 2 + bbox.orig.y;
+    a = node.attr("transform-center-x");
+    if(a) {
+	vstr = a.value();
+	orig.x += parseFloat(vstr);
+	a = node.attr("transform-center-y");
+	vstr = a.value();
+	orig.y -= parseFloat(vstr);
+    }
+    center.__proto__ = _center_proto;
+    center.owner = tgt;
+    center.update();
+    
+    return 1;
+}
+
+loadSVG.prototype._set_paint_style = function(style, tgt) {
+    var paint;
+    var fill_alpha = 1;
+    var stroke_alpha = 1;
+    var alpha = 1;
+    var fill_color;
+    var stroke_color;
+    var stroke_width = 1;
+    var i;
+    
+    if(style) {
+	if('opacity' in style)
+	    alpha = parseFloat(style['opacity']);
+	if('fill' in style)
+	    fill_color = style['fill'];
+	if('fill-opacity' in style)
+	    fill_alpha = parseFloat(style['fill-opacity']);
+	if('stroke' in style)
+	    stroke_color = style['stroke'];
+	if('stroke-width' in style)
+	    stroke_width = parseFloat(style['stroke-width']);
+	if('stroke-opacity' in style)
+	    stroke_alpha = parseFloat(style['stroke-opacity']);
+	if('display' in style && style['display'] == 'none')
+	    return;
+    }
+
+    if(fill_color) {
+	if(fill_color != "none") {
+	    paint = this._prepare_paint_color(fill_color, fill_alpha);
+	    paint.fill(tgt);
+	}
+    }
+    if(stroke_color) {
+	if(stroke_color != "none") {
+	    paint = this._prepare_paint_color(stroke_color, stroke_alpha);
+	    paint.stroke(tgt);
+	}
+    }
+
+    tgt.stroke_width = stroke_width;
+    
+    if(alpha < 1)
+	tgt.parent.opacity = alpha;
+};
+
+loadSVG.prototype._set_paint = function(node, tgt) {
+    var style;
+
+    style = parse_style(node);
+    this._set_paint_style(style, tgt);
+};
+
+function formalize_path_data(d) {
+    var posM, posm;
+    var pos;
+    var nums = "0123456789+-.";
+    var rel = false;
+    var cmd;
+
+    posM = d.search("M");
+    posm = d.search("m");
+    pos = posm < posM? posm: posM;
+    if(pos == -1)
+	pos = posM == -1? posm: posM;
+    if(pos == -1)
+	return d;
+
+    if(posm == pos)
+	rel = true;
+    
+    pos = pos + 1;
+    while(pos < d.length && " ,".search(d[pos]) >= 0)
+	pos++;
+    while(pos < d.length && nums.search(d[pos]) >= 0)
+	pos++;
+    while(pos < d.length && " ,".search(d[pos]) >= 0)
+	pos++;
+    while(pos < d.length && nums.search(d[pos]) >= 0)
+	pos++;
+    while(pos < d.length && " ,".search(d[pos]) >= 0)
+	pos++;
+    if(nums.search(d[pos]) >= 0) {
+	if(rel)
+	    cmd = "l";
+	else
+	    cmd = "L";
+	d = d.substring(0, pos) + cmd + formalize_path_data(d.substring(pos));
+    }
+    return d;
+}
+
+loadSVG.prototype.parsePath=function(accu, coord,id, n)
+{
+    var d = formalize_path_data(n.attr('d').value());
+    var style = n.attr('style');
+    var path = this.mb_rt.path_new(d);
+    var pcoord = this.mb_rt.coord_new(coord);
+
+    guessPathBoundingBox(pcoord,d);
+    pcoord.add_shape(path);
+    this._set_paint(n, path);
+    this._set_bbox(n, path);
+
+    make_mbnames(this.mb_rt, n, pcoord);
+};
+
+loadSVG.prototype.parseText=function(accu,coord,id, n)
+{
+    var x = getInteger(n,'x');
+    var y = getInteger(n,'y');
+    var tcoord = this.mb_rt.coord_new(coord);
+    var style;
+
+    if (n.attr('x')) {
+	var nx = coord[0]*x+coord[1]*y+coord[2];
+	if (coord.center.x > nx)
+	    coord.center.x = nx;
+    }
+    if (n.attr('y')) {
+	var ny = coord[3]*x+coord[4]*y+coord[5];
+	if (coord.center.y > ny)
+	    coord.center.y = ny;
+    }
+    style = parse_style(n);
+    var nodes = n.childNodes();
+    var k;
+    for(k in nodes) {
+	var c= nodes[k].name();
+	if (c == "tspan") {
+	    this.parseTSpan(tcoord,nodes[k],style);
+	} else {
+	}
+    }
+    this._set_bbox(n, tcoord);
+	
+    make_mbnames(this.mb_rt, n, tcoord);
+};
+
+
+function multiply(s,d) {
+    var m=[];
+    m[0] = s[0]*d[0]+s[1]*d[3];
+    m[1] = s[0]*d[1]+s[1]*d[4];
+    m[2] = s[0]*d[2]+s[1]*d[5]+s[2];
+    m[3] = s[3]*d[0]+s[4]*d[3];
+    m[4] = s[3]*d[1]+s[4]*d[4];
+    m[5] = s[3]*d[2]+s[4]*d[5]+s[5];
+    s[0] = m[0];
+    s[1] = m[1];
+    s[2] = m[2];
+    s[3] = m[3];
+    s[4] = m[4];
+    s[5] = m[5];
+};
+
+function parseTransform(coord, s)
+{
+    var off = s.indexOf('translate');
+    if (off != -1) {
+        var ss = s.substring(off+9);
+        for(i=0;i<ss.length;i++) {
+            if (ss[i] == '(') break;
+        }
+        ss = ss.substring(i+1);
+        for(i=0;i<ss.length;i++) {
+            if (ss[i] == ')') {
+                ss = ss.substring(0,i);
+                break;
+            }
+        }
+        var f = ss.split(',');
+        var x,y;
+        x = parseFloat(f[0]);
+        y = parseFloat(f[1]);
+        coord[2] += x;
+        coord[5] += y;
+    }
+    off = s.indexOf('matrix');
+    if (off != -1) {
+        var end = s.indexOf(')');
+        var m = s.substring(7,end);
+        var fields = m.split(',');
+        var newm=[];
+        newm[0] = parseFloat(fields[0]);
+        newm[1] = parseFloat(fields[2]);
+        newm[2] = parseFloat(fields[4]);
+        newm[3] = parseFloat(fields[1]);
+        newm[4] = parseFloat(fields[3]);
+        newm[5] = parseFloat(fields[5]);
+        multiply(coord,newm);
+    }
+}
+
+loadSVG.prototype.parseRect=function(accu_matrix,coord, id, n) 
+{
+    var x = getInteger(n,'x');
+    var y = getInteger(n,'y');
+    var rx,ry;
+    var w = getInteger(n,'width');
+    var h = getInteger(n,'height');
+    var trans = n.attr('transform');
+    var paint;
+    var tcoord = this.mb_rt.coord_new(coord);
+	
+    var style = n.attr('style');
+
+    if (trans)
+        parseTransform(tcoord,trans.value());
+
+    var rect = this.mb_rt.rect_new(x,y,w,h,10, 10);
+    tcoord.add_shape(rect);
+    this._set_paint(n, rect);
+    this._set_bbox(n, tcoord);
+
+    make_mbnames(this.mb_rt, n, tcoord);
+};
+
+// When we parse a group, we need to calculate the origin of the group
+// so that we can resize the group without changing its origin point.
+// This must be done recursively. For text/rect/image, we can get its
+// origin point directly by using the (x,y) and apply their
+// transformation matrix. For group, we need to send the acculumated
+// matrix so that each group can get their origin correctly.
+//
+// Each element must be responsible to calculate its absolute origin
+// point and update the origin of its parent.
+
+function parseGroupStyle(style,n)
+{
+    var attr;
+    if (n) {
+        attr = n.attr('style');
+    } else {
+        attr = null;
+    }
+    if (attr == null) {
+        return;
+    }
+    var f = attr.value().split(';');
+
+    for(i in f) {
+        var kv = f[i].split(':');
+        if (kv[0] == 'opacity') {
+            style.opacity = parseFloat(kv[1]);
+        } else {
+            sys.puts("Unknown style: "+kv[0]);
+        }
+    }
+}
+
+loadSVG.prototype.parseGroup=function(accu_matrix,root, group_id, n) {
+    var k;
+    var nodes = n.childNodes();
+    var coord = this.mb_rt.coord_new(root);
+    // Parse the transform and style here
+    var trans = n.attr('transform');
+    var accu=[1,0,0,0,1,0];
+    var style;
+
+    coord.center= new Object();
+    coord.center.x = 10000;
+    coord.center.y = 10000;
+    if (trans!=null) {
+	parseTransform(coord, trans.value());
+    } 
+    multiply(accu,accu_matrix);
+    multiply(accu,coord);
+
+    style = {};
+    parseGroupStyle(style, n);
+    if(style.opacity) {
+	sys.puts("opacity=" + style.opacity);
+	coord.opacity=style.opacity;
+    }
+
+    for(k in nodes) {
+	var c = nodes[k].name();
+	var attr = nodes[k].attr('id');
+	var id;
+	if (attr) {
+	    id = attr.value();
+	}
+	if (c == "g") {
+	    this.parseGroup(accu,coord, id, nodes[k]);
+	} else if (c == "path") {
+	    this.parsePath(accu,coord, id, nodes[k]);
+	} else if (c == "text") {
+	    this.parseText(accu,coord, id, nodes[k]);
+	} else if (c == "rect") {
+	    this.parseRect(accu_matrix,coord, id, nodes[k]);
+	} else if (c == "image") {
+	    this.parseImage(accu_matrix,coord, id, nodes[k]);
+	}
+    }
+    if (root.center.x > coord.center.x)
+	root.center.x = coord.center.x;
+    if (root.center.y > coord.center.y)
+	root.center.y = coord.center.y;
+
+    this._set_bbox(n, coord);
+    
+    make_mbnames(this.mb_rt, n, coord);
+};
+
+loadSVG.prototype.parseImage=function(accu,coord,id, n)
+{
+    var ref = n.attr('href').value();
+    var tcoord = this.mb_rt.coord_new(coord);
+    var trans = n.attr('transform');
+
+    if (ref == null) return;
+    if (ref.substr(0,7) == "file://") {
+	ref = ref.substring(7);
+    } else if (ref.substr(0,5)=="file:") {
+	ref = ref.substring(5);
+    } else {
+    }
+    var w;
+    var h;
+    var x,y,nx,ny;
+    coord.center= new Object();
+    coord.center.x = 10000;
+    coord.center.y = 10000;
+    if (trans!=null) {
+	parseTransform(coord, trans.value());
+    } 
+
+    w = n.attr("width");
+    if (w == null) return;
+    w = parseFloat(w.value());
+    h = n.attr("height");
+    if (h == null) return;
+    h = parseFloat(h.value());	
+    x = n.attr("x");
+    if (x == null) return;
+    x = parseFloat(x.value());
+    y = n.attr("y");
+    if (y == null) return;
+    y = parseFloat(y.value());
+    nx = tcoord[0]*x+tcoord[1]*y+tcoord[2];
+    ny = tcoord[3]*x+tcoord[4]*y+tcoord[5];
+    if (coord.center.x > nx) 
+	coord.center.x = nx;
+    if (coord.center.y > ny)
+	coord.center.y = ny;
+    var img = this.mb_rt.image_new(x,y,w,h);
+    var img_data = ldr.load(ref);
+    var paint = this.mb_rt.paint_image_new(img_data);
+    paint.fill(img);
+    tcoord.add_shape(img);
+    
+    this._set_bbox(n, img);
+    
+    make_mbnames(this.mb_rt, n, img);
+};
+
+function _parse_stops(n) {
+    var children;
+    var child;
+    var style;
+    var color;
+    var rgb;
+    var opacity;
+    var r, g, b, a;
+    var offset_atr, offset;
+    var stops = [];
+    var i;
+
+    children = n.childNodes();
+    for(i = 0; i < children.length; i++) {
+	child = children[i];
+	if(child.name() == "stop") {
+	    style = parse_style(child);
+	    
+	    color = style["stop-color"];
+	    if(color) {
+		rgb = parse_color(color);
+		r = rgb[0];
+		g = rgb[1];
+		b = rgb[2];
+	    }
+	    
+	    opacity = style["stop-opacity"];
+	    if(opacity)
+		a = parseFloat(opacity);
+	    else
+		a = 1;
+	    
+	    offset_attr = child.attr("offset");
+	    if(offset_attr)
+		offset = parseFloat(offset_attr.value());
+	    else
+		offset = 0;
+	    
+	    stops.push([offset, r, g, b, a]);
+	}
+    }
+
+    return stops;
+};
+
+loadSVG.prototype._MB_parseLinearGradient=function(root,n)
+{
+    var id = n.attr('id');
+    var k;
+    var nodes = n.childNodes();
+    var mtx = [1, 0, 0, 0, 1, 0];
+
+    if (id == null) return;
+    id = id.value();
+
+    var x1 = n.attr("x1");
+    var y1 = n.attr("y1");
+    var x2 = n.attr("x2");
+    var y2 = n.attr("y2");
+    var xy;
+    var gr;
+    var color, opacity;
+    var stops;
+    var r,g,b;
+    
+    if(x1)
+	x1 = parseFloat(x1.value());
+    if(x2)
+	x2 = parseFloat(x2.value());
+    if(y1)
+	y1 = parseFloat(y1.value());
+    if(y2)
+	y2 = parseFloat(y2.value());
+    
+    stops = _parse_stops(n);
+    
+    var href = n.attr('href');
+    if(href != null) {
+	href = href.value();
+	var hrefid = href.substring(1);
+	pstops = this.stop_ref[hrefid];
+	if (pstops)
+	    stops = pstops.concat(stops);
+	
+	var hrefgr = this.gradients[hrefid];
+	if(typeof x1 == "undefined")
+	    x1 = hrefgr[0];
+	if(typeof y1 == "undefined")
+	    y1 = hrefgr[1];
+	if(typeof x2 == "undefined")
+	    x2 = hrefgr[2];
+	if(typeof y2 == "undefined")
+	    y2 = hrefgr[3];
+    }
+
+    if(n.attr("gradientTransform")) {
+	parseTransform(mtx, n.attr("gradientTransform").value());
+	xy = _pnt_transform(x1, y1, mtx);
+	x1 = xy[0];
+	y1 = xy[1];
+	xy = _pnt_transform(x2, y2, mtx);
+	x2 = xy[0];
+	y2 = xy[1];
+    }
+
+    this.stop_ref[id] = stops;
+    this.gradients[id] = [x1,y1,x2,y2];
+};
+
+loadSVG.prototype._MB_parseRadialGradient = function(root,n) {
+    var stops;
+    var cx, cy;
+    var xy;
+    var mtx = [1, 0, 0, 0, 1, 0];
+    var id;
+    var href;
+    var r;
+
+    id = n.attr("id");
+    if(!id)
+	throw "Require an id";
+    id = id.value();
+
+    stops = _parse_stops(n);
+    
+    cx = n.attr("cx");
+    if(!cx)
+	throw "Miss cx attribute";
+    cy = n.attr("cy");
+    if(!cy)
+	throw "Miss cy attribute";
+    cx = parseFloat(cx.value());
+    cy = parseFloat(cy.value());
+
+    r = n.attr("r");
+    if(!r)
+	throw "Miss r attribute";
+    r = parseFloat(r.value());
+
+    href = n.attr("href");
+    if(href) {
+	href = href.value().substring(1);
+	stops = this.stop_ref[href];
+    }
+
+    if(n.attr("gradientTransform")) {
+	parseTransform(mtx, n.attr("gradientTransform").value());
+	xy = _pnt_transform(cx, cy, mtx);
+	cx = xy[0];
+	cy = xy[1];
+    }
+
+    this.radials[id] = [cx, cy, r];
+    this.stop_ref[id] = stops;
+}
+
+loadSVG.prototype.parseScenes=function(coord,node) {
+    var nodes = node.childNodes();
+
+    for(k in nodes) {
+        var name = nodes[k].name();
+	if (name == 'scene') {
+	    var node = nodes[k];
+
+	    scene = new Object();
+	    scene.start = parseInt(node.attr('start').value());
+	    try { 
+	        scene.end = parseInt(node.attr('end').value());
+	    } catch(e) {
+	        scene.end = scene.start;
+	    }
+	    scene.ref = node.attr('ref').value();
+
+	    try {
+	        this.scenenames[node.attr('name').value()] = scene.start;
+	    } catch(e) {
+	    }
+	    this.scenes.push(scene);
+	}
+    }
+}
+
+loadSVG.prototype.parseMetadata=function(coord,node) {
+    var nodes = node.childNodes();
+
+    for(k in nodes) {
+        var name = nodes[k].name();
+	if (name == 'scenes') {
+	    this.scenes=[];
+	    this.scenenames={};
+	    this.parseScenes(coord,nodes[k]);
+	}
+    }
+}
+loadSVG.prototype.parseDefs=function(root,n)
+{
+    var k;
+    var nodes = n.childNodes();
+    
+    for(k in nodes) {
+	var name = nodes[k].name();
+	if (name == "linearGradient") {
+	    this._MB_parseLinearGradient(root,nodes[k]);
+	} else if(name == "radialGradient") {
+	    this._MB_parseRadialGradient(root,nodes[k]);
+	}
+    }
+};
+
+
--- a/nodejs/testcase.js	Mon Jul 19 15:44:49 2010 +0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-var mbfly = require("mbfly");
-var r = mbfly.Hello(" test");
-var sys = require("sys");
-sys.puts(r);
-
-var mb_rt = new mbfly.mb_rt(":0.0", 300, 200);
-var root = mb_rt.root;
-sys.puts("root matrix: " +
-	 [root[0], root[1], root[2], root[3], root[4], root[5]]);
-var coord = mb_rt.coord_new(root);
-sys.puts("coord matrix: " + 
-	 [coord[0], coord[1], coord[2], coord[3], coord[4], coord[5]]);
-
-sys.puts(mb_rt.path_new);
-var path = mb_rt.path_new("m 100,50 L 120,50 L 200,150 L 180,150 z");
-sys.puts(path);
-sys.puts(coord.add_shape);
-coord.add_shape(path);
-
-sys.puts(mb_rt.paint_color_new);
-var paint = mb_rt.paint_color_new(1, 1, 1, 1);
-sys.puts(paint);
-paint.stroke(path);
-
-sys.puts(path.stroke_width);
-path.stroke_width = 2;
-sys.puts(path.stroke_width);
-
-var face = mb_rt.font_face_query("courier", 2, 100);
-var blks = [[5, face, 20]];
-var stext = mb_rt.stext_new("Hello", 100, 50);
-stext.set_style(blks);
-paint.fill(stext);
-coord.add_shape(stext);
-
-mb_rt.redraw_all();
-
-var i = 0;
-setInterval(function() {
-	var deg = (i++) * 0.1;
-	coord[2] = (i % 20) * 10;
-	mb_rt.redraw_changed();
-    }, 50);
-setTimeout(function() { sys.puts("timeout"); }, 1000);
--- a/nodejs/wscript	Mon Jul 19 15:44:49 2010 +0800
+++ b/nodejs/wscript	Wed Dec 01 12:25:56 2010 +0800
@@ -15,19 +15,33 @@
     conf.check_tool('compiler_cc')
     conf.check_tool('node_addon')
     conf.env.SRCDIR = Options.options.srcdir
-    conf.env.TOP_BUILDDIR = os.environ['TOP_BUILDDIR']    
+    conf.env.TOP_BUILDDIR = os.environ['TOP_BUILDDIR']
     pass
 
 def build(conf):
     import Utils
     
+    for m in 'observer coord mbfly_njs shapes paints'.split():
+        conf(rule='m4 -I ${SRCDIR}/../tools gen_v8_binding.m4 ${SRC} > ${TGT}',
+             source=m+'.m4', target=m+'-inc.h',
+             name=m+'-inc', shell=True, always=True, before=['cxx'])
+        pass
+
     obj = conf.new_task_gen('cxx', 'shlib', 'node_addon')
     obj.target = 'mbfly'
-    obj.source = 'mbfly_njs.cc coord.cc shapes.cc paints.cc font.cc'
-    obj.add_objects = 'X_supp_njs.o'
+    obj.source = 'font.cc image_ldr.cc'
+    obj.add_objects = 'njs_mb_supp.o observer.o coord.o mbfly_njs.o ' + \
+        'shapes.o paints.o'
     obj.staticlib = 'mbfly'
+
+    for src in 'observer.cc coord.cc mbfly_njs.cc shapes.cc paints.cc'.split():
+        obj = conf.new_task_gen('cxx', 'shlib', 'node_addon')
+        obj.target = src[:-3] + '.o'
+        obj.source = src
+        obj.includes = '.'
+        pass
     
     obj = conf.new_task_gen('cc', 'shlib', 'node_addon')
-    obj.target = 'X_supp_njs.o'
-    obj.source = 'X_supp_njs.c'
+    obj.target = 'njs_mb_supp.o'
+    obj.source = 'njs_mb_supp.c'
     pass
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pyink/MBScene.py	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,598 @@
+#!/usr/bin/python
+# -*- indent-tabs-mode: t; tab-width: 8; python-indent: 4; -*-
+# vim: sw=4:ts=8:sts=4
+import pygtk
+import gtk
+import glib
+from copy import deepcopy
+from lxml import etree
+import random
+import traceback
+import time
+import pybInkscape
+
+# Please refer to
+# http://www.assembla.com/wiki/show/MadButterfly/Inkscape_extention
+# for the designed document.
+
+
+# Algorithm:
+# 
+# We will parse the first two level of the SVG DOM. collect a table of
+# layer and scene.
+# - 1. Collect the layer table which will be displayed as the first
+#      column of the grid.
+# - 2. Get the maximum scene number. This will decide the size of the
+#      grid.
+# - 3. When F6 is pressed, we will check if this scene has been
+#      defined. This can be done by scan all second level group and
+#      check if the current scene number is within the range specified
+#      by scene field. The function IsSceneDefined(scene) can be used
+#      for this purpose.
+# - 4. If this is a new scene, we will append a new group which
+#      duplication the content of the last scene in the same
+#      group. The scene field will contain the number from the last
+#      scene number of the last scene to the current scenen
+#      number. For example, if the last scene is from 4-7 and the new
+#      scene is 10, we will set the scene field as "8-10".
+# - 5. If this scene are filled screne, we will split the existing
+#       scene into two scenes with the same content.
+#
+
+class Layer:
+    def __init__(self,node):
+	self.scenes = []
+	self.node = node
+	self.nodes=[]
+	pass
+    pass
+
+class Scene:
+    def __init__(self, node, start,end):
+	self.node = node
+	self.start = int(start)
+	self.end = int(end)
+	pass
+    pass
+
+_scenes = '{http://madbutterfly.sourceforge.net/DTD/madbutterfly.dtd}scenes'
+_scene = '{http://madbutterfly.sourceforge.net/DTD/madbutterfly.dtd}scene'
+class LayerAttributeWatcher(pybInkscape.PYNodeObserver):
+    def __init__(self,ui):
+        self.ui = ui
+    def notifyChildAdded(self,node,child,prev):
+        pass
+    def notifyChildRemoved(self,node,child,prev):
+        pass
+    def notifyChildOrderChanged(self,node,child,prev):
+        pass
+    def notifyContentChanged(self,node,old_content,new_content):
+        pass
+    def notifyAttributeChanged(self,node, name, old_value, new_value):
+        self.ui.updateUI()
+class LayerAddRemoveWatcher(pybInkscape.PYNodeObserver):
+    def __init__(self,ui):
+        self.ui = ui
+    def notifyChildAdded(self,node,child,prev):
+        self.ui.updateUI()
+    def notifyChildRemoved(self,node,child,prev):
+        self.ui.updateUI()
+    def notifyChildOrderChanged(self,node,child,prev):
+        self.ui.updateUI()
+    def notifyContentChanged(self,node,old_content,new_content):
+        self.ui.updateUI()
+    def notifyAttributeChanged(self,node, name, old_value, new_value):
+        self.ui.updateUI()
+class MBScene():
+    def __init__(self,desktop,win):
+	self.desktop = desktop
+	self.window = win
+	self.layers = []
+	self.layers.append(Layer(None))
+	self.scenemap = None
+	self.top = None
+	self.last_update = None
+	pass
+
+    def confirm(self,msg):
+	vbox = gtk.VBox()
+	vbox.pack_start(gtk.Label(msg))
+	self.button = gtk.Button('OK')
+	vbox.pack_start(self.button)
+	self.button.connect("clicked", self.onQuit)
+	self.window.add(vbox)
+	pass
+    
+    def dumpattr(self,n):
+	s = ""
+	for a,v in n.attrib.items():
+	    s = s + ("%s=%s"  % (a,v))
+	    pass
+	return s
+	
+    def dump(self,node,l=0):
+	print " " * l*2,"<", node.tag, self.dumpattr(node),">"
+	for n in node:
+	    self.dump(n,l+1)
+	    pass
+	print " " * l * 2,"/>"
+	pass
+    
+    def parseMetadata(self,node):
+	self.current = 1
+	for n in node.childList():
+	    if n.repr.name() == 'ns0:scenes':
+		self.scenemap={}
+		try:
+		    cur = int(n.repr.attribute("current"))
+		except:
+		    cur = 1
+		self.current = cur
+
+		for s in n.childList():
+		    if s.repr.name() == 'ns0:scene':
+			try:
+			    start = int(s.repr.attribute("start"))
+			except:
+			    traceback.print_exc()
+			    continue
+			try:
+			    end = s.repr.attribute("end")
+			    if end == None:
+				end = start
+				pass
+			except:
+			    end = start
+			    pass
+			link = s.repr.attribute("ref")
+			self.scenemap[link] = [int(start),int(end)]
+			print "scene %d to %d" % (self.scenemap[link][0],
+						  self.scenemap[link][1])
+			if cur >= start and cur <= end:
+			    self.currentscene = link
+			    pass
+			pass
+		    pass
+		pass
+	    pass
+	pass
+	if self.scenemap==None:
+	    self.desktop.doc().root().repr.setAttribute("xmlns:ns0","http://madbutterfly.sourceforge.net/DTD/madbutterfly.dtd",True)
+	    scenes = self.desktop.doc().rdoc.createElement("ns0:scenes")
+	    node.repr.appendChild(scenes)
+    def update(self):
+        doc = self.desktop.doc().root()
+	rdoc = self.desktop.doc().rdoc
+	for node in doc.childList():
+	    if node.repr.name() == 'svg:metadata':
+	        for t in node.childList():
+		    if t.repr.name() == "ns0:scenes":
+		        node.repr.removeChild(t.repr)
+			ns = rdoc.createElement("ns0:scenes")
+			node.repr.appendChild(ns)
+			for layer in range(0,len(self._framelines)):
+			    lobj = self._framelines[layer]
+			    lobj.addScenes(rdoc,ns)
+    
+    
+    def parseScene(self):
+	"""
+	In this function, we will collect all items for the current
+	scene and then relocate them back to the appropriate scene
+	object.
+	"""
+	self.layers = []
+	self.scenemap = None
+	doc = self.desktop.doc().root()
+
+        #obs = pybInkscape.PYNodeObserver()
+        obs = LayerAddRemoveWatcher(self)
+        doc.repr.addObserver(obs)
+	for node in doc.childList():
+	    if node.repr.name() == 'svg:metadata':
+		self.parseMetadata(node)
+		pass
+	    elif node.repr.name() == 'svg:g':
+		oldscene = None
+	        obs = LayerAttributeWatcher(self)
+	        node.repr.addObserver(obs)
+		lyobj = Layer(node)
+		self.layers.append(lyobj)
+		lyobj.current_scene = []
+		for scene in node.childList():
+		    if scene.repr.name() == 'svg:g':
+			try:
+			    scmap = self.scenemap[scene.getId()]
+			    if scmap == None:
+				lyobj.current_scene.append(scene)
+				continue
+			except:
+			    lyobj.current_scene.append(scene)
+			    continue
+
+			lyobj.scenes.append(Scene(scene,scmap[0],scmap[1]))
+			pass
+		    else:
+			lyobj.current_scene.append(scene)
+			pass
+		    pass
+		pass
+	    pass
+
+
+	self.collectID()
+	self.dumpID()
+	pass
+
+    def collectID(self):
+	self.ID = {}
+	root = self.desktop.doc().root()
+	for n in root.childList():
+	    self.collectID_recursive(n)
+	    pass
+	pass
+    
+    def collectID_recursive(self,node):
+	self.ID[node.getId()] = 1
+	for n in node.childList():
+	    self.collectID_recursive(n)
+	    pass
+	pass
+    
+    def newID(self):
+	while True:
+	    n = 's%d' % int(random.random()*10000)
+			#print "try %s" % n
+	    if self.ID.has_key(n) == False:
+		return n
+	    pass
+	pass
+    
+    def dumpID(self):
+	for a,v in self.ID.items():
+	    pass
+	pass
+    
+    def getLayer(self, layer):
+	for l in self.layers:
+	    if l.node.getId() == layer:
+		return l
+	    pass
+	return None
+    
+    
+    def insertKeyScene(self):
+	"""
+	Insert a new key scene into the stage. If the nth is always a
+	key scene, we will return without changing anything.  If the
+	nth is a filled scene, we will break the original scene into
+	two parts. If the nth is out of any scene, we will append a
+	new scene.
+
+	"""
+	x = self.last_frame
+	y = self.last_line
+	rdoc = self.desktop.doc().rdoc
+	ns = rdoc.createElement("svg:g")
+	txt = rdoc.createElement("svg:rect")
+	txt.setAttribute("x","0",True)
+	txt.setAttribute("y","0",True)
+	txt.setAttribute("width","100",True)
+	txt.setAttribute("height","100",True)
+	txt.setAttribute("style","fill:#ff00",True)
+	ns.appendChild(txt)
+	gid = self.last_line.node.label()+self.newID()
+	self.ID[gid]=1
+	ns.setAttribute("id",gid,True)
+	ns.setAttribute("inkscape:groupmode","layer",True)
+	self.last_line.node.repr.appendChild(ns)
+	print 'Add key ', x
+	self.last_line.add_keyframe(x,ns)
+	self.update()
+	self.last_line.update()
+    
+
+    def removeKeyScene(self):
+	nth = self.last_frame
+	y = self.last_line
+	rdoc = self.desktop.doc().rdoc
+	i = 0
+	layer = self.last_line
+	while i < len(layer._keys):
+	    s = layer._keys[i]
+	    print "nth:%d idx %d" % (nth,s.idx)
+	    if nth > s.idx:
+	        if i == len(layer._keys)-1:
+	            return
+	    if nth == s.idx:
+	        if s.left_tween:
+		    # This is left tween, we move the keyframe one frame ahead
+		    if s.idx == layer._keys[i-1].idx:
+			layer._keys[i].ref.parent().removeChild(layer._keys[i].ref)
+	                self.last_line.rm_keyframe(nth)
+	                self.last_line.rm_keyframe(nth-1)
+		    else:
+		        s.idx = s.idx-1
+		else:
+		    layer._keys[i].ref.parent().removeChild(layer._keys[i].ref)
+		    if s.right_tween:
+		        self.last_line.rm_keyframe(layer._keys[i+1].idx)
+	                self.last_line.rm_keyframe(nth)
+		    else:
+	                self.last_line.rm_keyframe(nth)
+
+		self.update()
+		self.last_line._draw_all_frames()
+	        self.last_line.update()
+		return
+	    i = i + 1
+    def extendScene(self):
+	nth = self.last_frame
+	layer = self.last_line
+	i = 0
+	while i < len(layer._keys):
+	    s = layer._keys[i]
+	    if s.right_tween:
+	        if nth > s.idx:
+		    if nth <= layer._keys[i+1].idx:
+		        return
+		    try:
+		        if nth <= layer._keys[i+2].idx:
+			    layer._keys[i+1].idx = nth
+			    layer.draw_all_frames()
+			    self.update()
+			    self.setCurrentScene(nth)
+			    self.last_line.update()
+			    return
+			else:
+			    # We may in the next scene
+			    i = i + 2
+			    pass
+		    except:
+		        # This is the last keyframe, extend the keyframe by 
+			# relocate the location of the keyframe
+			layer._keys[i+1].idx = nth
+			layer._draw_all_frames()
+			self.update()
+			self.last_line.update()
+			self.setCurrentScene(nth)
+			return
+		else:
+		    # We are in the front of all keyframes
+		    return
+	    else:
+		# This is a single keyframe
+		if nth < s.idx:
+		    return
+		if nth == s.idx:
+		    return
+		try:
+		    if nth < layer._keys[i+1].idx:
+			# We are after a single keyframe and no scene is 
+			# available here. Create a new tween here
+			idx = layer._keys[i].idx
+			layer.add_keyframe(nth,layer._keys[i].ref)
+			layer.tween(idx)
+		        layer._draw_all_frames()
+			self.update()
+			self.setCurrentScene(nth)
+			self.last_line.update()
+			return
+		    else:
+			# We may in the next scene
+			i = i + 1
+			pass
+		    pass
+		except:
+		    # This is the last scene, create a new one
+		    idx = layer._keys[i].idx
+		    layer.add_keyframe(nth,layer._keys[i].ref)
+		    layer.tween(idx)
+		    layer._draw_all_frames()
+		    self.update()
+		    self.setCurrentScene(nth)
+		    self.last_line.update()
+		    return
+		pass
+	    pass
+	pass
+
+    
+    def setCurrentScene(self,nth):
+	self.current = nth
+	for layer in self._framelines:
+	    i=0
+	    while i < len(layer._keys):
+	        s = layer._keys[i]
+		print s.ref.attribute("id"),s.idx,s.left_tween,s.right_tween
+		if s.right_tween is False:
+		    if nth == s.idx+1:
+		        s.ref.setAttribute("style","",True)
+		    else:
+		        s.ref.setAttribute("style","display:none",True)
+		    i = i + 1
+		    continue
+
+		if nth >= (s.idx+1) and nth <= (layer._keys[i+1].idx+1):
+		    s.ref.setAttribute("style","",True)
+		else:
+		    s.ref.setAttribute("style","display:none",True)
+		i = i + 2
+		pass
+	    pass
+	pass
+	
+	
+    def newCell(self,file):
+	img = gtk.Image()
+	img.set_from_file(file)
+	btn = gtk.EventBox()
+	btn.add(img)
+	btn.connect("button_press_event", self.cellSelect)
+	btn.modify_bg(gtk.STATE_NORMAL, btn.get_colormap().alloc_color("gray"))
+	return btn
+    
+    def onCellClick(self,line,frame,but):
+	self.last_line = line
+	self.last_frame = frame
+	self.last_line.active_frame(frame)
+        self.doEditScene(frame)
+        
+        
+    def _remove_active_frame(self,widget,event):
+        """
+	Hide all hover frames. This is a hack. We should use the lost focus event 
+	instead in the future to reduce the overhead.
+	"""
+        for f in self._framelines:
+	    if f != widget:
+	        f.hide_hover()
+	    
+    def _create_framelines(self):
+	import frameline
+	self.scrollwin = gtk.ScrolledWindow()
+	self.scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+	self.scrollwin.set_size_request(-1,150)
+	
+	nframes = 100
+	
+	vbox = gtk.VBox()
+	vbox.show()
+	self.scrollwin.add_with_viewport(vbox)
+	
+	ruler = frameline.frameruler(nframes)
+	ruler.set_size_request(nframes * 10, 20)
+	ruler.show()
+	hbox = gtk.HBox()
+	label=gtk.Label('')
+	label.set_size_request(100,0)
+	hbox.pack_start(label,expand=False,fill=True)
+	hbox.pack_start(ruler)
+	vbox.pack_start(hbox, False)
+
+	#
+	# Add a frameline for each layer
+	#
+	self._framelines = []
+	for i in range(len(self.layers)-1,-1,-1):
+	    line = frameline.frameline(nframes)
+	    hbox = gtk.HBox()
+	    label = gtk.Label(self.layers[i].node.label())
+	    label.set_size_request(100,0)
+	    hbox.pack_start(label,expand=False,fill=True)
+	    hbox.pack_start(line)
+	    line.set_size_request(nframes * 10, 20)
+	    vbox.pack_start(hbox, False)
+	    line.label = label
+	    self._framelines.append(line)
+	    line.connect(line.FRAME_BUT_PRESS, self.onCellClick)
+	    line.nLayer = i
+	    line.node = self.layers[i].node
+	    line.layer = self.layers[i]
+	    line.connect('motion-notify-event', self._remove_active_frame)
+	    pass
+	pass
+
+    ## \brief Update conetent of frameliens according layers.
+    #
+    def _update_framelines(self):
+	for frameline in self._framelines:
+	    layer = frameline.layer
+	    if frameline.node.label()==None:
+	        frameline.label.set_text('???')
+	    else:
+	        frameline.label.set_text(frameline.node.label())
+	    for scene in layer.scenes:
+		frameline.add_keyframe(scene.start-1,scene.node.repr)
+		if scene.start != scene.end:
+		    frameline.add_keyframe(scene.end-1,scene.node.repr)
+		    frameline.tween(scene.start-1)
+		pass
+	    pass
+	pass
+
+    def cellSelect(self, cell, data):
+	if self.last_cell:
+	    color = self.last_cell.get_colormap().alloc_color("gray")
+	    self.last_cell.modify_bg(gtk.STATE_NORMAL, color)
+	    pass
+	
+	self.last_cell = cell
+	color = cell.get_colormap().alloc_color("green")
+	cell.modify_bg(gtk.STATE_NORMAL, color)
+	pass
+    
+    def doEditScene(self,w):
+	self.setCurrentScene(self.last_frame+1)
+	pass
+    
+    def doInsertKeyScene(self,w):
+	self.insertKeyScene()
+	# self.grid.show_all()
+	return
+
+    def doRemoveScene(self,w):
+	self.removeKeyScene()
+	return
+
+    
+    def doExtendScene(self,w):
+	self.extendScene()
+	#self.grid.show_all()
+	pass
+    
+    def addButtons(self,hbox):
+	#btn = gtk.Button('Edit')
+	#btn.connect('clicked', self.doEditScene)
+	#hbox.pack_start(btn,expand=False,fill=False)
+	btn = gtk.Button('Insert Key')
+	btn.connect('clicked',self.doInsertKeyScene)
+	hbox.pack_start(btn,expand=False,fill=False)
+	btn=gtk.Button('Remove Key')
+	btn.connect('clicked', self.doRemoveScene)
+	hbox.pack_start(btn,expand=False,fill=False)
+	btn=gtk.Button('Extend scene')
+	btn.connect('clicked', self.doExtendScene)
+	hbox.pack_start(btn,expand=False,fill=False)
+	pass
+    
+    def onQuit(self, event):
+	self.OK = False
+	gtk.main_quit()
+	pass
+    
+    def onOK(self,event):
+	self.OK = True
+	gtk.main_quit()
+	pass
+
+    def updateUI(self):
+        if self.last_update!= None:
+            glib.source_remove(self.last_update)
+        self.last_update = glib.timeout_add(300,self.show)
+    def show(self):
+	self.OK = True
+	self.parseScene()
+	self._create_framelines()
+	self._update_framelines()
+	if self.top == None:
+	    self.top = gtk.VBox(False,0)
+	    self.desktop.getToplevel().child.child.pack_end(self.top,expand=False)
+	else:
+	    self.top.remove(self.startWindow)
+	vbox = gtk.VBox(False,0)
+	self.startWindow = vbox
+	self.top.pack_start(vbox,expand=False)
+	vbox.pack_start(self.scrollwin,expand=False)
+	hbox=gtk.HBox(False,0)
+	self.addButtons(hbox)
+	vbox.pack_start(hbox,expand=False)
+
+	# self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+	# self.window.connect("destroy", gtk.main_quit)
+	# self.window.set_position(gtk.WIN_POS_MOUSE)
+
+	self.top.show_all()
+	self.last_update = None
+	return False
+    pass
Binary file pyink/active-empty.png has changed
Binary file pyink/active-fill.png has changed
Binary file pyink/active-start.png has changed
Binary file pyink/empty.png has changed
Binary file pyink/fill.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pyink/frameline.py	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,689 @@
+import pygtk
+pygtk.require("2.0")
+import gtk
+import gtk.gdk
+import pango
+import gobject
+
+def color_to_rgb(v):
+    return (((v >> 16) & 0xff) * 65535 / 0xff,
+            ((v >> 8) & 0xff) * 65535 / 0xff,
+            (v & 0xff) * 65535 / 0xff)
+
+class keyframe(object):
+    def __init__(self, frame_idx):
+        self.idx = frame_idx
+        self.left_tween = False
+        self.right_tween = False
+        self.right_tween_type = 0
+	self.ref=''
+        pass
+    pass
+
+class frameruler(gtk.DrawingArea):
+    _type = 0
+    _frame_width = 10           # Width for each frame is 10 pixels
+    _mark_color = 0x808080      # color of mark lines
+    _number_color = 0x000000    # color of frame number
+    _number_sz = 8             # font size of frame number
+    
+    def __new__(clz, *args):
+        if not frameruler._type:
+            frameruler._type = gobject.type_register(frameruler)
+            pass
+        fr = gobject.new(frameruler._type)
+        return fr
+
+    def __init__(self, num_frames=20):
+        self.connect('expose_event', self._fr_expose)
+        self._num_frames = num_frames
+        pass
+
+    def _fr_expose(self, widget, event):
+        self.update()
+        pass
+
+    def queue_draw(self):
+        print 'queue_draw'
+        self.update()
+        pass
+
+    def queue_draw_area(self, x, y, w, h):
+        print 'queue_draw_area'
+        pass
+
+    def update(self):
+        win = self.window
+        w_x, w_y, w_w, w_h, depth = win.get_geometry()
+
+        gc = gtk.gdk.GC(win)
+
+        #
+        # Set color of mark lines
+        #
+        color_rgb = color_to_rgb(self._mark_color)
+        color = gtk.gdk.Color(*color_rgb)
+        gc.set_rgb_fg_color(color)
+        
+        #
+        # Mark mark lines
+        #
+        mark_h = w_h / 10
+        for i in range(self._num_frames):
+            mark_x = (i + 1) * self._frame_width
+            win.draw_line(gc, mark_x, 0, mark_x, mark_h)
+            win.draw_line(gc, mark_x, w_h - mark_h - 1, mark_x, w_h - 1)
+            pass
+
+        win.draw_line(gc, 0, w_h - 1, w_w, w_h -1)
+
+        #
+        # Set color of frame number
+        #
+        color_rgb = color_to_rgb(self._number_color)
+        color = gtk.gdk.Color(*color_rgb)
+        gc.set_rgb_fg_color(color)
+        
+        font_desc = pango.FontDescription()
+        font_desc.set_size(self._number_sz * pango.SCALE)
+
+        number_y = (w_h - self._number_sz) / 2
+        
+        #
+        # Draw frame number
+        #
+        layout = self.create_pango_layout('1')
+        layout.set_font_description(font_desc)
+        win.draw_layout(gc, 0, number_y, layout)
+        for i in range(4, self._num_frames, 5):
+            mark_x = i * self._frame_width
+            layout.set_text(str(i + 1))
+            win.draw_layout(gc, mark_x, number_y, layout)
+            pass
+        pass
+    pass
+
+## Show frame status of a layer
+#
+# \section frameline_sigs Signals
+# - 'frame-button-pree' for user press on a frame.
+#   - callback(widget, frame_idx, button)
+#
+class frameline(gtk.DrawingArea):
+    _type = 0
+    _frame_width = 10           # Width for each frame is 10 pixels
+    _select_color = 0xee2222    # color of border of selected frame
+    _key_mark_color = 0x000000  # color of marks for key frames.
+    _key_mark_sz = 4            # width and height of a key frame mark
+    _tween_color = 0x808080     # color of tween line
+    _tween_bgcolors = [0x80ff80, 0xff8080] # bg colors of tween frames
+    # Colors for normal frames
+    _normal_bgcolors = [0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xcccccc]
+    _normal_border = 0xaaaaaa   # border color of normal frames.
+    _active_border = 0xff3030   # border color of an active frame
+    _hover_border_color = 0xa0a0a0 # border when the pointer over a frame
+    # tween types
+    _tween_type_none=0
+    _tween_type_shape=3
+    
+
+    FRAME_BUT_PRESS = 'frame-button-press'
+    
+    def __new__(clz, *args):
+        if not frameline._type:
+            frameline._type = gobject.type_register(frameline)
+            but_press = gobject.signal_new(frameline.FRAME_BUT_PRESS,
+                                           frameline._type,
+                                           gobject.SIGNAL_RUN_FIRST,
+                                           gobject.TYPE_NONE,
+                                           (gobject.TYPE_INT,
+                                            gobject.TYPE_INT))
+            frameline._sig_frame_but_press = but_press
+            pass
+        fl_obj = gobject.new(frameline._type)
+        return fl_obj
+    
+    def __init__(self, num_frames=20):
+        self.connect('button-press-event', self._press_hdl)
+        self.connect('expose-event', self._fl_expose)
+        self.connect('motion-notify-event', self._motion_hdl)
+        self._num_frames = num_frames
+        self._keys = []
+        self._active_frame = -1
+        self._last_hover = -1   # frame index of last hover
+        self._drawing = False
+        pass
+
+    def _press_hdl(self, widget, event):
+        frame = event.x / self._frame_width
+        but = event.button
+        self.emit(frameline.FRAME_BUT_PRESS, frame, but)
+        pass
+    def hide_hover(self):
+        if self._active_frame != self._last_hover:
+            self._draw_normal_frame(self._last_hover)
+    
+    def _motion_hdl(self, widget, event):
+        frame = int(event.x / self._frame_width)
+	    
+        if frame < self._num_frames and frame >= 0:
+            self._draw_hover(frame)
+        pass
+
+    def _fl_expose(self, widget, event):
+        win = self.window
+        x, y, w, h, depth = win.get_geometry()
+        if not hasattr(self, '_gc'):
+            self._gc = gtk.gdk.GC(win)
+            #
+            # register for button press event
+            #
+            emask = win.get_events()
+            emask = emask | gtk.gdk.BUTTON_PRESS_MASK | \
+                gtk.gdk.POINTER_MOTION_MASK
+            win.set_events(emask)
+            self._drawing = True
+            pass
+        self.update()
+        pass
+
+    def _draw_tween(self, first_key, last_key):
+        if not self._drawing:
+            return
+        
+        win = self.window
+        w_x, w_y, w_w, w_h, depth = win.get_geometry()
+        
+        #
+        # Get background color of a tween
+        #
+        bg_idx = first_key.right_tween_type
+        bg_color_v = self._tween_bgcolors[bg_idx]
+        bg_color_rgb = color_to_rgb(bg_color_v)
+        bg_color = gtk.gdk.Color(*bg_color_rgb)
+        
+        gc = self._gc
+        gc.set_rgb_fg_color(bg_color)
+
+        draw_x = first_key.idx * self._frame_width + 1
+        draw_w = (last_key.idx -  first_key.idx + 1) * self._frame_width - 1
+
+        win.draw_rectangle(gc, True, draw_x, 0, draw_w, w_h)
+
+        #
+        # Set color of tween line
+        #
+        line_v = self._tween_color
+        line_rgb = color_to_rgb(line_v)
+        line_color = gtk.gdk.Color(*line_rgb)
+        gc.set_rgb_fg_color(line_color)
+        
+        #
+        # Draw tween line
+        #
+        line_x1 = int((first_key.idx + 0.5) * self._frame_width)
+        line_x2 = line_x1 + (last_key.idx - first_key.idx) * self._frame_width
+        line_y = int(w_h * 2 / 3)
+        win.draw_line(gc, line_x1, line_y, line_x2, line_y)
+        pass
+
+    def _draw_normal_frame(self, idx):
+        if not self._drawing:
+            return
+        
+        win = self.window
+        w_x, w_y, w_w, w_h, depth = win.get_geometry()
+        
+        gc = self._gc
+        bg_idx = idx % len(self._normal_bgcolors)
+        rgb = color_to_rgb(self._normal_bgcolors[bg_idx])
+        color = gtk.gdk.Color(*rgb)
+        gc.set_rgb_fg_color(color)
+        
+        f_x = self._frame_width * idx
+        win.draw_rectangle(gc, True, f_x + 1, 0, self._frame_width - 1, w_h)
+        next_f_x = f_x + self._frame_width
+        
+        border_rgb = color_to_rgb(self._normal_border)
+        border_color = gtk.gdk.Color(*border_rgb)
+        gc.set_rgb_fg_color(border_color)
+        gc.set_line_attributes(1, gtk.gdk.LINE_SOLID,
+                               gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER)
+        win.draw_line(gc, next_f_x, 0, next_f_x, w_h)
+        pass
+
+    ## \brief Draw a bottom line from start to the point before stop frame.
+    #
+    def _draw_bottom_line(self, start, stop):
+        if not self._drawing:
+            return
+        
+        win = self.window
+        w_x, w_y, w_w, w_h, depth = win.get_geometry()
+        gc = self._gc
+        
+        border_rgb = color_to_rgb(self._normal_border)
+        border_color = gtk.gdk.Color(*border_rgb)
+        gc.set_rgb_fg_color(border_color)
+        start_x = start * self._frame_width
+        stop_x = stop * self._frame_width
+        win.draw_line(gc, start_x, w_h - 1, stop_x, w_h - 1)
+        pass
+    
+    def _draw_all_frames(self):
+        if not self._drawing:
+            return
+        
+        win = self.window
+        w_x, w_y, w_w, w_h, depth = win.get_geometry()
+        gc = self._gc
+        
+        i = 0
+        key_i = 0
+        try:
+            key = self._keys[key_i]
+        except IndexError:
+            key = keyframe(self._num_frames)
+            pass
+        num_frames = self._num_frames
+        while i < num_frames:
+            if key.idx == i and key.right_tween:
+                #
+                # Skip tween keys
+                #
+                first_tween_key = key
+                while key.idx == i or key.left_tween:
+                    last_tween_key = key
+                    key_i = key_i + 1
+                    try:
+                        key = self._keys[key_i]
+                    except IndexError:
+                        key = keyframe(self._num_frames)
+                        pass
+                    pass
+
+                if first_tween_key != last_tween_key:
+                    self._draw_tween(first_tween_key, last_tween_key)
+                
+                i = last_tween_key.idx + 1
+                pass
+            else:
+	        if key.idx == i:
+		    key_i=key_i+1
+		    try:
+		        key = self._keys[key_i]
+		    except:
+		        key = keyframe(self._num_frames)
+                self._draw_normal_frame(i)
+                i = i + 1
+                pass
+            pass
+
+        self._draw_bottom_line(0, num_frames)
+        pass
+
+    def _draw_keyframe(self, frame_idx):
+        if not self._drawing:
+            return
+        
+        win = self.window
+        w_x, w_y, w_w, w_h, depth = win.get_geometry()
+        
+        color_v = self._key_mark_color
+        color_rgb = color_to_rgb(color_v)
+        color = gtk.gdk.Color(*color_rgb)
+        
+        gc = self._gc
+        gc.set_rgb_fg_color(color)
+        
+        mark_sz = self._key_mark_sz
+        mark_x = int((frame_idx + 0.5) * self._frame_width - mark_sz / 2)
+        mark_y = w_h * 2 / 3 - mark_sz / 2
+        
+        win.draw_rectangle(gc, True, mark_x, mark_y, mark_sz, mark_sz)
+        pass
+
+    def _draw_keyframes(self):
+        if not self._drawing:
+            return
+        
+        win = self.window
+        w_x, w_y, w_w, w_h, depth = win.get_geometry()
+        
+        color_v = self._key_mark_color
+        color_rgb = color_to_rgb(color_v)
+        color = gtk.gdk.Color(*color_rgb)
+        
+        gc = self._gc
+        gc.set_rgb_fg_color(color)
+        
+        for key in self._keys:
+	    if key.left_tween is True and lastkey.right_tween_type == frameline._tween_type_none:
+	        continue
+	        
+            mark_sz = self._key_mark_sz
+            mark_x = int((key.idx + 0.5) * self._frame_width - mark_sz / 2)
+            mark_y = w_h * 2 / 3 - mark_sz / 2
+
+            win.draw_rectangle(gc, True, mark_x, mark_y, mark_sz, mark_sz)
+	    lastkey = key
+            pass
+        pass
+
+    def _draw_active(self):
+        if not self._drawing:
+            return
+        
+        if self._active_frame == -1:
+            return
+        
+        win = self.window
+        w_x, w_y, w_w, w_h, depth = win.get_geometry()
+
+        color_v = self._active_border
+        color_rgb = color_to_rgb(color_v)
+        color = gtk.gdk.Color(*color_rgb)
+
+        gc = self._gc
+        gc.set_rgb_fg_color(color)
+        
+        idx = self._active_frame
+        line_x1 = idx * self._frame_width
+        line_x2 = line_x1 + self._frame_width
+
+        win.draw_line(gc, line_x1, 0, line_x1, w_h)
+        win.draw_line(gc, line_x2, 0, line_x2, w_h)
+        win.draw_line(gc, line_x1, w_h - 1, line_x2, w_h - 1)
+        win.draw_line(gc, line_x1, 0, line_x2, 0)
+        pass
+
+    ## \brief Find the range a continous tween.
+    #
+    def _find_tween_range(self, key_pos):
+        first_pos = key_pos
+        while first_pos and self._keys[first_pos].left_tween:
+            first_pos = first_pos - 1
+            pass
+        
+        max_pos = len(self._keys) - 1
+        
+        last_pos = key_pos
+        while last_pos < max_pos and self._keys[last_pos].right_tween:
+            last_pos = last_pos + 1
+            pass
+        
+        return first_pos, last_pos
+
+    ## \brief Redraw a frame specified by an index.
+    #
+    def _redraw_frame(self, frame_idx):
+        if not self._drawing:
+            return
+        
+        keys = [key.idx for key in self._keys]
+        if len(keys):
+            try:
+                pos = keys.index(frame_idx)
+            except ValueError:
+                keys.append(frame_idx)
+                keys.sort()
+                pos = keys.index(frame_idx) - 1
+                pass
+            if pos < 0:
+                pos = 0
+                pass
+            key = self._keys[pos]
+        else:
+            key = None
+            pass
+        
+        if key and (key.right_tween or \
+                (key.left_tween and key.idx == frame_idx)):
+            #
+            # in tween
+            #
+            first_pos, last_pos = self._find_tween_range(pos)
+            first_key = self._keys[first_pos]
+            last_key = self._keys[last_pos]
+            
+            self._draw_tween(first_key, last_key)
+            self._draw_bottom_line(first_key.idx, last_key.idx + 1)
+
+            for i in range(first_pos, last_pos + 1):
+                key = self._keys[i]
+		if key.left_tween is False or lastkey.right_tween_type != frameline._tween_type_none:
+                    self._draw_keyframe(key.idx)
+		lastkey = key
+                pass
+            pass
+        else:                   # not in tween
+            self._draw_normal_frame(frame_idx)
+            self._draw_bottom_line(frame_idx, frame_idx + 1)
+            if key and (key.idx == frame_idx):
+                self._draw_keyframe(frame_idx)
+                pass
+            pass
+        pass
+
+    ## \brief Show a mark for the pointer for a frame.
+    #
+    def _draw_hover(self, frame_idx):
+        if not self._drawing:
+            return
+        
+        if self._last_hover != -1:
+            self._redraw_frame(self._last_hover)
+            pass
+
+        self._draw_active()
+        
+        win = self.window
+        w_x, w_y, w_w, w_h, depth = win.get_geometry()
+        gc = self._gc
+        
+        color_rgb = color_to_rgb(self._hover_border_color)
+        color = gtk.gdk.Color(*color_rgb)
+        gc.set_rgb_fg_color(color)
+
+        line_x1 = frame_idx * self._frame_width + 1
+        line_x2 = line_x1 + self._frame_width - 2
+        
+        win.draw_line(gc, line_x1, 1, line_x1, w_h - 2)
+        win.draw_line(gc, line_x2, 1, line_x2, w_h - 2)
+        win.draw_line(gc, line_x1, 1, line_x2, 1)
+        win.draw_line(gc, line_x1, w_h - 2, line_x2, w_h - 2)
+
+        self._last_hover = frame_idx
+        pass
+    
+    def update(self):
+        if not self._drawing:
+            return
+        
+        win = self.window
+        x, y, w, h, depth = win.get_geometry()
+        self._draw_all_frames()
+        self._draw_keyframes()
+        if self._active_frame != -1:
+            self._draw_active()
+            pass
+        pass
+
+    ## Add a key frame
+    #
+    # A key frame is the frame that user specify actions.  For
+    # example, move a object or add new objects at the frame.
+    def add_keyframe(self, idx,ref=None):
+        key_indic = [key.idx for key in self._keys]
+        if idx in key_indic:
+            return
+
+        key_indic.append(idx)
+        key_indic.sort()
+        insert_pos = key_indic.index(idx)
+        
+        key = keyframe(idx)
+	key.ref = ref
+        self._keys[insert_pos:insert_pos] = [key]
+        if insert_pos > 0 and self._keys[insert_pos - 1].right_tween:
+            key.left_tween = True
+            pass
+        if insert_pos < (len(self._keys) - 1) and \
+                self._keys[insert_pos + 1].left_tween:
+            key.right_tween = True
+            pass
+
+        self._draw_keyframe(idx)
+        pass
+
+    def rm_keyframe(self, idx):
+        found=False
+	for i in range(0,len(self._keys)):
+	    if self._keys[i].idx == idx:
+	        idx = i
+		found = True
+		break
+	if not found: return
+        key = self._keys[idx]
+        
+        if key.right_tween ^ key.left_tween:
+            #
+            # tween in one side
+            #
+            if key.right_tween:
+                right_key = self._keys[idx]
+                right_key.left_tween = False
+                redraw_range = (right_key.idx, idx + 1)
+            else:
+                left_key = self._keys[idx - 1]
+                left_key.right_key = False
+                redraw_range = (idx, left_key.idx + 1)
+                pass
+            for i in range(*redraw_range):
+                self._redraw_frame(i)
+                pass
+        else:
+            self._redraw_frame(idx)
+            pass
+
+        del self._keys[idx]
+        self._draw_active()
+        pass
+
+    ## Tween the key frame specified by an index and the key frame at right.
+    #
+    # \see http://www.entheosweb.com/Flash/shape_tween.asp
+    def tween(self, idx, _type=0):
+        key_indic = [key.idx for key in self._keys]
+        pos = key_indic.index(idx)
+        key = self._keys[pos]
+        
+        try:
+            right_key = self._keys[pos + 1]
+        except IndexError:
+            raise ValueError, 'No right key frame'
+
+        key.right_tween = True
+        right_key.left_tween = True
+        key.right_tween_type = _type
+        pass
+
+    ## Set active frame
+    #
+    # The active frame is the frame that is working on.
+    #
+    def active_frame(self, idx):
+        if idx < 0 or idx >= self._num_frames:
+            raise IndexError, 'value of index (%d) is out of range' % (idx)
+
+        if self._active_frame != -1:
+            self._redraw_frame(self._active_frame)
+            pass
+        self._active_frame = idx
+        self._draw_active()
+        pass
+
+    def deactive(self):
+        self._redraw_frame(self._active_frame)
+        self._active_frame = -1
+        pass
+
+    def set_num_frames(self, num):
+        self._num_frames = num
+        pass
+
+    def reset(self):
+        self._keys = []
+        pass
+
+    def addScenes(self,rdoc,node):
+        for i in range(0,len(self._keys)):
+	    key = self._keys[i]
+	    if key.left_tween is True: continue
+	    if key.right_tween is True:
+	        ss = rdoc.createElement("ns0:scene")
+		node.appendChild(ss)
+		ss.setAttribute("start", str(key.idx+1),True)
+		ss.setAttribute("ref",key.ref.attribute("id"),True)
+		ss.setAttribute("end", str(self._keys[i+1].idx+1),True)
+	    else:
+	        ss = rdoc.createElement("ns0:scene")
+		node.appendChild(ss)
+		ss.setAttribute("start", str(key.idx+1),True)
+		ss.setAttribute("ref",key.ref.attribute("id"),True)
+
+	        
+    ## \brief Start future drawing actions
+    #
+    def start_drawing(self):
+        self._drawing = True
+        pass
+    
+    ## \brief Stop any future drawing actions
+    #
+    # When doing massive udpate, to stop drawing the screen make
+    # application more effecient.  The screen is updated by calling
+    # update() method after massive update and calliing start_drawing().
+    #
+    def stop_drawing(self):
+        self._drawing = False
+        pass
+
+    def __len__(self):
+        return self._num_frames
+    pass
+
+if __name__ == '__main__':
+    window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+    fr = frameruler(40)
+    fr.set_size_request(300, 20)
+    
+    fl = frameline(40)
+    fl.set_size_request(300, 20)
+    fl.add_keyframe(15)
+    fl.add_keyframe(3)
+    fl.tween(3)
+    fl.add_keyframe(9)
+    fl.add_keyframe(20)
+    fl.tween(9)
+    fl.active_frame(1)
+    fl.rm_keyframe(15)
+    print 'num of frames: %d' % (len(fl))
+
+    def press_sig(fl, frame, but):
+        print 'press_sig button %d for frame %d' % (but, frame)
+        pass
+    fl.connect(frameline.FRAME_BUT_PRESS, press_sig)
+
+    box = gtk.VBox()
+
+    box.pack_start(fr, False)
+    box.pack_start(fl, False)
+    window.add(box)
+    
+    fr.show()
+    fl.show()
+    box.show()
+    window.show()
+    gtk.main()
+    pass
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pyink/mbtest.svg	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,374 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:ns0="http://madbutterfly.sourceforge.net/DTD/madbutterfly.dtd"
+   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:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="640px"
+   height="480px"
+   id="svg2383"
+   sodipodi:version="0.32"
+   inkscape:version="0.48+devel r9764 custom"
+   sodipodi:docname="mbtest.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.1">
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.6029106"
+     inkscape:cx="69.206233"
+     inkscape:cy="290.40921"
+     inkscape:current-layer="layer2"
+     inkscape:document-units="px"
+     showgrid="false"
+     inkscape:window-width="1400"
+     inkscape:window-height="974"
+     inkscape:window-x="271"
+     inkscape:window-y="25"
+     inkscape:window-maximized="0" />
+  <defs
+     id="defs2385">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3211">
+      <stop
+         style="stop-color:#001dff;stop-opacity:1;"
+         offset="0"
+         id="stop3213" />
+      <stop
+         style="stop-color:#001dff;stop-opacity:0;"
+         offset="1"
+         id="stop3215" />
+    </linearGradient>
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 240 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="640 : 240 : 1"
+       inkscape:persp3d-origin="320 : 160 : 1"
+       id="perspective2391" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3211"
+       id="linearGradient3217"
+       x1="31.940987"
+       y1="28.009715"
+       x2="104.68548"
+       y2="28.009715"
+       gradientUnits="userSpaceOnUse" />
+    <filter
+       inkscape:collect="always"
+       id="filter3295">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.67110109"
+         id="feGaussianBlur3297" />
+    </filter>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3211"
+       id="linearGradient3316"
+       gradientUnits="userSpaceOnUse"
+       x1="31.940987"
+       y1="28.009715"
+       x2="104.68548"
+       y2="28.009715" />
+    <linearGradient
+       y2="28.009715"
+       x2="104.68548"
+       y1="28.009715"
+       x1="31.940987"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3542"
+       xlink:href="#linearGradient3211"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="28.009715"
+       x2="104.68548"
+       y1="28.009715"
+       x1="31.940987"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3544"
+       xlink:href="#linearGradient3211"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3211-0"
+       id="linearGradient3316-4"
+       gradientUnits="userSpaceOnUse"
+       x1="31.940987"
+       y1="28.009714"
+       x2="104.68548"
+       y2="28.009714" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3211-0">
+      <stop
+         style="stop-color:#001dff;stop-opacity:1;"
+         offset="0"
+         id="stop3213-3" />
+      <stop
+         style="stop-color:#001dff;stop-opacity:0;"
+         offset="1"
+         id="stop3215-9" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3211-0"
+       id="linearGradient3217-6"
+       x1="31.940987"
+       y1="28.009714"
+       x2="104.68548"
+       y2="28.009714"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3211-0-5"
+       id="linearGradient3316-4-8"
+       gradientUnits="userSpaceOnUse"
+       x1="31.940987"
+       y1="28.009714"
+       x2="104.68548"
+       y2="28.009714" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3211-0-5">
+      <stop
+         style="stop-color:#001dff;stop-opacity:1;"
+         offset="0"
+         id="stop3213-3-8" />
+      <stop
+         style="stop-color:#001dff;stop-opacity:0;"
+         offset="1"
+         id="stop3215-9-4" />
+    </linearGradient>
+    <filter
+       color-interpolation-filters="sRGB"
+       inkscape:collect="always"
+       id="filter3295-1-3">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.67110109"
+         id="feGaussianBlur3297-9-7" />
+    </filter>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3211-0-5"
+       id="linearGradient3217-6-1"
+       x1="31.940987"
+       y1="28.009714"
+       x2="104.68548"
+       y2="28.009714"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3211-0-5"
+       id="linearGradient3237"
+       gradientUnits="userSpaceOnUse"
+       x1="31.940987"
+       y1="28.009714"
+       x2="104.68548"
+       y2="28.009714" />
+  </defs>
+  <metadata
+     id="metadata2388">
+    <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>
+    <ns0:scenes>
+      <ns0:scene
+         start="1"
+         ref="g3189"
+         end="15" />
+      <ns0:scene
+         start="1"
+         ref="s4427" />
+      <ns0:scene
+         start="2"
+         ref="s4159" />
+      <ns0:scene
+         start="5"
+         ref="s9524"
+         end="10" />
+      <ns0:scene
+         start="15"
+         ref="s6546" />
+    </ns0:scenes>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer2"
+     inkscape:label="Background"
+     style="display:inline">
+    <g
+       id="g3189"
+       style="">
+      <rect
+         style="fill:#00ffff;fill-opacity:1;stroke:#000000;stroke-opacity:1;display:inline"
+         id="rect2437"
+         width="641.95721"
+         height="481.62387"
+         x="0.93578684"
+         y="-10.98185" />
+      <rect
+         style="fill:#ffcc1d;fill-opacity:1;stroke:none"
+         id="rect3698"
+         width="624.48901"
+         height="46.789886"
+         x="6.8625164"
+         y="5.8625031"
+         rx="10"
+         ry="10" />
+      <g
+         style="display:inline"
+         transform="translate(-11.385541,2.6514388)"
+         id="g3303">
+        <rect
+           y="15.22048"
+           x="32.440987"
+           height="25.57847"
+           width="71.744492"
+           id="rect2439"
+           style="fill:url(#linearGradient3237);fill-opacity:1;stroke:none;filter:url(#filter3295-1-3)" />
+        <text
+           id="text3299"
+           y="33.312569"
+           x="39.927368"
+           style="font-size:24px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+           xml:space="preserve"><tspan
+             style="font-size:16px"
+             y="33.312569"
+             x="39.927368"
+             id="tspan3301"
+             sodipodi:role="line">Action</tspan></text>
+      </g>
+      <g
+         style="display:inline"
+         id="g3308"
+         transform="translate(76.891374,2.9633707)">
+        <rect
+           y="15.22048"
+           x="32.440987"
+           height="25.57847"
+           width="71.744492"
+           id="rect3310"
+           style="fill:url(#linearGradient3316-4-8);fill-opacity:1;stroke:none;filter:url(#filter3295-1-3)" />
+        <text
+           id="text3312"
+           y="33.312569"
+           x="39.927368"
+           style="font-size:24px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+           xml:space="preserve"><tspan
+             style="font-size:16px"
+             y="33.312569"
+             x="39.927368"
+             id="tspan3314"
+             sodipodi:role="line">Select</tspan></text>
+      </g>
+    </g>
+    <g
+       id="s4393"
+       style="" />
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer3"
+     inkscape:label="Buton"
+     style="display:inline">
+    <g
+       id="s4427"
+       style="display:none" />
+    <g
+       id="s4159"
+       transform="translate(170.31517,0.62386544)"
+       style="display:none">
+      <g
+         transform="translate(4.9909171,0.3119319)"
+         id="g3370">
+        <rect
+           style="fill:url(#linearGradient3542);fill-opacity:1;stroke:none;filter:url(#filter3295)"
+           id="rect3372"
+           width="71.744492"
+           height="25.57847"
+           x="32.440987"
+           y="15.22048" />
+        <text
+           xml:space="preserve"
+           style="font-size:24px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+           x="35.927368"
+           y="33.312569"
+           id="text3374"><tspan
+             sodipodi:role="line"
+             id="tspan3376"
+             x="35.927368"
+             y="33.312569"
+             style="font-size:16px">GNOME</tspan></text>
+      </g>
+      <g
+         transform="translate(102.93775,-0.9357981)"
+         id="g3380">
+        <rect
+           style="fill:url(#linearGradient3544);fill-opacity:1;stroke:none;filter:url(#filter3295)"
+           id="rect3382"
+           width="71.744492"
+           height="25.57847"
+           x="32.440987"
+           y="15.22048" />
+        <text
+           xml:space="preserve"
+           style="font-size:24px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+           x="39.927368"
+           y="33.312569"
+           id="text3384"><tspan
+             sodipodi:role="line"
+             id="tspan3386"
+             x="39.927368"
+             y="33.312569"
+             style="font-size:16px">AAA</tspan></text>
+      </g>
+    </g>
+    <g
+       id="s9524"
+       transform="translate(90.460441,64.258106)"
+       style="display:none">
+      <rect
+         x="0"
+         y="0"
+         width="100"
+         height="100"
+         id="rect3118" />
+    </g>
+    <g
+       id="s6546"
+       style="display:none">
+      <rect
+         x="0"
+         y="0"
+         width="100"
+         height="100"
+         style="fill:#ff00"
+         id="rect3116" />
+    </g>
+  </g>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     style="display:inline" />
+</svg>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pyink/pyink.py	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,25 @@
+import pybInkscape
+import pygtk
+import gtk
+from MBScene import *
+global ink_inited
+ink_inited=0
+def start_desktop(inkscape,ptr):
+    global ink_inited
+    if ink_inited == 1:
+    	desktop = pybInkscape.GPointer_2_PYSPDesktop(ptr)
+	top = desktop.getToplevel()
+    	#dock = desktop.getDock()
+    	#item = dock.new_item("scene", "scene", "feBlend-icon", dock.ITEM_ST_DOCKED_STATE)
+    	scene = MBScene(desktop,top)
+    	scene.show()
+        return
+        
+
+    ink_inited = 1
+
+
+def pyink_start():
+    print 'pyink_start()'
+    pybInkscape.inkscape.connect('activate_desktop', start_desktop)
+    pass
Binary file pyink/start.png has changed
--- a/src/Makefile.am	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/Makefile.am	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+# -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+# vim: sw=4:ts=8:sts=4
 include $(top_srcdir)/config.mk
 
 lib_LTLIBRARIES = libmbfly.la
@@ -22,7 +24,7 @@
 	observer.c paint.c redraw_man.c rotate.c shape_path.c		\
 	shape_rect.c shift.c subtree_free.c timer.c 			\
 	timertool.c tools.c visibility.c prop.c sprite.c	\
-	mouse.c shape_image.c img_ldr.c $(MBAF_SOURCES)
+	mouse.c shape_image.c $(MBAF_SOURCES)
 libmbfly_la_CPPFLAGS = @imlib2_CFLAGS@
 libmbfly_la_LDFLAGS = @imlib2_LIBS@
 
@@ -34,10 +36,24 @@
 libmbfly_la_SOURCES += shape_stext.c
 endif
 
-if X_SUPP
+if X_BACKEND
 libmbfly_la_SOURCES += X_supp.c
 endif
 
+if DFB_BACKEND
+libmbfly_la_SOURCES += dfb_supp.c
+endif
+
+if CAIRO_IMG_LOADER
+libmbfly_la_SOURCES += img_ldr.c
+endif
+
+if IMLIB2_IMG_LOADER
+libmbfly_la_SOURCES += img_ldr_imlib2.c
+endif
+
+libmbfly_la_LDFLAGS =
+
 if CAIRO_GRAPH_ENGINE
 libmbfly_la_SOURCES += graph_engine_cairo.c
 
@@ -45,10 +61,18 @@
 libmbfly_la_LDFLAGS += @cairo_LIBS@ @pangocairo_LIBS@
 endif
 
+if XSHM
+libmbfly_la_LDFLAGS += -lXext
+endif
+
 if SKIA_GRAPH_ENGINE
 libmbfly_la_SOURCES += graph_engine_skia.cpp
 endif
 
+if DFB_GRAPH_ENGINE
+libmbfly_la_SOURCES += graph_engine_dfb.c
+endif
+
 if OPENVG_GRAPH_ENGINE
 libmbfly_la_SOURCES += graph_engine_openvg.c
 libmbfly_la_LDFLAGS += -lOpenVG -lEGL
--- a/src/Makefile.pmake	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/Makefile.pmake	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+# -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+# vim: sw=4:ts=8:sts=4
 SRCS =	coord.c geo.c shape_path.c shape_text.c shape_rect.c \
      	shape_image.c \
 	redraw_man.c timer.c animate.c paint.c event.c observer.c \
--- a/src/X_main.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/X_main.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include <stdio.h>
 #include <unistd.h>
 #include <sys/time.h>
@@ -13,6 +15,7 @@
 #include "mb_paint.h"
 #include "mb_timer.h"
 #include "mb_animate.h"
+#include "mb_backend_utils.h"
 
 #define OK 0
 #define ERR -1
@@ -67,15 +70,18 @@
     XFlush(display);
 }
 
-void handle_connection(Display *display, mb_tman_t *tman,
+void handle_connection(Display *display, mb_timer_man_t *timer_man,
 		       redraw_man_t *rdman, int w, int h) {
     int xcon;
     fd_set rds;
     int nfds;
     struct timeval tmo;
     mb_timeval_t mb_tmo, next_mb_tmo;
+    mb_tman_t *tman;
     int r;
 
+    tman = tman_timer_man_get_tman(timer_man);
+
     XSelectInput(display, win, PointerMotionMask | ExposureMask);
     XFlush(display);
 
@@ -134,7 +140,7 @@
     grad_stop_t fill3_stops[3];
     mbe_font_face_t *face;
     struct timeval tv;
-    mb_tman_t *tman;
+    mb_timer_man_t *timer_man;
     mb_timeval_t mbtv, start, playing;
     mb_progm_t *progm;
     mb_word_t *word;
@@ -191,7 +197,7 @@
     rdman_add_shape(&rdman, (shape_t *)path1, coord1);
     rdman_add_shape(&rdman, (shape_t *)path2, coord2);
 
-    
+
     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);
@@ -205,11 +211,11 @@
 
     XFlush(display);
 
-    tman = mb_tman_new();
-    if(tman) {
+    timer_man = mb_timer_man_new(&tman_timer_factory);
+    if(timer_man) {
 	/* Prepare an animation program. */
-	progm = mb_progm_new(10, &rdman);
-	
+	progm = mb_progm_new(30, &rdman);
+
 	MB_TIMEVAL_SET(&start, 0, 0);
 	MB_TIMEVAL_SET(&playing, 1, 0);
 	word = mb_progm_next_word(progm, &start, &playing);
@@ -224,7 +230,7 @@
 	act = mb_shift_new(0, 20, coord1, word);
 	act = mb_shift_new(0, -20, coord2, word);
 	act = mb_visibility_new(VIS_HIDDEN, coord3, word);
-	
+
 	MB_TIMEVAL_SET(&start, 3, 0);
 	MB_TIMEVAL_SET(&playing, 2, 0);
 	word = mb_progm_next_word(progm, &start, &playing);
@@ -234,16 +240,176 @@
 	act = mb_chgcolor_new(0, 0, 1, 0.5, fill1, word);
 	act = mb_chgcolor_new(1, 0, 0, 0.5, fill2, word);
 	act = mb_visibility_new(VIS_VISIBLE, coord3, word);
-	
+
+	MB_TIMEVAL_SET(&start, 5, 0);
+	MB_TIMEVAL_SET(&playing, 2, 0);
+	word = mb_progm_next_word(progm, &start, &playing);
+
+	act = mb_shift_new(0, 20, coord1, word);
+	act = mb_shift_new(0, -20, coord2, word);
+	act = mb_chgcolor_new(1, 0, 0, 1, fill1, word);
+	act = mb_chgcolor_new(0, 0, 1, 1, fill2, word);
+	act = mb_visibility_new(VIS_HIDDEN, coord3, word);
+
+	MB_TIMEVAL_SET(&start, 7, 0);
+	MB_TIMEVAL_SET(&playing, 2, 0);
+	word = mb_progm_next_word(progm, &start, &playing);
+
+	act = mb_shift_new(0, -20, coord1, word);
+	act = mb_shift_new(0, 20, coord2, word);
+	act = mb_chgcolor_new(0, 1, 0, 0.5, fill1, word);
+	act = mb_chgcolor_new(1, 0, 0, 0.5, fill2, word);
+	act = mb_visibility_new(VIS_VISIBLE, coord3, word);
+
+	MB_TIMEVAL_SET(&start, 9, 0);
+	MB_TIMEVAL_SET(&playing, 2, 0);
+	word = mb_progm_next_word(progm, &start, &playing);
+
+	act = mb_shift_new(0, 20, coord1, word);
+	act = mb_shift_new(0, -20, coord2, word);
+	act = mb_chgcolor_new(0, 0, 1, 1, fill1, word);
+	act = mb_chgcolor_new(1, 0, 0, 1, fill2, word);
+	act = mb_visibility_new(VIS_HIDDEN, coord3, word);
+
+	MB_TIMEVAL_SET(&start, 11, 0);
+	MB_TIMEVAL_SET(&playing, 2, 0);
+	word = mb_progm_next_word(progm, &start, &playing);
+
+	act = mb_shift_new(0, -20, coord1, word);
+	act = mb_shift_new(0, 20, coord2, word);
+	act = mb_chgcolor_new(0, 1, 0, 0.5, fill1, word);
+	act = mb_chgcolor_new(1, 0, 0, 0.5, fill2, word);
+	act = mb_visibility_new(VIS_VISIBLE, coord3, word);
+
+	MB_TIMEVAL_SET(&start, 13, 0);
+	MB_TIMEVAL_SET(&playing, 2, 0);
+	word = mb_progm_next_word(progm, &start, &playing);
+
+	act = mb_shift_new(0, 20, coord1, word);
+	act = mb_shift_new(0, -20, coord2, word);
+	act = mb_chgcolor_new(0, 0, 1, 1, fill1, word);
+	act = mb_chgcolor_new(1, 0, 0, 1, fill2, word);
+	act = mb_visibility_new(VIS_HIDDEN, coord3, word);
+
+	MB_TIMEVAL_SET(&start, 15, 0);
+	MB_TIMEVAL_SET(&playing, 2, 0);
+	word = mb_progm_next_word(progm, &start, &playing);
+
+	act = mb_shift_new(0, -20, coord1, word);
+	act = mb_shift_new(0, 20, coord2, word);
+	act = mb_chgcolor_new(0, 1, 0, 0.5, fill1, word);
+	act = mb_chgcolor_new(1, 0, 0, 0.5, fill2, word);
+	act = mb_visibility_new(VIS_VISIBLE, coord3, word);
+
+	MB_TIMEVAL_SET(&start, 17, 0);
+	MB_TIMEVAL_SET(&playing, 2, 0);
+	word = mb_progm_next_word(progm, &start, &playing);
+
+	act = mb_shift_new(0, 20, coord1, word);
+	act = mb_shift_new(0, -20, coord2, word);
+	act = mb_chgcolor_new(0, 0, 1, 1, fill1, word);
+	act = mb_chgcolor_new(1, 0, 0, 1, fill2, word);
+	act = mb_visibility_new(VIS_HIDDEN, coord3, word);
+
+	MB_TIMEVAL_SET(&start, 19, 0);
+	MB_TIMEVAL_SET(&playing, 2, 0);
+	word = mb_progm_next_word(progm, &start, &playing);
+
+	act = mb_shift_new(0, -20, coord1, word);
+	act = mb_shift_new(0, 20, coord2, word);
+	act = mb_chgcolor_new(0, 1, 0, 0.5, fill1, word);
+	act = mb_chgcolor_new(1, 0, 0, 0.5, fill2, word);
+	act = mb_visibility_new(VIS_VISIBLE, coord3, word);
+
+	MB_TIMEVAL_SET(&start, 21, 0);
+	MB_TIMEVAL_SET(&playing, 2, 0);
+	word = mb_progm_next_word(progm, &start, &playing);
+
+	act = mb_shift_new(0, 20, coord1, word);
+	act = mb_shift_new(0, -20, coord2, word);
+	act = mb_chgcolor_new(0, 0, 1, 1, fill1, word);
+	act = mb_chgcolor_new(1, 0, 0, 1, fill2, word);
+	act = mb_visibility_new(VIS_HIDDEN, coord3, word);
+
+	MB_TIMEVAL_SET(&start, 23, 0);
+	MB_TIMEVAL_SET(&playing, 2, 0);
+	word = mb_progm_next_word(progm, &start, &playing);
+
+	act = mb_shift_new(0, -20, coord1, word);
+	act = mb_shift_new(0, 20, coord2, word);
+	act = mb_chgcolor_new(0, 1, 0, 0.5, fill1, word);
+	act = mb_chgcolor_new(1, 0, 0, 0.5, fill2, word);
+	act = mb_visibility_new(VIS_VISIBLE, coord3, word);
+
+	MB_TIMEVAL_SET(&start, 25, 0);
+	MB_TIMEVAL_SET(&playing, 2, 0);
+	word = mb_progm_next_word(progm, &start, &playing);
+
+	act = mb_shift_new(0, 20, coord1, word);
+	act = mb_shift_new(0, -20, coord2, word);
+	act = mb_chgcolor_new(0, 0, 1, 1, fill1, word);
+	act = mb_chgcolor_new(1, 0, 0, 1, fill2, word);
+	act = mb_visibility_new(VIS_HIDDEN, coord3, word);
+
+	MB_TIMEVAL_SET(&start, 27, 0);
+	MB_TIMEVAL_SET(&playing, 2, 0);
+	word = mb_progm_next_word(progm, &start, &playing);
+
+	act = mb_shift_new(0, -20, coord1, word);
+	act = mb_shift_new(0, 20, coord2, word);
+	act = mb_chgcolor_new(0, 1, 0, 0.5, fill1, word);
+	act = mb_chgcolor_new(1, 0, 0, 0.5, fill2, word);
+	act = mb_visibility_new(VIS_VISIBLE, coord3, word);
+
+	MB_TIMEVAL_SET(&start, 29, 0);
+	MB_TIMEVAL_SET(&playing, 2, 0);
+	word = mb_progm_next_word(progm, &start, &playing);
+
+	act = mb_shift_new(0, 20, coord1, word);
+	act = mb_shift_new(0, -20, coord2, word);
+	act = mb_chgcolor_new(0, 0, 1, 1, fill1, word);
+	act = mb_chgcolor_new(1, 0, 0, 1, fill2, word);
+	act = mb_visibility_new(VIS_HIDDEN, coord3, word);
+
+	MB_TIMEVAL_SET(&start, 31, 0);
+	MB_TIMEVAL_SET(&playing, 2, 0);
+	word = mb_progm_next_word(progm, &start, &playing);
+
+	act = mb_shift_new(0, -20, coord1, word);
+	act = mb_shift_new(0, 20, coord2, word);
+	act = mb_chgcolor_new(0, 1, 0, 0.5, fill1, word);
+	act = mb_chgcolor_new(1, 0, 0, 0.5, fill2, word);
+	act = mb_visibility_new(VIS_VISIBLE, coord3, word);
+
+	MB_TIMEVAL_SET(&start, 33, 0);
+	MB_TIMEVAL_SET(&playing, 2, 0);
+	word = mb_progm_next_word(progm, &start, &playing);
+
+	act = mb_shift_new(0, 20, coord1, word);
+	act = mb_shift_new(0, -20, coord2, word);
+	act = mb_chgcolor_new(0, 0, 1, 1, fill1, word);
+	act = mb_chgcolor_new(1, 0, 0, 1, fill2, word);
+	act = mb_visibility_new(VIS_HIDDEN, coord3, word);
+
+	MB_TIMEVAL_SET(&start, 35, 0);
+	MB_TIMEVAL_SET(&playing, 2, 0);
+	word = mb_progm_next_word(progm, &start, &playing);
+
+	act = mb_shift_new(0, -20, coord1, word);
+	act = mb_shift_new(0, 20, coord2, word);
+	act = mb_chgcolor_new(0, 1, 0, 0.5, fill1, word);
+	act = mb_chgcolor_new(1, 0, 0, 0.5, fill2, word);
+	act = mb_visibility_new(VIS_VISIBLE, coord3, word);
+
 	/* Start playing the program. */
 	gettimeofday(&tv, NULL);
 	MB_TIMEVAL_SET(&mbtv, tv.tv_sec, tv.tv_usec);
-	mb_progm_start(progm, tman, &mbtv);
+	mb_progm_start(progm, timer_man, &mbtv);
 
-	handle_connection(display, tman, &rdman, w, h);
+	handle_connection(display, timer_man, &rdman, w, h);
 
 	mb_progm_free(progm);
-	mb_tman_free(tman);
+	mb_timer_man_free(&tman_timer_factory, timer_man);
     }
 
     rdman_paint_free(&rdman, fill1);
--- a/src/X_supp.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/X_supp.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,18 +1,36 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
+#include <cairo-xlib.h>
 #include "mb_graph_engine.h"
 #include "mb_redraw_man.h"
 #include "mb_timer.h"
 #include "mb_X_supp.h"
+#include "mb_backend.h"
+#include "mb_backend_utils.h"
+#include "config.h"
+
+#ifdef XSHM
+/* \sa http://www.xfree86.org/current/mit-shm.html */
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <X11/extensions/XShm.h>
+static void XSHM_update(X_supp_runtime_t *xmb_rt);
+#endif
 
 #define ERR -1
 #define OK 0
 
+#define ASSERT(x)
+
 #define ONLY_MOUSE_MOVE_RAW 1
 
+static mb_timer_factory_t *_timer_factory = &tman_timer_factory;
+
 /*! \ingroup xkb
  * @{
  */
@@ -21,18 +39,12 @@
     int ksym_per_code;
     KeySym *syms;
     subject_t *kbevents;
-    ob_factory_t *ob_factory;
+    observer_factory_t *observer_factory;
 };
 
 /* @} */
-#define MAX_MONITORS 200
-typedef struct {
-    int type;
-    int fd;
-    mb_eventcb_t f;
-    void *arg;
-}  monitor_t;
-struct _X_MB_runtime {
+
+struct _X_supp_runtime {
     Display *display;
     Window win;
     Visual *visual;
@@ -40,20 +52,221 @@
     mbe_pattern_t *surface_ptn;
     mbe_t *cr, *backend_cr;
     redraw_man_t *rdman;
-    mb_tman_t *tman;
     mb_img_ldr_t *img_ldr;
     int w, h;
 
     X_kb_info_t kbinfo;
-    monitor_t monitors[MAX_MONITORS];
-    int n_monitor;
+    mb_IO_man_t *io_man;
+    mb_timer_man_t *timer_man;
 
 #ifndef ONLY_MOUSE_MOVE_RAW
     /* States */
     shape_t *last;
 #endif
+
+#ifdef XSHM
+    XImage *ximage;
+    XShmSegmentInfo shminfo;
+#endif
+
+    /* For handle connection */
+    int io_hdl;
+
+    /*
+     * Following variables are used by handle_single_x_event()
+     */
+    int last_evt_type;	       /* Type of last event */
+    int eflag;
+    int ex1, ey1, ex2, ey2;    /* Aggregate expose events */
+    int mflag;
+    int mx, my;		       /* Position of last motion event */
+    int mbut_state;	       /* Button state of last motion event */
 };
 
+static void _x_supp_handle_x_event(X_supp_runtime_t *rt);
+
+/*! \defgroup x_supp_io IO manager for X.
+ * @{
+ */
+#define MAX_MONITORS 200
+
+typedef struct {
+    int type;
+    int fd;
+    mb_IO_cb_t cb;
+    void *data;
+}  monitor_t;
+
+struct _X_supp_IO_man {
+    mb_IO_man_t io_man;
+    monitor_t monitors[MAX_MONITORS];
+    int n_monitor;
+};
+
+static int _x_supp_io_man_reg(struct _mb_IO_man *io_man,
+			      int fd, MB_IO_TYPE type,
+			      mb_IO_cb_t cb, void *data);
+static void _x_supp_io_man_unreg(struct _mb_IO_man *io_man, int io_hdl);
+static mb_IO_man_t *_x_supp_io_man_new(void);
+static void _x_supp_io_man_free(mb_IO_man_t *io_man);
+
+static mb_IO_factory_t _X_supp_default_io_factory = {
+    _x_supp_io_man_new,
+    _x_supp_io_man_free
+};
+static mb_IO_factory_t *_io_factory = &_X_supp_default_io_factory;
+
+static struct _X_supp_IO_man _default_io_man = {
+    {_x_supp_io_man_reg, _x_supp_io_man_unreg},
+    {},			/* monitors */
+    0			/* n_monitor */
+};
+
+static mb_IO_man_t *
+_x_supp_io_man_new(void) {
+    return (mb_IO_man_t *)&_default_io_man;
+}
+
+static void
+_x_supp_io_man_free(mb_IO_man_t *io_man) {
+}
+
+static int
+_x_supp_io_man_reg(struct _mb_IO_man *io_man,
+		   int fd, MB_IO_TYPE type, mb_IO_cb_t cb, void *data) {
+    struct _X_supp_IO_man *xmb_io_man = (struct _X_supp_IO_man *)io_man;
+    int i;
+
+    for(i = 0; i < xmb_io_man->n_monitor; i++) {
+        if (xmb_io_man->monitors[i].type == MB_IO_DUMMY)
+	    break;
+    }
+    if (i == MAX_MONITORS)
+	return ERR;
+    
+    xmb_io_man->monitors[i].type = type;
+    xmb_io_man->monitors[i].fd = fd;
+    xmb_io_man->monitors[i].cb = cb;
+    xmb_io_man->monitors[i].data = data;
+    i++;
+    if(i > xmb_io_man->n_monitor)
+	xmb_io_man->n_monitor = i;
+    return i - 1;
+}
+
+static void
+_x_supp_io_man_unreg(struct _mb_IO_man *io_man, int io_hdl) {
+    struct _X_supp_IO_man *xmb_io_man = (struct _X_supp_IO_man *)io_man;
+    
+    ASSERT(io_hdl < xmb_io_man->n_monitor);
+    xmb_io_man->monitors[io_hdl].type = MB_IO_DUMMY;
+}
+
+/*! \brief Handle connection coming data and timeout of timers.
+ *
+ * \param display is a Display returned by XOpenDisplay().
+ * \param rdman is a redraw manager.
+ * \param tman is a timer manager.
+ *
+ * The display is managed by specified rdman and tman.  rdman draws
+ * on the display, and tman trigger actions according timers.
+ */
+static void
+_x_supp_event_loop(mb_rt_t *rt) {
+    struct _X_supp_runtime *xmb_rt = (struct _X_supp_runtime *)rt;
+    struct _X_supp_IO_man *io_man = (struct _X_supp_IO_man *)xmb_rt->io_man;
+    mb_timer_man_t *timer_man = (mb_timer_man_t *)xmb_rt->timer_man;
+    redraw_man_t *rdman;
+    mb_tman_t *tman = tman_timer_man_get_tman(timer_man);
+    mb_timeval_t now, tmo;
+    struct timeval tv;
+    fd_set rfds, wfds;
+    int nfds = 0;
+    int r, r1,i;
+
+    rdman = mb_runtime_rdman(rt);
+    
+    _x_supp_handle_x_event(xmb_rt);
+
+    while(1) {
+	FD_ZERO(&rfds);
+	FD_ZERO(&wfds);
+        for(i = 0; i < io_man->n_monitor; i++) {
+	    if(io_man->monitors[i].type == MB_IO_R ||
+	       io_man->monitors[i].type == MB_IO_RW) {
+		FD_SET(io_man->monitors[i].fd, &rfds);
+		nfds = MB_MAX(nfds, io_man->monitors[i].fd + 1);
+	    }
+	    if(io_man->monitors[i].type == MB_IO_W ||
+	       io_man->monitors[i].type == MB_IO_RW) {
+		FD_SET(io_man->monitors[i].fd, &wfds);
+		nfds = MB_MAX(nfds, io_man->monitors[i].fd + 1);
+	    }
+        }
+
+	get_now(&now);
+	r = mb_tman_next_timeout(tman, &now, &tmo);
+
+	if(r == 0) {
+	    tv.tv_sec = MB_TIMEVAL_SEC(&tmo);
+	    tv.tv_usec = MB_TIMEVAL_USEC(&tmo);
+	    r1 = select(nfds, &rfds, NULL, NULL, &tv);
+	} else
+	    r1 = select(nfds, &rfds, NULL, NULL, NULL);
+
+	if(r1 == -1) {
+	    perror("select");
+	    break;
+	}
+
+	if(r1 == 0) {
+	    get_now(&now);
+	    mb_tman_handle_timeout(tman, &now);
+	    rdman_redraw_changed(rdman);
+#ifdef XSHM
+	    XSHM_update(xmb_rt);
+#endif
+	    XFlush(xmb_rt->display);
+	} else {
+            for(i = 0; i < io_man->n_monitor; i++) {
+	        if(io_man->monitors[i].type == MB_IO_R ||
+		   io_man->monitors[i].type == MB_IO_RW) {
+		    if(FD_ISSET(io_man->monitors[i].fd, &rfds))
+		    	io_man->monitors[i].cb(i, io_man->monitors[i].fd,
+					       MB_IO_R,
+					       io_man->monitors[i].data);
+		}
+		if(io_man->monitors[i].type == MB_IO_W ||
+		   io_man->monitors[i].type == MB_IO_RW) {
+		    if(FD_ISSET(io_man->monitors[i].fd, &wfds))
+			io_man->monitors[i].cb(i, io_man->monitors[i].fd,
+					       MB_IO_W,
+					       io_man->monitors[i].data);
+		}
+            }
+	}
+    }
+}
+
+/* @} */
+
+#ifdef XSHM
+static void
+XSHM_update(X_supp_runtime_t *xmb_rt) {
+    GC gc;
+
+    gc = DefaultGC(xmb_rt->display, DefaultScreen(xmb_rt->display));
+    if(xmb_rt->ximage) {	/* support XSHM */
+	XShmPutImage(xmb_rt->display,
+		     xmb_rt->win,
+		     gc,
+		     xmb_rt->ximage,
+		     0, 0, 0, 0,
+		     xmb_rt->w, xmb_rt->h, 0);
+    }
+}
+#endif
+
 /*! \defgroup xkb X Keyboard Handling
  *
  * Accept keyboard events from X server and delivery it to
@@ -73,7 +286,7 @@
 static int X_kb_init(X_kb_info_t *kbinfo, Display *display,
 		     redraw_man_t *rdman) {
     int n_syms;
-    ob_factory_t *factory;
+    observer_factory_t *factory;
     int r;
 
     r = XDisplayKeycodes(display,
@@ -89,12 +302,12 @@
     if(kbinfo->syms == NULL)
 	return ERR;
 
-    factory = rdman_get_ob_factory(rdman);
+    factory = rdman_get_observer_factory(rdman);
     kbinfo->kbevents = subject_new(factory, kbinfo, OBJT_KB);
     if(kbinfo->kbevents == NULL)
 	return ERR;
-    /*! \todo Make sure ob_factory is still need. */
-    kbinfo->ob_factory = factory;
+    /*! \todo Make sure observer_factory is still need. */
+    kbinfo->observer_factory = factory;
 
     return OK;
 }
@@ -104,7 +317,8 @@
     XFree(kbinfo->syms);
 }
 
-/*! \brief Accept X keyboard events from handle_x_event() and dispatch it.
+/*! \brief Accept X keyboard events from _x_supp_handle_x_event() and
+ *         dispatch it.
  */
 static void X_kb_handle_event(X_kb_info_t *kbinfo, XKeyEvent *xkey) {
     unsigned int code;
@@ -123,7 +337,6 @@
 
     subject_notify(kbinfo->kbevents, &event.event);
 }
-
 /* @} */
 
 static unsigned int get_button_state(unsigned int state) {
@@ -135,7 +348,7 @@
 	but |= MOUSE_BUT2;
     if(state & Button3Mask)
 	but |= MOUSE_BUT3;
-    
+
     return but;
 }
 
@@ -171,35 +384,193 @@
     mouse_event.y = y;
     mouse_event.but_state = state;
     mouse_event.button = button;
-    
+
     if(IS_MBO_SHAPES(obj))
 	subject = sh_get_mouse_event_subject((shape_t *)obj);
     else
 	subject = coord_get_mouse_event((coord_t *)obj);
+
+    subject_notify(subject, (event_t *)&mouse_event);
+}
+
+/*! \brief Handle motion event.
+ */
+static void
+handle_motion_event(X_supp_runtime_t *rt) {
+    redraw_man_t *rdman = rt->rdman;
+    int x, y;
+    int state;
+    shape_t *shape;
+    coord_t *root;
+    int in_stroke;
     
-    subject_notify(subject, (event_t *)&mouse_event);
+    x = rt->mx;
+    y = rt->my;
+    state = rt->mbut_state;
+    
+    shape = find_shape_at_pos(rdman, x, y,
+			      &in_stroke);
+#ifdef ONLY_MOUSE_MOVE_RAW
+    if(shape != NULL) {
+	notify_coord_or_shape(rdman, (mb_obj_t *)shape,
+			      x, y, EVT_MOUSE_MOVE_RAW, state, 0);
+    } else {
+	root = rdman_get_root(rdman);
+	notify_coord_or_shape(rdman, (mb_obj_t *)root,
+			      x, y, EVT_MOUSE_MOVE_RAW, state, 0);
+    }
+#else
+    if(shape != NULL) {
+	if(rt->last != shape) {
+	    if(rt->last)
+		notify_coord_or_shape(rdman, rt->last, x, y,
+				      EVT_MOUSE_OUT, state, 0);
+	    notify_coord_or_shape(rdman, shape, x, y,
+				  EVT_MOUSE_OVER, state, 0);
+	    rt->last = shape;
+	} else
+	    notify_coord_or_shape(rdman, shape, x, y,
+				  EVT_MOUSE_MOVE, state, 0);
+    } else {
+	if(rt->last) {
+	    notify_coord_or_shape(rdman, rt->last, x, y,
+				  EVT_MOUSE_OUT, state, 0);
+	    rt->last = NULL;
+	}
+    }
+#endif
+    
+    rt->mflag = 0;
+}
+
+/*! \brief Redraw exposed area.
+ */
+static void
+handle_expose_event(X_supp_runtime_t *rt) {
+    redraw_man_t *rdman = rt->rdman;
+    int ex1, ey1, ex2, ey2;
+
+    ex1 = rt->ex1;
+    ey1 = rt->ey1;
+    ex2 = rt->ex2;
+    ey2 = rt->ey2;
+    
+    rdman_redraw_area(rdman, ex1, ey1, (ex2 - ex1), (ey2 - ey1));
+    
+    rt->eflag = 0;
+}
+
+/*! \brief Handle single X event and maintain internal states.
+ *
+ * It keeps internal state in rt to improve performance.
+ */
+static void
+handle_single_x_event(X_supp_runtime_t *rt, XEvent *evt) {
+    redraw_man_t *rdman = rt->rdman;
+    XMotionEvent *mevt;
+    XButtonEvent *bevt;
+    XExposeEvent *eevt;
+    XKeyEvent *xkey;
+    int x, y, w, h;
+
+    shape_t *shape;
+
+    unsigned int state, button;
+    int in_stroke;
+
+    if(evt->type != MotionNotify && rt->mflag)
+	handle_motion_event(rt);
+
+    switch(evt->type) {
+    case ButtonPress:
+	bevt = (XButtonEvent *)evt;
+	x = bevt->x;
+	y = bevt->y;
+	state = get_button_state(bevt->state);
+	button = get_button(bevt->button);
+
+	shape = find_shape_at_pos(rdman, x, y,
+				  &in_stroke);
+	if(shape)
+	    notify_coord_or_shape(rdman, (mb_obj_t *)shape,
+				  x, y, EVT_MOUSE_BUT_PRESS,
+				  state, button);
+	break;
+
+    case ButtonRelease:
+	bevt = (XButtonEvent *)evt;
+	x = bevt->x;
+	y = bevt->y;
+	state = get_button_state(bevt->state);
+	button = get_button(bevt->button);
+
+	shape = find_shape_at_pos(rdman, x, y,
+				  &in_stroke);
+	if(shape)
+	    notify_coord_or_shape(rdman, (mb_obj_t *)shape,
+				  x, y, EVT_MOUSE_BUT_RELEASE,
+				  state, button);
+	break;
+
+    case MotionNotify:
+	mevt = (XMotionEvent *)evt;
+	rt->mx = mevt->x;
+	rt->my = mevt->y;
+	rt->mbut_state = get_button_state(mevt->state);
+	rt->mflag = 1;
+	break;
+
+    case KeyPress:
+    case KeyRelease:
+	xkey = &evt->xkey;
+	X_kb_handle_event(&rt->kbinfo, xkey);
+	break;
+
+    case Expose:
+	eevt = &evt->xexpose;
+	x = eevt->x;
+	y = eevt->y;
+	w = eevt->width;
+	h = eevt->height;
+
+	if(rt->eflag) {
+	    if(x < rt->ex1)
+		rt->ex1 = x;
+	    if(y < rt->ey1)
+		rt->ey1 = y;
+	    if((x + w) > rt->ex2)
+		rt->ex2 = x + w;
+	    if((y + h) > rt->ey2)
+		rt->ey2 = y + h;
+	} else {
+	    rt->ex1 = x;
+	    rt->ey1 = y;
+	    rt->ex2 = x + w;
+	    rt->ey2 = y + h;
+	    rt->eflag = 1;
+	}
+	break;
+    }
+}
+
+/*! \brief Call when no more event in an event iteration.
+ *
+ * No more event means event queue is emplty.  This function will
+ * perform some actions according current internal state.
+ */
+static void
+no_more_event(X_supp_runtime_t *rt) {
+    if(rt->mflag)
+	handle_motion_event(rt);
+    if(rt->eflag)
+	handle_expose_event(rt);
 }
 
 /*! \brief Dispatch all X events in the queue.
  */
-static void handle_x_event(X_MB_runtime_t *rt) {
+static void _x_supp_handle_x_event(X_supp_runtime_t *rt) {
     Display *display = rt->display;
-    redraw_man_t *rdman = rt->rdman;
-    XEvent evt, peek_evt;
-    XMotionEvent *mevt;
-    XButtonEvent *bevt;
-    XExposeEvent *eevt;
-    XKeyEvent *xkey;
-    co_aix x, y, w, h;
-
-    int eflag = 0;
-    int ex1=0, ey1=0, ex2=0, ey2=0;
-
-    shape_t *shape;
-    coord_t *root;
-
-    unsigned int state, button;
-    int in_stroke;
+    XEvent evt;
     int r;
 
     /* XXX: For some unknown reason, it causes a segmentation fault to
@@ -211,200 +582,23 @@
 	if(r == -1)
 	    break;
 
-	switch(evt.type) {
-	case ButtonPress:
-	    bevt = (XButtonEvent *)&evt;
-	    x = bevt->x;
-	    y = bevt->y;
-	    state = get_button_state(bevt->state);
-	    button = get_button(bevt->button);
-
-	    shape = find_shape_at_pos(rdman, x, y,
-				      &in_stroke);
-	    if(shape)
-		notify_coord_or_shape(rdman, (mb_obj_t *)shape,
-				      x, y, EVT_MOUSE_BUT_PRESS,
-				      state, button);
-	    break;
-
-	case ButtonRelease:
-	    bevt = (XButtonEvent *)&evt;
-	    x = bevt->x;
-	    y = bevt->y;
-	    state = get_button_state(bevt->state);
-	    button = get_button(bevt->button);
-
-	    shape = find_shape_at_pos(rdman, x, y,
-				      &in_stroke);
-	    if(shape)
-		notify_coord_or_shape(rdman, (mb_obj_t *)shape,
-			      x, y, EVT_MOUSE_BUT_RELEASE,
-			      state, button);
-	    break;
-
-	case MotionNotify:
-	    while(XEventsQueued(display, QueuedAfterReading) > 0) {
-		r = XPeekEvent(display, &peek_evt);
-		if(r == -1)
-		    break;
-		if(peek_evt.type != MotionNotify)
-		    break;
-		XNextEvent(display, &evt);
-	    }
-	    if(r == -1)
-		break;
-	    
-	    mevt = (XMotionEvent *)&evt;
-	    x = mevt->x;
-	    y = mevt->y;
-	    state = get_button_state(mevt->state);
-
-	    shape = find_shape_at_pos(rdman, x, y,
-				      &in_stroke);
-#ifdef ONLY_MOUSE_MOVE_RAW
-	    if(shape != NULL) {
-		notify_coord_or_shape(rdman, (mb_obj_t *)shape,
-			      x, y, EVT_MOUSE_MOVE_RAW, state, 0);
-	    } else {
-		root = rdman_get_root(rdman);
-		notify_coord_or_shape(rdman, (mb_obj_t *)root,
-			      x, y, EVT_MOUSE_MOVE_RAW, state, 0);
-	    }
-#else
-	    if(shape != NULL) {
-		if(rt->last != shape) {
-		    if(rt->last)
-			notify_coord_or_shape(rdman, rt->last, x, y,
-				      EVT_MOUSE_OUT, state, 0);
-		    notify_coord_or_shape(rdman, shape, x, y,
-				  EVT_MOUSE_OVER, state, 0);
-		    rt->last = shape;
-		} else
-		    notify_coord_or_shape(rdman, shape, x, y,
-				  EVT_MOUSE_MOVE, state, 0);
-	    } else {
-		if(rt->last) {
-		    notify_coord_or_shape(rdman, rt->last, x, y,
-				  EVT_MOUSE_OUT, state, 0);
-		    rt->last = NULL;
-		}
-	    }
+	handle_single_x_event(rt, &evt);
+    }
+    no_more_event(rt);
+    
+#ifdef XSHM
+    XSHM_update(rt);
 #endif
-	    break;
-
-	case KeyPress:
-	case KeyRelease:
-	    xkey = &evt.xkey;
-	    X_kb_handle_event(&rt->kbinfo, xkey);
-	    break;
-
-	case Expose:
-	    eevt = &evt.xexpose;
-	    x = eevt->x;
-	    y = eevt->y;
-	    w = eevt->width;
-	    h = eevt->height;
-
-	    if(eflag) {
-		if(x < ex1)
-		    ex1 = x;
-		if(y < ey1)
-		    ey1 = y;
-		if((x + w) > ex2)
-		    ex2 = x + w;
-		if((y + h) > ey2)
-		    ey2 = y + h;
-	    } else {
-		ex1 = x;
-		ey1 = y;
-		ex2 = x + w;
-		ey2 = y + h;
-		eflag = 1;
-	    }
-	    break;
-	}
-    }
-    if(eflag) {
-	rdman_redraw_area(rdman, ex1, ey1, (ex2 - ex1), (ey2 - ey1));
-	eflag = 0;
-    }
     XFlush(display);
 }
 
-/*! \brief Handle connection coming data and timeout of timers.
- *
- * \param display is a Display returned by XOpenDisplay().
- * \param rdman is a redraw manager.
- * \param tman is a timer manager.
- *
- * The display is managed by specified rdman and tman.  rdman draws
- * on the display, and tman trigger actions according timers.
- */
-void X_MB_handle_connection(void *be) {
-    X_MB_runtime_t *rt = (X_MB_runtime_t *) be;
-    Display *display = rt->display;
-    redraw_man_t *rdman = rt->rdman;
-    mb_tman_t *tman = rt->tman;
-    int fd;
-    mb_timeval_t now, tmo;
-    struct timeval tv;
-    fd_set rfds,wfds;
-    int nfds;
-    int r, r1,i;
-
-    handle_x_event(rt);
+static void
+_x_supp_handle_connection(int hdl, int fd, MB_IO_TYPE type, void *data) {
+    X_supp_runtime_t *xmb_rt = (X_supp_runtime_t *)data;
 
-    fd = XConnectionNumber(display);
-    nfds = fd + 1;
-    while(1) {
-	FD_ZERO(&rfds);
-	FD_ZERO(&wfds);
-	FD_SET(fd, &rfds);
-        for(i=0;i<rt->n_monitor;i++) {
-	    if (rt->monitors[i].type == MONITOR_READ)
-		FD_SET(rt->monitors[i].fd, &rfds);
-	    else if (rt->monitors[i].type == MONITOR_WRITE)
-		FD_SET(rt->monitors[i].fd, &wfds);
-        }
-	
-	get_now(&now);
-	r = mb_tman_next_timeout(tman, &now, &tmo);
-
-	if(r == 0) {
-	    tv.tv_sec = MB_TIMEVAL_SEC(&tmo);
-	    tv.tv_usec = MB_TIMEVAL_USEC(&tmo);
-	    r1 = select(nfds, &rfds, NULL, NULL, &tv);
-	} else
-	    r1 = select(nfds, &rfds, NULL, NULL, NULL);
-
-	if(r1 == -1) {
-	    perror("select");
-	    break;
-	}
-
-	if(r1 == 0) {
-	    get_now(&now);
-	    mb_tman_handle_timeout(tman, &now);
-	    rdman_redraw_changed(rdman);
-	    XFlush(display);
-	} else if(FD_ISSET(fd, &rfds)){
-	    handle_x_event(rt);
-	} else {
-            for(i=0;i<rt->n_monitor;i++) {
-	        if (rt->monitors[i].type == MONITOR_READ)
-		    if (FD_ISSET(rt->monitors[i].fd, &rfds))
-		    	rt->monitors[i].f(rt->monitors[i].fd,rt->monitors[i].arg);
-	        else if (rt->monitors[i].type == MONITOR_WRITE)
-		    if (FD_ISSET(rt->monitors[i].fd, &wfds))
-		    	rt->monitors[i].f(rt->monitors[i].fd,rt->monitors[i].arg);
-            }
-	}
-    }
+    _x_supp_handle_x_event(xmb_rt);
 }
 
-#define ERR -1
-#define OK 0
-
 static int X_init_connection(const char *display_name,
 			     int w, int h,
 			     Display **displayp,
@@ -417,9 +611,27 @@
     XSetWindowAttributes wattr;
     int depth;
     int x, y;
+    int draw_root = 0;
+    const char *disp_name;
+    char disp_buf[32];
+    int cp;
     int r;
 
-    display = XOpenDisplay(display_name);
+    /*
+     * Support drawing on the root window.
+     */
+    disp_name = display_name;
+    if(strstr(display_name, ":root") != NULL) {
+	draw_root = 1;
+	cp = strlen(display_name) - 5;
+	if(cp >= 32)
+	    cp = 31;
+	memcpy(disp_buf, display_name, cp);
+	disp_buf[cp] = 0;
+	disp_name = disp_buf;
+    }
+
+    display = XOpenDisplay(disp_name);
     if(display == NULL)
 	return ERR;
 
@@ -430,15 +642,19 @@
     wattr.override_redirect = False;
     x = 10;
     y = 10;
-    win = XCreateWindow(display, root,
-			 x, y,
-			 w, h,
-			 1, depth, InputOutput, visual,
-			 CWOverrideRedirect, &wattr);
-    r = XMapWindow(display, win);
-    if(r == -1) {
-	XCloseDisplay(display);
-	return ERR;
+    if(draw_root)
+	win = RootWindowOfScreen(ScreenOfDisplay(display, screen));
+    else {
+	win = XCreateWindow(display, root,
+			    x, y,
+			    w, h,
+			    1, depth, InputOutput, visual,
+			    CWOverrideRedirect, &wattr);
+	r = XMapWindow(display, win);
+	if(r == -1) {
+	    XCloseDisplay(display);
+	    return ERR;
+	}
     }
 
     XSelectInput(display, win, PointerMotionMask | ExposureMask |
@@ -453,89 +669,231 @@
     return OK;
 }
 
+#ifdef XSHM
+static void
+xshm_destroy(X_supp_runtime_t *xmb_rt) {
+    XShmSegmentInfo *shminfo;
+
+    shminfo = &xmb_rt->shminfo;
+
+    if(xmb_rt->shminfo.shmaddr) {
+	XShmDetach(xmb_rt->display, shminfo);
+    }
+
+    if(xmb_rt->ximage) {
+	XDestroyImage(xmb_rt->ximage);
+	xmb_rt->ximage = NULL;
+    }
+
+    if(shminfo->shmaddr) {
+	shmdt(shminfo->shmaddr);
+	shminfo->shmaddr = NULL;
+    }
+
+    if(shminfo->shmid) {
+	shmctl(shminfo->shmid, IPC_RMID, 0);
+	shminfo->shmid = 0;
+    }
+}
+
+static void
+xshm_init(X_supp_runtime_t *xmb_rt) {
+    Display *display;
+    Visual *visual;
+    XImage *ximage;
+    int screen;
+    int depth;
+    int support_shm;
+    int mem_sz;
+    XShmSegmentInfo *shminfo;
+    int surf_fmt;
+
+    display = xmb_rt->display;
+    visual = xmb_rt->visual;
+    shminfo = &xmb_rt->shminfo;
+
+    support_shm = XShmQueryExtension(display);
+    if(!support_shm)
+	return;
+
+    screen = DefaultScreen(display);
+    depth = DefaultDepth(display, screen);
+
+    if(depth != 24 && depth != 32)
+	return;
+
+    xmb_rt->ximage = XShmCreateImage(display, visual, depth,
+				     ZPixmap, NULL, shminfo,
+				     xmb_rt->w, xmb_rt->h);
+    ximage = xmb_rt->ximage;
+
+    mem_sz = ximage->bytes_per_line * ximage->height;
+    shminfo->shmid = shmget(IPC_PRIVATE, mem_sz, IPC_CREAT | 0777);
+    if(shminfo->shmid == -1) {
+	xshm_destroy(xmb_rt);
+	return;
+    }
+
+    shminfo->shmaddr = shmat(shminfo->shmid, 0, 0);
+    ximage->data = shminfo->shmaddr;
+
+    shminfo->readOnly = 0;
+
+    XShmAttach(display, shminfo);
+
+    switch(depth) {
+    case 24: surf_fmt = CAIRO_FORMAT_RGB24; break;
+    case 32: surf_fmt = CAIRO_FORMAT_ARGB32; break;
+    }
+
+    xmb_rt->backend_surface =
+	mbe_image_surface_create_for_data((unsigned char *)ximage->data,
+					  surf_fmt,
+					  xmb_rt->w,
+					  xmb_rt->h,
+					  ximage->bytes_per_line);
+    if(xmb_rt->backend_surface == NULL)
+	xshm_destroy(xmb_rt);
+}
+#endif /* XSHM */
+
 /*! \brief Initialize a MadButterfy runtime for Xlib.
  *
- * It setups a runtime environment to run MadButterfly with Xlib.
- * Users should specify width and height of the opening window.
+ * This one is very like _x_supp_init(), except it accepts a
+ * X_supp_runtime_t object initialized with a display connected to a X
+ * server and an opened window.
+ *
+ * Following field of the X_supp_runtime_t object should be initialized.
+ *   - w, h
+ *   - win
+ *   - display
+ *   - visual
  */
-static int X_MB_init(const char *display_name,
-	      int w, int h, X_MB_runtime_t *xmb_rt) {
+static int
+_x_supp_init_with_win_internal(X_supp_runtime_t *xmb_rt) {
     mb_img_ldr_t *img_ldr;
-    
-    memset(xmb_rt, 0, sizeof(X_MB_runtime_t));
+    int w, h;
+    int disp_fd;
 
-    xmb_rt->w = w;
-    xmb_rt->h = h;
-    X_init_connection(display_name, w, h, &xmb_rt->display,
-		      &xmb_rt->visual, &xmb_rt->win);
-#ifdef OPENVG_GRAPH_ENGINE
-    _ge_openvg_disp_id = xmb_rt->display;
+    w = xmb_rt->w;
+    h = xmb_rt->h;
+
+#ifdef XSHM
+    xshm_init(xmb_rt);
 #endif
 
-    mbe_init();
-
     xmb_rt->surface =
 	mbe_image_surface_create(MB_IFMT_ARGB32, w, h);
 
     xmb_rt->surface_ptn =
 	mbe_pattern_create_for_surface(xmb_rt->surface);
-    
-#ifdef OPENVG_GRAPH_ENGINE
-    xmb_rt->backend_surface =
-	mbe_vg_win_surface_create(xmb_rt->display,
-				  xmb_rt->win,
-				  xmb_rt->visual,
-				  w, h);
-#else
-    xmb_rt->backend_surface =
-	mbe_xlib_surface_create(xmb_rt->display,
-				  xmb_rt->win,
-				  xmb_rt->visual,
-				  w, h);
-#endif
+
+    if(xmb_rt->backend_surface == NULL) /* xshm_init() may create one */
+	xmb_rt->backend_surface =
+	    mbe_xlib_surface_create(xmb_rt->display,
+				    xmb_rt->win,
+				    xmb_rt->visual,
+				    w, h);
 
     xmb_rt->cr = mbe_create(xmb_rt->surface);
     xmb_rt->backend_cr = mbe_create(xmb_rt->backend_surface);
 
-    /* TODO: Remove this line.  Since we use mbe_copy_source(), it
-     * will set source for the backend.  So, this line is redundants.
-     * It can be removed.  sourface_ptn can be removed, too.
-     */
     mbe_set_source(xmb_rt->backend_cr, xmb_rt->surface_ptn);
 
     xmb_rt->rdman = (redraw_man_t *)malloc(sizeof(redraw_man_t));
     redraw_man_init(xmb_rt->rdman, xmb_rt->cr, xmb_rt->backend_cr);
-    // FIXME: This is a wired loopback reference. This is inly required when we need 
+    // FIXME: This is a wired loopback reference. This is inly required when we need
     //        to get the xmb_rt->tman for the animation. We should relocate the tman
     //	      to the redraw_man_t instead.
     xmb_rt->rdman->rt = xmb_rt;
-    xmb_rt->rdman->w = w;
-    xmb_rt->rdman->h = h;
 
-    xmb_rt->tman = mb_tman_new();
+    xmb_rt->io_man = mb_io_man_new(_io_factory);
+    xmb_rt->timer_man = mb_timer_man_new(_timer_factory);
 
     img_ldr = simple_mb_img_ldr_new("");
     xmb_rt->img_ldr = img_ldr;
-    rdman_set_img_ldr(xmb_rt->rdman, img_ldr);
-    memset(xmb_rt->monitors,0,sizeof(xmb_rt->monitors));
-    
+    /*! \todo Remove rdman_set_img_ldr() */
+    rdman_set_img_ldr(xmb_rt->rdman, img_ldr); /* this is ncessary? */
+
 #ifndef ONLY_MOUSE_MOVE_RAW
     xmb_rt->last = NULL;
 #endif
 
     X_kb_init(&xmb_rt->kbinfo, xmb_rt->display, xmb_rt->rdman);
 
+    disp_fd = XConnectionNumber(xmb_rt->display);
+    xmb_rt->io_hdl = mb_io_man_reg(xmb_rt->io_man, disp_fd,
+				   MB_IO_R,
+				   _x_supp_handle_connection,
+				   xmb_rt);
+
     return OK;
 }
 
-static void X_MB_destroy(X_MB_runtime_t *xmb_rt) {
+/*! \brief Initialize a MadButterfy runtime for Xlib.
+ *
+ * It setups a runtime environment to run MadButterfly with Xlib.
+ * Users should specify width and height of the opening window.
+ */
+static int _x_supp_init(X_supp_runtime_t *xmb_rt, const char *display_name,
+			int w, int h) {
+    int r;
+
+    memset(xmb_rt, 0, sizeof(X_supp_runtime_t));
+
+    xmb_rt->w = w;
+    xmb_rt->h = h;
+    r = X_init_connection(display_name, w, h, &xmb_rt->display,
+			  &xmb_rt->visual, &xmb_rt->win);
+    if(r != OK)
+	return ERR;
+
+    r = _x_supp_init_with_win_internal(xmb_rt);
+
+    return r;
+}
+
+/*! \brief Initialize a MadButterfly runtime for a window of X.
+ *
+ * Runtimes initialized with this function should be destroyed with
+ * x_supp_destroy_keep_win().
+ */
+static int
+_x_supp_init_with_win(X_supp_runtime_t *xmb_rt,
+		      Display *display, Window win) {
+    XWindowAttributes attrs;
+    int r;
+
+    r = XGetWindowAttributes(display, win, &attrs);
+    if(r == 0)
+	return ERR;
+    
+    memset(xmb_rt, 0, sizeof(X_supp_runtime_t));
+
+    xmb_rt->display = display;
+    xmb_rt->win = win;
+    xmb_rt->visual = attrs.visual;
+    xmb_rt->w = attrs.width;
+    xmb_rt->h = attrs.height;
+
+    r = _x_supp_init_with_win_internal(xmb_rt);
+
+    return r;
+}
+
+static void x_supp_destroy(X_supp_runtime_t *xmb_rt) {
     if(xmb_rt->rdman) {
 	redraw_man_destroy(xmb_rt->rdman);
 	free(xmb_rt->rdman);
     }
 
-    if(xmb_rt->tman)
-	mb_tman_free(xmb_rt->tman);
+    if(xmb_rt->io_hdl)
+	mb_io_man_unreg(xmb_rt->io_man, xmb_rt->io_hdl);
+
+    if(xmb_rt->io_man)
+	mb_io_man_free(_io_factory, xmb_rt->io_man);
+    if(xmb_rt->timer_man)
+	mb_timer_man_free(_timer_factory, xmb_rt->timer_man);
 
     if(xmb_rt->img_ldr)
 	MB_IMG_LDR_FREE(xmb_rt->img_ldr);
@@ -558,133 +916,179 @@
     X_kb_destroy(&xmb_rt->kbinfo);
 }
 
-void *X_MB_new(const char *display_name, int w, int h) {
-    X_MB_runtime_t *rt;
+/*! \brief Destroy a MadButterfly runtime initialized with
+ *	_x_supp_init_with_win().
+ *
+ * Destroying a runtime with this function prevent the window and
+ * display associated with the runtime being closed.
+ */
+static void
+x_supp_destroy_keep_win(X_supp_runtime_t *xmb_rt) {
+    Display *display;
+    Window win;
+
+    display = xmb_rt->display;
+    xmb_rt->display = NULL;
+    win = xmb_rt->win;
+    xmb_rt->win = 0;
+
+    x_supp_destroy(xmb_rt);
+    
+    xmb_rt->display = display;
+    xmb_rt->win = win;
+}
+
+static mb_rt_t *
+_x_supp_new(const char *display_name, int w, int h) {
+    X_supp_runtime_t *rt;
     int r;
 
-    rt = O_ALLOC(X_MB_runtime_t);
+    rt = O_ALLOC(X_supp_runtime_t);
     if(rt == NULL)
 	return NULL;
 
-    r = X_MB_init(display_name, w, h, rt);
+    r = _x_supp_init(rt, display_name, w, h);
     if(r != OK) {
 	free(rt);
 	return NULL;
     }
 
-    return rt;
+    return (mb_rt_t *)rt;
 }
 
-void X_MB_free(void *rt) {
-    X_MB_destroy((X_MB_runtime_t *) rt);
+/*! \brief Create a new runtime for existed window for X.
+ *
+ * The object returned by this function must be free with
+ * _x_supp_free_keep_win() to prevent the window from closed.
+ */
+static mb_rt_t *
+_x_supp_new_with_win(MB_DISPLAY display, MB_WINDOW win) {
+    X_supp_runtime_t *rt;
+    int r;
+
+    rt = O_ALLOC(X_supp_runtime_t);
+    if(rt == NULL)
+	return NULL;
+
+    r = _x_supp_init_with_win(rt, display, win);
+    if(r != OK) {
+	free(rt);
+	return NULL;
+    }
+
+    return (mb_rt_t *)rt;
+}
+
+static void
+_x_supp_free(mb_rt_t *rt) {
+    x_supp_destroy((X_supp_runtime_t *) rt);
     free(rt);
 }
 
-subject_t *X_MB_kbevents(void *rt) {
-    X_MB_runtime_t *xmb_rt = (X_MB_runtime_t *) rt;
+/*! \brief Free runtime created with _x_supp_new_with_win().
+ */
+static void
+_x_supp_free_keep_win(mb_rt_t *rt) {
+    x_supp_destroy_keep_win((X_supp_runtime_t *) rt);
+    free(rt);
+}
+
+static subject_t *
+_x_supp_kbevents(mb_rt_t *rt) {
+    X_supp_runtime_t *xmb_rt = (X_supp_runtime_t *) rt;
     return xmb_rt->kbinfo.kbevents;
 }
 
-redraw_man_t *X_MB_rdman(void *rt) {
-    X_MB_runtime_t *xmb_rt = (X_MB_runtime_t *) rt;
+static redraw_man_t *
+_x_supp_rdman(mb_rt_t *rt) {
+    X_supp_runtime_t *xmb_rt = (X_supp_runtime_t *) rt;
     return xmb_rt->rdman;
 }
 
-mb_tman_t *X_MB_tman(void *rt) {
-    X_MB_runtime_t *xmb_rt = (X_MB_runtime_t *) rt;
-    return xmb_rt->tman;
+static mb_timer_man_t *
+_x_supp_timer_man(mb_rt_t *rt) {
+    X_supp_runtime_t *xmb_rt = (X_supp_runtime_t *) rt;
+    return xmb_rt->timer_man;
 }
 
-ob_factory_t *X_MB_ob_factory(void *rt) {
-    X_MB_runtime_t *xmb_rt = (X_MB_runtime_t *) rt;
-    ob_factory_t *factory;
+static observer_factory_t *
+_x_supp_observer_factory(mb_rt_t *rt) {
+    X_supp_runtime_t *xmb_rt = (X_supp_runtime_t *) rt;
+    observer_factory_t *factory;
 
-    factory = rdman_get_ob_factory(xmb_rt->rdman);
+    factory = rdman_get_observer_factory(xmb_rt->rdman);
     return factory;
 }
 
-mb_img_ldr_t *X_MB_img_ldr(void *rt) {
-    X_MB_runtime_t *xmb_rt = (X_MB_runtime_t *) rt;
-    X_MB_runtime_t *img_ldr;
+static mb_img_ldr_t *
+_x_supp_img_ldr(mb_rt_t *rt) {
+    X_supp_runtime_t *xmb_rt = (X_supp_runtime_t *) rt;
+    mb_img_ldr_t *img_ldr;
 
     img_ldr = xmb_rt->img_ldr;
 
     return img_ldr;
 }
 
-void X_MB_add_event(void *rt, int type, int fd, mb_eventcb_t f,void *arg)
+static int
+_x_supp_add_event(mb_rt_t *rt, int fd, MB_IO_TYPE type,
+	       mb_IO_cb_t cb, void *data)
 {
-    X_MB_runtime_t *xmb_rt = (X_MB_runtime_t *) rt;
-    int i;
+    X_supp_runtime_t *xmb_rt = (X_supp_runtime_t *) rt;
+    mb_IO_man_t *io_man = xmb_rt->io_man;
+    int hdl;
 
-    for(i=0;i<xmb_rt->n_monitor;i++) {
-        if (xmb_rt->monitors[i].type == type && xmb_rt->monitors[i].fd == fd) {
-	    xmb_rt->monitors[i].f = f;
-	    xmb_rt->monitors[i].arg = arg;
-	    return;
-	}
-    }
-    for(i=0;i<xmb_rt->n_monitor;i++) {
-        if (xmb_rt->monitors[i].type == 0) {
-	    xmb_rt->monitors[i].type = type;
-	    xmb_rt->monitors[i].fd = fd;
-	    xmb_rt->monitors[i].f = f;
-	    xmb_rt->monitors[i].arg = arg;
-	    return;
-	}
-    }
-    if (i == MAX_MONITORS) return;
-    xmb_rt->monitors[i].type = type;
-    xmb_rt->monitors[i].fd = fd;
-    xmb_rt->monitors[i].f = f;
-    xmb_rt->monitors[i].arg = arg;
-    i++;
-    xmb_rt->n_monitor=i;
+    hdl = mb_io_man_reg(io_man, fd, type, cb, data);
+    return hdl;
+}
+
+static void
+_x_supp_remove_event(mb_rt_t *rt, int hdl)
+{
+    X_supp_runtime_t *xmb_rt = (X_supp_runtime_t *) rt;
+    mb_IO_man_t *io_man = xmb_rt->io_man;
+
+    mb_io_man_unreg(io_man, hdl);
 }
 
-void X_MB_remove_event(void *rt, int type, int fd)
-{
-    X_MB_runtime_t *xmb_rt = (X_MB_runtime_t *) rt;
-    int i;
-    for(i=0;i<xmb_rt->n_monitor;i++) {
-        if (xmb_rt->monitors[i].type == type && xmb_rt->monitors[i].fd == fd) {
-	    xmb_rt->monitors[i].type = 0;
-	    return;
-	}
-    }
+static int
+_x_supp_flush(mb_rt_t *rt) {
+    X_supp_runtime_t *xmb_rt = (X_supp_runtime_t *) rt;
+    int r;
+
+#ifdef XSHM
+    XSHM_update(xmb_rt);
+#endif
+    r = XFlush(xmb_rt->display);
+    return r == 0? ERR: OK;
 }
-mb_backend_t backend = { X_MB_new,
-			 X_MB_free,
-			 X_MB_add_event,
-			 X_MB_remove_event,
-			 X_MB_handle_connection,
-			 X_MB_kbevents,
-			 X_MB_rdman,
-			 X_MB_tman,
-			 X_MB_ob_factory,
-			 X_MB_img_ldr
-		};
-/*! \defgroup x_supp_nodejs_sup Export functions for supporting nodejs plugin.
- *
- * These functions are for internal using.
- * @{
- */			 
-/*! \brief Exported for nodejs plugin to call handle_x_event.
- */
-void _X_MB_handle_x_event_for_nodejs(void *rt) {
-    handle_x_event((X_MB_runtime_t *)rt);
+
+static void
+_x_supp_reg_IO_factory(mb_IO_factory_t *io_factory) {
+    _io_factory = io_factory;
 }
 
-/*! \brief Get X connect for nodejs plugin.
- */
-int _X_MB_get_x_conn_for_nodejs(void *rt) {
-    return XConnectionNumber(((X_MB_runtime_t *)rt)->display);
+static void
+_x_supp_reg_timer_factory(mb_timer_factory_t *timer_factory) {
+    _timer_factory = timer_factory;
 }
 
-/*! \brief Flush buffer for the X connection of a runtime object.
- */
-int _X_MB_flush_x_conn_for_nodejs(void *rt) {
-    return XFlush(((X_MB_runtime_t *)rt)->display);
-}
+mb_backend_t mb_dfl_backend = { _x_supp_new,
+				_x_supp_new_with_win,
+				
+				_x_supp_free,
+				_x_supp_free_keep_win,
+				_x_supp_add_event,
+				_x_supp_remove_event,
+				_x_supp_event_loop,
+				_x_supp_flush,
+				
+				_x_supp_kbevents,
+				_x_supp_rdman,
+				_x_supp_timer_man,
+				_x_supp_observer_factory,
+				_x_supp_img_ldr,
 
-/* @} */
+				_x_supp_reg_IO_factory,
+				_x_supp_reg_timer_factory,
+};
--- a/src/animate.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/animate.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 /*! \file
  * \brief Animation tools.
  *
@@ -24,7 +26,7 @@
  * a word, it call stop of actions in the word.
  *
  * A program is driven by a timer.  Once a program is started, it registers
- * with timer for periodic running.  \ref mb_tman_t is timer of
+ * with timer for periodic running.  \ref mb_timer_man_t is timer of
  * MadButterfly.  The update frequence is 10fps by default, now.
  *
  * \section use_progm How to Use Animation Program?
@@ -41,7 +43,7 @@
  *
  * \code
  *	progm = mb_progm_new(10, &rdman);
- *	
+ *
  *	MB_TIMEVAL_SET(&start, 0, 0);
  *	MB_TIMEVAL_SET(&playing, 1, 0);
  *	word = mb_progm_next_word(progm, &start, &playing);
@@ -59,7 +61,7 @@
  *
  *	gettimeofday(&tv, NULL);
  *	MB_TIMEVAL_SET(&mbtv, tv.tv_sec, tv.tv_usec);
- *	mb_progm_start(progm, tman, &mbtv);
+ *	mb_progm_start(progm, timer_man, &mbtv);
  * \endcode
  *
  *
@@ -96,9 +98,9 @@
 
     mb_timeval_t start_time;
     int first_playing;		/*!< first playing word. */
-    mb_tman_t *tman;
+    mb_timer_man_t *timer_man;
     subject_t *complete;	/*!< notify when a program is completed. */
-    mb_timer_t *cur_timer;
+    int cur_timer;
 
     int n_words;
     int max_words;
@@ -113,7 +115,7 @@
 mb_progm_t *mb_progm_new(int max_words, redraw_man_t *rdman) {
     mb_progm_t *progm;
 #ifndef UNITTEST
-    ob_factory_t *factory;
+    observer_factory_t *factory;
 #endif /* UNITTEST */
     int i;
 
@@ -125,7 +127,7 @@
     progm->rdman = rdman;
 
 #ifndef UNITTEST
-    factory = rdman_get_ob_factory(rdman);
+    factory = rdman_get_observer_factory(rdman);
     progm->complete = subject_new(factory, progm, OBJT_PROGM);
     if(progm->complete == NULL) {
 	free(progm);
@@ -137,6 +139,7 @@
     progm->max_words = max_words;
     for(i = 0; i < max_words; i++)
 	STAILQ_INIT(progm->words[i].actions);
+    progm->cur_timer = -1;
     return progm;
 }
 
@@ -231,7 +234,8 @@
  *	between now and next_tmo.  It is not obviously if time stepping
  *	small.
  */
-static void mb_progm_step(const mb_timeval_t *tmo,
+static void mb_progm_step(int timer_hdl,
+			  const mb_timeval_t *tmo,
 			  const mb_timeval_t *now,
 			  void *arg) {
     mb_progm_t *progm = (mb_progm_t *)arg;
@@ -287,8 +291,8 @@
 	word = progm->words + progm->first_playing;
 	if(MB_TIMEVAL_LATER(&word->abs_start, &next_tmo))
 	    MB_TIMEVAL_CP(&next_tmo, &word->abs_start);
-	timer = mb_tman_timeout(progm->tman, &next_tmo,
-				mb_progm_step, progm);	
+	timer = mb_timer_man_timeout(progm->timer_man, &next_tmo,
+				     mb_progm_step, progm);
 	progm->cur_timer = timer;
     } else {
 	/* Make program to complete. */
@@ -298,20 +302,20 @@
 	comp_evt.progm = progm;
 	subject_notify(progm->complete, &comp_evt.event);
 #endif /* UNITTEST */
-	progm->cur_timer = NULL;
+	progm->cur_timer = -1;
     }
 }
 
-void mb_progm_start(mb_progm_t *progm, mb_tman_t *tman,
+void mb_progm_start(mb_progm_t *progm, mb_timer_man_t *timer_man,
 		    mb_timeval_t *now) {
-    mb_timer_t *timer;
+    int timer;
     mb_word_t *word;
     int i;
 
     if(progm->n_words == 0)
 	return;
 
-    progm->tman = tman;
+    progm->timer_man = timer_man;
     MB_TIMEVAL_CP(&progm->start_time, now);
     progm->first_playing = 0;
 
@@ -324,13 +328,13 @@
     }
 
     if(MB_TIMEVAL_EQ(&progm->words[0].abs_start, now)) {
-	mb_progm_step(now, now, progm);
+	mb_progm_step(-1, now, now, progm);
 	return;
     }
-    
-    timer = mb_tman_timeout(tman, &progm->words[0].abs_start,
-			    mb_progm_step, progm);
-    ASSERT(timer != NULL);
+
+    timer = mb_timer_man_timeout(timer_man, &progm->words[0].abs_start,
+				 mb_progm_step, progm);
+    ASSERT(timer != -1);
 
     /* We need timer to abort it. */
     progm->cur_timer = timer;
@@ -341,13 +345,13 @@
 
     mb_progm_abort(progm);
     MB_TIMEVAL_SET(&infi, 0x7fffffff,0);
-    mb_progm_step(&progm->start_time, &infi,progm);
+    mb_progm_step(-1, &progm->start_time, &infi,progm);
 }
 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;
+    if(progm->cur_timer != -1) {
+	mb_timer_man_remove(progm->timer_man, progm->cur_timer);
+	progm->cur_timer = -1;
     }
 }
 
@@ -373,6 +377,7 @@
 
 #ifdef UNITTEST
 
+#include "mb_backend_utils.h"
 #include <CUnit/Basic.h>
 
 typedef struct _mb_dummy mb_dummy_t;
@@ -439,14 +444,17 @@
     mb_progm_t *progm;
     mb_word_t *word;
     mb_action_t *acts[4];
+    mb_timer_man_t *timer_man;
     mb_tman_t *tman;
     mb_timeval_t tv1, tv2, now, tmo_after;
     int logcnt = 0;
     int logs[256];
     int r;
 
-    tman = mb_tman_new();
-    CU_ASSERT(tman != NULL);
+    timer_man = mb_timer_man_new(&tman_timer_factory);
+    CU_ASSERT(timer_man != -1);
+
+    tman = tman_timer_man_get_tman(timer_man);
 
     progm = mb_progm_new(3, NULL);
     CU_ASSERT(progm != NULL);
@@ -473,7 +481,7 @@
     CU_ASSERT(acts[2] != NULL);
 
     MB_TIMEVAL_SET(&now, 0, 0);
-    mb_progm_start(progm, tman, &now);
+    mb_progm_start(progm, timer_man, &now);
 
     r = mb_tman_next_timeout(tman, &now, &tmo_after);
     CU_ASSERT(r == 0);
@@ -489,7 +497,7 @@
     CU_ASSERT(r == 0);
     CU_ASSERT(MB_TIMEVAL_SEC(&tmo_after) == 0 &&
 	      MB_TIMEVAL_USEC(&tmo_after) == STEP_INTERVAL);
-    
+
     /* 1.1s */
     MB_TIMEVAL_ADD(&now, &tmo_after);
     mb_tman_handle_timeout(tman, &now);
@@ -499,7 +507,7 @@
     CU_ASSERT(r == 0);
     CU_ASSERT(MB_TIMEVAL_SEC(&tmo_after) == 0 &&
 	      MB_TIMEVAL_USEC(&tmo_after) == STEP_INTERVAL);
-    
+
     /* 1.2s */
     MB_TIMEVAL_ADD(&now, &tmo_after);
     mb_tman_handle_timeout(tman, &now);
@@ -509,7 +517,7 @@
     CU_ASSERT(r == 0);
     CU_ASSERT(MB_TIMEVAL_SEC(&tmo_after) == 0 &&
 	      MB_TIMEVAL_USEC(&tmo_after) == STEP_INTERVAL);
-    
+
     /* 1.3s */
     MB_TIMEVAL_ADD(&now, &tmo_after);
     mb_tman_handle_timeout(tman, &now);
@@ -519,7 +527,7 @@
     CU_ASSERT(r == 0);
     CU_ASSERT(MB_TIMEVAL_SEC(&tmo_after) == 0 &&
 	      MB_TIMEVAL_USEC(&tmo_after) == STEP_INTERVAL);
-    
+
     /* 1.4s */
     MB_TIMEVAL_ADD(&now, &tmo_after);
     mb_tman_handle_timeout(tman, &now);
@@ -529,7 +537,7 @@
     CU_ASSERT(r == 0);
     CU_ASSERT(MB_TIMEVAL_SEC(&tmo_after) == 0 &&
 	      MB_TIMEVAL_USEC(&tmo_after) == STEP_INTERVAL);
-    
+
     /* 1.5s */
     MB_TIMEVAL_ADD(&now, &tmo_after);
     mb_tman_handle_timeout(tman, &now);
@@ -539,7 +547,7 @@
     CU_ASSERT(r == -1);
 
     mb_progm_free(progm);
-    mb_tman_free(tman);
+    mb_timer_man_free(&tman_timer_factory, timer_man);
 }
 
 CU_pSuite get_animate_suite(void) {
@@ -550,7 +558,7 @@
 	return NULL;
 
     CU_ADD_TEST(suite, test_animate_words);
-    
+
     return suite;
 }
 
--- a/src/chgcolor.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/chgcolor.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include <stdio.h>
 #include <stdlib.h>
 #include "mb_animate.h"
--- a/src/coord.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/coord.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 /*! \brief Implement coordination tranform mechanism.
  * \file
  * This file implements coordination transforming for containers.
@@ -28,10 +30,10 @@
 void matrix_mul(co_aix *m1, co_aix *m2, co_aix *dst) {
     co_aix *_dst = dst;
     co_aix fake_dst[6];
-    
+
     if(m1 == dst || m2 == dst)
 	_dst = fake_dst;
-    
+
     mul_matrix(m1, m2, _dst);
 
     if(m1 == dst || m2 == dst) {
@@ -60,7 +62,7 @@
 /*! \brief Compute aggregated transform matrix.
  *
  * Base on parent's aggregated matrix if it is existed, or use transform
- * matrix as aggregated matrix. 
+ * matrix as aggregated matrix.
  */
 static void compute_transform_function(coord_t *visit) {
     if(!coord_is_root(visit))
@@ -82,7 +84,7 @@
     co_aix *p_matrix;
     co_aix cache_p_matrix[6];
     co_aix cache_scale_x, cache_scale_y;
-    
+
     if(!coord_is_root(visit)) {
 	p_matrix = coord_get_aggr_matrix(visit->parent);
 	cache_scale_x =
@@ -116,7 +118,7 @@
 void compute_reverse(co_aix *orig, co_aix *reverse) {
     co_aix working[6];
     co_aix factor;
-    
+
 #define VEC_MAC(src, factor, dst)		\
     do {					\
 	(dst)[0] += (src)[0] * (factor);	\
@@ -130,7 +132,7 @@
     reverse[3] = 0;
     reverse[4] = 1;
     reverse[5] = 0;
-    
+
     memcpy(working, orig, sizeof(co_aix) * 6);
 
     factor = -working[3] / working[0];
@@ -144,12 +146,14 @@
     reverse[2] = -working[2];
     reverse[5] = -working[5];
 
-    reverse[0] /= working[0];
-    reverse[1] /= working[0];
-    reverse[2] /= working[0];
-    reverse[3] /= working[4];
-    reverse[4] /= working[4];
-    reverse[5] /= working[4];
+    factor = 1 / working[0];
+    reverse[0] *= factor;
+    reverse[1] *= factor;
+    reverse[2] *= factor;
+    factor = 1 / working[4];
+    reverse[3] *= factor;
+    reverse[4] *= factor;
+    reverse[5] *= factor;
 }
 
 /*! \brief Update aggregate matrices of elements under a sub-tree.
@@ -243,7 +247,7 @@
     coord_t *next = NULL;
 
     ASSERT(last != NULL);
-    
+
     if((!(last->flags & COF_SKIP_TRIVAL)) &&
        STAILQ_HEAD(last->children)) {
 	next = STAILQ_HEAD(last->children);
@@ -270,7 +274,7 @@
 
     if(root == last)
 	return NULL;
-    
+
     if(last == NULL) {
 	/* Go most left leaf. */
 	next = root;
@@ -323,7 +327,7 @@
     update_aggr_matrix(elms);
 
     /* | -3 5 0 |
-     * | 5  1 0 | 
+     * | 5  1 0 |
      * | 0  0 1 |
      */
     CU_ASSERT(elms[3].aggr_matrix[0] == -3);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/dfb_supp.c	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,552 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <directfb.h>
+#include <cairo/cairo.h>
+#include <cairo-directfb.h>
+#include "mb_graph_engine.h"
+#include "mb_redraw_man.h"
+#include "mb_timer.h"
+#include "mb_dfb_supp.h"
+#include "config.h"
+
+#define ERR -1
+#define OK 0
+
+#define ONLY_MOUSE_MOVE_RAW 1
+
+/*! \ingroup xkb
+ * @{
+ */
+struct _X_kb_info {
+    int keycode_min, keycode_max;
+    int ksym_per_code;
+    subject_t *kbevents;
+    observer_factory_t *observer_factory;
+};
+
+/* @} */
+#define MAX_MONITORS 200
+typedef struct {
+    int type;
+    int fd;
+    mb_eventcb_t f;
+    void *arg;
+}  monitor_t;
+
+struct _X_MB_runtime {
+    IDirectFB *dfb;
+    IDirectFBSurface *primary;
+    mbe_surface_t *surface, *backend_surface;
+    mbe_pattern_t *surface_ptn;
+    mbe_t *cr, *backend_cr;
+    redraw_man_t *rdman;
+    mb_tman_t *tman;
+    mb_img_ldr_t *img_ldr;
+    int w, h;
+
+    X_kb_info_t kbinfo;
+    monitor_t monitors[MAX_MONITORS];
+    int n_monitor;
+
+#ifndef ONLY_MOUSE_MOVE_RAW
+    /* States */
+    shape_t *last;
+#endif
+
+    /*
+     * Following variables are used by handle_single_x_event()
+     */
+    int last_evt_type;	       /* Type of last event */
+    int eflag;
+    int ex1, ey1, ex2, ey2;    /* Aggregate expose events */
+    int mflag;
+    int mx, my;		       /* Position of last motion event */
+    int mbut_state;	       /* Button state of last motion event */
+};
+
+/*! \defgroup xkb X Keyboard Handling
+ *
+ * Accept keyboard events from X server and delivery it to
+ * application through observer pattern.  There is a subject,
+ * per X-connection, for that.
+ * @{
+ */
+static int keycode2sym(X_kb_info_t *kbinfo, unsigned int keycode) {
+    int sym_idx;
+    int sym;
+
+    sym_idx = kbinfo->ksym_per_code * (keycode - kbinfo->keycode_min);
+/*    sym =  kbinfo->syms[sym_idx];*/
+    return sym;
+}
+
+static void X_kb_destroy(X_kb_info_t *kbinfo) {
+    subject_free(kbinfo->kbevents);
+}
+
+/* @} */
+
+static unsigned int get_button_state(unsigned int state) {
+    return 0;
+}
+
+static unsigned int get_button(unsigned int button) {
+    return 0;
+}
+
+/*! \brief Notify observers of the shape at specified
+ *	position for mouse event.
+ *
+ * Observers of parent shapes may be called if the subject is not
+ * with SUBF_STOP_PROPAGATE flag.  The subject of mouse event
+ * for a shape is returned by sh_get_mouse_event_subject().
+ */
+static void notify_coord_or_shape(redraw_man_t *rdman,
+				   mb_obj_t *obj,
+				   co_aix x, co_aix y, int etype,
+				   unsigned int state,
+				   unsigned int button) {
+    mouse_event_t mouse_event;
+    subject_t *subject;
+
+    mouse_event.event.type = etype;
+    mouse_event.x = x;
+    mouse_event.y = y;
+    mouse_event.but_state = state;
+    mouse_event.button = button;
+
+    if(IS_MBO_SHAPES(obj))
+	subject = sh_get_mouse_event_subject((shape_t *)obj);
+    else
+	subject = coord_get_mouse_event((coord_t *)obj);
+
+    subject_notify(subject, (event_t *)&mouse_event);
+}
+
+/*! \brief Handle motion event.
+ */
+static void
+handle_motion_event(X_MB_runtime_t *rt) {
+    redraw_man_t *rdman = rt->rdman;
+    int x, y;
+    int state;
+    shape_t *shape;
+    coord_t *root;
+    int in_stroke;
+
+    x = rt->mx;
+    y = rt->my;
+    state = rt->mbut_state;
+
+    shape = find_shape_at_pos(rdman, x, y,
+			      &in_stroke);
+#ifdef ONLY_MOUSE_MOVE_RAW
+    if(shape != NULL) {
+	notify_coord_or_shape(rdman, (mb_obj_t *)shape,
+			      x, y, EVT_MOUSE_MOVE_RAW, state, 0);
+    } else {
+	root = rdman_get_root(rdman);
+	notify_coord_or_shape(rdman, (mb_obj_t *)root,
+			      x, y, EVT_MOUSE_MOVE_RAW, state, 0);
+    }
+#else
+    if(shape != NULL) {
+	if(rt->last != shape) {
+	    if(rt->last)
+		notify_coord_or_shape(rdman, rt->last, x, y,
+				      EVT_MOUSE_OUT, state, 0);
+	    notify_coord_or_shape(rdman, shape, x, y,
+				  EVT_MOUSE_OVER, state, 0);
+	    rt->last = shape;
+	} else
+	    notify_coord_or_shape(rdman, shape, x, y,
+				  EVT_MOUSE_MOVE, state, 0);
+    } else {
+	if(rt->last) {
+	    notify_coord_or_shape(rdman, rt->last, x, y,
+				  EVT_MOUSE_OUT, state, 0);
+	    rt->last = NULL;
+	}
+    }
+#endif
+
+    rt->mflag = 0;
+}
+
+/*! \brief Redraw exposed area.
+ */
+static void
+handle_expose_event(X_MB_runtime_t *rt) {
+    redraw_man_t *rdman = rt->rdman;
+    int ex1, ey1, ex2, ey2;
+
+    ex1 = rt->ex1;
+    ey1 = rt->ey1;
+    ex2 = rt->ex2;
+    ey2 = rt->ey2;
+
+    rdman_redraw_area(rdman, ex1, ey1, (ex2 - ex1), (ey2 - ey1));
+
+    rt->eflag = 0;
+}
+
+/*! \brief Call when no more event in an event iteration.
+ *
+ * No more event means event queue is emplty.  This function will
+ * perform some actions according current internal state.
+ */
+static void
+no_more_event(X_MB_runtime_t *rt) {
+    if(rt->mflag)
+	handle_motion_event(rt);
+    if(rt->eflag)
+	handle_expose_event(rt);
+}
+
+/*! \brief Handle connection coming data and timeout of timers.
+ *
+ * \param display is a Display returned by XOpenDisplay().
+ * \param rdman is a redraw manager.
+ * \param tman is a timer manager.
+ *
+ * The display is managed by specified rdman and tman.  rdman draws
+ * on the display, and tman trigger actions according timers.
+ */
+void X_MB_handle_connection(void *be) {
+    X_MB_runtime_t *rt = (X_MB_runtime_t *) be;
+    redraw_man_t *rdman = rt->rdman;
+    mb_tman_t *tman = rt->tman;
+    int fd = 0;
+    mb_timeval_t now, tmo;
+    struct timeval tv;
+    fd_set rfds,wfds;
+    int nfds;
+    int r, r1,i;
+
+    handle_x_event(rt);
+
+/*    fd = XConnectionNumber(display);*/
+    nfds = fd + 1;
+    while(1) {
+	FD_ZERO(&rfds);
+	FD_ZERO(&wfds);
+	FD_SET(fd, &rfds);
+        for(i=0;i<rt->n_monitor;i++) {
+	    if (rt->monitors[i].type == MONITOR_READ)
+		FD_SET(rt->monitors[i].fd, &rfds);
+	    else if (rt->monitors[i].type == MONITOR_WRITE)
+		FD_SET(rt->monitors[i].fd, &wfds);
+        }
+
+	get_now(&now);
+	r = mb_tman_next_timeout(tman, &now, &tmo);
+
+	if(r == 0) {
+	    tv.tv_sec = MB_TIMEVAL_SEC(&tmo);
+	    tv.tv_usec = MB_TIMEVAL_USEC(&tmo);
+	    r1 = select(nfds, &rfds, NULL, NULL, &tv);
+	} else
+	    r1 = select(nfds, &rfds, NULL, NULL, NULL);
+
+	if(r1 == -1) {
+	    perror("select");
+	    break;
+	}
+
+	if(r1 == 0) {
+	    get_now(&now);
+	    mb_tman_handle_timeout(tman, &now);
+	    rdman_redraw_changed(rdman);
+	} else if(FD_ISSET(fd, &rfds)){
+	    handle_x_event(rt);
+	} else {
+            for(i=0;i<rt->n_monitor;i++) {
+	        if (rt->monitors[i].type == MONITOR_READ) {
+		    if (FD_ISSET(rt->monitors[i].fd, &rfds))
+		    	rt->monitors[i].f(rt->monitors[i].fd,rt->monitors[i].arg);
+		} else if (rt->monitors[i].type == MONITOR_WRITE) {
+		    if (FD_ISSET(rt->monitors[i].fd, &wfds))
+			rt->monitors[i].f(rt->monitors[i].fd,rt->monitors[i].arg);
+		}
+            }
+	}
+    }
+}
+
+#define ERR -1
+#define OK 0
+
+static int dfb_init_connection(int w, int h,
+			     IDirectFB **dfb,
+			     IDirectFBSurface **primary) {
+    DFBSurfaceDescription dsc;
+
+    DirectFBInit(NULL, NULL);
+    DirectFBCreate(dfb);
+    (*dfb)->SetCooperativeLevel(*dfb, DFSCL_FULLSCREEN);
+    dsc.flags = DSDESC_CAPS;
+    dsc.caps  = DSCAPS_PRIMARY;
+    (*dfb)->CreateSurface(*dfb, &dsc, primary);
+    (*primary)->GetSize(*primary, &w, &h);
+    (*primary)->SetColor(*primary, 0xff, 0xff, 0xff, 0xff);
+    (*primary)->FillRectangle(*primary, 0, 0, w, h);
+
+    return OK;
+}
+
+/*! \brief Initialize a MadButterfy runtime for DirectFB.
+ *
+ * This one is very like X_MB_init(), except it accepts a
+ * X_MB_runtime_t object initialized with a display connected to a DirectFB
+ * server and an opened window.
+ *
+ * Following field of the X_MB_runtime_t object should be initialized.
+ *   - w, h
+ *   - win
+ *   - dfb
+ *   - primary
+ */
+static int X_MB_init_with_win_internal(X_MB_runtime_t *xmb_rt) {
+    mb_img_ldr_t *img_ldr;
+    int w, h;
+
+    w = xmb_rt->w;
+    h = xmb_rt->h;
+
+    xmb_rt->surface =
+	mbe_image_surface_create(MB_IFMT_ARGB32, w, h);
+
+    xmb_rt->surface_ptn =
+	mbe_pattern_create_for_surface(xmb_rt->surface);
+
+    if (xmb_rt->backend_surface == NULL)
+	xmb_rt->backend_surface =
+	    mbe_directfb_surface_create(xmb_rt->dfb, xmb_rt->primary);
+
+    xmb_rt->cr = mbe_create(xmb_rt->surface);
+    xmb_rt->backend_cr = mbe_create(xmb_rt->backend_surface);
+
+    mbe_set_source(xmb_rt->backend_cr, xmb_rt->surface_ptn);
+
+    xmb_rt->rdman = (redraw_man_t *)malloc(sizeof(redraw_man_t));
+    redraw_man_init(xmb_rt->rdman, xmb_rt->cr, xmb_rt->backend_cr);
+    // FIXME: This is a wired loopback reference. This is inly required when we need
+    //        to get the xmb_rt->tman for the animation. We should relocate the tman
+    //	      to the redraw_man_t instead.
+    xmb_rt->rdman->rt = xmb_rt;
+
+    xmb_rt->tman = mb_tman_new();
+
+    img_ldr = simple_mb_img_ldr_new("");
+    xmb_rt->img_ldr = img_ldr;
+    rdman_set_img_ldr(xmb_rt->rdman, img_ldr);
+    memset(xmb_rt->monitors,0,sizeof(xmb_rt->monitors));
+
+#ifndef ONLY_MOUSE_MOVE_RAW
+    xmb_rt->last = NULL;
+#endif
+
+/*    X_kb_init(&xmb_rt->kbinfo, xmb_rt->display, xmb_rt->rdman);*/
+
+    return OK;
+}
+
+/*! \brief Initialize a MadButterfy runtime for DirectFB.
+ *
+ * It setups a runtime environment to run MadButterfly with DirectFB.
+ * Users should specify width and height of the opening window.
+ */
+static int X_MB_init(X_MB_runtime_t *xmb_rt, const char *display_name,
+		     int w, int h) {
+    int r;
+
+    memset(xmb_rt, 0, sizeof(X_MB_runtime_t));
+
+    xmb_rt->w = w;
+    xmb_rt->h = h;
+    r = dfb_init_connection(w, h, &xmb_rt->dfb, &xmb_rt->primary);
+
+    if(r != OK)
+	return ERR;
+
+    r = X_MB_init_with_win_internal(xmb_rt);
+
+    return r;
+}
+
+static void X_MB_destroy(X_MB_runtime_t *xmb_rt) {
+    if (xmb_rt->rdman) {
+	redraw_man_destroy(xmb_rt->rdman);
+	free(xmb_rt->rdman);
+    }
+
+    if (xmb_rt->tman)
+	mb_tman_free(xmb_rt->tman);
+
+    if (xmb_rt->img_ldr)
+	MB_IMG_LDR_FREE(xmb_rt->img_ldr);
+
+    if (xmb_rt->cr)
+	mbe_destroy(xmb_rt->cr);
+
+    if (xmb_rt->backend_cr)
+	mbe_destroy(xmb_rt->backend_cr);
+
+    if(xmb_rt->surface)
+	mbe_surface_destroy(xmb_rt->surface);
+
+    if(xmb_rt->surface_ptn)
+	mbe_pattern_destroy(xmb_rt->surface_ptn);
+
+    if(xmb_rt->backend_surface)
+	mbe_surface_destroy(xmb_rt->backend_surface);
+
+    if (xmb_rt->primary)
+	xmb_rt->primary->Release(xmb_rt->primary);
+
+    if (xmb_rt->dfb)
+	xmb_rt->dfb->Release(xmb_rt->dfb);
+}
+
+void *X_MB_new(const char *display_name, int w, int h) {
+    X_MB_runtime_t *rt;
+    int r;
+
+    rt = O_ALLOC(X_MB_runtime_t);
+
+    if (rt == NULL)
+	return NULL;
+
+    r = X_MB_init(rt, display_name, w, h);
+
+    if (r != OK) {
+	free(rt);
+	return NULL;
+    }
+
+    return rt;
+}
+
+void X_MB_free(void *rt) {
+    X_MB_destroy((X_MB_runtime_t *) rt);
+    free(rt);
+}
+
+/*! \brief Free runtime created with X_MB_new_with_win().
+ */
+void
+X_MB_free_keep_win(void *rt) {
+    X_MB_destroy_keep_win((X_MB_runtime_t *) rt);
+    free(rt);
+}
+
+subject_t *X_MB_kbevents(void *rt) {
+    X_MB_runtime_t *xmb_rt = (X_MB_runtime_t *) rt;
+    return xmb_rt->kbinfo.kbevents;
+}
+
+redraw_man_t *X_MB_rdman(void *rt) {
+    X_MB_runtime_t *xmb_rt = (X_MB_runtime_t *) rt;
+    return xmb_rt->rdman;
+}
+
+mb_tman_t *X_MB_tman(void *rt) {
+    X_MB_runtime_t *xmb_rt = (X_MB_runtime_t *) rt;
+    return xmb_rt->tman;
+}
+
+observer_factory_t *X_MB_observer_factory(void *rt) {
+    X_MB_runtime_t *xmb_rt = (X_MB_runtime_t *) rt;
+    observer_factory_t *factory;
+
+    factory = rdman_get_observer_factory(xmb_rt->rdman);
+    return factory;
+}
+
+mb_img_ldr_t *X_MB_img_ldr(void *rt) {
+    X_MB_runtime_t *xmb_rt = (X_MB_runtime_t *) rt;
+    mb_img_ldr_t *img_ldr;
+
+    img_ldr = xmb_rt->img_ldr;
+
+    return img_ldr;
+}
+
+void X_MB_add_event(void *rt, int type, int fd, mb_eventcb_t f,void *arg)
+{
+    X_MB_runtime_t *xmb_rt = (X_MB_runtime_t *) rt;
+    int i;
+
+    for(i=0;i<xmb_rt->n_monitor;i++) {
+        if (xmb_rt->monitors[i].type == type && xmb_rt->monitors[i].fd == fd) {
+	    xmb_rt->monitors[i].f = f;
+	    xmb_rt->monitors[i].arg = arg;
+	    return;
+	}
+    }
+    for(i=0;i<xmb_rt->n_monitor;i++) {
+        if (xmb_rt->monitors[i].type == 0) {
+	    xmb_rt->monitors[i].type = type;
+	    xmb_rt->monitors[i].fd = fd;
+	    xmb_rt->monitors[i].f = f;
+	    xmb_rt->monitors[i].arg = arg;
+	    return;
+	}
+    }
+    if (i == MAX_MONITORS) return;
+    xmb_rt->monitors[i].type = type;
+    xmb_rt->monitors[i].fd = fd;
+    xmb_rt->monitors[i].f = f;
+    xmb_rt->monitors[i].arg = arg;
+    i++;
+    xmb_rt->n_monitor=i;
+}
+
+void X_MB_remove_event(void *rt, int type, int fd)
+{
+    X_MB_runtime_t *xmb_rt = (X_MB_runtime_t *) rt;
+    int i;
+    for(i=0;i<xmb_rt->n_monitor;i++) {
+        if (xmb_rt->monitors[i].type == type && xmb_rt->monitors[i].fd == fd) {
+	    xmb_rt->monitors[i].type = 0;
+	    return;
+	}
+    }
+}
+
+mb_backend_t backend = { X_MB_new,
+			 X_MB_free,
+			 X_MB_add_event,
+			 X_MB_remove_event,
+			 X_MB_handle_connection,
+			 X_MB_kbevents,
+			 X_MB_rdman,
+			 X_MB_tman,
+			 X_MB_observer_factory,
+			 X_MB_img_ldr
+		};
+
+/*! \defgroup x_supp_nodejs_sup Export functions for supporting nodejs plugin.
+ *
+ * These functions are for internal using.
+ * @{
+ */
+/*! \brief Exported for nodejs plugin to call handle_x_event.
+ */
+void _X_MB_handle_x_event_for_nodejs(void *rt) {
+    handle_x_event((X_MB_runtime_t *)rt);
+}
+
+/*! \brief Called at end of an iteration of X event loop.
+ */
+void
+_X_MB_no_more_event(void *rt) {
+    X_MB_runtime_t *xmb_rt = (X_MB_runtime_t *)rt;
+
+    no_more_event(xmb_rt);
+}
+
+/* @} */
--- a/src/event.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/event.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 /*! \file
  * \brief Convenience functions for event relative work.
  */
@@ -135,11 +137,11 @@
 #define MB_OBJ_INIT(obj, type) do { (obj)->obj_type = type; } while(0)
 
 #define GEF_OV_DRAW 0x1
-#define GEF_HIDDEN 0x2
+#define GEF_NOT_SHOWED 0x20
 
 struct shape {
     mb_obj_t obj;
-    
+
     coord_t *coord;
     area_t area;
     shape_t *all_next;
@@ -148,7 +150,7 @@
     void *fill, *stroke;
     struct shape *sibling;
     int flags;
-    
+
     int num_points;
     co_aix points[32][2];
 };
@@ -179,7 +181,7 @@
 
 struct coord {
     mb_obj_t obj;
-  
+
     area_t area;
     int flags;
     coord_t *parent;
@@ -208,7 +210,7 @@
 void _areas_merge(area_t *area1, area_t *area2) {
     co_aix lu_x, lu_y;
     co_aix rb_x, rb_y;
-    
+
     lu_x = area2->x;
     lu_y = area2->y;
     rb_x = lu_x + area2->w - 1;
@@ -225,7 +227,7 @@
     area_t *cur_area;
 
     area = coord_get_area(coord);
-    
+
     shape = STAILQ_HEAD(coord->shapes);
     if(shape != NULL) {
 	cur_area = sh_get_area(shape);
@@ -236,7 +238,7 @@
 	cur_area = coord_get_area(child);
     }
     memcpy(area, cur_area, sizeof(area_t));
-	
+
     FOR_COORD_SHAPES(coord, shape) {
 	cur_area = sh_get_area(shape);
 	_areas_merge(area, cur_area);
@@ -261,9 +263,9 @@
 coord_t *preorder_coord_subtree(coord_t *root, coord_t *last) {
     if(STAILQ_HEAD(last->children) && !(last->flags & COF_SKIP))
 	return STAILQ_HEAD(last->children);
-    
+
     last->flags &= ~COF_SKIP;
-    
+
     if(last == root)
 	return NULL;
     while(STAILQ_NEXT(coord_t, sibling, last) == NULL) {
@@ -282,7 +284,7 @@
 static
 coord_t *postorder_coord_subtree(coord_t *root, coord_t *last) {
     coord_t *cur;
-    
+
     if(last != NULL) {
 	if(STAILQ_NEXT(coord_t, sibling, last) == NULL) {
 	    if(cur == root)
@@ -445,7 +447,7 @@
     memset(shape, 0, sizeof(shape_t));
     MB_OBJ_INIT(&shape->obj, MBO_PATH);
     STAILQ_INS(rdman->all_shapes, shape_t, all_next, shape);
-    
+
     return shape;
 }
 
@@ -482,7 +484,7 @@
     int i, j;
 
     cr = surf->cr;
-    
+
     STAILQ_FOR_EACH(cr->drawed, shape_t, sibling, shape1) {
 	for(i = 0; i < shape1->num_points; i++) {
 	    x1 = shape1->points[i][0];
@@ -510,7 +512,7 @@
 				   co_aix x, co_aix y) {
     shape_t *shape;
     int r;
-    
+
     r = rdman_force_clean(rdman);
     if(r != OK)
 	return ERR;
@@ -611,7 +613,7 @@
     cr = rdman_get_cr(rdman);
     for(i = rdman_shape_gl_len(rdman) - 1; i >= 0; i--) {
 	shape = rdman_get_shape_gl(rdman, i);
-	if(sh_get_flags(shape, GEF_HIDDEN))
+	if(sh_get_flags(shape, GEF_NOT_SHOWED))
 	    continue;
 	r = _shape_pos_is_in(shape, x, y, in_stroke, cr);
 	if(r)
@@ -676,7 +678,7 @@
     rdman_surface = mbe_get_target(rdman_get_cr(rdman));
     w = mbe_image_surface_get_width(rdman_surface);
     h = mbe_image_surface_get_height(rdman_surface);
-    
+
     surface = mbe_image_surface_create(MB_IFMT_A1, w, h);
     if(surface == NULL)
 	return NULL;
@@ -684,7 +686,7 @@
     cr = mbe_create(surface);
     if(cr == NULL)
 	mbe_surface_destroy(surface);
-    
+
     return cr;
 }
 
@@ -693,20 +695,14 @@
     mbe_destroy(cr);
 }
 
-#ifdef OVERLAY_DRAW_TEST
-/*
- * Since we remove mbe_clip(), mb_objs_are_overlay() should not use
- * clip for testing overlay anymore.
- */
-
 static
 void _draw_to_mask(shape_t *shape, mbe_t *cr) {
     if(sh_get_flags(shape, GEF_OV_DRAW))
 	return;
-    
+
     draw_shape_path(shape, cr);
     mbe_clip(cr);
-    
+
     sh_set_flags(shape, GEF_OV_DRAW);
 }
 
@@ -734,7 +730,6 @@
 
     return FALSE;
 }
-#endif
 
 /*! \brief Is a mb_obj_t overlaid with another mb_obj_t and
  *	descendants.
@@ -752,9 +747,9 @@
     shape_t *shape, *candi_shape;
     int obj_is_shape;
     int r;
-    
+
     obj_is_shape = IS_MBO_SHAPES(obj);
-    
+
     if(obj_is_shape) {
 	shape = (shape_t *)obj;
 	area = sh_get_area(shape);
@@ -763,30 +758,26 @@
 	area = coord_get_area(coord);
 	shape = NULL;
     }
-	
+
     if(IS_MBO_SHAPES(others_root)) {
 	candi_shape = (shape_t *)others_root;
 	candi_area =  sh_get_area(candi_shape);
-	
+
 	r = areas_are_overlay(area, candi_area);
 	if(!r)
 	    return FALSE;
-	
-#ifdef OVERLAY_DRAW_TEST
+
 	if(!obj_is_shape)
 	    return TRUE;
 
 	_draw_to_mask(candi_shape, cr);
 	r = _fill_and_check(shape, cr);
-	
+
 	return r;
-#else
-	return TRUE;
-#endif
     }
-    
+
     ASSERT(IS_MBO_COORD(others_root));
-    
+
     root = (coord_t *)others_root;
     FOR_COORDS_PREORDER(root, candi_coord) {
 	candi_area = coord_get_area(candi_coord);
@@ -795,27 +786,23 @@
 	    preorder_coord_skip_subtree(candi_coord);
 	    continue;
 	}
-	
+
 	FOR_COORD_SHAPES(candi_coord, candi_shape) {
 	    candi_area = sh_get_area(candi_shape);
 	    r = areas_are_overlay(area, candi_area);
 	    if(!r)
 		continue;
 
-#ifdef OVERLAY_DRAW_TEST
 	    if(!obj_is_shape)
 		return TRUE;
-	    
+
 	    _draw_to_mask(candi_shape, cr);
 	    r = _fill_and_check(shape, cr);
 	    if(r)
 		return TRUE;
-#else
-	    return TRUE;
-#endif
 	}
     }
-    
+
     return FALSE;
 }
 
@@ -858,7 +845,7 @@
 	r = _is_obj_objs_overlay(obj1, obj2, cr);
 	goto out;
     }
-    
+
     root = (coord_t *)obj1;
     FOR_COORDS_PREORDER(root, coord) {
 	area = coord_get_area(coord);
@@ -875,7 +862,7 @@
 	}
     }
     r = FALSE;
-    
+
  out:
     _clear_ov_draw(obj2);	/* marked by _is_obj_objs_overlay() */
     _release_mbe_for_testing(cr);
@@ -896,7 +883,7 @@
     cr = mbe_create(surf);
     backend = mbe_create(surf);
     rdman = redraw_man_new(cr, backend);
-    
+
     return rdman;
 }
 
@@ -949,7 +936,6 @@
     _free_fake_rdman(rdman);
 }
 
-#ifdef OVERLAY_DRAW_TEST
 static
 void test_is_obj_objs_overlay(void) {
     redraw_man_t *rdman;
@@ -978,7 +964,7 @@
     shape_add_point(shape1, 3, 2);
     shape_add_point(shape2, 5, 5);
     shape_add_point(shape3, 4, 3);
-    
+
     surf = mbe_image_surface_create(MB_IFMT_A1, 100, 100);
     cr = mbe_create(surf);
     r = _is_obj_objs_overlay((mb_obj_t *)coord1, (mb_obj_t *)coord2, cr);
@@ -1026,7 +1012,7 @@
     mbe_destroy(cr);
     mbe_surface_destroy(surf);
     sh_clear_flags(shape3, GEF_OV_DRAW);
-    
+
     shape_add_point(shape1, 5, 5);
 
     surf = mbe_image_surface_create(MB_IFMT_A1, 100, 100);
@@ -1113,55 +1099,52 @@
 
     r = mb_objs_are_overlay(rdman, (mb_obj_t *)coord1, (mb_obj_t *)coord2);
     CU_ASSERT(!r);
-    
+
     r = mb_objs_are_overlay(rdman, (mb_obj_t *)shape1, (mb_obj_t *)coord2);
     CU_ASSERT(!r);
-    
+
     r = mb_objs_are_overlay(rdman, (mb_obj_t *)shape1, (mb_obj_t *)shape2);
     CU_ASSERT(!r);
-    
+
     r = mb_objs_are_overlay(rdman, (mb_obj_t *)coord1, (mb_obj_t *)shape2);
     CU_ASSERT(!r);
-    
+
     r = mb_objs_are_overlay(rdman, (mb_obj_t *)shape1, (mb_obj_t *)shape3);
     CU_ASSERT(!r);
-    
+
     r = mb_objs_are_overlay(rdman, (mb_obj_t *)coord1, (mb_obj_t *)shape3);
     CU_ASSERT(!r);
 
     shape_add_point(shape1, 5, 5);
-    
+
     r = mb_objs_are_overlay(rdman, (mb_obj_t *)coord1, (mb_obj_t *)coord2);
     CU_ASSERT(r);
-    
+
     r = mb_objs_are_overlay(rdman, (mb_obj_t *)shape1, (mb_obj_t *)coord2);
     CU_ASSERT(r);
-    
+
     r = mb_objs_are_overlay(rdman, (mb_obj_t *)shape1, (mb_obj_t *)shape2);
     CU_ASSERT(r);
-    
+
     r = mb_objs_are_overlay(rdman, (mb_obj_t *)coord1, (mb_obj_t *)shape2);
     CU_ASSERT(r);
-    
+
     r = mb_objs_are_overlay(rdman, (mb_obj_t *)shape1, (mb_obj_t *)shape3);
     CU_ASSERT(!r);
-    
+
     r = mb_objs_are_overlay(rdman, (mb_obj_t *)coord1, (mb_obj_t *)shape3);
     CU_ASSERT(!r);
 
     _free_fake_rdman(rdman);
 }
-#endif
 
 CU_pSuite get_event_suite(void) {
     CU_pSuite suite;
 
     suite = CU_add_suite("Suite_event", NULL, NULL);
     CU_ADD_TEST(suite, test_mb_obj_pos_is_in);
-#ifdef OVERLAY_DRAW_TEST
     CU_ADD_TEST(suite, test_is_obj_objs_overlay);
     CU_ADD_TEST(suite, test_mb_objs_are_overlay);
-#endif
 
     return suite;
 }
--- a/src/geo.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/geo.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 /*! \brief Determine who should be re-drawed.
  * \file
  * When part of graphic are chagned, not mater size, shape, or position,
@@ -23,7 +25,7 @@
     if(is_scale_overlay(r1->x, r1->w, r2->x, r2->w) &&
        is_scale_overlay(r1->y, r1->h, r2->y, r2->h))
 	return 1;
-    
+
     return 0;
 }
 
@@ -36,7 +38,7 @@
     co_aix min_y, max_y;
     co_aix x, y;
     int i;
-    
+
     /*! \note {x=0, y=0, w=0, h=0} geometry value for a n_pos=0 request
      * can prevent cursor flasing for Cairo XLib.
      * (see changeset 21db69d46835)
@@ -102,7 +104,7 @@
 	{33, 25}, {49, 12},
 	{14, 28}, {39, 56}};
     geo_t g;
-    
+
     geo_init(&g);
     geo_from_positions(&g, 4, data);
     CU_ASSERT(g.cur_area->x == 14);
--- a/src/graph_engine_cairo.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/graph_engine_cairo.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include <fontconfig/fontconfig.h>
 #include <cairo-ft.h>
 #include "mb_graph_engine_cairo.h"
@@ -17,7 +19,7 @@
  */
 static
 FcPattern *query_font_pattern(const char *family, int slant, int weight) {
-    FcPattern *ptn, *p, *fn_ptn;
+    FcPattern *ptn, *fn_ptn;
     FcValue val;
     FcConfig *cfg;
     FcBool r;
@@ -30,33 +32,32 @@
 
     cfg = FcConfigGetCurrent();
     ptn = FcPatternCreate();
-    p = FcPatternCreate();
-    if(ptn == NULL || p == NULL)
+    if(ptn == NULL)
 	goto err;
 
     val.type = FcTypeString;
     val.u.s = family;
     FcPatternAdd(ptn, "family", val, FcTrue);
-    
+
     val.type = FcTypeInteger;
     val.u.i = slant_map[slant];
     FcPatternAdd(ptn, "slant", val, FcTrue);
-    
+
     val.type = FcTypeInteger;
     val.u.i = weight;
     FcPatternAdd(ptn, "weight", val, FcTrue);
 
-    r = FcConfigSubstituteWithPat(cfg, ptn, NULL, FcMatchPattern);
-    if(!r)
-	goto err;
-    
-    r = FcConfigSubstituteWithPat(cfg, p, ptn, FcMatchFont);
+    FcDefaultSubstitute(ptn);
+
+    r = FcConfigSubstituteWithPat(cfg, ptn, ptn, FcMatchPattern);
     if(!r)
 	goto err;
 
-    FcDefaultSubstitute(p);
+    r = FcConfigSubstituteWithPat(cfg, ptn, ptn, FcMatchFont);
+    if(!r)
+	goto err;
 
-    fn_ptn = FcFontMatch(cfg, p, &result);
+    fn_ptn = FcFontMatch(cfg, ptn, &result);
 
     /* It is supposed to return FcResultMatch.  But, it is no, now.
      * I don't know why.  Someone should figure out the issue.
@@ -71,15 +72,12 @@
 	goto err;
 
     FcPatternDestroy(ptn);
-    FcPatternDestroy(p);
-    
+
     return fn_ptn;
-    
+
 err:
     if(ptn)
 	FcPatternDestroy(ptn);
-    if(p)
-	FcPatternDestroy(p);
     return NULL;
 
 }
@@ -94,11 +92,11 @@
 mbe_query_font_face(const char *family, int slant, int weight) {
     mbe_font_face_t *cface;
     FcPattern *ptn;
-    
+
     ptn = query_font_pattern(family, slant, weight);
     cface = cairo_ft_font_face_create_for_pattern(ptn);
     FcPatternDestroy(ptn);
-    
+
     return cface;
 }
 
@@ -121,7 +119,7 @@
 				      cx1, cy1, radius1);
     if(ptn == NULL)
 	return NULL;
-    
+
     stop = stops;
     for(i = 0; i < stop_cnt; i++) {
 	cairo_pattern_add_color_stop_rgba(ptn, stop->offset,
@@ -152,57 +150,3 @@
 
     return ptn;
 }
-
-mbe_pattern_t *
-mbe_pattern_create_image(mb_img_data_t *img) {
-    cairo_surface_t *surf;
-    cairo_pattern_t *ptn;
-    cairo_format_t fmt;
-
-    switch(img->fmt) {
-    case MB_IFMT_ARGB32:
-	fmt = CAIRO_FORMAT_ARGB32;
-	break;
-	
-    case MB_IFMT_RGB24:
-	fmt = CAIRO_FORMAT_RGB24;
-	break;
-	
-    case MB_IFMT_A8:
-	fmt = CAIRO_FORMAT_A8;
-	break;
-	
-    case MB_IFMT_A1:
-	fmt = CAIRO_FORMAT_A1;
-	break;
-	
-    case MB_IFMT_RGB16_565:
-	fmt = CAIRO_FORMAT_RGB16_565;
-	break;
-	
-    default:
-	return NULL;
-    }
-    
-    surf = cairo_image_surface_create_for_data(img->content, fmt,
-					       img->w, img->h, img->stride);
-    ptn = cairo_pattern_create_for_surface(surf);
-    cairo_surface_destroy(surf);
-    
-    return ptn;
-}
-
-void
-mbe_scissoring(mbe_t *canvas, int n_areas, area_t **areas) {
-    area_t *area;
-    int i;
-    
-    cairo_new_path(canvas);
-    
-    for(i = 0; i < n_areas; i++) {
-	area = areas[i];
-	cairo_rectangle(canvas, area->x, area->y, area->w, area->h);
-    }
-
-    cairo_clip(canvas);
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/graph_engine_dfb.c	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,1 @@
+/* Keep Me */
--- a/src/graph_engine_skia.cpp	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/graph_engine_skia.cpp	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 /*! \page ge_layer Graphic Engine Layer
  *
  * Graphic Engine Layer is an abstract of graphic engine; likes Cairo
@@ -81,7 +83,7 @@
     SkPath *path, *subpath;
     SkPaint *paint;
     SkRegion *saved_region;
-    
+
     struct _mbe_states_t *states;
 };
 
@@ -135,9 +137,9 @@
     SkPath path;
     co_aix x, y;
     co_aix reverse[6];
-    
+
     *mbe->saved_region = canvas->getTotalClip();
-    
+
     compute_reverse(ptn->matrix, reverse);
     x = 0; y = 0;
     matrix_trans_pos(reverse, &x, &y);
@@ -149,14 +151,14 @@
     matrix_trans_pos(reverse, &x, &y);
     path.moveTo(CO_AIX_2_SKSCALAR(x), CO_AIX_2_SKSCALAR(y));
     path.close();
-    
+
     canvas->clipPath(path, SkRegion::kIntersect_Op);
 }
 
 static void
 _finish_sized_pattern(mbe_t *mbe) {
     SkCanvas *canvas = mbe->canvas;
-    
+
     canvas->setClipRegion(*mbe->saved_region);
 }
 
@@ -180,7 +182,7 @@
 
     MB_MATRIX_2_SKMATRIX(canvas_matrix, mbe->states->matrix);
     path->addPath(*subpath, canvas_matrix);
-    
+
     subpath->getLastPt(&point);
     subpath->rewind();
     subpath->moveTo(point);
@@ -200,7 +202,7 @@
     SkMatrix skmatrix;
 
     paint->setStyle(style);
-    
+
     if(ptn != NULL) {
 	/* Local matrix of SkShader is a mapping from source pattern to
 	 * user space.  Unlikely, for Cairo is a mapping from user space
@@ -223,7 +225,7 @@
 static void
 _finish_paint(mbe_t *mbe) {
     mbe_pattern_t *ptn = mbe->states->ptn;
-    
+
     mbe->paint->reset();
     if(ptn != NULL && ptn->has_size)
 	_finish_sized_pattern(mbe);
@@ -241,13 +243,13 @@
 	free(ptn);
 	return NULL;
     }
-    
+
     ptn->has_size = 1;
     ptn->w = bitmap->width();
     ptn->h = bitmap->height();
 
     memcpy(ptn->matrix, id_matrix, sizeof(co_aix) * 6);
-    
+
     return ptn;
 }
 
@@ -269,7 +271,7 @@
 	goto fail;
 
     center.set(CO_AIX_2_SKSCALAR(cx1), CO_AIX_2_SKSCALAR(cy1));
-    
+
     stop = stops;
     for(i = 0; i < stop_cnt; i++) {
 	colors[i] = MBSTOP_2_SKCOLOR(stop);
@@ -289,11 +291,11 @@
 	goto fail;
 
     memcpy(ptn->matrix, id_matrix, sizeof(co_aix) * 6);
-    
+
     delete colors;
     delete poses;
     return ptn;
-    
+
  fail:
     if(ptn) free(ptn);
     if(colors) delete colors;
@@ -320,7 +322,7 @@
 
     points[0].set(CO_AIX_2_SKSCALAR(x0), CO_AIX_2_SKSCALAR(y0));
     points[1].set(CO_AIX_2_SKSCALAR(x1), CO_AIX_2_SKSCALAR(y1));
-    
+
     stop = stops;
     for(i = 0; i < stop_cnt; i++) {
 	colors[i] = MBSTOP_2_SKCOLOR(stop);
@@ -339,11 +341,11 @@
 	goto fail;
 
     memcpy(ptn->matrix, id_matrix, sizeof(co_aix) * 6);
-    
+
     delete colors;
     delete poses;
     return ptn;
-    
+
  fail:
     if(ptn) free(ptn);
     if(colors) delete colors;
@@ -351,11 +353,6 @@
     return NULL;
 }
 
-mbe_pattern_t *
-mbe_pattern_create_image(mb_img_data_t *img) {
-    return NULL;
-}
-
 void mbe_pattern_set_matrix(mbe_pattern_t *ptn, const co_aix matrix[6]) {
     SkMatrix skmatrix;
 
@@ -386,11 +383,49 @@
     return (unsigned char *)((SkBitmap *)surface)->getPixels();
 }
 
+mbe_surface_t *mbe_image_surface_create_from_png(const char *filename) {}
+
+mbe_surface_t *
+mbe_image_surface_create_for_data(unsigned char *data,
+				  mb_img_fmt_t fmt,
+				  int width, int height,
+				  int stride) {
+    SkBitmap *bitmap;
+    SkBitmap::Config cfg;
+
+    switch(fmt) {
+    case MB_IFMT_ARGB32:
+	cfg = SkBitmap::kARGB_8888_Config; break;
+
+    case MB_IFMT_A8:
+	cfg = SkBitmap::kA8_Config; break;
+
+    case MB_IFMT_A1:
+	cfg = SkBitmap::kA1_Config; break;
+
+    case MB_IFMT_RGB16_565:
+	cfg = SkBitmap::kRGB_565_Config; break;
+
+    case MB_IFMT_RGB24:
+    default:
+	return NULL;
+    }
+
+    bitmap = new SkBitmap();
+    if(bitmap == NULL)
+	return NULL;
+
+    bitmap->setConfig(cfg, width, height, stride);
+    bitmap->setPixels(data);
+
+    return (mbe_surface_t *)bitmap;
+}
+
 mb_img_fmt_t mbe_image_surface_get_format(mbe_surface_t *surface) {
     SkBitmap *bitmap = (SkBitmap *)surface;
     mb_img_fmt_t fmt;
     SkBitmap::Config cfg;
-    
+
     cfg = bitmap->getConfig();
     switch(cfg) {
     case SkBitmap::kARGB_8888_Config:
@@ -420,25 +455,25 @@
     switch(fmt) {
     case MB_IFMT_ARGB32:
 	cfg = SkBitmap::kARGB_8888_Config; break;
-	
+
     case MB_IFMT_A8:
 	cfg = SkBitmap::kA8_Config; break;
-	
+
     case MB_IFMT_A1:
 	cfg = SkBitmap::kA1_Config; break;
-	
+
     case MB_IFMT_RGB16_565:
 	cfg = SkBitmap::kRGB_565_Config; break;
-	
+
     case MB_IFMT_RGB24:
     default:
 	return NULL;
     }
-    
+
     bitmap = new SkBitmap();
     if(bitmap == NULL)
 	return NULL;
-    
+
     bitmap->setConfig(cfg, width, height);
     bitmap->allocPixels();
 
@@ -470,12 +505,12 @@
 	SkColorFilter::CreatePorterDuffFilter(color,
 					      SkPorterDuff::kSrcOver_Mode);
     mbe_paint(canvas);
-    
+
 }
 
 void mbe_surface_destroy(mbe_surface_t *surface) {
     SkBitmap *bmap = (SkBitmap *)surface;
-    
+
     delete bmap;
 }
 
@@ -511,7 +546,7 @@
 
     if(!canvas->subpath->isEmpty())
 	_update_path(canvas);
-    
+
     _prepare_paint(canvas, SkPaint::kFill_Style);
 
     canvas->canvas->drawPath(*path, *paint);
@@ -523,7 +558,7 @@
     canvas->states->ptn = source;
 }
 
-void mbe_reset_scissoring(mbe_t *canvas) {
+void mbe_reset_clip(mbe_t *canvas) {
     SkRegion clip;
 
     _canvas_device_region(canvas->canvas, &clip);
@@ -543,7 +578,7 @@
 void mbe_rectangle(mbe_t *canvas, co_aix x, co_aix y,
 			  co_aix width, co_aix height) {
     SkPath *subpath = canvas->subpath;
-    
+
     subpath->addRect(CO_AIX_2_SKSCALAR(x), CO_AIX_2_SKSCALAR(y),
 		     CO_AIX_2_SKSCALAR(x + width),
 		     CO_AIX_2_SKSCALAR(y + height));
@@ -572,7 +607,7 @@
     struct _mbe_states_t *states;
 
     _update_path(canvas);
-    
+
     states = canvas->states;
     ASSERT(states->next);
     canvas->states = states->next;
@@ -595,10 +630,10 @@
 
     if(!canvas->subpath->isEmpty())
 	_update_path(canvas);
-    
+
     _canvas_device_region(canvas->canvas, &dev_region);
     region.setPath(*canvas->path, dev_region);
-    
+
     in_fill = region.contains(x, y);
 
     return in_fill;
@@ -637,7 +672,7 @@
     mbe = (mbe_t *)malloc(sizeof(mbe_t));
     if(mbe == NULL)
 	return NULL;
-    
+
     mbe->states = (struct _mbe_states_t *)
 	malloc(sizeof(struct _mbe_states_t));
     states = mbe->states;
@@ -645,7 +680,7 @@
 	free(mbe);
 	return NULL;
     }
-    
+
     canvas->ref();
     mbe->canvas = canvas;
     mbe->path = new SkPath();
@@ -663,7 +698,7 @@
 	goto fail;
 
     memcpy(states->matrix, id_matrix, sizeof(co_aix) * 6);
-    
+
     return mbe;
 
  fail:
@@ -674,7 +709,7 @@
     if(mbe->saved_region) delete mbe->saved_region;
     free(states);
     free(mbe);
-    
+
     return NULL;
 }
 
@@ -688,20 +723,20 @@
 	delete bitmap;
 	return NULL;
     }
-	
+
     mbe = skia_mbe_create_by_canvas(canvas);
     canvas->unref();
-    
+
     if(mbe == NULL) {
 	delete bitmap;
     }
-    
+
     return mbe;
 }
 
 void mbe_destroy(mbe_t *canvas) {
     struct _mbe_states_t *states;
-    
+
     canvas->canvas->unref();
     delete canvas->path;
     delete canvas->subpath;
@@ -710,7 +745,7 @@
     while(canvas->states) {
 	states = canvas->states;
 	canvas->states = states->next;
-	
+
 	if(states->ptn && states->ptn_owned)
 	    mbe_pattern_destroy(states->ptn);
 	free(states);
@@ -722,9 +757,9 @@
     SkPaint *paint = canvas->paint;
 
     ASSERT(paint);
-    
+
     _prepare_paint(canvas, SkPaint::kFill_Style);
-    
+
     canvas->canvas->drawPaint(*paint);
 
     _finish_paint(canvas);
@@ -735,7 +770,7 @@
 
     states = (struct _mbe_states_t *)malloc(sizeof(struct _mbe_states_t));
     ASSERT(states);
-    
+
     memcpy(states, canvas->states, sizeof(struct _mbe_states_t));
     states->next = canvas->states;
     canvas->states = states;
@@ -747,23 +782,13 @@
     canvas->subpath->rewind();
 }
 
-void mbe_scissoring(mbe_t *canvas, int n_areas, area_t **areas) {
-    int i;
-    area_t *area;
-    SkPath *path;
+void mbe_clip(mbe_t *canvas) {
+    if(!canvas->subpath->isEmpty())
+	_update_path(canvas);
 
-    mbe_new_path(canvas);
-    
-    path = canvas->path;
-    for(i = 0; i < n_areas; i++) {
-	area = areas[i];
-	path->addRect(CO_AIX_2_SKSCALAR(area->x), CO_AIX_2_SKSCALAR(area->y),
-		      CO_AIX_2_SKSCALAR(area->x + area->width),
-		      CO_AIX_2_SKSCALAR(area->y + area->height));
-    }
-
-    canvas->canvas->clipPath(*path, SkRegion::kIntersect_Op);
-    path->rewind();
+    canvas->canvas->clipPath(*canvas->path, SkRegion::kIntersect_Op);
+    canvas->path->rewind();
+    canvas->subpath->rewind();
 }
 
 mbe_font_face_t * mbe_query_font_face(const char *family,
@@ -787,7 +812,7 @@
     bmap = &src->canvas->getDevice()->accessBitmap(false);
 
     dst->canvas->drawBitmap(*bmap, 0, 0, paint);
-    
+
     paint->reset();
     mode->unref();
     /* _finish_paint(dst); */
@@ -795,7 +820,7 @@
 
 void mbe_transform(mbe_t *mbe, co_aix matrix[6]) {
     _update_path(mbe);
-    
+
     matrix_mul(matrix, mbe->states->matrix, mbe->states->matrix);
 }
 
@@ -815,7 +840,7 @@
     r = CO_AIX_2_SKSCALAR(radius);
     ang_start = CO_AIX_2_SKSCALAR(angle_start * 180 / PI);
     ang_stop = CO_AIX_2_SKSCALAR(angle_stop * 180 / PI);
-    
+
     /* Skia can only draw an arc in clockwise directly.  We negative
      * start and stop point to draw the arc in the mirror along x-axis
      * in a sub-path.  Then, the sub-path are reflected along x-axis,
@@ -826,7 +851,7 @@
 	SkMatrix matrix;
 	co_aix reflect[6] = { 1, 0, 0,
 			      0, -1, 0};
-	
+
 	rect.set(-r, -r, r, r);
 	sweep = ang_start - ang_stop;
 	tmppath.arcTo(rect, -ang_start, sweep, false);
--- a/src/img_ldr.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/img_ldr.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,6 +1,7 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include <stdio.h>
 #include <string.h>
-#include <Imlib2.h>
 #include "mb_graph_engine.h"
 #include "mb_tools.h"
 #include "mb_paint.h"
@@ -17,7 +18,7 @@
 
 struct _simple_mb_img_data {
     mb_img_data_t img;
-    Imlib_Image img_hdl;
+    mbe_surface_t *surf;
 };
 typedef struct _simple_mb_img_data simple_mb_img_data_t;
 
@@ -27,38 +28,35 @@
 mb_img_data_t *simple_mb_img_ldr_load(mb_img_ldr_t *ldr, const char *img_id) {
     simple_mb_img_ldr_t *sldr = (simple_mb_img_ldr_t *)ldr;
     simple_mb_img_data_t *img;
-    Imlib_Image img_hdl;
-    int w, h;
-    void *data;
+    mbe_surface_t *surf;
     char *fname;
     int sz;
 
     sz = strlen(sldr->repo);
     sz += strlen(img_id);
     fname = (char *)malloc(sz + 2);
-    strcpy(fname, sldr->repo);
+	if (img_id[0] != '/')
+        strcpy(fname, sldr->repo);
+	else
+		fname[0] = 0;
     strcat(fname, img_id);
-    
-    img_hdl = imlib_load_image(fname);
-    if(!img_hdl)
+
+    surf = mbe_image_surface_create_from_png(fname);
+    if(surf == NULL)
 	return NULL;
-    imlib_context_set_image(img_hdl);
-    w = imlib_image_get_width();
-    h = imlib_image_get_height();
-    data = imlib_image_get_data_for_reading_only();
 
     img = O_ALLOC(simple_mb_img_data_t);
     if(img == NULL) {
-	imlib_free_image();
+	mbe_surface_destroy(surf);
 	return NULL;
     }
-    img->img.content = data;
-    img->img.w = w;
-    img->img.h = h;
-    img->img.stride = w * 4;
-    img->img.fmt = MB_IFMT_ARGB32;
+    img->img.content = mbe_image_surface_get_data(surf);
+    img->img.w = mbe_image_surface_get_width(surf);
+    img->img.h = mbe_image_surface_get_height(surf);
+    img->img.stride = mbe_image_surface_get_stride(surf);
+    img->img.fmt = mbe_image_surface_get_format(surf);
     img->img.free = simple_mb_img_ldr_img_free;
-    img->img_hdl = img_hdl;
+    img->surf = surf;
 
     return (mb_img_data_t *)img;
 }
@@ -66,9 +64,7 @@
 static
 void simple_mb_img_ldr_img_free(mb_img_data_t *img) {
     simple_mb_img_data_t *simg = (simple_mb_img_data_t *)img;
-
-    imlib_context_set_image(simg->img_hdl);
-    imlib_free_image();
+    mbe_surface_destroy((mbe_surface_t *)simg->surf);
     free(img);
 }
 
@@ -104,9 +100,9 @@
 	((char *)ldr->repo)[sz] = '/';
 	((char *)ldr->repo)[sz + 1] = 0;
     }
-    
+
     ldr->ldr.load = simple_mb_img_ldr_load;
     ldr->ldr.free = simple_mb_img_ldr_free;
-    
+
     return (mb_img_ldr_t *)ldr;
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/img_ldr_imlib2.c	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,117 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
+#include <stdio.h>
+#include <string.h>
+#include <Imlib2.h>
+#include "mb_graph_engine.h"
+#include "mb_tools.h"
+#include "mb_paint.h"
+#include "mb_img_ldr.h"
+
+/*! \brief Simple image loader.
+ *
+ */
+struct _simple_mb_img_ldr {
+    mb_img_ldr_t ldr;
+    const char *repo;		/*!< \brief The directory of repository. */
+};
+typedef struct _simple_mb_img_ldr simple_mb_img_ldr_t;
+
+struct _simple_mb_img_data {
+    mb_img_data_t img;
+    Imlib_Image img_hdl;
+};
+typedef struct _simple_mb_img_data simple_mb_img_data_t;
+
+static void simple_mb_img_ldr_img_free(mb_img_data_t *img);
+
+static
+mb_img_data_t *simple_mb_img_ldr_load(mb_img_ldr_t *ldr, const char *img_id) {
+    simple_mb_img_ldr_t *sldr = (simple_mb_img_ldr_t *)ldr;
+    simple_mb_img_data_t *img;
+    Imlib_Image img_hdl;
+    int w, h;
+    void *data;
+    char *fname;
+    int sz;
+
+    sz = strlen(sldr->repo);
+    sz += strlen(img_id);
+    fname = (char *)malloc(sz + 2);
+	if (img_id[0] != '/')
+        strcpy(fname, sldr->repo);
+	else
+		fname[0] = 0;
+    strcat(fname, img_id);
+    
+    img_hdl = imlib_load_image(fname);
+    if(!img_hdl)
+	return NULL;
+    imlib_context_set_image(img_hdl);
+    w = imlib_image_get_width();
+    h = imlib_image_get_height();
+    data = imlib_image_get_data_for_reading_only();
+
+    img = O_ALLOC(simple_mb_img_data_t);
+    if(img == NULL) {
+	imlib_free_image();
+	return NULL;
+    }
+    img->img.content = data;
+    img->img.w = w;
+    img->img.h = h;
+    img->img.stride = w * 4;
+    img->img.fmt = MB_IFMT_ARGB32;
+    img->img.free = simple_mb_img_ldr_img_free;
+    img->img_hdl = img_hdl;
+
+    return (mb_img_data_t *)img;
+}
+
+static
+void simple_mb_img_ldr_img_free(mb_img_data_t *img) {
+    simple_mb_img_data_t *simg = (simple_mb_img_data_t *)img;
+
+    imlib_context_set_image(simg->img_hdl);
+    imlib_free_image();
+    free(img);
+}
+
+static
+void simple_mb_img_ldr_free(mb_img_ldr_t *ldr) {
+    simple_mb_img_ldr_t *defldr = (simple_mb_img_ldr_t *)ldr;
+
+    free((void *)defldr->repo);
+}
+
+mb_img_ldr_t *simple_mb_img_ldr_new(const char *img_repository) {
+    simple_mb_img_ldr_t *ldr;
+    int sz;
+
+    if(img_repository == NULL)
+	return NULL;
+
+    ldr = O_ALLOC(simple_mb_img_ldr_t);
+    if(ldr == NULL)
+	return NULL;
+
+    /*
+     * Copy and formalize path of image repository.
+     */
+    sz = strlen(img_repository);
+    ldr->repo = (const char *)malloc(sz + 2);
+    if(ldr->repo == NULL) {
+	free(ldr);
+	return NULL;
+    }
+    strcpy((char *)ldr->repo, img_repository);
+    if(img_repository[sz - 1] != '/' && strlen(img_repository) != 0) {
+	((char *)ldr->repo)[sz] = '/';
+	((char *)ldr->repo)[sz + 1] = 0;
+    }
+
+    ldr->ldr.load = simple_mb_img_ldr_load;
+    ldr->ldr.free = simple_mb_img_ldr_free;
+
+    return (mb_img_ldr_t *)ldr;
+}
--- a/src/mbaf/animated_menu.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/mbaf/animated_menu.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include <stdio.h>
 #include <mb.h>
 #include <string.h>
@@ -12,9 +14,11 @@
 
     FOR_COORD_MEMBERS(g, geo) {
         shape = geo_get_shape(geo);
+#ifdef SH_TEXT
         if(shape->obj.obj_type == MBO_TEXT) {
 		sh_text_set_text(shape, text);
         }
+#endif
     }
 }
 
@@ -22,13 +26,8 @@
 {
     int i;
     coord_t *textgroup;
-    shape_t *text;
     coord_t *group;
     coord_t *lightbar;
-    int tmp;
-    mb_timeval_t start, playing, now;
-    mb_progm_t *progm;
-    mb_word_t *word;
 
     // fill new item
     for(i=0;i<8;i++) {
@@ -63,7 +62,6 @@
 {
     int i;
     coord_t *textgroup;
-    shape_t *text;
     coord_t *group;
     coord_t *lightbar;
     int tmp;
@@ -100,7 +98,7 @@
 
     lightbar = (coord_t *) m->lightbar;
     mb_shift_new(0,m->menus_y[m->cur]-coord_y(lightbar),lightbar,word);
-    
+
     MB_TIMEVAL_SET(&start, 0, m->speed);
     MB_TIMEVAL_SET(&playing, 0, 0);
     word = mb_progm_next_word(progm, &start, &playing);
@@ -110,7 +108,7 @@
     mb_progm_free_completed(progm);
     m->ready--;
     subject_add_observer(mb_progm_get_complete(progm), mb_animated_menu_complete,m);
-    mb_progm_start(progm, X_MB_tman(MBAF_RDMAN(m->app)->rt), &now);
+    mb_progm_start(progm, mb_runtime_timer_man(MBAF_RDMAN(m->app)->rt), &now);
     rdman_redraw_changed(MBAF_RDMAN(m->app));
     tmp = m->items[8];
     for(i=8;i>0;i--) {
@@ -123,11 +121,6 @@
 static void mb_animated_menu_fillMenuContentDown(mb_animated_menu_t *m)
 {
     int i;
-    coord_t *textgroup;
-    shape_t *text;
-    coord_t *group;
-    coord_t *lightbar;
-    char name[255];
     int tmp;
     mb_timeval_t start, playing, now;
     mb_progm_t *progm;
@@ -165,7 +158,7 @@
     mb_progm_free_completed(progm);
     m->ready--;
     subject_add_observer(mb_progm_get_complete(progm), mb_animated_menu_complete,m);
-    mb_progm_start(progm, X_MB_tman(MBAF_RDMAN(m->app)->rt), &now);
+    mb_progm_start(progm, mb_runtime_timer_man(MBAF_RDMAN(m->app)->rt), &now);
     rdman_redraw_changed(MBAF_RDMAN(m->app));
     tmp = m->items[0];
     for(i=0;i<8;i++) {
@@ -179,7 +172,6 @@
     mb_timeval_t start, playing, now;
     mb_progm_t *progm;
     mb_word_t *word;
-    coord_t *group;
     coord_t *lightbar;
 
     m->progm = progm = mb_progm_new(1, MBAF_RDMAN(m->app));
@@ -193,7 +185,7 @@
     mb_progm_free_completed(progm);
     m->ready--;
     subject_add_observer(mb_progm_get_complete(progm), mb_animated_menu_complete,m);
-    mb_progm_start(progm, X_MB_tman(MBAF_RDMAN(m->app)->rt), &now);
+    mb_progm_start(progm, mb_runtime_timer_man(MBAF_RDMAN(m->app)->rt), &now);
     rdman_redraw_changed(MBAF_RDMAN(m->app));
 }
 
@@ -209,7 +201,7 @@
             mb_animated_menu_fillMenuContentUp(m);
 	    mb_animated_menu_update(m);
         } else {
-	    if (m->cur == 0) 
+	    if (m->cur == 0)
 	        return;
 	    m->cur--;
             mb_animated_menu_moveLightBar(m);
@@ -268,7 +260,7 @@
 {
     mb_animated_menu_t *m = (mb_animated_menu_t *) arg;
     X_kb_event_t *xkey;
-    
+
     xkey = &m->pending_keys[m->pending_pos];
     m->pending_pos = (m->pending_pos + 1) & 0xf;
     mb_animated_menu_keyHandler((event_t *) xkey, m);
@@ -314,9 +306,9 @@
     }
 }
 
-/** \brief Create an instace of animated menu. 
+/** \brief Create an instace of animated menu.
  *
- *   The objectnames is used to extract symbols from the SVG file. 
+ *   The objectnames is used to extract symbols from the SVG file.
  *         ${objectnames}0 - ${objectnames}8 is the text object.
  *         ${objectnames}_lightbar is the lightbar.
  *
@@ -324,7 +316,7 @@
 mb_animated_menu_t *mb_animated_menu_new(mbaf_t *app,mb_sprite_t *sp,char *objnames,char *menus[])
 {
     mb_animated_menu_t *m;
-    int i,len;
+    int i;
     char name[255];
     mb_obj_t *l;
     int ii;
@@ -334,7 +326,7 @@
     else
 	    for(i=0;menus[i];i++);
     ii=9;
-    
+
     m = (mb_animated_menu_t *) malloc(sizeof(mb_animated_menu_t));
     m->items = (int *) malloc(sizeof(int)*ii*2+sizeof(mb_obj_t *)*ii);
     m->app = app;
--- a/src/mbaf/mbapp.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/mbaf/mbapp.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,31 +1,34 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include <mb.h>
 #include <mb_af.h>
+#include <mb_backend.h>
 
 mbaf_t *mbaf_init(const char *module, const char *module_dir)
 {
     mbaf_t *app = (mbaf_t *) malloc(sizeof(mbaf_t));
-    void *rt;
+    mb_rt_t *rt;
 
-    rt = backend.init(":0.0", 800, 600);
+    rt = mb_runtime_new(":0.0", 800, 600);
     if(rt == NULL)
 	return NULL;
 
     sprite_set_search_path(module_dir);
 
     app->rt = rt;
-    app->rdman =  backend.rdman(rt);
-    app->kbevents = backend.kbevents(rt);
-    
+    app->rdman =  mb_runtime_rdman(rt);
+    app->kbevents = mb_runtime_kbevents(rt);
+
     app->rootsprite= sprite_load(module,app->rdman, app->rdman->root_coord);
     if(app->rootsprite == NULL) {
-	backend.free(rt);
+	mb_runtime_free(rt);
 	free(app);
 	return NULL;
     }
-    
+
     rdman_attach_backend(app->rdman, rt);
     MB_SPRITE_GOTO_SCENE(app->rootsprite, 1);
-    
+
     return app;
 }
 
@@ -34,9 +37,9 @@
     app->private = (void *) data;
 }
 
-mb_tman_t *mbaf_get_timer(mbaf_t *app)
+mb_timer_man_t *mbaf_get_timer(mbaf_t *app)
 {
-    return backend.tman(app->rt);
+    return mb_runtime_timer_man(app->rt);
 }
 
 void mbaf_loop(mbaf_t *app)
@@ -45,11 +48,11 @@
      * Start handle connections, includes one to X server.
      * User start to interact with the application.
      */
-    backend.loop(app->rt);
+    mb_runtime_event_loop(app->rt);
 
     /*
      * Clean
      */
-    backend.free(app->rt);
+    mb_runtime_free(app->rt);
     free(app);
 }
--- a/src/mbaf/mbbutton.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/mbaf/mbbutton.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 
 #include <stdio.h>
 #include <mb.h>
@@ -24,16 +26,16 @@
     rdman_redraw_changed(btn->rdman);
 }
 
-static void mb_button_move(event_t *evt, void *arg) 
+static void mb_button_move(event_t *evt, void *arg)
 {
     mb_button_t *btn = (mb_button_t *) arg;
 
-    
+
     printf("Mouse move\n");
     coord_show(btn->active);
     mb_button_refresh(btn);
 }
-static void mb_button_out(event_t *evt, void *arg) 
+static void mb_button_out(event_t *evt, void *arg)
 {
     mb_button_t *btn = (mb_button_t *) arg;
 
@@ -61,7 +63,7 @@
     mb_button_t *btn = (mb_button_t *) arg;
 
     btn->progm = NULL;
-    
+
 }
 
 static void mb_button_pressed(event_t *evt, void *arg)
@@ -91,7 +93,7 @@
     subject_add_observer(mb_progm_get_complete(btn->progm), mb_button_end_animated_cb,btn);
     get_now(&now);
     printf("rt = %x\n", btn->rdman->rt);
-    mb_progm_start(progm, X_MB_tman(btn->rdman->rt), &now);
+    mb_progm_start(progm, mb_runtime_timer_man(btn->rdman->rt), &now);
     if (btn->press)
     	btn->press(btn->arg);
 }
--- a/src/mbaf/mbobject.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/mbaf/mbobject.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,5 +1,8 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include "mb_types.h"
 #include "mb_obj.h"
+#include "mb_config.h"
 
 
 void mb_obj_set_pos(mb_obj_t *obj, co_aix x, co_aix y)
@@ -7,12 +10,14 @@
     if (MBO_TYPE(obj) == MBO_COORD) {
 	    coord_x(((coord_t *) obj)) = x;
 	    coord_y(((coord_t *) obj)) = y;
+#ifdef SH_TEXT
     } else if (MBO_TYPE(obj) == MBO_TEXT) {
 	    sh_text_set_pos((shape_t *) obj, x, y);
+#endif
     } else {
 	    return;
     }
-    
+
 }
 
 void mb_obj_get_pos(mb_obj_t *obj, co_aix *x, co_aix *y)
@@ -20,12 +25,14 @@
     if (MBO_TYPE(obj) == MBO_COORD) {
 	    *x = coord_x((coord_t *) obj);
 	    *y = coord_y((coord_t *) obj);
+#ifdef SH_TEXT
     } else if (MBO_TYPE(obj) == MBO_TEXT) {
 	    sh_text_get_pos((shape_t *) obj, x, y);
+#endif
     } else {
 	    return;
     }
-    
+
 }
 
 void mb_obj_set_text(mb_obj_t *obj, const char *text)
@@ -37,17 +44,21 @@
 
         FOR_COORD_MEMBERS(g, geo) {
             shape = geo_get_shape(geo);
+#ifdef SH_TEXT
             if(shape->obj.obj_type == MBO_TEXT) {
 		sh_text_set_text(shape, text);
 		return;
             }
+#endif
         }
+#ifdef SH_TEXT
     } else if (MBO_TYPE(obj) == MBO_TEXT) {
 	    sh_text_set_text((shape_t *) obj,text);
+#endif
     } else {
 	    return;
     }
-    
+
 }
 
 
@@ -60,13 +71,17 @@
 
         FOR_COORD_MEMBERS(g, geo) {
             shape = geo_get_shape(geo);
+#ifdef SH_TEXT
             if(shape->obj.obj_type == MBO_TEXT) {
 		sh_text_get_text(shape, text,size);
 		return;
             }
+#endif
         }
+#ifdef SH_TEXT
     } else if (MBO_TYPE(obj) == MBO_TEXT) {
 	    sh_text_get_text((shape_t *) obj,text,size);
+#endif
     } else {
 	    *text = 0;
 	    return;
--- a/src/mouse.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/mouse.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include "mb_types.h"
 #include "mb_redraw_man.h"
 
@@ -10,20 +12,20 @@
     mouse_event_t new_evt;
     coord_t *coord;
     shape_t *shape;
-    
+
     ASSERT(evt->type == EVT_MOUSE_MOVE_RAW);
-    
+
     obj = (mb_obj_t *)subject_get_object(evt->cur_tgt);
     if(rdman->last_mouse_over == obj) {
 	evt->type = EVT_MOUSE_MOVE;
 	return;
     }
-    
+
     new_evt.x = mevt->x;
     new_evt.y = mevt->y;
     new_evt.but_state = mevt->but_state;
     new_evt.button = mevt->button;
-    
+
     if(rdman->last_mouse_over != NULL) {
 	new_evt.event.type = EVT_MOUSE_OUT;
 	if(IS_MBO_COORD(rdman->last_mouse_over)) {
@@ -33,14 +35,14 @@
 	    shape = (shape_t *)rdman->last_mouse_over;
 	    ASSERT(shape->geo != NULL);
 	    subject_notify(sh_get_mouse_event_subject(shape),
-			   (event_t *)&new_evt); 
+			   (event_t *)&new_evt);
 	}
     }
 
     new_evt.event.type = EVT_MOUSE_OVER;
     subject_notify(evt->cur_tgt, (event_t *)&new_evt);
     rdman->last_mouse_over = obj;
-    
+
     evt->flags |= EVTF_STOP_NOTIFY;
 }
 
@@ -61,7 +63,7 @@
     mb_prop_store_t *props;
     observer_t *observer;
     int cnt = 0;
-    
+
     mevt = (monitor_event_t *)evt;
     rdman = (redraw_man_t *)arg;
     obj = (mb_obj_t *)subject_get_object(mevt->subject);
@@ -73,7 +75,7 @@
 	    cnt = 0;
 	else
 	    cnt = (int)mb_prop_get(props, PROP_MEVT_OB_CNT);
-	
+
 	cnt++;
 	mb_prop_set(props, PROP_MEVT_OB_CNT, (void *)cnt);
 	if(cnt == 1) {
@@ -86,7 +88,7 @@
 	    mb_prop_set(props, PROP_MEVT_OBSERVER, observer);
 	}
 	break;
-	
+
     case EVT_MONITOR_REMOVE:
 	cnt = (int)mb_prop_get(props, PROP_MEVT_OB_CNT);
 	cnt--;
@@ -97,7 +99,7 @@
 	    mb_prop_del(props, PROP_MEVT_OBSERVER);
 	}
 	break;
-	
+
     case EVT_MONITOR_FREE:
 	break;
     }
--- a/src/observer.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/observer.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include <stdio.h>
 #include "mb_redraw_man.h"
 #include "mb_observer.h"
@@ -7,7 +9,7 @@
 #define ASSERT(x)
 #endif
 
-subject_t *subject_new(ob_factory_t *factory, void *obj, int obj_type) {
+subject_t *subject_new(observer_factory_t *factory, void *obj, int obj_type) {
     subject_t *subject;
 
     subject = factory->subject_alloc(factory);
@@ -26,10 +28,10 @@
 }
 
 /*!
- * \todo Keep ob_factory following subject objects.
+ * \todo Keep observer_factory following subject objects.
  */
 void subject_free(subject_t *subject) {
-    ob_factory_t *factory = subject->factory;
+    observer_factory_t *factory = subject->factory;
     observer_t *observer;
     monitor_event_t mevt;
 
@@ -41,7 +43,7 @@
 	subject->flags |= SUBF_FREE;
 	return;
     }
-    
+
     if(subject->monitor_sub) {
 	mevt.event.type = EVT_MONITOR_FREE;
 	mevt.subject = subject;
@@ -58,7 +60,7 @@
 
 
 void subject_notify(subject_t *subject, event_t *evt) {
-    ob_factory_t *factory = subject->factory;
+    observer_factory_t *factory = subject->factory;
     observer_t *observer;
     subject_t *old_subject;
     int stop_propagate = 0;
@@ -81,7 +83,7 @@
 	    observer = STAILQ_NEXT(observer_t, next, observer)) {
 	    if (observer->type == EVT_ANY || observer->type == evt->type) {
 		observer->hdr(evt, observer->arg);
-		
+
 		if(evt->flags & EVTF_STOP_NOTIFY) {
 		    stop_propagate = 1;
 			break;
@@ -112,7 +114,7 @@
  */
 observer_t *subject_add_event_observer(subject_t *subject, int type,
 				 evt_handler hdr, void *arg) {
-    ob_factory_t *factory = subject->factory;
+    observer_factory_t *factory = subject->factory;
     observer_t *observer;
     monitor_event_t mevt;
 
@@ -139,7 +141,7 @@
  */
 observer_t *subject_add_event_observer_head(subject_t *subject, int type,
 					    evt_handler hdr, void *arg) {
-    ob_factory_t *factory = subject->factory;
+    observer_factory_t *factory = subject->factory;
     observer_t *observer;
     monitor_event_t mevt;
 
@@ -164,11 +166,11 @@
 
 void subject_remove_observer(subject_t *subject,
 			     observer_t *observer) {
-    ob_factory_t *factory = subject->factory;
+    observer_factory_t *factory = subject->factory;
     monitor_event_t mevt;
 
     STAILQ_REMOVE(subject->observers, observer_t, next, observer);
-    
+
     if(subject->monitor_sub) {
 	mevt.event.type = EVT_MONITOR_REMOVE;
 	mevt.subject = subject;
@@ -184,34 +186,36 @@
 #include <CUnit/Basic.h>
 #include <stdlib.h>
 
-static subject_t *test_subject_alloc(ob_factory_t *factory) {
+static subject_t *test_subject_alloc(observer_factory_t *factory) {
     subject_t *subject;
 
     subject = (subject_t *)malloc(sizeof(subject_t));
     return subject;
 }
 
-static void test_subject_free(ob_factory_t *factory, subject_t *subject) {
+static void
+test_subject_free(observer_factory_t *factory, subject_t *subject) {
     free(subject);
 }
 
-static observer_t *test_observer_alloc(ob_factory_t *factory) {
+static observer_t *test_observer_alloc(observer_factory_t *factory) {
     observer_t *observer;
 
     observer = (observer_t *)malloc(sizeof(observer_t));
     return observer;
 }
 
-static void test_observer_free(ob_factory_t *factory, observer_t *observer) {
+static void
+test_observer_free(observer_factory_t *factory, observer_t *observer) {
     free(observer);
 }
 
-static subject_t *test_get_parent_subject(ob_factory_t *factory,
+static subject_t *test_get_parent_subject(observer_factory_t *factory,
 					  subject_t *subject) {
     return NULL;
 }
 
-static ob_factory_t test_factory = {
+static observer_factory_t test_factory = {
     test_subject_alloc,
     test_subject_free,
     test_observer_alloc,
--- a/src/paint.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/paint.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include <stdio.h>
 #include <stdlib.h>
 #include "mb_graph_engine.h"
@@ -15,7 +17,7 @@
 int _paint_color_size = sizeof(paint_color_t);
 
 
-static void paint_color_prepare(paint_t *paint, mbe_t *cr) {
+static void paint_color_prepare(paint_t *paint, mbe_t *cr, shape_t *sh) {
     paint_color_t *color = (paint_color_t *)paint;
 
     mbe_set_source_rgba(cr, color->r, color->g, color->b, color->a);
@@ -80,19 +82,29 @@
 
 #define LIF_DIRTY 0x1
 
-static void paint_linear_prepare(paint_t *paint, mbe_t *cr) {
+int _paint_linear_size = sizeof(paint_linear_t);
+
+static void paint_linear_prepare(paint_t *paint, mbe_t *cr, shape_t *sh) {
     paint_linear_t *linear = (paint_linear_t *)paint;
     mbe_pattern_t *ptn;
-    grad_stop_t *stop;
-    int i;
+    co_aix x1, y1;
+    co_aix x2, y2;
+    co_aix *mtx;
 
     ptn = linear->ptn;
     if(linear->flags & LIF_DIRTY) {
+	mtx = sh_get_aggr_matrix(sh);
+	x1 = linear->x1;
+	y1 = linear->y1;
+	matrix_trans_pos(mtx, &x1, &y1);
+	x2 = linear->x2;
+	y2 = linear->y2;
+	matrix_trans_pos(mtx, &x2, &y2);
+
 	if(ptn)
 	    mbe_pattern_destroy(ptn);
 	linear->flags &= ~LIF_DIRTY;
-	ptn = mbe_pattern_create_linear(linear->x1, linear->y1,
-					linear->x2, linear->y2,
+	ptn = mbe_pattern_create_linear(x1, y1, x2, y2,
 					linear->stops, linear->n_stops);
 	ASSERT(ptn != NULL);
 	linear->ptn = ptn;
@@ -107,7 +119,7 @@
     if(linear->ptn)
 	mbe_pattern_destroy(linear->ptn);
     paint_destroy(paint);
-    free(paint);
+    elmpool_elm_free(rdman->paint_linear_pool, linear);
 }
 
 paint_t *rdman_paint_linear_new(redraw_man_t *rdman,
@@ -115,7 +127,7 @@
 				co_aix x2, co_aix y2) {
     paint_linear_t *linear;
 
-    linear = (paint_linear_t *)malloc(sizeof(paint_linear_t));
+    linear = (paint_linear_t *)elmpool_elm_alloc(rdman->paint_linear_pool);
     if(linear == NULL)
 	return NULL;
 
@@ -145,7 +157,7 @@
 				grad_stop_t *stops) {
     paint_linear_t *linear = (paint_linear_t *)paint;
     grad_stop_t *old_stops;
-    
+
     old_stops = linear->stops;
     linear->n_stops = n_stops;
     linear->stops = stops;
@@ -170,14 +182,22 @@
 
 #define RDF_DIRTY 0x1
 
-static void paint_radial_prepare(paint_t *paint, mbe_t *cr) {
+int _paint_radial_size = sizeof(paint_radial_t);
+
+static void paint_radial_prepare(paint_t *paint, mbe_t *cr, shape_t *sh) {
     paint_radial_t *radial = (paint_radial_t *)paint;
     mbe_pattern_t *ptn;
-    int i;
+    co_aix cx, cy;
+    co_aix *mtx;
 
     if(radial->flags & RDF_DIRTY) {
-	ptn = mbe_pattern_create_radial(radial->cx, radial->cy, 0,
-					  radial->cx, radial->cy,
+	mtx = sh_get_aggr_matrix(sh);
+	cx = radial->cx;
+	cy = radial->cy;
+	matrix_trans_pos(mtx, &cx, &cy);
+	
+	ptn = mbe_pattern_create_radial(cx, cy, 0,
+					cx, cy,
 					radial->r,
 					radial->stops,
 					radial->n_stops);
@@ -194,14 +214,14 @@
     if(radial->ptn)
 	mbe_pattern_destroy(radial->ptn);
     paint_destroy(paint);
-    free(paint);
+    elmpool_elm_free(rdman->paint_radial_pool, radial);
 }
 
 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);
+    radial = elmpool_elm_alloc(rdman->paint_radial_pool);
     if(radial == NULL)
 	return NULL;
 
@@ -229,7 +249,7 @@
 				grad_stop_t *stops) {
     paint_radial_t *radial = (paint_radial_t *)paint;
     grad_stop_t *old_stops;
-    
+
     old_stops = radial->stops;
     radial->n_stops = n_stops;
     radial->stops = stops;
@@ -246,11 +266,14 @@
 typedef struct _paint_image {
     paint_t paint;
     mb_img_data_t *img;
+    mbe_surface_t *surf;
     mbe_pattern_t *ptn;
 } paint_image_t;
 
+int _paint_image_size = sizeof(paint_image_t);
+
 static
-void paint_image_prepare(paint_t *paint, mbe_t *cr) {
+void paint_image_prepare(paint_t *paint, mbe_t *cr, shape_t *sh) {
     paint_image_t *paint_img = (paint_image_t *)paint;
     mb_img_data_t *img_data;
 
@@ -262,11 +285,12 @@
 void paint_image_free(redraw_man_t *rdman, paint_t *paint) {
     paint_image_t *paint_img = (paint_image_t *)paint;
     mb_img_data_t *img_data;
-    
+
+    mbe_surface_destroy(paint_img->surf);
     img_data = paint_img->img;
     MB_IMG_DATA_FREE(img_data);
     paint_destroy(&paint_img->paint);
-    free(paint);
+    elmpool_elm_free(rdman->paint_image_pool, paint_img);
 }
 
 /*! \brief Create an image painter.
@@ -280,18 +304,29 @@
 			       mb_img_data_t *img) {
     paint_image_t *paint;
 
-    paint = O_ALLOC(paint_image_t);
+    paint = elmpool_elm_alloc(rdman->paint_image_pool);
     if(paint == NULL)
 	return NULL;
-    
+
     paint_init(&paint->paint, MBP_IMAGE,
 	       paint_image_prepare, paint_image_free);
     paint->img = img;
-    
-    paint->ptn = mbe_pattern_create_image(img);
+    paint->surf = mbe_image_surface_create_for_data(img->content,
+						      img->fmt,
+						      img->w,
+						      img->h,
+						      img->stride);
+    if(paint->surf == NULL) {
+	paint_destroy(&paint->paint);
+	elmpool_elm_free(rdman->paint_image_pool, paint);
+	return NULL;
+    }
+
+    paint->ptn = mbe_pattern_create_for_surface(paint->surf);
     if(paint->ptn == NULL) {
 	paint_destroy(&paint->paint);
-	free(paint);
+	mbe_surface_destroy(paint->surf);
+	elmpool_elm_free(rdman->paint_image_pool, paint);
 	return NULL;
     }
 
@@ -306,13 +341,13 @@
  */
 void paint_image_set_matrix(paint_t *paint, co_aix matrix[6]) {
     paint_image_t *img_paint = (paint_image_t *)paint;
-    
+
     mbe_pattern_set_matrix(img_paint->ptn, matrix);
 }
 
 void paint_image_get_size(paint_t *paint, int *w, int *h) {
     paint_image_t *ipaint = (paint_image_t *)paint;
-    
+
     ASSERT(paint->pnt_type == MBP_IMAGE);
     *w = ipaint->img->w;
     *h = ipaint->img->h;
--- a/src/prop.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/prop.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include "mb_prop.h"
 
 #define ASSERT(x)
@@ -13,7 +15,7 @@
 	if(entry->id == id)
 	    return entry;
     }
-    
+
     return NULL;
 }
 
@@ -79,6 +81,6 @@
     entry = _mb_prop_find(prop_store, id);
     if(entry)
 	return 1;
-    
+
     return 0;
 }
--- a/src/redraw_man.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/redraw_man.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -158,7 +160,7 @@
  *   - areas of descendants of cached coord are in space defined
  *     by aggr_matrix of cached coord.
  *   - descendants are marked with \ref COF_ANCESTOR_CACHE
- * 
+ *
  * Since *_transform of shapes compute area with aggr_matrix that is
  * derived from aggr_matrix of a cached ancestor, area of
  * \ref COF_ANCESTOR_CACHE ones should be transformed to device space in
@@ -237,7 +239,7 @@
  * process of adjusting left-top of bounding box is zeroing.
  *
  * Following is rules.
- * - zeroing on a cached coord is performed by adjust coord_t::aggr_matrix 
+ * - zeroing on a cached coord is performed by adjust coord_t::aggr_matrix
  *   of the cached coord and descendnats.
  * - Clean coords works just like before without change.
  *   - in preorder
@@ -302,6 +304,38 @@
 #define ASSERT(x)
 #endif
 
+/*
+ * Conitions for coords.
+ */
+#define coord_is_cached(co) ((co)->flags & COF_OWN_CANVAS)
+#define coord_is_always_cached(co) ((co)->flags & COF_ALWAYS_CACHE)
+#define coord_is_fast_cached(co) ((co)->flags & COF_FAST_MASK)
+#define coord_is_precise_cached(co) ((co)->flags & COF_PRECISE_MASK)
+#define coord_is_zeroing(co) ((co)->flags & COF_MUST_ZEROING)
+#define coord_set_zeroing(co) \
+    do { (co)->flags |= COF_MUST_ZEROING; } while(0)
+#define coord_clear_zeroing(co) \
+    do { (co)->flags &= ~COF_MUST_ZEROING; } while(0)
+#define coord_set_flags(co, _flags)		\
+    do { (co)->flags |= (_flags); } while(0)
+#define coord_get_parent(co) ((co)->parent)
+#define coord_get_flags(co, _flags) ((co)->flags & (_flags))
+#define coord_clear_flags(co, _flags)		\
+    do { (co)->flags &= ~(_flags); } while(0)
+
+#define coord_get_pcache_area(coord) ((coord)->canvas_info->pcache_cur_area)
+#define coord_get_pcache_last_area(coord)	\
+    ((coord)->canvas_info->pcache_last_area)
+#define coord_get_cached(coord) ((coord)->canvas_info->owner)
+#define _coord_get_dirty_areas(coord) (&(coord)->canvas_info->dirty_areas)
+#define _coord_get_aggr_dirty_areas(coord)	\
+    ((coord)->canvas_info->aggr_dirty_areas)
+#define coord_get_2pdev(coord) ((coord)->canvas_info->cache_2_pdev)
+#define coord_get_2pdev_rev(coord) ((coord)->canvas_info->cache_2_pdev_rev)
+#define coord_get_aggr2pdev(coord) ((coord)->canvas_info->aggr_2_pdev)
+#define coord_get_aggr2pdev_rev(coord) ((coord)->canvas_info->aggr_2_pdev_rev)
+
+
 /* NOTE: bounding box should also consider width of stroke.
  */
 
@@ -336,12 +370,14 @@
 extern void sh_dummy_fill(shape_t *, mbe_t *);
 #endif /* UNITTEST */
 
-static subject_t *ob_subject_alloc(ob_factory_t *factory);
-static void ob_subject_free(ob_factory_t *factory, subject_t *subject);
-static observer_t *ob_observer_alloc(ob_factory_t *factory);
-static void ob_observer_free(ob_factory_t *factory, observer_t *observer);
-static subject_t *ob_get_parent_subject(ob_factory_t *factory,
-					subject_t *cur_subject);
+static subject_t *observer_subject_alloc(observer_factory_t *factory);
+static void observer_subject_free(observer_factory_t *factory,
+				  subject_t *subject);
+static observer_t *observer_observer_alloc(observer_factory_t *factory);
+static void observer_observer_free(observer_factory_t *factory,
+				   observer_t *observer);
+static subject_t *observer_get_parent_subject(observer_factory_t *factory,
+					      subject_t *cur_subject);
 /* Functions for children. */
 #define FORCHILDREN(coord, child)				\
     for((child) = STAILQ_HEAD((coord)->children);		\
@@ -456,10 +492,10 @@
 
 static int add_dirty_area(redraw_man_t *rdman, coord_t *coord, area_t *area) {
     int r;
-    
+
     if(area->w < 0.01 || area->h < 0.01)
 	return OK;
-    
+
     rdman->n_dirty_areas++;
     r = areas_add(_coord_get_dirty_areas(coord), area);
     return r == 0? OK: ERR;
@@ -472,8 +508,10 @@
 }
 
 static int add_dirty_pcache_area_coord(redraw_man_t *rdman, coord_t *coord) {
-    coord_set_flags(coord, COF_DIRTY_PCACHE_AREA);
-    ADD_DATA(coords, dirty_pcache_area_coords, coord);
+    if(!coord_get_flags(coord, COF_DIRTY_PCACHE_AREA)) {
+	coord_set_flags(coord, COF_DIRTY_PCACHE_AREA);
+	ADD_DATA(coords, zeroing_coords, coord);
+    }
     return OK;
 }
 
@@ -515,26 +553,41 @@
 	free(rdman->free_objs.objs);
 }
 
-
+#ifdef UNITTEST
+/*! \brief This is only used for unittest.
+ */
+typedef struct {
+    co_aix parent_2_cache[6];
+    int w, h;
+} mock_mbe_t;
+#endif
 
 static mbe_t *canvas_new(int w, int h) {
 #ifndef UNITTEST
     mbe_surface_t *surface;
     mbe_t *cr;
-    
+
     surface = mbe_image_surface_create(MB_IFMT_ARGB32,
 					 w, h);
     cr = mbe_create(surface);
 
     return cr;
 #else
-    return NULL;
+    mock_mbe_t *mbe;
+
+    mbe = malloc(sizeof(mock_mbe_t));
+    mbe->w = w;
+    mbe->h = h;
+    
+    return (mbe_t *)mbe;
 #endif
 }
 
 static void canvas_free(mbe_t *canvas) {
 #ifndef UNITTEST
     mbe_destroy(canvas);
+#else
+    free(canvas);
 #endif
 }
 
@@ -546,8 +599,11 @@
     *w = mbe_image_surface_get_width(surface);
     *h = mbe_image_surface_get_height(surface);
 #else
-    *w = 0;
-    *h = 0;
+    mock_mbe_t *mbe;
+
+    mbe = (mock_mbe_t *)canvas;
+    *w = mbe->w;
+    *h = mbe->h;
 #endif
 }
 
@@ -584,22 +640,29 @@
     coord->num_members--;
 }
 
-static coord_canvas_info_t *coord_canvas_info_new(redraw_man_t *rdman,
-						  coord_t *coord,
-						  mbe_t *canvas) {
+/*! \brief Create a new canvas and respective info struct for a coord.
+ */
+static coord_canvas_info_t *
+coord_canvas_info_new(redraw_man_t *rdman, coord_t *coord,
+		      mbe_t *canvas) {
     coord_canvas_info_t *info;
+    static co_aix id[6] = {1, 0, 0, 0, 1, 0};
 
     info = (coord_canvas_info_t *)elmpool_elm_alloc(rdman->coord_canvas_pool);
     if(info == NULL)
 	return info;
-    
+
     info->owner = coord;
     info->canvas = canvas;
     DARRAY_INIT(&info->dirty_areas);
-    
+
     bzero(info->pcache_areas, sizeof(area_t) * 2);
     info->pcache_cur_area = &info->pcache_areas[0];
     info->pcache_last_area = &info->pcache_areas[1];
+    memcpy(info->cache_2_pdev, id, sizeof(co_aix) * 6);
+    memcpy(info->cache_2_pdev_rev, id, sizeof(co_aix) * 6);
+    memcpy(info->aggr_2_pdev, id, sizeof(co_aix) * 6);
+    memcpy(info->aggr_2_pdev_rev, id, sizeof(co_aix) * 6);
 
     return info;
 }
@@ -615,24 +678,33 @@
 
 int redraw_man_init(redraw_man_t *rdman, mbe_t *cr, mbe_t *backend) {
     extern void redraw_man_destroy(redraw_man_t *rdman);
+    extern int _sh_path_size;
+    extern int _sh_rect_size;
     extern int _paint_color_size;
+    extern int _paint_linear_size;
+    extern int _paint_radial_size;
+    extern int _paint_image_size;
     observer_t *addrm_ob;
     extern void addrm_monitor_hdlr(event_t *evt, void *arg);
 
     memset(rdman, 0, sizeof(redraw_man_t));
 
     DARRAY_INIT(&rdman->dirty_coords);
-    DARRAY_INIT(&rdman->dirty_pcache_area_coords);
     DARRAY_INIT(&rdman->dirty_geos);
     DARRAY_INIT(&rdman->gen_geos);
     DARRAY_INIT(&rdman->zeroing_coords);
-    
+
     rdman->geo_pool = elmpool_new(sizeof(geo_t), 128);
     rdman->coord_pool = elmpool_new(sizeof(coord_t), 16);
     rdman->shnode_pool = elmpool_new(sizeof(shnode_t), 16);
+    rdman->sh_path_pool = elmpool_new(_sh_path_size, 16);
+    rdman->sh_rect_pool = elmpool_new(_sh_rect_size, 16);
     rdman->observer_pool = elmpool_new(sizeof(observer_t), 32);
     rdman->subject_pool = elmpool_new(sizeof(subject_t), 32);
     rdman->paint_color_pool = elmpool_new(_paint_color_size, 64);
+    rdman->paint_linear_pool = elmpool_new(_paint_linear_size, 64);
+    rdman->paint_radial_pool = elmpool_new(_paint_radial_size, 64);
+    rdman->paint_image_pool = elmpool_new(_paint_image_size, 64);
     rdman->pent_pool = elmpool_new(sizeof(mb_prop_entry_t), 128);
     rdman->coord_canvas_pool = elmpool_new(sizeof(coord_canvas_info_t), 16);
     if(!(rdman->geo_pool && rdman->coord_pool && rdman->shnode_pool &&
@@ -640,16 +712,16 @@
 	 rdman->paint_color_pool && rdman->coord_canvas_pool))
 	goto err;
 
-    rdman->ob_factory.subject_alloc = ob_subject_alloc;
-    rdman->ob_factory.subject_free = ob_subject_free;
-    rdman->ob_factory.observer_alloc = ob_observer_alloc;
-    rdman->ob_factory.observer_free = ob_observer_free;
-    rdman->ob_factory.get_parent_subject = ob_get_parent_subject;
+    rdman->observer_factory.subject_alloc = observer_subject_alloc;
+    rdman->observer_factory.subject_free = observer_subject_free;
+    rdman->observer_factory.observer_alloc = observer_observer_alloc;
+    rdman->observer_factory.observer_free = observer_observer_free;
+    rdman->observer_factory.get_parent_subject = observer_get_parent_subject;
 
     rdman->redraw =
-	subject_new(&rdman->ob_factory, rdman, OBJT_RDMAN);
+	subject_new(&rdman->observer_factory, rdman, OBJT_RDMAN);
     rdman->addrm_monitor =
-	subject_new(&rdman->ob_factory, rdman, OBJT_RDMAN);
+	subject_new(&rdman->observer_factory, rdman, OBJT_RDMAN);
     if(!(rdman->redraw && rdman->addrm_monitor))
 	goto err;
 
@@ -666,7 +738,7 @@
     rdman->n_coords = 1;
     coord_init(rdman->root_coord, NULL);
     mb_prop_store_init(&rdman->root_coord->obj.props, rdman->pent_pool);
-    rdman->root_coord->mouse_event = subject_new(&rdman->ob_factory,
+    rdman->root_coord->mouse_event = subject_new(&rdman->observer_factory,
 						 rdman->root_coord,
 						 OBJT_COORD);
     coord_set_flags(rdman->root_coord, COF_OWN_CANVAS);
@@ -678,7 +750,7 @@
     rdman->backend = backend;
 
     STAILQ_INIT(rdman->shapes);
-    
+
     /* \note To make root coord always have at leat one observer.
      * It triggers mouse interpreter to be installed on root.
      */
@@ -697,18 +769,27 @@
 	elmpool_free(rdman->coord_pool);
     if(rdman->shnode_pool)
 	elmpool_free(rdman->shnode_pool);
+    if(rdman->sh_path_pool)
+	elmpool_free(rdman->sh_path_pool);
+    if(rdman->sh_rect_pool)
+	elmpool_free(rdman->sh_rect_pool);
     if(rdman->observer_pool)
 	elmpool_free(rdman->observer_pool);
     if(rdman->subject_pool)
 	elmpool_free(rdman->subject_pool);
     if(rdman->paint_color_pool)
 	elmpool_free(rdman->paint_color_pool);
+    if(rdman->paint_linear_pool)
+	elmpool_free(rdman->paint_linear_pool);
+    if(rdman->paint_radial_pool)
+	elmpool_free(rdman->paint_radial_pool);
+    if(rdman->paint_image_pool)
+	elmpool_free(rdman->paint_image_pool);
     if(rdman->pent_pool)
 	elmpool_free(rdman->pent_pool);
     if(rdman->coord_canvas_pool)
 	elmpool_free(rdman->coord_canvas_pool);
     DARRAY_DESTROY(&rdman->dirty_coords);
-    DARRAY_DESTROY(&rdman->dirty_pcache_area_coords);
     DARRAY_DESTROY(&rdman->dirty_geos);
     DARRAY_DESTROY(&rdman->gen_geos);
     DARRAY_DESTROY(&rdman->zeroing_coords);
@@ -717,7 +798,7 @@
 
 void redraw_man_destroy(redraw_man_t *rdman) {
     coord_t *coord, *saved_coord;
-    shape_t *shape, *saved_shape;
+    shape_t *shape;
     geo_t *member;
 
     mb_prop_store_destroy(&rdman->props);
@@ -729,7 +810,6 @@
      *  successfully.
      */
     DARRAY_CLEAN(&rdman->dirty_coords);
-    DARRAY_CLEAN(&rdman->dirty_pcache_area_coords);
     DARRAY_CLEAN(&rdman->dirty_geos);
 
     coord = postorder_coord_subtree(rdman->root_coord, NULL);
@@ -748,24 +828,28 @@
     while((shape = STAILQ_HEAD(rdman->shapes)) != NULL) {
 	rdman_shape_free(rdman, shape);
     }
-    
+
     coord_canvas_info_free(rdman, rdman->root_coord->canvas_info);
 
     /* XXX: paints are not freed, here.  All resources of paints would
      * be reclaimed by freeing elmpools.
      */
-    
+
     elmpool_free(rdman->coord_pool);
     elmpool_free(rdman->geo_pool);
     elmpool_free(rdman->shnode_pool);
+    elmpool_free(rdman->sh_path_pool);
+    elmpool_free(rdman->sh_rect_pool);
     elmpool_free(rdman->observer_pool);
     elmpool_free(rdman->subject_pool);
     elmpool_free(rdman->paint_color_pool);
+    elmpool_free(rdman->paint_linear_pool);
+    elmpool_free(rdman->paint_radial_pool);
+    elmpool_free(rdman->paint_image_pool);
     elmpool_free(rdman->pent_pool);
     elmpool_free(rdman->coord_canvas_pool);
 
     DARRAY_DESTROY(&rdman->dirty_coords);
-    DARRAY_DESTROY(&rdman->dirty_pcache_area_coords);
     DARRAY_DESTROY(&rdman->dirty_geos);
     DARRAY_DESTROY(&rdman->gen_geos);
     DARRAY_DESTROY(&rdman->zeroing_coords);
@@ -802,9 +886,9 @@
     geo = elmpool_elm_alloc(rdman->geo_pool);
     if(geo == NULL)
 	return ERR;
-    
+
     geo_init(geo);
-    geo->mouse_event = subject_new(&rdman->ob_factory, geo, OBJT_GEO);
+    geo->mouse_event = subject_new(&rdman->observer_factory, geo, OBJT_GEO);
     subject_set_monitor(geo->mouse_event, rdman->addrm_monitor);
 
     geo_attach_coord(geo, coord);
@@ -854,7 +938,7 @@
 	rdman_paint_stroke(rdman, (paint_t *)NULL, shape);
     if(shape->fill != NULL)
 	rdman_paint_fill(rdman, (paint_t *)NULL, shape);
-    
+
     if(geo != NULL) {
 	subject_free(geo->mouse_event);
 	geo_detach_coord(geo, shape->coord);
@@ -869,7 +953,7 @@
     if(rdman->last_mouse_over == (mb_obj_t *)shape)
 	rdman->last_mouse_over = NULL;
 
-    
+
     return OK;
 }
 
@@ -889,7 +973,7 @@
     shape_t *shape;
 
     if(rdman_is_dirty(rdman)) {
-	if(!(paint->flags & PNTF_FREE))
+	if(paint->flags & PNTF_FREE)
 	    return ERR;
 	add_free_obj(rdman, paint, (free_func_t)rdman_paint_free);
 	paint->flags |= PNTF_FREE;
@@ -901,26 +985,26 @@
     FORPAINTMEMBERS(paint, shnode) {
 	if(saved_shnode) {
 	    RM_PAINTMEMBER(paint, saved_shnode);
-	    
+
 	    shape = saved_shnode->shape;
 	    if(shape->stroke == paint)
 		rdman_paint_stroke(rdman, (paint_t *)NULL, shape);
 	    if(shape->fill == paint)
 		rdman_paint_fill(rdman, (paint_t *)NULL, shape);
-	    
+
 	    shnode_free(rdman, saved_shnode);
 	}
 	saved_shnode = shnode;
     }
     if(saved_shnode) {
 	RM_PAINTMEMBER(paint, saved_shnode);
-	
+
 	shape = saved_shnode->shape;
 	if(shape->stroke == paint)
 	    rdman_paint_stroke(rdman, (paint_t *)NULL, shape);
 	if(shape->fill == paint)
 	    rdman_paint_fill(rdman, (paint_t *)NULL, shape);
-	
+
 	shnode_free(rdman, saved_shnode);
     }
 
@@ -952,7 +1036,7 @@
 
     coord_init(coord, parent);
     mb_prop_store_init(&coord->obj.props, rdman->pent_pool);
-    coord->mouse_event = subject_new(&rdman->ob_factory,
+    coord->mouse_event = subject_new(&rdman->observer_factory,
 				     coord,
 				     OBJT_COORD);
     subject_set_monitor(coord->mouse_event, rdman->addrm_monitor);
@@ -988,7 +1072,7 @@
 
     if(coord->flags & COF_FREE)
 	return ERR;
-    
+
     coord->flags |= COF_FREE;
     coord_hide(coord);
     if(!(coord->flags & COF_DIRTY)) {
@@ -1033,7 +1117,7 @@
 	if(!(member->flags & GEF_FREE))
 	    return ERR;
     }
-    
+
     if(cm_cnt || rdman_is_dirty(rdman))
 	return rdman_coord_free_postponse(rdman, coord);
 
@@ -1226,10 +1310,11 @@
     }
     shape->geo->flags &= ~GEF_DIRTY;
 
-    if(is_coord_subtree_hidden(shape->coord))
-	sh_hide(shape);
+    if(sh_get_flags(shape, GEF_HIDDEN) ||
+       is_coord_subtree_hidden(shape->coord))
+	sh_set_flags(shape, GEF_NOT_SHOWED);
     else
-	sh_show(shape);
+	sh_clear_flags(shape, GEF_NOT_SHOWED);
 }
 
 /*! \brief Setup canvas_info for the coord.
@@ -1242,7 +1327,7 @@
     if(coord->parent == NULL)
 	return;
 
-    if(coord->opacity != 1 || coord_is_cached(coord)) {
+    if(coord->opacity != 1 || coord_is_always_cached(coord)) {
 	if(!coord_is_cached(coord)) {
 	    /* canvas is assigned latter, in zeroing_coord() */
 	    coord->canvas_info = coord_canvas_info_new(rdman, coord, NULL);
@@ -1264,8 +1349,8 @@
 
 /* \brief Compute matrix from cached canvas to parent device space.
  */
-static void compute_cached_2_pdev_matrix(coord_t *coord,
-					   co_aix canvas2pdev_matrix[6]) {
+static void compute_cached_2_pdev_matrix(coord_t *coord) {
+    co_aix *canvas2pdev_matrix = coord_get_2pdev(coord);
     coord_t *parent;
     co_aix *aggr;
     co_aix *matrix, *paggr;
@@ -1277,9 +1362,9 @@
     matrix = coord->matrix;
     parent = coord->parent;
     paggr = coord_get_aggr_matrix(parent);
-    
+
     scale_x = matrix[0] / aggr[0];
-    scale_y = matrix[3] / aggr[3];
+    scale_y = matrix[4] / aggr[4];
     shift_x = matrix[2] - scale_x * aggr[2];
     shift_y = matrix[5] - scale_y * aggr[5];
 
@@ -1291,6 +1376,8 @@
     canvas2p[5] = shift_y;
 
     matrix_mul(paggr, canvas2p, canvas2pdev_matrix);
+
+    compute_reverse(canvas2pdev_matrix, coord_get_2pdev_rev(coord));
 }
 
 /*! \brief Compute area in parent cached coord for a cached coord.
@@ -1303,22 +1390,21 @@
  * ancestral cached coord can be retreived by shifting and resizing
  * canvas box in reverse and transform to coordination system of
  * ancestral cached coord.
- */ 
+ */
 static void compute_pcache_area(coord_t *coord) {
-    co_aix cached2pdev[6];
+    co_aix *cached2pdev = coord_get_2pdev(coord);
     int c_w, c_h;
     canvas_t *canvas;
     coord_canvas_info_t *canvas_info;
     co_aix poses[4][2];
-    
+
     canvas_info = coord->canvas_info;
     SWAP(canvas_info->pcache_cur_area, canvas_info->pcache_last_area,
 	 area_t *);
-    compute_cached_2_pdev_matrix(coord, cached2pdev);
-    
+
     canvas = _coord_get_canvas(coord);
     canvas_get_size(canvas, &c_w, &c_h);
-    
+
     poses[0][0] = 0;
     poses[0][1] = 0;
     poses[1][0] = c_w;
@@ -1331,28 +1417,29 @@
     matrix_trans_pos(cached2pdev, &poses[1][0], &poses[1][1]);
     matrix_trans_pos(cached2pdev, &poses[2][0], &poses[2][1]);
     matrix_trans_pos(cached2pdev, &poses[3][0], &poses[3][1]);
-    
+
     area_init(coord_get_pcache_area(coord), 4, poses);
 
-    coord_set_flags(coord, COF_DIRTY_PCACHE_AREA);
+    coord_clear_flags(coord, COF_DIRTY_PCACHE_AREA);
 }
 
 /*! \brief Compute area of a coord.
  */
 static int
 compute_area(coord_t *coord) {
-    static co_aix (*poses)[2];
+    static co_aix (*poses)[2] = NULL;
     static int max_poses = 0;
     geo_t *geo;
     int cnt, pos_cnt;
-    
+
     cnt = 0;
     FORMEMBERS(coord, geo) {
 	cnt++;
     }
-    
+
     if(max_poses < (cnt * 2)) {
-	free(poses);
+	if(poses)
+	    free(poses);
 	max_poses = cnt * 2;
 	poses = (co_aix (*)[2])malloc(sizeof(co_aix [2]) * max_poses);
 	if(poses == NULL)
@@ -1374,7 +1461,7 @@
     geo_t *geo;
     int r;
     /*! \note poses is shared by invokings, it is not support reentrying. */
-    
+
     /* Clean member shapes. */
     FORMEMBERS(coord, geo) {
 	clean_shape(geo->shape);
@@ -1396,9 +1483,8 @@
  * \note coords their opacity != 1 are also traded as cached ones.
  */
 static int clean_coord(redraw_man_t *rdman, coord_t *coord) {
-    coord_t *child;
     int r;
-    
+
     setup_canvas_info(rdman, coord);
 
     compute_aggr(coord);
@@ -1412,17 +1498,14 @@
     if(r != OK)
 	return ERR;
 
+    /* Dirty areas of cached one is added after update pcache_areas.
+     */
     add_dirty_area(rdman, coord, coord->cur_area);
     add_dirty_area(rdman, coord, coord->last_area);
-
+    
     coord_clear_flags(coord, COF_DIRTY);
     coord_set_flags(coord, COF_JUST_CLEAN);
-    
-    FORCHILDREN(coord, child) {
-	if(coord_is_cached(child))
-	    add_dirty_pcache_area_coord(rdman, child);
-    }
-    
+
     return OK;
 }
 
@@ -1434,7 +1517,7 @@
  * coord, coord_canvas_info_t::pcache_cur_area, for its cached children.
  */
 static int clean_rdman_coords(redraw_man_t *rdman) {
-    coord_t *coord, *child;
+    coord_t *coord;
     coord_t **dirty_coords;
     int n_dirty_coords;
     int i, r;
@@ -1476,7 +1559,7 @@
 	    add_dirty_area(rdman, coord, visit_geo->cur_area);
 	    add_dirty_area(rdman, coord, visit_geo->last_area);
 	}
-    }    
+    }
 
     return OK;
 }
@@ -1489,7 +1572,7 @@
 static
 void zeroing_coord(redraw_man_t *rdman, coord_t *coord) {
     coord_t *cur;
-    area_t *area, *saved_area;
+    area_t *area;
     geo_t *geo;
     co_aix min_x, min_y;
     co_aix max_x, max_y;
@@ -1498,7 +1581,6 @@
     int c_w, c_h;
     mbe_t *canvas;
     co_aix *aggr;
-    co_aix poses[4][2];
 
     if(coord->parent == NULL)	/*! \note Should not zeroing root coord */
 	abort();
@@ -1519,26 +1601,44 @@
     for(cur = preorder_coord_subtree(coord, coord);
 	cur != NULL;
 	cur = preorder_coord_subtree(coord, cur)) {
-	area = coord_get_area(cur);
+	if(coord_is_cached(cur)) {
+	    preorder_coord_skip_subtree(cur);
+	    /* This means pcache_area of descendants must be computed
+	     * before zeroing ancestor cached one.
+	     * (See add_rdman_zeroing_n_pcache_coords())
+	     */
+	    area = coord_get_pcache_area(cur);
+	} else
+	    area = coord_get_area(cur);
+
+	if(area->w == 0 && area->h == 0)
+	    continue;
+	
+	if(min_x == max_x && min_y == max_y) {
+	    min_x = area->x;
+	    max_x = area->x + area->w;
+	    min_y = area->y;
+	    max_y = area->y + area->h;
+	    continue;
+	}
+	
 	if(area->x < min_x)
 	    min_x = area->x;
 	if(area->y < min_y)
 	    min_y = area->y;
-	
+
 	x = area->x + area->w;
 	y = area->y + area->h;
-	
+
 	if(x > max_x)
 	    max_x = x;
 	if(y > max_y)
 	    max_y = y;
-	if(coord_is_cached(cur))
-	    preorder_coord_skip_subtree(cur);
     }
 
     w = max_x - min_x;
     h = max_y - min_y;
-    
+
     canvas = _coord_get_canvas(coord);
     if(canvas)
 	canvas_get_size(canvas, &c_w, &c_h);
@@ -1560,7 +1660,7 @@
 	coord_set_flags(coord, COF_SKIP_ZERO);
 	return;
     }
-    
+
     /*
      * Adjust matrics of descendants to align left-top corner of
      * minimum covering area with origin of space defined by
@@ -1577,9 +1677,9 @@
 	}
 	/* Shift space */
 	aggr = coord_get_aggr_matrix(cur);
-	aggr[3] -= min_x;
+	aggr[2] -= min_x;
 	aggr[5] -= min_y;
-	
+
 	FOR_COORD_MEMBERS(coord, geo) {
 	    /* \see GEO_SWAP() */
 	    if(!geo_get_flags(geo, GEF_SWAP))
@@ -1587,7 +1687,7 @@
 	}
 	coord_clean_members_n_compute_area(cur);
     }
-    
+
     /*
      * Setup canvas
      *
@@ -1604,16 +1704,29 @@
     coord_set_flags(coord, COF_JUST_ZERO);
 }
 
-/*! \brief Add canvas owner of dirty geos to redraw_man_t::zeroing_coords.
+/*! \brief Add coords that need to perform zeroing or re-compute pcache_area.
+ *
+ * A coord that need to perform zeroing has one or more dirty members
+ * in its descendants.
  *
- * All possible coords that need a zeroing have at least one dirty geo.
+ * To zeroing a coord, pcache_area of first level cached descendants
+ * must be updated.  To update the pcache_area of a cached coord, the
+ * cached coord also need to perform zeroing.  So, zeroing and
+ * re-computing pcache_area are interleaved.
+ *
+ * The pcache_area of a cached coord must be re-computed if its
+ * parent/ancestors is dirty/just cleaned, or it must be zeroed.  It
+ * means cached coord with jsut cleaned parent should also re-compute
+ * pcache_area.  So, this function also check and add coords for this
+ * situation.
  */
-static int add_rdman_zeroing_coords(redraw_man_t *rdman) {
+static int add_rdman_zeroing_n_pcache_coords(redraw_man_t *rdman) {
     int i;
     int n_dirty_geos;
     geo_t **dirty_geos, *geo;
     int n_dirty_coords;
     coord_t **dirty_coords, *coord;
+    coord_t *parent_coord;
 
     /* Mark all cached ancestral coords of dirty geos */
     n_dirty_geos = rdman->dirty_geos.num;
@@ -1628,7 +1741,7 @@
 	    coord = coord_get_cached(coord_get_parent(coord));
 	}
     }
-    
+
     /* Mark all cached ancestral coords of dirty coords */
     n_dirty_coords = rdman->dirty_coords.num;
     dirty_coords = rdman->dirty_coords.ds;
@@ -1641,23 +1754,28 @@
 	    coord = coord_get_cached(coord_get_parent(coord));
 	}
     }
-    
+
     /* Add all marked coords into redraw_man_t::zeroing_coords list */
     FOR_COORDS_PREORDER(rdman->root_coord, coord) {
-	if(!coord_is_cached(coord))
+	if(!coord_is_cached(coord) || coord_is_root(coord))
 	    continue;		/* skip coords that is not cached */
-	
+
 	if(!coord_get_flags(coord, COF_TEMP_MARK)) {
-	    if(coord_get_flags(coord, COF_DIRTY_PCACHE_AREA))
+	    parent_coord = coord_get_parent(coord);
+	    /* The pcache_area of a cached coord that is a child of a
+	     * just cleaned one must be recomputed.
+	     */
+	    if(coord_get_flags(parent_coord, COF_JUST_CLEAN))
 		add_dirty_pcache_area_coord(rdman, coord);
+	    
 	    preorder_coord_skip_subtree(coord);
 	    continue;
 	}
 	add_zeroing_coord(rdman, coord);
-	
+
 	coord_clear_flags(coord, COF_TEMP_MARK);
     }
-    
+
     return OK;
 }
 
@@ -1671,7 +1789,7 @@
     int i;
     coords_t *all_zeroing;
     coord_t *coord;
-   
+
     all_zeroing = &rdman->zeroing_coords;
     /*! Zeroing is performed from leaves to root.
      *
@@ -1686,34 +1804,43 @@
      */
     for(i = all_zeroing->num - 1; i >= 0; i--) {
 	coord = all_zeroing->ds[i];
-	zeroing_coord(rdman, coord);
+	if(coord_is_zeroing(coord))
+	    zeroing_coord(rdman, coord);
+	compute_cached_2_pdev_matrix(coord);
+	/* This is required by ancester cached ones to perform
+	 * zeroing.
+	 */
 	compute_pcache_area(coord);
     }
-    
+
     return OK;
 }
 
-/*! \brief Compute pcache_area for coords whoes pcache_area is dirty.
+/*! \brief Update aggregated cache_2_pdev matrix for cached coords.
  *
- * coord_t::dirty_pcache_area_coords also includes part of coords in
- * coord_t::zeroing_coords.  The pcache_area of coords that is in
- * coord_t::dirty_pcache_area_coords, but is not in
- * coord_t::zeroing_coords should be computed here.
- * zeroing_rdman_coords() is responsible for computing pcache_area for
- * zeroing ones.
+ * This is perfromed from root to leaves.  Aggregated cache_2_pdev is
+ * named as aggr_2_pdev field of canvas_info_t.  It is the matrix to
+ * transform a point from space of a cached coord to the space of root
+ * coord.
  */
 static int
-compute_rdman_coords_pcache_area(redraw_man_t *rdman) {
-    coords_t *all_coords;
-    coord_t *coord;
+update_aggr_pdev(redraw_man_t *rdman) {
     int i;
-    
-    all_coords = &rdman->dirty_pcache_area_coords;
-    for(i = 0; i < all_coords->num; i++) {
-	coord = all_coords->ds[i];
-	if(coord_get_flags(coord, COF_DIRTY_PCACHE_AREA))
-	    compute_pcache_area(coord);
+    coords_t *all_zeroing;
+    coord_t *coord, *parent_cached;
+
+    all_zeroing = &rdman->zeroing_coords;
+    for(i = 0; i < all_zeroing->num; i++) {
+	coord = all_zeroing->ds[i];
+	parent_cached = coord_get_cached(coord_get_parent(coord));
+	matrix_mul(coord_get_2pdev(parent_cached),
+		   coord_get_2pdev(coord),
+		   coord_get_aggr2pdev(coord));
+	matrix_mul(coord_get_2pdev_rev(coord),
+		   coord_get_2pdev_rev(parent_cached),
+		   coord_get_aggr2pdev_rev(coord));
     }
+
     return OK;
 }
 
@@ -1727,19 +1854,31 @@
 					      coord_t *coord) {
     int i;
     int n_areas;
-    int enable_poses1 = 0;
     co_aix poses0[2][2], poses1[2][2];
-    co_aix reverse[6];
-    co_aix canvas2pdev_matrix[6];
+    co_aix *canvas2pdev_matrix;
     area_t **areas, *area;
     area_t *area0, *area1;
     coord_t *parent, *pcached_coord;
 
     n_areas = _coord_get_dirty_areas(coord)->num;
     areas = _coord_get_dirty_areas(coord)->ds;
-    if(n_areas == 0)
-	abort();		/* should not happen! */
-    
+    if(n_areas == 0) {
+	/* Go here for cached one that is descendant of another zeroed
+	 * one, but itself is not zeroed.  It is here for recomputing
+	 * pcache areas.
+	 */
+	if(coord_get_flags(coord, COF_JUST_CLEAN | COF_JUST_ZERO))
+	    abort();		/* should not happen! */
+	
+	parent = coord_get_parent(coord);
+	pcached_coord = coord_get_cached(parent);
+	area = coord_get_pcache_area(coord);
+	add_dirty_area(rdman, pcached_coord, area);
+	area = coord_get_pcache_last_area(coord);
+	add_dirty_area(rdman, pcached_coord, area);
+	return;
+    }
+
     area0 = _coord_get_aggr_dirty_areas(coord);
     area1 = area0 + 1;
 
@@ -1755,13 +1894,13 @@
 
     if(i >= n_areas)
 	return;
-    
+
     area = areas[i++];
     poses0[0][0] = area->x;
     poses0[0][1] = area->y;
     poses0[1][0] = area->x + area->w;
     poses0[1][1] = area->y + area->h;
-    
+
     if(i < n_areas) {
 	area = areas[i++];
 	poses1[0][0] = area->x;
@@ -1774,7 +1913,7 @@
 	poses1[1][0] = 0;
 	poses1[1][1] = 0;
     }
-    
+
     for(; i < n_areas - 1;) {
 	/* Even areas */
 	area = areas[i++];
@@ -1793,7 +1932,7 @@
 	    poses1[1][1] = MB_MAX(poses1[1][1], area->y + area->h);
 	}
     }
-    
+
     if(i < n_areas) {
 	area = areas[i];
 	if(area->w != 0 || area->h != 0) {
@@ -1803,18 +1942,18 @@
 	    poses0[1][1] = MB_MAX(poses0[1][1], area->y + area->h);
 	}
     }
-    
+
     parent = coord_get_parent(coord);
     pcached_coord = coord_get_cached(parent);
-    
-    compute_cached_2_pdev_matrix(coord, canvas2pdev_matrix);
+
+    canvas2pdev_matrix = coord_get_2pdev(coord);
 
     /* Add dirty areas to parent cached coord. */
     matrix_trans_pos(canvas2pdev_matrix, poses0[0], poses0[0] + 1);
     matrix_trans_pos(canvas2pdev_matrix, poses0[1], poses0[1] + 1);
     area_init(area0, 2, poses0);
     add_dirty_area(rdman, pcached_coord, area0);
-    
+
     matrix_trans_pos(canvas2pdev_matrix, poses1[0], poses1[0] + 1);
     matrix_trans_pos(canvas2pdev_matrix, poses1[1], poses1[1] + 1);
     area_init(area1, 2, poses1);
@@ -1840,29 +1979,18 @@
  */
 static int add_rdman_aggr_dirty_areas(redraw_man_t *rdman) {
     int i;
-    int n_zeroing;
-    coord_t **zeroings;
-    coord_t *coord, *pcached_coord;
-    int n_dpca_coords;		/* number of dirty pcache area coords */
-    coord_t **dpca_coords;	/* dirty pcache area coords */
-    
-    /* Add aggregated areas to parent cached one for coords in zeroing
-     * list
-     */
-    n_zeroing = rdman->zeroing_coords.num;
-    zeroings = rdman->zeroing_coords.ds;
-    for(i = 0; i < n_zeroing; i++) {
-	if(coord_get_flags(coord, COF_TEMP_MARK))
-	    continue;
-	coord_set_flags(coord, COF_TEMP_MARK);
-	
-	coord = zeroings[i];
-	pcached_coord = coord_get_cached(coord_get_parent(coord));
-	
-	if(coord_is_root(coord) || IS_CACHE_REDRAW_ALL(pcached_coord))
-	    continue;
-	
+    coord_t *coord, *parent_coord, *pcached_coord;
+    int n_zeroing_coords;     /* number of dirty pcache area coords */
+    coord_t **zeroing_coords; /* dirty pcache area coords */
+
+    n_zeroing_coords = rdman->zeroing_coords.num;
+    zeroing_coords = rdman->zeroing_coords.ds;
+    for(i = n_zeroing_coords - 1; i >= 0; i--) {
+	coord = zeroing_coords[i];
 	if(IS_CACHE_REDRAW_ALL(coord)) {
+	    parent_coord = coord_get_parent(coord);
+	    pcached_coord = coord_get_cached(parent_coord);
+	    
 	    add_dirty_area(rdman, pcached_coord,
 			   coord_get_pcache_area(coord));
 	    add_dirty_area(rdman, pcached_coord,
@@ -1871,36 +1999,6 @@
 	    add_aggr_dirty_areas_to_ancestor(rdman, coord);
 	}
     }
-    
-    /* Add pcache_areas to parent cached one for coord that is
-     * non-zeroing and its parent is changed.
-     */
-    n_dpca_coords = rdman->dirty_pcache_area_coords.num;
-    dpca_coords = rdman->dirty_pcache_area_coords.ds;
-    for(i = 0; i < n_dpca_coords; i++) {
-	if(coord_get_flags(coord, COF_TEMP_MARK))
-	    continue;
-	coord_set_flags(coord, COF_TEMP_MARK);
-
-	coord = dpca_coords[i];
-	pcached_coord = coord_get_cached(coord_get_parent(coord));
-	
-	if(coord_is_root(coord) || IS_CACHE_REDRAW_ALL(pcached_coord))
-	    continue;
-	
-	add_dirty_area(rdman, pcached_coord,
-		       coord_get_pcache_area(coord));
-	add_dirty_area(rdman, pcached_coord,
-		       coord_get_pcache_last_area(coord));
-    }
-
-    /* Remove temporary mark */
-    for(i = 0; i < n_zeroing; i++) {
-	coord_clear_flags(zeroings[i], COF_TEMP_MARK);
-    }
-    for(i = 0; i < n_dpca_coords; i++) {
-	coord_clear_flags(dpca_coords[i], COF_TEMP_MARK);
-    }
 
     return OK;
 }
@@ -1981,13 +2079,15 @@
 	    GEO_SWAP(geo);
 	}
     }
-    
+
+    /* XXX: some geo may swap two times.  Should avoid it.
+     */
     geos = rdman->dirty_geos.ds;
     for(i = 0; i < rdman->dirty_geos.num; i++) {
 	geo = geos[i];
 	GEO_SWAP(geo);
     }
-    
+
     r = clean_rdman_coords(rdman);
     if(r != OK)
 	return ERR;
@@ -2003,7 +2103,7 @@
     /* Zeroing must be performed after clearing to get latest position
      * of shapes for computing new bounding box
      */
-    r = add_rdman_zeroing_coords(rdman);
+    r = add_rdman_zeroing_n_pcache_coords(rdman);
     if(r != OK)
 	return ERR;
 
@@ -2011,11 +2111,11 @@
     if(r != OK)
 	return ERR;
 
-    r = compute_rdman_coords_pcache_area(rdman);
+    r = add_rdman_aggr_dirty_areas(rdman);
     if(r != OK)
 	return ERR;
-    
-    r = add_rdman_aggr_dirty_areas(rdman);
+
+    r = update_aggr_pdev(rdman);
     if(r != OK)
 	return ERR;
 
@@ -2035,17 +2135,13 @@
     for(i = 0; i < rdman->zeroing_coords.num; i++)
 	coord_clear_flags(coords[i],
 			  COF_JUST_CLEAN | COF_JUST_ZERO | COF_SKIP_ZERO);
-    coords = rdman->dirty_pcache_area_coords.ds;
-    for(i = 0; i < rdman->dirty_pcache_area_coords.num; i++)
-	coord_clear_flags(coords[i],
-			  COF_JUST_CLEAN | COF_JUST_ZERO | COF_SKIP_ZERO);
     
     /* \see GEO_SWAP() */
     for(i = 0; i < rdman->dirty_geos.num; i++) {
 	geo = geos[i];
 	geo_clear_flags(geo, GEF_SWAP);
     }
-    
+
     return OK;
 }
 
@@ -2059,28 +2155,28 @@
     mbe_set_line_width(cr, shape->stroke_width);
 }
 
-static void fill_path_preserve(redraw_man_t *rdman) {
-    mbe_fill_preserve(rdman->cr);
+static void fill_path_preserve(redraw_man_t *rdman, mbe_t *cr) {
+    mbe_fill_preserve(cr);
 }
 
-static void fill_path(redraw_man_t *rdman) {
-    mbe_fill(rdman->cr);
+static void fill_path(redraw_man_t *rdman, mbe_t *cr) {
+    mbe_fill(cr);
 }
 
-static void stroke_path(redraw_man_t *rdman) {
-    mbe_stroke(rdman->cr);
+static void stroke_path(redraw_man_t *rdman, mbe_t *cr) {
+    mbe_stroke(cr);
 }
 #else
 static void set_shape_stroke_param(shape_t *shape, mbe_t *cr) {
 }
 
-static void fill_path_preserve(redraw_man_t *rdman) {
+static void fill_path_preserve(redraw_man_t *rdman, mbe_t *cr) {
 }
 
-static void fill_path(redraw_man_t *rdman) {
+static void fill_path(redraw_man_t *rdman, mbe_t *cr) {
 }
 
-static void stroke_path(redraw_man_t *rdman) {
+static void stroke_path(redraw_man_t *rdman, mbe_t *cr) {
 }
 #endif
 
@@ -2120,20 +2216,20 @@
 
 	fill = shape->fill;
 	if(shape->fill) {
-	    fill->prepare(fill, cr);
+	    fill->prepare(fill, cr, shape);
 	    if(shape->stroke)
-		fill_path_preserve(rdman);
+		fill_path_preserve(rdman, cr);
 	    else
-		fill_path(rdman);
+		fill_path(rdman, cr);
 	}
 
 	stroke = shape->stroke;
 	if(stroke) {
-	    stroke->prepare(stroke, cr);
+	    stroke->prepare(stroke, cr, shape);
 	    set_shape_stroke_param(shape, cr);
-	    stroke_path(rdman);
+	    stroke_path(rdman, cr);
 	}
-    } 
+    }
 }
 
 #ifndef UNITTEST
@@ -2141,22 +2237,36 @@
     mbe_clear(canvas);
 }
 
-#define make_clip(canvas, n_dirty_areas, dirty_areas)	\
-    mbe_scissoring(canvas, n_dirty_areas, dirty_areas)
+static void make_clip(mbe_t *cr, int n_dirty_areas,
+		      area_t **dirty_areas) {
+    int i;
+    area_t *area;
+
+    mbe_new_path(cr);
+    for(i = 0; i < n_dirty_areas; i++) {
+	area = dirty_areas[i];
+	if(area->w < 0.1 || area->h < 0.1)
+	    continue;
+	mbe_rectangle(cr, area->x, area->y, area->w, area->h);
+    }
+    mbe_clip(cr);
+}
 
 static void reset_clip(canvas_t *cr) {
-    mbe_reset_scissoring(cr);
+    mbe_reset_clip(cr);
 }
 
 static void copy_cr_2_backend(redraw_man_t *rdman, int n_dirty_areas,
 			      area_t **dirty_areas) {
     if(n_dirty_areas)
 	make_clip(rdman->backend, n_dirty_areas, dirty_areas);
-    
+
     mbe_copy_source(rdman->cr, rdman->backend);
 }
 #else /* UNITTEST */
-#define make_clip(canvas, n_dirty_areas, dirty_areas)
+static void make_clip(mbe_t *cr, int n_dirty_areas,
+		      area_t **dirty_areas) {
+}
 
 static void clear_canvas(canvas_t *canvas) {
 }
@@ -2169,27 +2279,39 @@
 }
 #endif /* UNITTEST */
 
-static void update_cached_canvas_2_parent(redraw_man_t *rdman,
-					  coord_t *coord) {
-    mbe_t *pcanvas, *canvas;
+static void
+_update_cached_canvas_2_parent(redraw_man_t *rdman, co_aix reverse[6],
+			       mbe_t *canvas, mbe_t *pcanvas,
+			       co_aix opacity) {
     mbe_surface_t *surface;
     mbe_pattern_t *pattern;
-    co_aix reverse[6];
-    co_aix canvas2pdev_matrix[6];
-
-    if(coord_is_root(coord))
-	return;
-
-    compute_cached_2_pdev_matrix(coord, canvas2pdev_matrix);
-    compute_reverse(canvas2pdev_matrix, reverse);
     
-    canvas = _coord_get_canvas(coord);
-    pcanvas = _coord_get_canvas(coord->parent);
     surface = mbe_get_target(canvas);
     pattern = mbe_pattern_create_for_surface(surface);
     mbe_pattern_set_matrix(pattern, reverse);
     mbe_set_source(pcanvas, pattern);
-    mbe_paint_with_alpha(pcanvas, coord->opacity);
+    mbe_paint_with_alpha(pcanvas, opacity);
+}
+
+static void update_cached_canvas_2_parent(redraw_man_t *rdman,
+					  coord_t *coord) {
+    mbe_t *pcanvas, *canvas;
+    co_aix *c2pdev_reverse;
+
+    if(coord_is_root(coord))
+	return;
+
+    c2pdev_reverse = coord_get_2pdev_rev(coord);
+
+    canvas = _coord_get_canvas(coord);
+    pcanvas = _coord_get_canvas(coord->parent);
+#ifndef UNITTEST
+    _update_cached_canvas_2_parent(rdman, c2pdev_reverse, canvas, pcanvas,
+				   coord->opacity);
+#else
+    memcpy(((mock_mbe_t *)canvas)->parent_2_cache, c2pdev_reverse,
+	   sizeof(co_aix) * 6);
+#endif
 }
 
 static int draw_coord_shapes_in_dirty_areas(redraw_man_t *rdman,
@@ -2205,11 +2327,11 @@
 
     if(coord->flags & COF_HIDDEN)
 	return OK;
-    
+
     areas = _coord_get_dirty_areas(coord)->ds;
     n_areas = _coord_get_dirty_areas(coord)->num;
     canvas = _coord_get_canvas(coord);
-    
+
     member = FIRST_MEMBER(coord);
     mem_idx = 0;
     child = FIRST_CHILD(coord);
@@ -2217,7 +2339,8 @@
 	if(child && child->before_pmem == mem_idx) {
 	    if(coord_is_cached(child)) {
 		if(!(child->flags & COF_HIDDEN) &&
-		   is_area_in_areas(coord_get_area(child), n_areas, areas)) {
+		   is_area_in_areas(coord_get_pcache_area(child),
+				    n_areas, areas)) {
 		    update_cached_canvas_2_parent(rdman, child);
 		    dirty = 1;
 		}
@@ -2228,7 +2351,7 @@
 	    child = NEXT_CHILD(child);
 	} else {
 	    ASSERT(member != NULL);
-	    if((!(member->flags & GEF_HIDDEN)) &&
+	    if((!(member->flags & GEF_NOT_SHOWED)) &&
 	       is_geo_in_areas(member, n_areas, areas)) {
 		draw_shape(rdman, canvas, member->shape);
 		dirty = 1;
@@ -2251,9 +2374,9 @@
     mbe_surface_t *surface;
     int i;
     int r;
-    
+
     canvas = _coord_get_canvas(coord);
-    
+
     if(IS_CACHE_REDRAW_ALL(coord)) {
 	/*
 	 * full_area covers all dirty areas of the cached coord.
@@ -2269,7 +2392,7 @@
 
     areas = _coord_get_dirty_areas(coord)->ds;
     n_areas = _coord_get_dirty_areas(coord)->num;
-    
+
     for(i = 0; i < n_areas; i++) {
 	area = areas[i];
 	area->x = floorf(area->x);
@@ -2282,7 +2405,7 @@
     clear_canvas(canvas);
 
     r = draw_coord_shapes_in_dirty_areas(rdman, coord);
-    
+
     reset_clip(canvas);
 
     return OK;
@@ -2306,8 +2429,10 @@
 	draw_dirty_cached_coord(rdman, coord);
 	coord_set_flags(coord, COF_TEMP_MARK);
     }
-    for(i = 0; i < num; i++)
+    for(i = 0; i < num; i++) {
+	coord = zeroings[i];
 	coord_clear_flags(coord, COF_TEMP_MARK);
+    }
 
     draw_dirty_cached_coord(rdman, rdman->root_coord);
 }
@@ -2350,10 +2475,10 @@
     event_t event;
     subject_t *redraw;
     int i;
-    coord_t *coord, **coords;
+    coord_t *coord;
     int n_areas;
     area_t **areas;
-    
+
     r = rdman_clean_dirties(rdman);
     if(r != OK)
 	return ERR;
@@ -2378,8 +2503,7 @@
     DARRAY_CLEAN(&rdman->dirty_coords);
     DARRAY_CLEAN(&rdman->dirty_geos);
     DARRAY_CLEAN(&rdman->zeroing_coords);
-    DARRAY_CLEAN(&rdman->dirty_pcache_area_coords);
-    
+
     /* Free postponsed removing */
     free_free_objs(rdman);
 
@@ -2441,7 +2565,7 @@
 geo_t *rdman_geos(redraw_man_t *rdman, geo_t *last) {
     geo_t *next;
     coord_t *coord;
-    
+
     if(last == NULL) {
 	coord = rdman->root_coord;
 	while(coord != NULL && FIRST_MEMBER(coord) == NULL)
@@ -2497,48 +2621,51 @@
  * Implment factory and strategy functions for observers and subjects.
  * @{
  */
-static subject_t *ob_subject_alloc(ob_factory_t *factory) {
+static subject_t *observer_subject_alloc(observer_factory_t *factory) {
     redraw_man_t *rdman;
     subject_t *subject;
 
-    rdman = MEM2OBJ(factory, redraw_man_t, ob_factory);
+    rdman = MEM2OBJ(factory, redraw_man_t, observer_factory);
     subject = elmpool_elm_alloc(rdman->subject_pool);
 
     return subject;
 }
 
-static void ob_subject_free(ob_factory_t *factory, subject_t *subject) {
+static void
+observer_subject_free(observer_factory_t *factory, subject_t *subject) {
     redraw_man_t *rdman;
 
-    rdman = MEM2OBJ(factory, redraw_man_t, ob_factory);
+    rdman = MEM2OBJ(factory, redraw_man_t, observer_factory);
     elmpool_elm_free(rdman->subject_pool, subject);
 }
 
-static observer_t *ob_observer_alloc(ob_factory_t *factory) {
+static observer_t *observer_observer_alloc(observer_factory_t *factory) {
     redraw_man_t *rdman;
     observer_t *observer;
 
-    rdman = MEM2OBJ(factory, redraw_man_t, ob_factory);
+    rdman = MEM2OBJ(factory, redraw_man_t, observer_factory);
     observer = elmpool_elm_alloc(rdman->observer_pool);
 
     return observer;
 }
 
-static void ob_observer_free(ob_factory_t *factory, observer_t *observer) {
+static void
+observer_observer_free(observer_factory_t *factory, observer_t *observer) {
     redraw_man_t *rdman;
 
-    rdman = MEM2OBJ(factory, redraw_man_t, ob_factory);
+    rdman = MEM2OBJ(factory, redraw_man_t, observer_factory);
     elmpool_elm_free(rdman->observer_pool, observer);
 }
 
-static subject_t *ob_get_parent_subject(ob_factory_t *factory,
-					subject_t *cur_subject) {
+static subject_t *
+observer_get_parent_subject(observer_factory_t *factory,
+			    subject_t *cur_subject) {
     redraw_man_t *rdman;
     coord_t *coord, *parent_coord;
     geo_t *geo;
     subject_t *parent;
 
-    rdman = MEM2OBJ(factory, redraw_man_t, ob_factory);
+    rdman = MEM2OBJ(factory, redraw_man_t, observer_factory);
     switch(cur_subject->obj_type) {
     case OBJT_GEO:
 	geo = (geo_t *)cur_subject->obj;
@@ -2570,15 +2697,15 @@
     mb_img_data_t *img_data;
     paint_t *paint;
     mb_img_ldr_t *ldr = rdman_img_ldr(rdman);
-    
+
     img_data = MB_IMG_LDR_LOAD(ldr, img_id);
     if(img_data == NULL)
 	return NULL;
-    
+
     paint = rdman_paint_image_new(rdman, img_data);
     if(paint == NULL)
 	MB_IMG_DATA_FREE(img_data);
-    
+
     return paint;
 }
 
@@ -2593,6 +2720,7 @@
     co_aix w, h;
     int trans_cnt;
     int draw_cnt;
+    mbe_t *last_draw;
 };
 
 void sh_dummy_free(shape_t *sh) {
@@ -2616,8 +2744,8 @@
     dummy->trans_cnt = 0;
     dummy->draw_cnt = 0;
     dummy->shape.free = sh_dummy_free;
-    
-    rdman_shape_man(rdman, (shape_t *)dummy);
+
+    rdman_man_shape(rdman, (shape_t *)dummy);
 
     return (shape_t *)dummy;
 }
@@ -2626,7 +2754,7 @@
     sh_dummy_t *dummy = (sh_dummy_t *)shape;
     co_aix poses[2][2];
     co_aix x1, y1, x2, y2;
-    
+
     if(shape->geo && shape->coord) {
 	x1 = dummy->x;
 	y1 = dummy->y;
@@ -2639,7 +2767,7 @@
 	poses[0][1] = y1;
 	poses[1][0] = x2;
 	poses[1][1] = y2;
-    
+
 	if(shape->geo)
 	    geo_from_positions(shape->geo, 2, poses);
     }
@@ -2651,6 +2779,7 @@
 
     dummy = (sh_dummy_t *)shape;
     dummy->draw_cnt++;
+    dummy->last_draw = cr;
 }
 
 static void dummy_paint_prepare(paint_t *paint, mbe_t *cr) {
@@ -2703,7 +2832,7 @@
     CU_ASSERT(dummys[0]->draw_cnt == 1);
     CU_ASSERT(dummys[1]->draw_cnt == 1);
     CU_ASSERT(dummys[2]->draw_cnt == 1);
-    
+
     coords[2]->matrix[2] = 100;
     coords[2]->matrix[5] = 100;
     rdman_coord_changed(rdman, coords[0]);
@@ -2741,12 +2870,168 @@
     CU_ASSERT(test_free_pass == 4);
 }
 
+static void
+test_setup_canvas_info(void) {
+    redraw_man_t *rdman;
+    redraw_man_t _rdman;
+    coord_t *coord;
+
+    redraw_man_init(&_rdman, NULL, NULL);
+    rdman = &_rdman;
+
+    coord = rdman_coord_new(rdman, rdman->root_coord);
+    CU_ASSERT(coord->parent == rdman->root_coord);
+
+    coord_set_opacity(coord, 0.9);
+    setup_canvas_info(rdman, coord);
+
+    CU_ASSERT(coord->canvas_info != rdman->root_coord->canvas_info);
+
+    coord_set_opacity(coord, 1);
+    setup_canvas_info(rdman, coord);
+
+    CU_ASSERT(coord->canvas_info == rdman->root_coord->canvas_info);
+}
+
+static void
+test_own_canvas_area(void) {
+    redraw_man_t *rdman;
+    redraw_man_t _rdman;
+    coord_t *coord1, *coord2;
+    sh_dummy_t *sh;
+
+    redraw_man_init(&_rdman, NULL, NULL);
+    rdman = &_rdman;
+
+    coord1 = rdman_coord_new(rdman, rdman->root_coord);
+    CU_ASSERT(coord1->parent == rdman->root_coord);
+
+    coord2 = rdman_coord_new(rdman, coord1);
+    CU_ASSERT(coord2->parent == coord1);
+
+    coord_set_opacity(coord2, 0.9);
+    rdman_coord_changed(rdman, coord2);
+
+    sh = (sh_dummy_t *)sh_dummy_new(rdman, 100, 100, 20, 20);
+    rdman_add_shape(rdman, (shape_t *)sh, coord2);
+    rdman_shape_changed(rdman, (shape_t *)sh);
+
+    clean_coord(rdman, coord2);
+
+    /* Parent cached coord must be updated */
+    CU_ASSERT(geo_get_area(coord2)->x == 100);
+    CU_ASSERT(geo_get_area(coord2)->y == 100);
+    CU_ASSERT(geo_get_area(coord2)->w <= 22 && geo_get_area(coord2)->w >= 19);
+    CU_ASSERT(geo_get_area(coord2)->h <= 22 && geo_get_area(coord2)->h >= 19);
+
+    redraw_man_destroy(rdman);
+}
+
+static void
+test_own_canvas(void) {
+    redraw_man_t *rdman;
+    redraw_man_t _rdman;
+    coord_t *coord1, *coord2;
+    sh_dummy_t *sh;
+
+    redraw_man_init(&_rdman, NULL, NULL);
+    rdman = &_rdman;
+
+    coord1 = rdman_coord_new(rdman, rdman->root_coord);
+    CU_ASSERT(coord1->parent == rdman->root_coord);
+
+    coord2 = rdman_coord_new(rdman, coord1);
+    CU_ASSERT(coord2->parent == coord1);
+
+    coord_set_opacity(coord2, 0.9);
+    rdman_coord_changed(rdman, coord2);
+
+    sh = (sh_dummy_t *)sh_dummy_new(rdman, 100, 100, 20, 20);
+    rdman_add_shape(rdman, (shape_t *)sh, coord2);
+    rdman_shape_changed(rdman, (shape_t *)sh);
+
+    rdman_clean_dirties(rdman);
+
+    /* Parent cached coord must be updated */
+    CU_ASSERT(_coord_get_dirty_areas(rdman->root_coord)->num == 1);
+
+    CU_ASSERT(geo_get_area(coord2)->x == 0);
+    CU_ASSERT(geo_get_area(coord2)->y == 0);
+    CU_ASSERT(geo_get_area(coord2)->w <= 22 && geo_get_area(coord2)->w >= 19);
+    CU_ASSERT(geo_get_area(coord2)->h <= 22 && geo_get_area(coord2)->h >= 19);
+    
+    redraw_man_destroy(rdman);
+}
+
+static void
+test_own_canvas_redraw(void) {
+    redraw_man_t *rdman;
+    redraw_man_t _rdman;
+    coord_t *coord1, *coord2;
+    sh_dummy_t *sh;
+    paint_t *paint;
+    co_aix *parent_2_cache;
+
+    redraw_man_init(&_rdman, NULL, NULL);
+    rdman = &_rdman;
+    
+    coord1 = rdman_coord_new(rdman, rdman->root_coord);
+    CU_ASSERT(coord1->parent == rdman->root_coord);
+
+    coord2 = rdman_coord_new(rdman, coord1);
+    CU_ASSERT(coord2->parent == coord1);
+
+    coord_set_opacity(coord2, 0.9);
+    rdman_coord_changed(rdman, coord2);
+
+    sh = (sh_dummy_t *)sh_dummy_new(rdman, 100, 100, 20, 20);
+    rdman_add_shape(rdman, (shape_t *)sh, coord2);
+    rdman_shape_changed(rdman, (shape_t *)sh);
+
+    paint = dummy_paint_new(rdman);
+    rdman_paint_fill(rdman, paint, (shape_t *)sh);
+    
+    rdman_redraw_all(rdman);
+
+    CU_ASSERT(sh->draw_cnt == 1);
+    CU_ASSERT(sh->last_draw == _coord_get_canvas(coord2));
+
+    parent_2_cache = ((mock_mbe_t *)_coord_get_canvas(coord2))->parent_2_cache;
+    CU_ASSERT(parent_2_cache[0] == 1);
+    CU_ASSERT(parent_2_cache[1] == 0);
+    CU_ASSERT(parent_2_cache[2] == -100);
+    CU_ASSERT(parent_2_cache[3] == 0);
+    CU_ASSERT(parent_2_cache[4] == 1);
+    CU_ASSERT(parent_2_cache[5] == -100);
+
+    coord2->matrix[2] = 20;
+    coord2->matrix[5] = 30;
+    rdman_coord_changed(rdman, coord2);
+    rdman_redraw_changed(rdman);
+    
+    /* To test if transform matrix of cached coord working */
+    parent_2_cache = ((mock_mbe_t *)_coord_get_canvas(coord2))->parent_2_cache;
+    CU_ASSERT(parent_2_cache[0] == 1);
+    CU_ASSERT(parent_2_cache[1] == 0);
+    CU_ASSERT(parent_2_cache[2] == -120);
+    CU_ASSERT(parent_2_cache[3] == 0);
+    CU_ASSERT(parent_2_cache[4] == 1);
+    CU_ASSERT(parent_2_cache[5] == -130);    
+    
+    rdman_paint_free(rdman, paint);
+    redraw_man_destroy(rdman);
+}
+
 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);
+    CU_ADD_TEST(suite, test_setup_canvas_info);
+    CU_ADD_TEST(suite, test_own_canvas_area);
+    CU_ADD_TEST(suite, test_own_canvas);
+    CU_ADD_TEST(suite, test_own_canvas_redraw);
 
     return suite;
 }
--- a/src/rotate.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/rotate.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
--- a/src/shape_image.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/shape_image.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include <stdio.h>
 #include <string.h>
 #include "mb_graph_engine.h"
@@ -53,11 +55,11 @@
  */
 typedef struct _sh_image {
     shape_t shape;
-    
+
     co_aix x, y;
     co_aix w, h;
     co_aix poses[4][2];
-    
+
     redraw_man_t *rdman;
 } sh_image_t;
 
@@ -70,8 +72,6 @@
 shape_t *rdman_shape_image_new(redraw_man_t *rdman,
 			       co_aix x, co_aix y, co_aix w, co_aix h) {
     sh_image_t *img;
-    mb_img_fmt_t fmt;
-    int r;
 
     img = O_ALLOC(sh_image_t);
     if(img == NULL)
@@ -86,7 +86,9 @@
     img->y = y;
     img->w = w;
     img->h = h;
-    
+
+    rdman_man_shape(rdman, (shape_t *)img);
+
     return (shape_t *)img;
 }
 
@@ -102,10 +104,11 @@
     paint_t *paint;
     co_aix (*poses)[2];
     co_aix img_matrix[6];
+    co_aix rev_matrix[6];
     co_aix x_factor, y_factor;
     int img_w, img_h;
     int i;
-    
+
     poses = img->poses;
     poses[0][0] = img->x;
     poses[0][1] = img->y;
@@ -117,7 +120,7 @@
     poses[3][1] = img->y + img->h;
     for(i = 0; i < 4; i++)
 	coord_trans_pos(img->shape.coord, &poses[i][0], &poses[i][1]);
-    
+
     geo_from_positions(sh_get_geo(shape), 4, poses);
 
     paint = sh_get_fill(shape);
@@ -125,29 +128,28 @@
 	return;
 
     ASSERT(paint.pnt_type == MBP_IMAGE);
-    
+
     paint_image_get_size(paint, &img_w, &img_h);
-    
-    /* Transformation from user space to image space */
+
+    /* Transformation from image space to user space */
     img_matrix[0] = (poses[1][0] - poses[0][0]) / img->w;
-    img_matrix[1] = (poses[1][1] - poses[0][1]) / img->w;
-    img_matrix[2] = -poses[0][0];
-    img_matrix[3] = (poses[3][0] - poses[0][0]) / img->h;
+    img_matrix[1] = (poses[3][0] - poses[3][0]) / img->h;
+    img_matrix[2] = poses[0][0];
+    img_matrix[3] = (poses[1][1] - poses[0][1]) / img->w;
     img_matrix[4] = (poses[3][1] - poses[0][1]) / img->h;
-    img_matrix[5] = -poses[0][1];
+    img_matrix[5] = poses[0][1];
     if(img->w != img_w ||
        img->h != img_h) {
 	/* Resize image */
-	x_factor = img_w / img->w;
+	x_factor = img->w / img_w;
 	img_matrix[0] *= x_factor;
 	img_matrix[1] *= x_factor;
-	img_matrix[2] *= x_factor;
-	y_factor = img_h / img->h;
+	y_factor = img->h / img_h;
 	img_matrix[3] *= y_factor;
 	img_matrix[4] *= y_factor;
-	img_matrix[5] *= y_factor;
     }
-    paint_image_set_matrix(sh_get_fill(shape), img_matrix);
+    compute_reverse(img_matrix, rev_matrix);
+    paint_image_set_matrix(sh_get_fill(shape), rev_matrix);
 }
 
 /*! \brief Draw image for an image shape.
@@ -156,9 +158,7 @@
  */
 void sh_image_draw(shape_t *shape, mbe_t *cr) {
     sh_image_t *img = (sh_image_t *)shape;
-    mbe_pattern_t *saved_source;
-    co_aix *aggr;
-    
+
     mbe_move_to(cr, img->poses[0][0], img->poses[0][1]);
     mbe_line_to(cr, img->poses[1][0], img->poses[1][1]);
     mbe_line_to(cr, img->poses[2][0], img->poses[2][1]);
--- a/src/shape_path.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/shape_path.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
@@ -23,9 +25,13 @@
     int float_arg_len;
     char *user_data;
     char *dev_data;		/* device space data */
+    
+    redraw_man_t *rdman;	/*!< \brief This is used by sh_path_free() */
 } sh_path_t;
 #define RESERVED_AIXS sizeof(co_aix[2])
 
+int _sh_path_size = sizeof(sh_path_t);
+
 #define ASSERT(x)
 #define SKIP_SPACE(x) while(*(x) && (isspace(*(x)) || *(x) == ',')) { (x)++; }
 #define SKIP_NUM(x)					\
@@ -35,7 +41,7 @@
 	   *(x) == 'E' ||				\
 	   *(x) == '-' ||				\
 	   *(x) == '+' ||				\
-	   *(x) == '.'))  {					\
+	   *(x) == '.'))  {				\
 	(x)++;						\
     }
 #define OK 0
@@ -43,8 +49,22 @@
 #define PI 3.1415926535897931
 
 #ifdef UNITTEST
-#undef rdman_shape_man
-#define rdman_shape_man(x, y)
+#undef rdman_man_shape
+#define rdman_man_shape(x, y)
+
+#undef elmpool_elm_alloc
+#define elmpool_elm_alloc(pool) _elmpool_elm_alloc(pool)
+static void *
+_elmpool_elm_alloc(void *dummy) {
+    return malloc(sizeof(sh_path_t));
+}
+
+#undef elmpool_elm_free
+#define elmpool_elm_free(pool, elm) _elmpool_elm_free(pool, elm)
+static void
+_elmpool_elm_free(void *pool, void *elm) {
+    free(elm);
+}
 #endif
 
 /* ============================================================
@@ -108,13 +128,13 @@
     float _sin = sinf(x_rotate);
     float _cos = cosf(x_rotate);
     int reflect;
-    
+
     /* Compute center of the ellipse */
     nrx = x * _cos + y * _sin;
     nry = x * -_sin + y * _cos;
     nrx0 = x0 * _cos + y0 * _sin;
     nry0 = x0 * -_sin + y0 * _cos;
-    
+
     udx = (nrx - nrx0) / 2 / rx; /* ux - umx */
     udy = (nry - nry0) / 2 / ry; /* uy - umy */
     umx = (nrx + nrx0) / 2 / rx;
@@ -168,12 +188,12 @@
     nrx = (x * _cos + y * _sin) / rx;
     nry = (-x * _sin + y * _cos) / ry;
     xy_tan = nry / nrx;
-    
+
     angle = atan(xy_tan);
 
     if(nrx < 0)
 	angle = PI + angle;
-    
+
     return angle;
 }
 
@@ -261,7 +281,7 @@
 	    *pnts++ = corners[i][0] + cx;
 	    *pnts++ = corners[i][1] + cy;
 	}
-	
+
 	*(pnts++) = x;
 	*(pnts++) = y;
 
@@ -274,7 +294,7 @@
 	    angle_stop += 2 * PI;
 	else if((!sweep) && angle_start < angle_stop)
 	    angle_start += 2 * PI;
-	
+
 	*float_args++ = cx;
 	*float_args++ = cy;
 	*float_args++ = rx;
@@ -282,7 +302,7 @@
 	*float_args++ = angle_start;
 	*float_args++ = angle_stop;
 	*float_args++ = x_rotate;
-	
+
 	*cmds++ = toupper(cmd);
     }
 
@@ -308,7 +328,7 @@
 
     rd2 = distance_pow2(dx, dy);
     rd = sqrtf(rd2);
-    
+
     inner = INNER(sx, sy, dx, dy);
     cross = CROSS(sx, sy, dx, dy);
     angle = acos(inner / rd);
@@ -352,7 +372,7 @@
 
     _sin = sinf(x_rotate);
     _cos = cosf(x_rotate);
-    
+
     xyratio = ry / rx;
     aggr = sh_get_aggr_matrix((shape_t *)path);
     matrix[0] = _cos;
@@ -365,7 +385,7 @@
     matrix_mul(aggr, matrix, dev_matrix);
     mbe_save(cr);
     mbe_transform(cr, dev_matrix);
-    mbe_arc(cr, 0, 0, rx, angle_start, angle_stop); 
+    mbe_arc(cr, 0, 0, rx, angle_start, angle_stop);
     mbe_restore(cr);
 
     *pnts_p = pnts;
@@ -376,9 +396,11 @@
 
 static void sh_path_free(shape_t *shape) {
     sh_path_t *path = (sh_path_t *)shape;
+
+    mb_obj_destroy(path);
     if(path->user_data)
 	free(path->user_data);
-    free(path);
+    elmpool_elm_free(path->rdman->sh_path_pool, path);
 }
 
 /*! \brief Count number of arguments.
@@ -529,7 +551,7 @@
 		SKIP_NUM(p);
 		if(p == old)
 		    break;
-		
+
 		for(i = 0; i < 6; i++) {
 		    SKIP_SPACE(p);
 		    old = p;
@@ -570,6 +592,7 @@
     char cmd;
     co_aix *pnts;
     co_aix *float_args;
+    co_aix sx = 0, sy = 0;
     co_aix x, y;
     int r;
 
@@ -626,6 +649,7 @@
 		if(p == old)
 		    return ERR;
 		*pnts = TO_ABSX;
+		x = *pnts;
 		pnts++;
 
 		SKIP_SPACE(p);
@@ -634,6 +658,7 @@
 		if(p == old)
 		    return ERR;
 		*pnts = TO_ABSY;
+		y = *pnts;
 		pnts++;
 
 		*cmds++ = toupper(cmd);
@@ -667,6 +692,7 @@
 		if(p == old)
 		    return ERR;
 		*pnts = TO_ABSX;
+		x = *pnts;
 		pnts++;
 
 		SKIP_SPACE(p);
@@ -675,13 +701,43 @@
 		if(p == old)
 		    return ERR;
 		*pnts = TO_ABSY;
+		y = *pnts;
 		pnts++;
 
 		*cmds++ = toupper(cmd);
 	    }
 	    break;
+
 	case 'm':
 	case 'M':
+	    while(*p) {
+		old = p;
+		SKIP_SPACE(p);
+		old = p;
+		SKIP_NUM(p);
+		if(p == old)
+		    break;
+		*pnts = TO_ABSX;
+		x = *pnts;
+		pnts++;
+
+		SKIP_SPACE(p);
+		old = p;
+		SKIP_NUM(p);
+		if(p == old)
+		    return ERR;
+		*pnts = TO_ABSY;
+		y = *pnts;
+		pnts++;
+
+		*cmds++ = toupper(cmd);
+
+		/* save initial point of a subpath */
+		sx = x;
+		sy = y;
+	    }
+	    break;
+
 	case 'l':
 	case 'L':
 	case 't':
@@ -694,6 +750,7 @@
 		if(p == old)
 		    break;
 		*pnts = TO_ABSX;
+		x = *pnts;
 		pnts++;
 
 		SKIP_SPACE(p);
@@ -702,6 +759,7 @@
 		if(p == old)
 		    return ERR;
 		*pnts = TO_ABSY;
+		y = *pnts;
 		pnts++;
 
 		*cmds++ = toupper(cmd);
@@ -726,6 +784,9 @@
 	case 'z':
 	case 'Z':
 	    *cmds++ = toupper(cmd);
+	    /* Go back to initial point of a subpath */
+	    x = sx;
+	    y = sy;
 	    break;
 	default:
 	    return ERR;
@@ -757,7 +818,7 @@
     cmd_cnt = (cmd_cnt + 3) & ~0x3;
 
     /*! \todo Use elmpool to manage sh_path_t objects. */
-    path = (sh_path_t *)malloc(sizeof(sh_path_t));
+    path = (sh_path_t *)elmpool_elm_alloc(rdman->sh_path_pool);
     /*! \todo Remove this memset()? */
     memset(&path->shape, 0, sizeof(shape_t));
     mb_obj_init(path, MBO_PATH);
@@ -769,7 +830,7 @@
 	sizeof(co_aix) * float_arg_cnt;
     path->user_data = (char *)malloc(msz * 2);
     if(path->user_data == NULL) {
-	free(path);
+	elmpool_elm_free(rdman->sh_path_pool, path);
 	return NULL;
     }
 
@@ -778,14 +839,15 @@
     r = sh_path_cmd_arg_fill(data, path);
     if(r == ERR) {
 	free(path->user_data);
-	free(path);
+	elmpool_elm_free(rdman->sh_path_pool, path);
 	return NULL;
     }
     memcpy(path->dev_data, path->user_data, msz);
 
     path->shape.free = sh_path_free;
+    path->rdman = rdman;
 
-    rdman_shape_man(rdman, (shape_t *)path);
+    rdman_man_shape(rdman, (shape_t *)path);
 
     return (shape_t *)path;
 }
@@ -800,7 +862,7 @@
     int cmd_cnt = strlen(commands);
 
     /*! \todo Use elmpool to manage sh_path_t objects. */
-    path = (sh_path_t *)malloc(sizeof(sh_path_t));
+    path = (sh_path_t *)elmpool_elm_alloc(rdman->sh_path_pool);
     /*! \todo Remove this memset()? */
     memset(&path->shape, 0, sizeof(shape_t));
     mb_obj_init(path, MBO_PATH);
@@ -812,7 +874,7 @@
 	sizeof(co_aix) * float_arg_cnt;
     path->user_data = (char *)malloc(msz * 2);
     if(path->user_data == NULL) {
-	free(path);
+	elmpool_elm_free(rdman->sh_path_pool, path);
 	return NULL;
     }
 
@@ -822,10 +884,11 @@
     memcpy(path->user_data + cmd_cnt + pnt_cnt * sizeof(co_aix),
 	   float_args, sizeof(co_aix) * float_arg_cnt);
     memcpy(path->dev_data, path->user_data, msz);
-    
+
     path->shape.free = sh_path_free;
-    
-    rdman_shape_man(rdman, (shape_t *)path);
+    path->rdman = rdman;
+
+    rdman_man_shape(rdman, (shape_t *)path);
 
     return (shape_t *)path;
 }
@@ -960,8 +1023,9 @@
 void test_rdman_shape_path_new(void) {
     sh_path_t *path;
     co_aix *pnts;
+    redraw_man_t rdman;
 
-    path = (sh_path_t *)rdman_shape_path_new(NULL, "M 33 25l33 55c 33 87 44 22 55 99L33 77z");
+    path = (sh_path_t *)rdman_shape_path_new(&rdman, "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->pnt_len == 12);
@@ -989,8 +1053,9 @@
     co_aix *pnts;
     coord_t coord;
     geo_t geo;
+    redraw_man_t rdman;
 
-    path = (sh_path_t *)rdman_shape_path_new(NULL, "M 33 25l33 55C 33 87 44 22 55 99L33 77z");
+    path = (sh_path_t *)rdman_shape_path_new(&rdman, "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->pnt_len == 12);
@@ -1029,9 +1094,10 @@
 
 void test_spaces_head_tail(void) {
     sh_path_t *path;
+    redraw_man_t rdman;
 
     path = (sh_path_t *)
-	rdman_shape_path_new(NULL,
+	rdman_shape_path_new(&rdman,
 			     " M 33 25l33 55C 33 87 44 22 55 99L33 77z ");
     CU_ASSERT(path != NULL);
     sh_path_free((shape_t *)path);
--- a/src/shape_rect.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/shape_rect.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -10,10 +12,16 @@
     co_aix w, h;
     co_aix rx, ry;
     co_aix poses[12][2];
+
+    redraw_man_t *rdman;	/*!< \brief This is used by sh_rect_free() */
 } sh_rect_t;
 
+int _sh_rect_size = sizeof(sh_rect_t);
+
 static void sh_rect_free(shape_t *shape) {
-    free(shape);
+    sh_rect_t *rect = (sh_rect_t *)shape;
+    
+    elmpool_elm_free(rect->rdman->sh_rect_pool, rect);
 }
 
 shape_t *rdman_shape_rect_new(redraw_man_t *rdman,
@@ -21,7 +29,7 @@
 			      co_aix rx, co_aix ry) {
     sh_rect_t *rect;
 
-    rect = (sh_rect_t *)malloc(sizeof(sh_rect_t));
+    rect = (sh_rect_t *)elmpool_elm_alloc(rdman->sh_rect_pool);
     if(rect == NULL)
 	return NULL;
 
@@ -35,8 +43,9 @@
     rect->rx = rx;
     rect->ry = ry;
     rect->shape.free = sh_rect_free;
+    rect->rdman = rdman;
 
-    rdman_shape_man(rdman, (shape_t *)rect);
+    rdman_man_shape(rdman, (shape_t *)rect);
 
     return (shape_t *)rect;
 }
@@ -77,21 +86,21 @@
 	poses[1][1] = y;
 	poses[2][0] = x + w;
 	poses[2][1] = y + ry;
-	
+
 	poses[3][0] = x + w;
 	poses[3][1] = y + h - ry;
 	poses[4][0] = x + w;
 	poses[4][1] = y + h;
 	poses[5][0] = x + w - rx;
 	poses[5][1] = y + h;
-	
+
 	poses[6][0] = x + rx;
 	poses[6][1] = y + h;
 	poses[7][0] = x;
 	poses[7][1] = y + h;
 	poses[8][0] = x;
 	poses[8][1] = y + h - ry;
-	
+
 	poses[9][0] = x;
 	poses[9][1] = y + ry;
 	poses[10][0] = x;
--- a/src/shape_stext.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/shape_stext.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include <stdio.h>
 #include <string.h>
 #include "mb_graph_engine.h"
@@ -34,8 +36,8 @@
 
 #undef mb_obj_init
 #define mb_obj_init(o, t)
-#undef rdman_shape_man
-#define rdman_shape_man(rdman, sh)
+#undef rdman_man_shape
+#define rdman_man_shape(rdman, sh)
 
 #define rdman_shape_stext_new ut_rdman_shape_stext_new
 #define sh_stext_transform ut_sh_stext_transform
@@ -100,7 +102,7 @@
  *
  * Although, mb_text_extents_t is defined as a mbe_scaled_font_t, but
  * programmers should assume it is opague.
- * 
+ *
  * An extents is the span of showing a fragement of text on the output device.
  * It includes x and y advance of cursor after showinng the text.
  * The cursor maybe not really existed.  But, the advance is computed as
@@ -151,9 +153,9 @@
     mbe_scaled_font_t *scaled_font;
     static co_aix id[6] = { 1, 0, 0,
 			    0, 1, 0 };
-    
+
     ASSERT(matrix != NULL);
-    
+
     scaled_font = mbe_scaled_font_create((mbe_font_face_t *)face,
 					 matrix, &id);
 
@@ -198,14 +200,14 @@
 	buf = strndup(txt, tlen);
     else
 	buf = txt;
-    
+
     saved_scaled = mbe_get_scaled_font(cr);
     mbe_scaled_font_reference(saved_scaled);
     mbe_set_scaled_font(cr, (mbe_scaled_font_t *)scaled);
-    
+
     mbe_move_to(cr, x, y);
     mbe_text_path(cr, buf);
-    
+
     mbe_set_scaled_font(cr, saved_scaled);
     mbe_scaled_font_destroy(saved_scaled);
 
@@ -265,12 +267,12 @@
     int i;
 
     DARRAY_DESTROY(&txt_o->style_blks);
-    
+
     for(i = 0; i < txt_o->scaled_fonts.num; i++)
 	scaled_font_free(txt_o->scaled_fonts.ds[i]);
     DARRAY_DESTROY(&txt_o->scaled_fonts);
     DARRAY_DESTROY(&txt_o->sub_exts);
-    
+
     if(txt_o->txt)
 	free((void *)txt_o->txt);
 
@@ -282,14 +284,14 @@
     sh_stext_t *txt_o;
 
     ASSERT(txt != NULL);
-    
+
     txt_o = (sh_stext_t *)malloc(sizeof(sh_stext_t));
     if(txt_o == NULL)
 	return NULL;
 
     memset(&txt_o->shape, 0, sizeof(shape_t));
     mb_obj_init(txt_o, MBO_STEXT);
-    
+
     txt_o->txt = strdup(txt);
     DARRAY_INIT(&txt_o->style_blks);
     txt_o->x = x;
@@ -303,9 +305,9 @@
     }
 
     txt_o->shape.free = _rdman_shape_stext_free;
-    
-    rdman_shape_man(rdman, (shape_t *)txt_o);
-    
+
+    rdman_man_shape(rdman, (shape_t *)txt_o);
+
     return (shape_t *)txt_o;
 }
 
@@ -314,7 +316,7 @@
     int i;
     const char *p = txt;
     const char *v;
-    
+
     for(i = 0; i < n_chars && *p; i++) {
 	if(!(*p & 0x80))	/* single byte */
 	    p++;
@@ -333,7 +335,7 @@
     for(v = txt; v != p; v++)
 	if(*v == '\x0')
 	    return ERR;
-    
+
     return p - txt;
 }
 
@@ -351,15 +353,15 @@
     memcpy(noshift_aggr, aggr, sizeof(co_aix) * 6);
     noshift_aggr[2] = 0;
     noshift_aggr[5] = 0;
-    
+
     matrix[0] = font_sz;
     matrix[1] = 0;
     matrix[2] = shift_x;
     matrix[3] = 0;
     matrix[4] = font_sz;
-    matrix[5] += shift_y;
+    matrix[5] = shift_y;
     matrix_mul(noshift_aggr, matrix, scaled_matrix);
-    
+
     scaled = make_scaled_font_face_matrix(face, scaled_matrix);
 
     return scaled;
@@ -384,13 +386,13 @@
     s_ybr = MBE_GET_Y_BEARING(sub) + MBE_GET_Y_ADV(full);
     s_rbx = s_xbr + MBE_GET_WIDTH(sub);
     s_rby = s_ybr + MBE_GET_HEIGHT(sub);
-    
+
     /* set bearing */
     if(MBE_GET_X_BEARING(full) > s_xbr)
 	MBE_SET_X_BEARING(full, s_xbr);
     if(MBE_GET_Y_BEARING(full) > s_ybr)
 	MBE_SET_Y_BEARING(full, s_ybr);
-    
+
     /* set width/height */
     if(f_rbx < s_rbx)
 	MBE_SET_WIDTH(full, s_rbx - MBE_GET_X_BEARING(full));
@@ -435,19 +437,19 @@
     mb_text_extents_t *sub;
     char *txt, saved;
     int i, nscaled;
-    
+
     scaled_fonts = &txt_o->scaled_fonts;
     for(i = 0; i < scaled_fonts->num; i++)
 	scaled_font_free(scaled_fonts->ds[i]);
     DARRAY_CLEAN(scaled_fonts);
-    
+
     style_blks = &txt_o->style_blks;
     blk = style_blks->ds;
 
     sub_exts = &txt_o->sub_exts;
     DARRAY_CLEAN(sub_exts);
     extents_lst_adv(sub_exts, style_blks->num);
-    
+
     txt = (char *)txt_o->txt;
     for(i = 0; i < style_blks->num; i++) {
 	scaled = make_scaled_font_face(txt_o, blk->face,
@@ -455,10 +457,10 @@
 	ASSERT(scaled != NULL);
 	scaled_fonts_lst_add(scaled_fonts, scaled);
 	sub = sub_exts->ds + i;
-	
+
 	blk_txt_len = compute_utf8_chars_sz(txt, blk->n_chars);
 	ASSERT(blk_txt_len != ERR);
-	
+
 	saved = txt[blk_txt_len];
 	txt[blk_txt_len] = 0;
 	compute_text_extents(scaled, txt, sub);
@@ -467,7 +469,7 @@
 	blk++;
 	txt += blk_txt_len;
     }
-    
+
     if(style_blks->num > 0) {
 	sub = sub_exts->ds;
 	memcpy(&txt_o->extents, sub, sizeof(mb_text_extents_t));
@@ -476,7 +478,7 @@
 	    extent_extents(&txt_o->extents, sub);
 	}
     } else
-	memset(&txt_o->extents, sizeof(mb_text_extents_t), 0);    
+	memset(&txt_o->extents, sizeof(mb_text_extents_t), 0);
 }
 
 /*
@@ -499,14 +501,14 @@
     ASSERT(txt_o != NULL);
 
     aggr = sh_get_aggr_matrix(shape);
-    
+
     txt_o->dx = txt_o->x;
     txt_o->dy = txt_o->y;
     matrix_trans_pos(aggr, &txt_o->dx, &txt_o->dy);
-    
+
     compute_styled_extents_n_scaled_font(txt_o);
     ext = &txt_o->extents;
-    
+
     area = sh_get_area(shape);
     area->x = MBE_GET_X_BEARING(ext) + txt_o->dx;
     area->y = MBE_GET_Y_BEARING(ext) + txt_o->dy;
@@ -527,20 +529,20 @@
     int i;
 
     ASSERT(txt_o != NULL);
-    
+
     x = txt_o->dx;
     y = txt_o->dy;
     txt = txt_o->txt;
     scaled_fonts = &txt_o->scaled_fonts;
     style_blks = &txt_o->style_blks;
     ext = txt_o->sub_exts.ds;
-    
+
     for(i = 0; i < scaled_fonts->num; i++) {
 	scaled = scaled_fonts->ds[i];
 	blk = style_blks->ds + i;
 	blk_txt_len = compute_utf8_chars_sz(txt, blk->n_chars);
 	draw_text_scaled(cr, txt, blk_txt_len, scaled, x, y);
-	
+
 	x += MBE_GET_X_ADV(ext);
 	y += MBE_GET_Y_ADV(ext);
 	txt += blk_txt_len;
@@ -554,15 +556,15 @@
 
     ASSERT(txt_o != NULL);
     ASSERT(txt != NULL);
-    
+
     sz = strlen(txt) + 1;
     new_txt = (char *)realloc((void *)txt_o->txt, sz);
     if(new_txt == NULL)
 	return ERR;
-    
+
     memcpy(new_txt, txt, sz);
     txt_o->txt = new_txt;
-    
+
     return OK;
 }
 
@@ -576,14 +578,14 @@
 
     ASSERT(txt_o != NULL);
     ASSERT(nblks >= 0);
-    
+
     style_blks = &txt_o->style_blks;
     DARRAY_CLEAN(style_blks);
     style_blks_lst_adv(style_blks, nblks);
-    
+
     memcpy(style_blks->ds,
 	   blks, nblks * sizeof(mb_style_blk_t));
-	   
+
     return OK;
 }
 
@@ -597,7 +599,7 @@
 
     face = query_font_face("serif", MB_FONT_SLANT_ROMAN, 100);
     CU_ASSERT(face != NULL);
-    
+
     free_font_face(face);
 }
 
@@ -609,10 +611,10 @@
 
     face = query_font_face("serif", MB_FONT_SLANT_ROMAN, 100);
     CU_ASSERT(face != NULL);
-    
+
     scaled = make_scaled_font_face_matrix(face, matrix);
     CU_ASSERT(scaled != NULL);
-    
+
     scaled_font_free(scaled);
     free_font_face(face);
 }
@@ -665,14 +667,14 @@
     MBE_SET_Y_BEARING(&ext1, -8);
     MBE_SET_X_ADV(&ext1, 21);
     MBE_SET_Y_ADV(&ext1, -3);
-    
+
     MBE_SET_WIDTH(&ext2, 30);
     MBE_SET_HEIGHT(&ext2, 11);
     MBE_SET_X_BEARING(&ext2, 2);
     MBE_SET_Y_BEARING(&ext2, -11);
     MBE_SET_X_ADV(&ext2, 32);
     MBE_SET_Y_ADV(&ext2, -5);
-    
+
     extent_extents(&ext1, &ext2);
 
     CU_ASSERT(MBE_GET_WIDTH(&ext1) == 52);
@@ -687,7 +689,7 @@
 void test_compute_utf8_chars_sz(void) {
     const char *str = "\xe4\xb8\xad\xe6\x96\x87test\xe6\xb8\xac\xe8\xa9\xa6";
     int sz;
-    
+
     sz = compute_utf8_chars_sz(str, 4);
     CU_ASSERT(sz == 8);
 
@@ -718,15 +720,15 @@
 
     face = query_font_face("serif", MB_FONT_SLANT_ROMAN, 100);
     CU_ASSERT(face != NULL);
-    
+
     blks[0].n_chars = 5;
     blks[0].face = face;
     blks[0].font_sz = 10;
-    
+
     blks[1].n_chars = 4;
     blks[1].face = face;
     blks[1].font_sz = 15.5;
-    
+
     r = sh_stext_set_style((shape_t *)txt_o, blks, 2);
     CU_ASSERT(r == OK);
 
@@ -740,7 +742,7 @@
     CU_ASSERT(MBE_GET_X_ADV(ext) > 36);
     CU_ASSERT(MBE_GET_X_ADV(ext) < 72);
     CU_ASSERT(MBE_GET_Y_ADV(ext) == 0);
-    
+
     _rdman_shape_stext_free((shape_t *)txt_o);
     free_font_face(face);
 }
@@ -768,15 +770,15 @@
 
     face = query_font_face("serif", MB_FONT_SLANT_ROMAN, 100);
     CU_ASSERT(face != NULL);
-    
+
     blks[0].n_chars = 5;
     blks[0].face = face;
     blks[0].font_sz = 10;
-    
+
     blks[1].n_chars = 4;
     blks[1].face = face;
     blks[1].font_sz = 15.5;
-    
+
     r = sh_stext_set_style((shape_t *)txt_o, blks, 2);
     CU_ASSERT(r == OK);
 
@@ -791,7 +793,7 @@
     CU_ASSERT(MBE_GET_X_ADV(ext) < 72);
     CU_ASSERT(MBE_GET_Y_ADV(ext) > 36);
     CU_ASSERT(MBE_GET_Y_ADV(ext) < 72);
-    
+
     _rdman_shape_stext_free((shape_t *)txt_o);
     free_font_face(face);
 }
@@ -818,15 +820,15 @@
 
     face = query_font_face("serif", MB_FONT_SLANT_ROMAN, 100);
     CU_ASSERT(face != NULL);
-    
+
     blks[0].n_chars = 5;
     blks[0].face = face;
     blks[0].font_sz = 10;
-    
+
     blks[1].n_chars = 4;
     blks[1].face = face;
     blks[1].font_sz = 15.5;
-    
+
     r = sh_stext_set_style((shape_t *)txt_o, blks, 2);
     CU_ASSERT(r == OK);
 
@@ -837,7 +839,7 @@
     CU_ASSERT(area->y >= 40 && area->y < 50);
     CU_ASSERT(area->w >= 80 && area->w < 120);
     CU_ASSERT(area->h >= 8 && area->h < 12);
-    
+
     _rdman_shape_stext_free((shape_t *)txt_o);
     free_font_face(face);
 }
@@ -853,7 +855,7 @@
 
     txt_o = (sh_stext_t *)rdman_shape_stext_new(NULL, "hello world", 100, 50);
     CU_ASSERT(txt_o != NULL);
-    
+
     aggr = txt_o->shape.aggr;
     aggr[0] = 2;
     aggr[1] = 0;
@@ -864,15 +866,15 @@
 
     face = query_font_face("serif", MB_FONT_SLANT_ROMAN, 100);
     CU_ASSERT(face != NULL);
-    
+
     blks[0].n_chars = 5;
     blks[0].face = face;
     blks[0].font_sz = 10;
-    
+
     blks[1].n_chars = 6;
     blks[1].face = face;
     blks[1].font_sz = 15.5;
-    
+
     r = sh_stext_set_style((shape_t *)txt_o, blks, 2);
     CU_ASSERT(r == OK);
 
@@ -901,7 +903,7 @@
 
     txt_o = (sh_stext_t *)rdman_shape_stext_new(NULL, "hello world", 100, 50);
     CU_ASSERT(txt_o != NULL);
-    
+
     aggr = txt_o->shape.aggr;
     aggr[0] = 2;
     aggr[1] = 0;
@@ -912,15 +914,15 @@
 
     face = query_font_face("serif", MB_FONT_SLANT_ROMAN, 100);
     CU_ASSERT(face != NULL);
-    
+
     blks[0].n_chars = 5;
     blks[0].face = face;
     blks[0].font_sz = 10;
-    
+
     blks[1].n_chars = 6;
     blks[1].face = face;
     blks[1].font_sz = 15.5;
-    
+
     r = sh_stext_set_style((shape_t *)txt_o, blks, 2);
     CU_ASSERT(r == OK);
 
--- a/src/shape_text.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/shape_text.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -65,8 +67,8 @@
     text->attrs = attrs;
     text->align = TEXTALIGN_START;
     text->w = text->h = 0;
-    
-    rdman_shape_man(rdman, (shape_t *)text);
+
+    rdman_man_shape(rdman, (shape_t *)text);
 
     return (shape_t *)text;
 }
@@ -222,9 +224,9 @@
     int r;
 
     text = (sh_text_t *)shape;
-    
+
     text->d_font_size = coord_trans_size(shape->coord, text->font_size);
-    
+
     coord = sh_get_coord(shape);
     canvas = _coord_get_canvas(coord);
     sh_text_P_generate_layout(text, (mbe_t *)canvas);
--- a/src/shift.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/shift.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -45,7 +47,7 @@
     coord_t *coord;
     float ratio;
 
-    
+
     MB_TIMEVAL_CP(&diff, now);
     MB_TIMEVAL_DIFF(&diff, &shift->start_time);
     ratio = comp_mb_timeval_ratio(&diff, shift->playing_time);
--- a/src/sprite.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/sprite.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,15 +1,13 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include "mb_graph_engine.h"
 #include <dlfcn.h>
 #include <sys/stat.h>
 #include "mb_types.h"
-#include "mb_shapes.h"
-#include "mb_tools.h"
 #include "mb_redraw_man.h"
-#include "mb_observer.h"
-#include "mb_prop.h"
+#include "mb_sprite.h"
 
 #define ASSERT(x)
 #define OK 0
@@ -22,10 +20,10 @@
     int fsz;
     char *fullname;
     int r;
-    
+
     if(sprite_search_path == NULL)
 	sprite_search_path = strdup("/usr/share/madbutterffly");
-    
+
     fsz = strlen(sprite_search_path) + strlen(name) + 5;
     fullname = (char *)malloc(fsz);
 
@@ -39,14 +37,14 @@
     return fullname;
 }
 
-void sprite_set_search_path(char *path) {
+void sprite_set_search_path(const char *path) {
     int sz;
-    
+
     if (sprite_search_path)
 	free(sprite_search_path);
-    
+
     sprite_search_path = strdup(path);
-    
+
     sz = strlen(sprite_search_path);
     if(sprite_search_path[sz - 1] == '/')
 	sprite_search_path[sz - 1] = 0;
@@ -61,33 +59,31 @@
     mb_sprite_t *(*cnstr)(redraw_man_t *, coord_t *);
     mb_sprite_t *obj;
     int r;
-    
+
     so_path = sprite_search_so(name);
     if(so_path == NULL)
 	return NULL;
-    
+
     handle = dlopen(so_path, RTLD_LAZY);
     free(so_path);
     if (handle == NULL)
 	return NULL;
-    
+
     bname = strrchr(name, '/');
     if(bname != NULL && strlen(bname) > 250)
 	return NULL;
-    
+
     if(bname == NULL)
 	bname = name;
     else
 	bname++;
-    
+
     snprintf(cnstr_name, sizeof(cnstr_name), "%s_new", bname);
     cnstr = dlsym(handle, cnstr_name);
     if (cnstr == NULL)
 	return NULL;
-    
+
     obj = cnstr(rdman, root);
-    
+
     return obj;
 }
-
-/* vim: set ts=4 */
--- a/src/subtree_free.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/subtree_free.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include "mb_redraw_man.h"
 #include "mb_animate.h"
 
--- a/src/testcase.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/testcase.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include <CUnit/Basic.h>
 
 extern CU_pSuite get_tools_suite(void);
--- a/src/timer.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/timer.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,9 +1,12 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include <stdio.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 #include "mb_timer.h"
 #include "mb_tools.h"
+#include "mb_backend.h"
 
 
 #define OK 0
@@ -23,7 +26,7 @@
 
 mb_tman_t *mb_tman_new(void) {
     mb_tman_t *tman;
-    
+
     tman = (mb_tman_t *)malloc(sizeof(mb_tman_t));
     if(tman == NULL)
 	return NULL;
@@ -76,7 +79,7 @@
 
     return timer;
 }
-			    
+
 int mb_tman_remove(mb_tman_t *tman, mb_timer_t *timer) {
     STAILQ_REMOVE(tman->timers, mb_timer_t, next, timer);
     elmpool_elm_free(tman->timer_pool, timer);
@@ -120,3 +123,101 @@
 
     return OK;
 }
+
+/*! \defgroup tman_timer_man Timer manager based on mb_tman_t.
+ *
+ * This implmentation of timer manager is based on mb_tman_t.
+ * @{
+ */
+struct _tman_timer_man {
+    mb_timer_man_t timer_man;
+    mb_tman_t *tman;
+};
+
+static int _tman_timer_man_timeout(struct _mb_timer_man *tm_man,
+				   mb_timeval_t *tmout,
+				   mb_timer_cb_t cb, void *data);
+static void _tman_timer_man_remove(struct _mb_timer_man *tm_man, int tm_hdl);
+static mb_timer_man_t *_tman_timer_fact_new(void);
+static void _tman_timer_fact_free(mb_timer_man_t *timer_man);
+
+static struct _tman_timer_man _tman_default_timer_man = {
+    {_tman_timer_man_timeout, _tman_timer_man_remove},
+    NULL
+};
+
+mb_timer_factory_t tman_timer_factory = {
+    _tman_timer_fact_new,
+    _tman_timer_fact_free
+};
+
+/*! \brief Content of a timeout request.
+ *
+ * This is only used by internal of X support.  This data structure
+ * carry information to adopt mb_tman_t to mb_timer_man_t.
+ */
+struct _tman_timeout_data {
+    mb_timer_t *timer;		/*!< Handle returned by mb_tman_timeout() */
+    mb_timer_cb_t cb;		/*!< Real callback function */
+    void *data;			/*!< data for real callback */
+};
+
+static void
+_tman_tmo_hdlr(const mb_timeval_t *tmo,
+		 const mb_timeval_t *now,
+		 void *arg) {
+    struct _tman_timeout_data *data = (struct _tman_timeout_data *)arg;
+    
+    data->cb((int)data, tmo, now, data->data);
+}
+
+static int
+_tman_timer_man_timeout(struct _mb_timer_man *tm_man,
+			  mb_timeval_t *tmout, /* timeout (wall time) */
+			  mb_timer_cb_t cb, void *data) {
+    struct _tman_timer_man *timer_man = (struct _tman_timer_man *)tm_man;
+    mb_timer_t *timer;
+    struct _tman_timeout_data *tmout_data;
+
+    tmout_data = O_ALLOC(struct _tman_timeout_data);
+    tmout_data->cb = cb;
+    tmout_data->data = data;
+    timer = mb_tman_timeout(timer_man->tman, tmout,
+			    _tman_tmo_hdlr, tmout_data);
+    if(timer == NULL)
+	return ERR;
+    tmout_data->timer = timer;
+
+    return (int)tmout_data;
+}
+
+static void
+_tman_timer_man_remove(struct _mb_timer_man *tm_man, int tm_hdl) {
+    struct _tman_timer_man *timer_man = (struct _tman_timer_man *)tm_man;
+    struct _tman_timeout_data *tmout_data =
+	(struct _tman_timeout_data *)tm_hdl;
+
+    mb_tman_remove(timer_man->tman, tmout_data->timer);
+    free(tmout_data);
+}
+
+static mb_timer_man_t *
+_tman_timer_fact_new(void) {
+    if(_tman_default_timer_man.tman == NULL)
+	_tman_default_timer_man.tman = mb_tman_new();
+    return (mb_timer_man_t *)&_tman_default_timer_man;
+}
+
+static void
+_tman_timer_fact_free(mb_timer_man_t *timer_man) {
+}
+
+mb_tman_t *
+tman_timer_man_get_tman(mb_timer_man_t *tm_man) {
+    struct _tman_timer_man *timer_man = (struct _tman_timer_man *)tm_man;
+
+    return timer_man->tman;
+}
+
+
+/* @} */
--- a/src/timertool.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/timertool.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include <stdio.h>
 #include <sys/time.h>
 #ifdef __FreeBSD__
--- a/src/tools.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/tools.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include <stdlib.h>
 #include "mb_tools.h"
 
--- a/src/visibility.c	Mon Jul 19 15:44:49 2010 +0800
+++ b/src/visibility.c	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+// vim: sw=4:ts=8:sts=4
 #include <stdio.h>
 #include <stdlib.h>
 #include "mb_animate.h"
--- a/tools/Makefile.am	Mon Jul 19 15:44:49 2010 +0800
+++ b/tools/Makefile.am	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+# -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+# vim: sw=4:ts=8:sts=4
 m4datadir = $(datadir)/mb
 dist_m4data_DATA = foreach.m4 mb_c_header.m4 mb_c_source.m4
 dist_bin_SCRIPTS = svg2code.py
--- a/tools/Makefile.pmake	Mon Jul 19 15:44:49 2010 +0800
+++ b/tools/Makefile.pmake	Wed Dec 01 12:25:56 2010 +0800
@@ -1,3 +1,5 @@
+# -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+# vim: sw=4:ts=8:sts=4
 BINS= svg2code.py
 M4SCRIPTS= mb_c_header.m4 mb_c_source.m4 foreach.m4
 PREFIX?= /usr/local/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/gen_v8_binding.m4	Wed Dec 01 12:25:56 2010 +0800
@@ -0,0 +1,718 @@
+dnl
+dnl Developers should provide SET, WRAP, UNWRAP, and THROW C macros.
+dnl
+changequote(`[', `]')dnl
+include([foreach.m4])dnl
+divert([-1])dnl
+
+define([UNQUOTE], [$*])
+
+define([QUOTE], [[[$*]]])
+
+define([VARFRAME], [dnl
+pushdef([_FRAME_VARS], [])dnl
+])
+
+define([UNVARFRAME], [dnl
+EXPAND(_FRAME_VARS)dnl
+popdef([_FRAME_VARS])dnl
+])
+
+define([fdefine], [dnl
+pushdef([$1], [$2])dnl
+define([_FRAME_VARS], QUOTE(_FRAME_VARS[popdef([$1])]))dnl
+])
+
+define([COUNT],[ifelse([$*],[],0,[$#])])
+
+define([IMPORT],[define([$1],[$2$1(]$[]@[)])])
+
+define([EXPAND], [$1])
+
+define([PROJ_PREFIX], [xnjsmb_])
+
+define([START_ACCESSOR], [dnl
+divert([-1])dnl
+VARFRAME[]dnl
+  fdefine([INT], [
+static Handle<Value>
+]PROJ_PREFIX[]STRUCT_NAME[_get_$][1(Local<String> property, const AccessorInfo &info) {
+    Handle<Object> self = info.This();
+    STRUCT_TYPE *data;
+
+    data = (STRUCT_TYPE *)UNWRAP(self);
+    if(data == NULL)
+        THROW("Invalid object");
+    return Integer::New(data->$][1);
+}
+
+static void
+]PROJ_PREFIX[]STRUCT_NAME[_set_$][1(Local<String> property,
+		      Local<Value> value,
+		      const AccessorInfo &info) {
+    Handle<Object> self = info.This();
+    STRUCT_TYPE *data;
+
+    data = (STRUCT_TYPE *)UNWRAP(self);
+    if(data == NULL)
+        THROW_noret("Invalid object");
+    data->$][1 = value->Int32Value();
+}
+])
+  fdefine([NUMBER], [
+static Handle<Value>
+]PROJ_PREFIX[]STRUCT_NAME[_get_$][1(Local<String> property, const AccessorInfo &info) {
+    Handle<Object> self = info.This();
+    STRUCT_TYPE *data;
+
+    data = (STRUCT_TYPE *)UNWRAP(self);
+    if(data == NULL)
+        THROW("Invalid object");
+    return Number::New(data->$][1);
+}
+
+static void
+]PROJ_PREFIX[]STRUCT_NAME[_set_$][1(Local<String> property,
+		      Local<Value> value,
+		      const AccessorInfo &info) {
+    Handle<Object> self = info.This();
+    STRUCT_TYPE *data;
+
+    data = (STRUCT_TYPE *)UNWRAP(self);
+    if(data == NULL)
+        THROW_noret("Invalid object");
+    data->$][1 = value->NumberValue();
+}
+])
+  fdefine([OBJ], [
+static Handle<Value>
+]PROJ_PREFIX[]STRUCT_NAME[_get_$][1(Local<String> property, const AccessorInfo &info) {
+    Handle<Object> self = info.This();
+    STRUCT_TYPE *data;
+
+    data = (STRUCT_TYPE *)UNWRAP(self);
+    if(data == NULL)
+        THROW("Invalid object");
+    return ]PROJ_PREFIX[$][2_new(($][3 *)data->$][1);
+}
+
+static void
+]PROJ_PREFIX[]STRUCT_NAME[_set_$][1(Local<String> property,
+		      Local<Value> value,
+		      const AccessorInfo &info) {
+    Handle<Object> self = info.This();
+    Handle<Object> obj;
+    $][3 *v;
+    STRUCT_TYPE *data;
+
+    data = (STRUCT_TYPE *)UNWRAP(self);
+    if(data == NULL)
+        THROW_noret("Invalid object");
+    obj = value->ToObject();
+    v = ($][3 *)UNWRAP(obj);
+    if(v == NULL)
+        THROW_noret("Invalid object");
+    data->$][1 = v;
+}
+])
+  fdefine([ARRAY], [
+static Handle<Value>
+]PROJ_PREFIX[]STRUCT_NAME[_get_$][1(Local<String> property, const AccessorInfo &info) {
+    Handle<Object> self = info.This();
+    STRUCT_TYPE *data;
+
+    data = (STRUCT_TYPE *)UNWRAP(self);
+    if(data == NULL)
+        THROW("Invalid object");
+    return data->$][1;
+}
+
+static void
+]PROJ_PREFIX[]STRUCT_NAME[_set_$][1(Local<String> property,
+		      Local<Value> value,
+		      const AccessorInfo &info) {
+    Handle<Object> self = info.This();
+    Handle<Object> obj;
+    STRUCT_TYPE *data;
+
+    data = (STRUCT_TYPE *)UNWRAP(self);
+    if(data == NULL)
+        THROW_noret("Invalid object");
+    data->$][1 = value;
+}
+])
+  fdefine([STR], [
+static Handle<Value>
+]PROJ_PREFIX[]STRUCT_NAME[_get_$][1(Local<String> property, const AccessorInfo &info) {
+    Handle<Object> self = info.This();
+    STRUCT_TYPE *data;
+
+    data = (STRUCT_TYPE *)UNWRAP(self);
+    if(data == NULL)
+        THROW("Invalid object");
+    return String::New(data->$][1);
+}
+
+static void
+]PROJ_PREFIX[]STRUCT_NAME[_set_$][1(Local<String> property,
+		      Local<Value> value,
+		      const AccessorInfo &info) {
+    Handle<Object> self = info.This();
+    STRUCT_TYPE *data;
+
+    data = (STRUCT_TYPE *)UNWRAP(self);
+    if(data == NULL)
+        THROW_noret("Invalid object");
+    String::Utf8Value utf8(value->ToString());
+    free(data->$][1);
+    data->$][1 = strdup(*utf8);
+}
+])
+dnl
+dnl ACCESSOR(name, getter, setter)
+dnl
+  fdefine([ACCESSOR], [
+static Handle<Value>
+]PROJ_PREFIX[]STRUCT_NAME[_get_$][1(Local<String> property, const AccessorInfo &info) {
+    Handle<Object> self = info.This();
+    Handle<Value> _ret;
+    STRUCT_TYPE *data;
+    const char *err = NULL;
+
+    data = (STRUCT_TYPE *)UNWRAP(self);
+    if(data == NULL)
+        THROW("Invalid object");
+    _ret = $][2(self, data, &err);
+    if(err)
+	THROW(err);
+    return _ret;
+}
+
+static void
+]PROJ_PREFIX[]STRUCT_NAME[_set_$][1(Local<String> property,
+		      Local<Value> value,
+		      const AccessorInfo &info) {
+    Handle<Object> self = info.This();
+    STRUCT_TYPE *data;
+    const char *err = NULL;
+
+    data = (STRUCT_TYPE *)UNWRAP(self);
+    if(data == NULL)
+        THROW_noret("Invalid object");
+    $][3(self, data, value, &err);
+    if(err)
+	THROW_noret(err);
+}
+])
+divert([])dnl
+])
+
+define([STOP_ACCESSOR], [dnl
+divert([-1])dnl
+UNVARFRAME[]dnl
+divert([])dnl
+])
+
+define([SET_ACCESSOR], [dnl
+VARFRAME[]dnl
+fdefine([INT], [$][1])dnl
+fdefine([NUMBER], [$][1])dnl
+fdefine([OBJ], [$][1])dnl
+fdefine([ARRAY], [$][1])dnl
+fdefine([STR], [$][1])dnl
+fdefine([ACCESSOR], [$][1])dnl
+    inst_temp->SetAccessor(String::New("$1"),
+			   PROJ_PREFIX[]STRUCT_NAME[]_get_[]$1,
+			   PROJ_PREFIX[]STRUCT_NAME[]_set_[]$1);
+UNVARFRAME[]dnl
+])
+
+define([START_METHOD_ARG_VAR], [dnl
+VARFRAME[]dnl
+fdefine([INT], [dnl
+    int arg_$][1;
+])dnl
+fdefine([NUMBER], [dnl
+    double arg_$][1;
+])dnl
+fdefine([OBJ], [dnl
+    $][3 *arg_$][1;
+])dnl
+fdefine([ARRAY], [dnl
+    Handle<Value> arg_$][1;
+])dnl
+fdefine([STR], [dnl
+    char *arg_$][1;
+])dnl
+fdefine([FUNC], [dnl
+    Handle<Function> arg_$][1;
+])dnl
+fdefine([SELF], [])dnl
+fdefine([ERR], [])dnl
+])
+
+define([START_METHOD_ARG_TYPE_CHK], [dnl
+VARFRAME[]dnl
+fdefine([INT], [ ||
+       !args[[i++]]->IsInt32()])dnl
+fdefine([NUMBER], [ ||
+       !args[[i++]]->IsNumber()])dnl
+fdefine([OBJ], [ ||
+       !args[[i++]]->IsObject()])dnl
+fdefine([ARRAY], [ ||
+       !args[[i++]]->IsArray()])dnl
+fdefine([STR], [ ||
+       !args[[i++]]->IsString()])dnl
+fdefine([FUNC], [ ||
+       !args[[i++]]->IsFunction()])dnl
+fdefine([SELF], [])dnl
+fdefine([ERR], [])dnl
+])
+
+define([START_TYPE_CHK], [dnl
+VARFRAME[]dnl
+fdefine([INT], [$1->IsInt32()])dnl
+fdefine([NUMBER], [$1->IsNumber()])dnl
+fdefine([OBJ], [$1->IsObject()])dnl
+fdefine([ARRAY], [$1->IsArray()])dnl
+fdefine([STR], [$1->IsString()])dnl
+fdefine([FUNC], [$1->IsFunction()])dnl
+])
+
+define([START_METHOD_ARG_ASSIGN], [dnl
+VARFRAME[]dnl
+fdefine([INT], [dnl
+    arg_$][1 = args[[i++]]->Int32Value();
+])dnl
+fdefine([NUMBER], [dnl
+    arg_$][1 = args[[i++]]->NumberValue();
+])dnl
+fdefine([OBJ], [dnl
+    arg_$][1 = ($][3 *)UNWRAP(args[[i++]]->ToObject());
+    if(arg_$][1 == NULL)
+        THROW("Invalid argument");
+])dnl
+fdefine([ARRAY], [dnl
+    arg_$][1 = args[[i++]];
+])dnl
+fdefine([STR], [dnl
+    arg_$][1 = strdup(*String::Utf8Value(args[[i++]]->ToString()));
+])dnl
+fdefine([FUNC], [dnl
+    arg_$][1 = args[[i++]].As<Function>();
+])dnl
+fdefine([SELF], [])dnl
+fdefine([ERR], [])dnl
+])
+
+define([START_VALUE_ASSIGN], [dnl
+VARFRAME[]dnl
+fdefine([INT], [dnl
+    $1 = $2->Int32Value();
+])dnl
+fdefine([NUMBER], [dnl
+    $1 = $2->NumberValue();
+])dnl
+fdefine([OBJ], [dnl
+    $1 = ($][2 *)UNWRAP($2->ToObject());
+    if($1 == NULL)
+        THROW("Invalid argument");
+])dnl
+fdefine([ARRAY], [dnl
+    $1 = $2;
+])dnl
+fdefine([STR], [dnl
+    $1 = strdup(*String::Utf8Value($2->ToString()));
+])dnl
+fdefine([FUNC], [dnl
+    $1 = $2.As<Function>();
+])dnl
+])
+
+define([START_METHOD_ARG_PASS], [dnl
+VARFRAME[]dnl
+fdefine([INT], [arg_$][1])dnl
+fdefine([NUMBER], [arg_$][1])dnl
+fdefine([OBJ], [arg_$][1])dnl
+fdefine([ARRAY], [arg_$][1])dnl
+fdefine([STR], [arg_$][1])dnl
+fdefine([FUNC], [arg_$][1])dnl
+fdefine([SELF], [self])dnl
+fdefine([ERR], [&_err])dnl
+fdefine([VAL], [&_err])dnl
+])
+
+define([START_METHOD_RET_VAL], [dnl
+VARFRAME[]dnl
+fdefine([INT], [dnl
+    int _ret;
+])dnl
+fdefine([NUMBER], [dnl
+    double _ret;
+])dnl
+fdefine([OBJ], [dnl
+    $][2 *_ret;
+])dnl
+fdefine([ARRAY], [dnl
+    Handle<Value> _ret;
+])dnl
+fdefine([STR], [dnl
+    char *_ret;
+])dnl
+fdefine([FUNC], [dnl
+    Handle<Function> _ret;
+])dnl
+fdefine([VAL], [dnl
+    Handle<Value> _ret;
+])dnl
+])
+
+define([START_VAR], [dnl
+VARFRAME[]dnl
+fdefine([INT], [dnl
+    int $1;
+])dnl
+fdefine([NUMBER], [dnl
+    double $1;
+])dnl
+fdefine([OBJ], [dnl
+    $][2 *$1;
+])dnl
+fdefine([ARRAY], [dnl
+    Handle<Value> *$1;
+])dnl
+fdefine([STR], [dnl
+    char *$1;
+])dnl
+fdefine([FUNC], [dnl
+    Handle<Function> $1;
+])dnl
+])
+
+define([START_METHOD_RET_ASSIGN], [dnl
+VARFRAME[]dnl
+fdefine([INT], [_ret = (int)])dnl
+fdefine([NUMBER], [_ret = (double)])dnl
+fdefine([OBJ], [_ret = ($][2 *)])dnl
+fdefine([ARRAY], [_ret = ])dnl
+fdefine([STR], [_ret = (char *)])dnl
+fdefine([FUNC], [_ret = ])dnl
+fdefine([VAL], [_ret = ])dnl
+])
+
+define([START_METHOD_RET], [dnl
+VARFRAME[]dnl
+fdefine([INT], [
+    _ret_val = Integer::New(_ret);
+])dnl
+fdefine([NUMBER], [
+    _ret_val = Number::New(_ret);
+])dnl
+fdefine([OBJ], [
+    _ret_val = PROJ_PREFIX[]$][1[]_new(_ret);
+])dnl
+fdefine([ARRAY], [
+    _ret_val = _ret;
+])dnl
+fdefine([STR], [
+    _ret_val = String::New(_ret);
+])dnl
+fdefine([FUNC], [
+    _rt_val = _ret;
+])dnl
+fdefine([VAL], [
+    _rt_val = _ret;
+])dnl
+])
+
+define([STOP_METHOD_ARG], [dnl
+UNVARFRAME[]dnl
+])
+
+define([START_METHOD], [dnl
+dnl
+dnl METHOD(name, func, arguments, cnt, ret_type, options)
+dnl
+define([METHOD], [
+VARFRAME[]dnl
+dnl
+ifelse($][6, [], [], [dnl
+foreach([ITER], ]$][6[, [EXPAND([fdefine]ITER)])dnl
+])dnl
+dnl
+static Handle<Value>
+PROJ_PREFIX[]STRUCT_NAME[]_$][1(const Arguments &args) {
+    HandleScope scope;
+    int i;
+    int argc = args.Length();
+    Handle<Object> self = args.This();
+    STRUCT_TYPE *_self = (STRUCT_TYPE *)UNWRAP(self);
+    const char *_err = NULL;
+foreach([ITER], $][3, [START_METHOD_ARG_VAR[]ITER[]STOP_METHOD_ARG])dnl
+START_METHOD_RET_VAL[]$][5[]STOP_METHOD_ARG
+    Handle<Value> _ret_val;
+
+    if(_self == NULL)
+    	THROW("Invalid object");
+    if(argc != $][4)
+        THROW("Invalid number of arguments (!=$][4)");
+    i = 0;
+    if(0[]dnl
+foreach([ITER], $][3, [START_METHOD_ARG_TYPE_CHK[]ITER[]STOP_METHOD_ARG]))
+        THROW("Invalid argument type");
+
+    i = 0;
+foreach([ITER], $][3, [START_METHOD_ARG_ASSIGN[]ITER[]STOP_METHOD_ARG])dnl
+
+    START_METHOD_RET_ASSIGN[]$][5[]STOP_METHOD_ARG[]$][2(_self[]foreach([ITER], $][3, [START_METHOD_ARG_PASS[], ITER[]STOP_METHOD_ARG]));
+    if(_err)
+        THROW(_err);
+START_METHOD_RET[]$][5[]STOP_METHOD_ARG[]dnl
+ifelse($][5, [], [
+    return Null();
+], [dnl
+dnl
+dnl Modify returned object
+dnl
+ifdef([MOD], [
+    MOD[](self, _ret_val);
+])dnl
+    scope.Close(_ret_val);
+    return _ret_val;
+])dnl
+}
+UNVARFRAME[]dnl
+])dnl
+])
+
+define([STOP_METHOD], [undefine([METHOD])])
+
+define([SET_METHOD], [dnl
+define([METHOD], [dnl
+    SET(proto_temp, "$][1",
+        FunctionTemplate::New(PROJ_PREFIX[]STRUCT_NAME[]_$][1));
+])dnl
+$1[]dnl
+undefine([METHOD])dnl
+])
+
+define([DEF_GET_INDEX], [
+static Handle<Value>
+PROJ_PREFIX[]STRUCT_NAME[]_get_index(uint32_t index, const AccessorInfo &info) {
+    Handle<Object> self = info.This();
+    STRUCT_TYPE *obj = (STRUCT_TYPE *)UNWRAP(self);
+    const char *_err = NULL;
+START_METHOD_RET_VAL[]$2[]STOP_METHOD_ARG[]dnl
+    Handle<Value> _ret_val;
+
+    if(obj == NULL)
+        THROW("Invalid object");
+    _ret = $1(obj, self, index, &_err);
+    if(_err)
+        THROW(_err);
+START_METHOD_RET[]$2[]STOP_METHOD_ARG[]dnl
+    return _ret_val;
+}
+])
+
+define([DEF_SET_INDEX], [
+static Handle<Value>
+PROJ_PREFIX[]STRUCT_NAME[]_set_index(uint32_t index, Local<Value> value,
+	const AccessorInfo &info) {
+    Handle<Object> self = info.This();
+    STRUCT_TYPE *obj = (STRUCT_TYPE *)UNWRAP(self);
+    const char *_err = NULL;
+START_VAR([in_value])[]$2[]STOP_METHOD_ARG[]dnl
+START_METHOD_RET_VAL[]$2[]STOP_METHOD_ARG[]dnl
+    Handle<Value> _ret_val;
+
+    if(obj == NULL)
+        THROW("Invalid object");
+    if(START_TYPE_CHK(value)[]![]$2[]STOP_METHOD_ARG)
+        THROW("Invalid value type");
+
+START_VALUE_ASSIGN(in_value, value)[]$2[]STOP_METHOD_ARG[]dnl
+    _ret = $1(obj, self, index, in_value, &_err);
+    if(_err) THROW(_err);
+START_METHOD_RET[]$2[]STOP_METHOD_ARG[]dnl
+    return _ret_val;
+}
+])
+
+define([INSTALL_INDEX_FUNCTIONS],[dnl
+define([FIRST], [$][1])dnl
+ifdef([GET_INDEX], [ifdef([SET_INDEX], [dnl
+    inst_temp->SetIndexedPropertyHandler(PROJ_PREFIX[]STRUCT_NAME[]_get_index,
+					 PROJ_PREFIX[]STRUCT_NAME[]_set_index);
+], [dnl
+    inst_temp->SetIndexedPropertyHandler(PROJ_PREFIX[]STRUCT_NAME[]_get_index);
+])])dnl
+undefine([FIRST])dnl
+])
+
+define([CTOR_INTERNAL], [dnl
+    int argc = args.Length();
+    Handle<Object> self = args.This();
+    $4 *obj;
+foreach([ITER], $2, [START_METHOD_ARG_VAR[]ITER[]STOP_METHOD_ARG])dnl
+    int i;
+
+    if(argc != $3)
+        THROW("Invalid number of arguments (!=$][4)");
+    i = 0;
+    if(0]dnl
+[foreach([ITER], $2, [START_METHOD_ARG_TYPE_CHK[]ITER[]STOP_METHOD_ARG]))
+        THROW("Invalid argument type");
+
+    i = 0;
+foreach([ITER], $2, [START_METHOD_ARG_ASSIGN[]ITER[]STOP_METHOD_ARG])dnl
+
+define([SEP], [])dnl
+    obj = ($4 *)$1(foreach([ITER], $2, [START_METHOD_ARG_PASS[]SEP[]ITER[]STOP_METHOD_ARG[]define([SEP], [, ])]));[]undefine([SEP])
+
+    WRAP(self, obj);]
+ifdef([STMOD], [
+    STMOD(self, obj);
+])dnl
+)
+
+dnl
+dnl STRUCT(struct_name, struct_type, member_vars, methods, options)
+dnl
+define([STRUCT], [dnl
+define([STRUCT_NAME], [$1])dnl
+define([STRUCT_TYPE], [$2])dnl
+dnl
+VARFRAME[]dnl
+ifelse([$5], [], [], [dnl
+foreach([ITER], $5, [dnl
+EXPAND([fdefine]ITER)[]dnl
+])dnl
+])dnl
+dnl
+[
+/* **************************************************
+ * STRUCT: $1
+ * Generated by gen_v8_binding.m4
+ */
+static Handle<Value>
+]PROJ_PREFIX[$1(const Arguments &args) {
+]ifdef([CTOR], [EXPAND([CTOR_INTERNAL](EXPAND([UNQUOTE]CTOR), [$2]))])dnl
+    return Null();
+[}
+
+static Persistent<FunctionTemplate> ]PROJ_PREFIX[$1][_temp;
+
+static Handle<Value>
+]PROJ_PREFIX[$1][_new($2 *data) {
+    Handle<Object> obj;
+    Handle<Function> func;
+
+    func = ]PROJ_PREFIX[$1][_temp->GetFunction();
+    obj = func->NewInstance();
+    WRAP(obj, data);]
+ifdef([STMOD], [
+    STMOD[(obj, data)];
+])dnl
+[   return obj;
+}
+]dnl
+foreach([ITER], ($3), [START_ACCESSOR ITER STOP_ACCESSOR])dnl
+foreach([ITER], ($4), [START_METHOD ITER STOP_METHOD])dnl
+ifdef([GET_INDEX], [EXPAND([DEF_GET_INDEX]GET_INDEX)])dnl
+ifdef([SET_INDEX], [EXPAND([DEF_SET_INDEX]SET_INDEX)])dnl
+[
+static void
+]PROJ_PREFIX[$1][_init(void) {
+    Handle<FunctionTemplate> func_temp;
+    Handle<ObjectTemplate> inst_temp;
+    Handle<ObjectTemplate> proto_temp;
+
+    func_temp = FunctionTemplate::New(]PROJ_PREFIX[$1);
+    func_temp->SetClassName(String::New("]STRUCT_NAME["));
+]ifdef([INHERIT], [dnl
+    func_temp->Inherit(]PROJ_PREFIX[]INHERIT[_temp);
+])dnl
+[    inst_temp = func_temp->InstanceTemplate();
+    inst_temp->SetInternalFieldCount(1);
+]foreach([ITER], ($3), [SET_ACCESSOR(ITER)])dnl
+INSTALL_INDEX_FUNCTIONS[]dnl
+
+    proto_temp = func_temp->PrototypeTemplate();
+foreach([ITER], ($4), [SET_METHOD(ITER)])dnl
+
+    PROJ_PREFIX[$1][_temp = Persistent<FunctionTemplate>::New(func_temp);
+}]dnl
+dnl
+UNVARFRAME[]dnl
+dnl
+])
+
+dnl
+dnl FUNCTION(func_name, real_func, arguments, arguement_count,
+dnl          return_type, options)
+dnl
+define([FUNCTION], [dnl
+dnl
+VARFRAME[]dnl
+ifelse($6, [], [], [dnl
+foreach([ITER], $6, [EXPAND([fdefine]ITER)])dnl
+])dnl
+dnl
+/* **************************************************
+ * [FUNCTION]: $1
+ * Generated by gen_v8_binding.m4
+ */
+static Handle<Value>
+PROJ_PREFIX[]$1(const Arguments &args) {
+    HandleScope scope;
+    int argc = args.Length();
+    int i;
+    const char *_err = NULL;
+foreach([ITER], ($3), [START_METHOD_ARG_VAR[]ITER[]STOP_METHOD_ARG])dnl
+START_METHOD_RET_VAL[]$5[]STOP_METHOD_ARG[]dnl
+    Handle<Value> _ret_val;
+    
+    if(argc != $4)
+        THROW("Invalid number of arguments (!=$][4)");
+    i = 0;
+    if(0]dnl
+[foreach([ITER], ($3), [START_METHOD_ARG_TYPE_CHK[]ITER[]STOP_METHOD_ARG]))
+        THROW("Invalid argument type");
+
+    i = 0;
+foreach([ITER], ($3), [START_METHOD_ARG_ASSIGN[]ITER[]STOP_METHOD_ARG])dnl
+
+define([SEP], [])dnl
+    START_METHOD_RET_ASSIGN[]$5[]STOP_METHOD_ARG[]$2(foreach([ITER], ($3), [START_METHOD_ARG_PASS[]SEP[]ITER[]STOP_METHOD_ARG[]define([SEP], [, ])]));[]undefine([SEP])
+    if(_err)
+        THROW(_err);
+START_METHOD_RET[]$][5[]STOP_METHOD_ARG[]dnl
+ifelse($][5, [], [
+    return Null();
+], [dnl
+dnl
+dnl Modify returned object
+dnl
+ifdef([MOD], [
+    MOD[](self, _ret_val);
+])dnl
+    scope.Close(_ret_val);
+    return _ret_val;
+])dnl
+}
+static Persistent<FunctionTemplate> PROJ_PREFIX[]$1[]_temp;
+
+static void
+PROJ_PREFIX[]$1[]_init(void) {
+    Handle<FunctionTemplate> func_temp;
+
+    func_temp = FunctionTemplate::New(PROJ_PREFIX[]$1);
+    PROJ_PREFIX[]$1[]_temp = Persistent<FunctionTemplate>::New(func_temp);
+}
+dnl
+UNVARFRAME[]dnl
+dnl
+])
+
+divert([])dnl
--- a/tools/mb_c_source.m4	Mon Jul 19 15:44:49 2010 +0800
+++ b/tools/mb_c_source.m4	Wed Dec 01 12:25:56 2010 +0800
@@ -475,6 +475,7 @@
 #include <mb_redraw_man.h>
 #include <mb_shapes.h>
 #include <mb_paint.h>
+#include <mb_sprite.h>
 #include "$1.h"
 
 #ifdef MB_SPRITE_OFFSET
--- a/tools/svg2code.py	Mon Jul 19 15:44:49 2010 +0800
+++ b/tools/svg2code.py	Wed Dec 01 12:25:56 2010 +0800
@@ -1,4 +1,6 @@
 #! /usr/bin/env python
+# -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
+# vim: sw=4:ts=8:sts=4
 from xml.dom.minidom import parse
 import sys
 import re
@@ -131,7 +133,7 @@
         style_str = node.getAttribute('style')
         prop_map = get_style_map(style_str)
         pass
-    
+
     if node.hasAttribute('fill-opacity'):
         opacity = float(node.getAttribute('fill-opacity'))
     elif node.hasAttribute('opacity'):
@@ -234,15 +236,15 @@
 # \see calc_center_and_x_aix()
 def _calc_ellipse_of_arc(x0, y0, rx, ry, x_rotate, large, sweep, x, y):
     import math
-    
+
     _sin = math.sin(x_rotate)
     _cos = math.cos(x_rotate)
-    
+
     nrx = x * _cos + y * _sin   # Not Rotated X
     nry = x * -_sin + y * _cos
     nrx0 = x0 * _cos + y0 * _sin
     nry0 = x0 * -_sin + y0 * _cos
-    
+
     udx = (nrx - nrx0) / 2 / rx # ux - umx
     udy = (nry - nry0) / 2 / ry # uy - umy
     umx = (nrx + nrx0) / 2 / rx
@@ -279,10 +281,10 @@
 
     nrcx = rx * (udcx + umx)
     nrcy = ry * (udcy + umy)
-    
+
     cx = nrcx * _cos - nrcy * _sin
     cy = nrcx * _sin + nrcy * _cos
-    
+
     return cx, cy
 
 # M x y             : Move to (x,y)
@@ -313,20 +315,20 @@
 
 def _angle_rotated_ellipse(x, y, rx, ry, x_rotate):
     import math
-    
+
     _cos = math.cos(x_rotate)
     _sin = math.sin(x_rotate)
 
     nrx = (x * _cos + y * _sin) / rx
     nry = (-x * _sin + y * _cos) / ry
-    
+
     xy_tan = nry / nrx
     xy_angle = math.atan(xy_tan)
 
     if nrx < 0:
         xy_angle = math.pi + xy_angle
         pass
-    
+
     return xy_angle
 
 def rotate(x, y, angle):
@@ -340,7 +342,7 @@
 def translate_path_data(data, codefo):
     import string
     import math
-    
+
     temp = data.split()
     fields=[]
     for f in temp:
@@ -385,7 +387,7 @@
             pnts.append(pnts[-2])
             pnts.append(arg + pnts[-2])
             continue
-            
+
         arg = float(f)
         if (cmd not in 'am') and (cmd in string.lowercase):
             # relative and not arc or moveto
@@ -424,14 +426,14 @@
 
             c3x, c3y = rotate(-rx, ry, x_rotate)
             c3x, c3y = c3x + cx, c3y + cy
-            
+
             pnts[-7:] = [c0x, c0y, c1x, c1y, c2x, c2y, c3x, c3y, abs_x, abs_y]
-            
+
             start_angle = _angle_rotated_ellipse(x0 - cx, y0 - cy,
                                              rx, ry, x_rotate)
             stop_angle = _angle_rotated_ellipse(x - cx, y - cy,
                                             rx, ry, x_rotate)
-            
+
             # sweep == 1 for positive-angle direction
             # sweep == 0 for negative-angle direction
             if start_angle > stop_angle and sweep:
@@ -439,7 +441,7 @@
             elif start_angle < stop_angle and not sweep:
                 start_angle = math.pi * 2 + start_angle
                 pass
-            
+
             float_args.extend([cx, cy, rx, ry,
                                start_angle, stop_angle, x_rotate])
             pass
@@ -548,7 +550,7 @@
         pass
     if tspan.hasAttribute('x'):
         # Render the tspan as an independent text if the x
-        # attribute is defined. All elements inside 
+        # attribute is defined. All elements inside
         # the tspan will be ignore by the outter text or tspan elements.
         # FIXME: We need to apply the style map recursively.
         merge_style(tspan, text)
@@ -585,7 +587,7 @@
                 print >> codefo, 'PANGO_SIZE(%d,%d,%d)dnl' % (font_sz*1024,start,end)
                 pass
             pass
-        
+
         if style_map.has_key('font-style'):
             font_style = style_map['font-style'].lower()
 	    if font_style == 'normal':
@@ -674,10 +676,10 @@
 
 def stext_generate_font_attributes(text, attrs, coord_id, codefo, doc):
     text_id = _get_id(text)
-    
+
     for start, end, node in attrs:
         style_map = node.style_map
-        
+
         font_sz = 10
         if style_map.has_key('font-size'):
             fsz = style_map['font-size']
@@ -687,13 +689,13 @@
                 font_sz = float(fsz)
                 pass
             pass
-        
+
         if style_map.has_key('font-family'):
             font_family = style_map['font-family']
         else:
             font_family = 'serif'
             pass
-        
+
         font_slant = 0
         if style_map.has_key('font-style'):
             fn_style = style_map['font-style']
@@ -706,7 +708,7 @@
             else:
                 raise ValueError, '%s is not a valid font-style' % (fn_style)
             pass
-        
+
         font_weight = 80
         if style_map.has_key('font-weight'):
             fn_weight = style_map['font-weight']
@@ -726,7 +728,7 @@
                 font_weight = int(fn_weight)
                 pass
             pass
-        
+
         print >> codefo, 'STYLE_BLOCK([%s], %d, [%s], %f, %d, %d)dnl' % (
             text_id, end - start, font_family, font_sz,
             font_slant, font_weight)
@@ -736,7 +738,7 @@
 def stext_gen_text(text, coord_id, codefo, doc, txt_strs, attrs):
     if not txt_strs:
         return
-    
+
     text_id = _get_id(text)
     x = float(text.getAttribute('x'))
     y = float(text.getAttribute('y'))
@@ -757,7 +759,7 @@
     coord_id = translate_shape_transform(text, coord_id, codefo)
     try:
         map = text.style_map
-    except:	
+    except:
         map = translate_font_style(text, codefo)
         text.style_map = map
         pass
@@ -781,7 +783,7 @@
 @check_mbname
 def translate_image(image, coord_id, codefo, doc):
     coord_id = translate_shape_transform(image, coord_id, codefo)
-    
+
     image_id = _get_id(image)
     if not image.hasAttributeNS(xlinkns, 'href'):
 	raise ValueError, 'image %s must has a href attribute.' % (image_id)
@@ -815,7 +817,7 @@
 		'ADD_IMAGE([%s], [%s], %f, %f, %f, %f, [%s])dnl' % (
 		        image_id, href, x, y, width, height, coord_id)
     pass
- 
+
 
 reo_func = re.compile('([a-zA-Z]+)\\([^\\)]*\\)')
 reo_translate = re.compile('translate\\(([-+]?[0-9]+(\\.[0-9]+)?),([-+]?[0-9]+(\\.[0-9]+)?)\\)')
@@ -889,7 +891,7 @@
                 not scene.hasAttribute('ref') or \
                 not scene.hasAttribute('start'):
             continue
-        
+
         start_str = scene.getAttribute('start')
         start = end = int(start_str)
         if scene.hasAttribute('end'):
@@ -897,11 +899,11 @@
             end = int(end_str)
             pass
         ref = scene.getAttribute('ref')
-        
+
         while len(scenes) <= end:
             scenes.append([])
             pass
-        
+
         for i in range(start, end + 1):
             scenes[i].append(ref)
             pass
@@ -925,7 +927,7 @@
         pass
     else:
         raise ValueErr, 'no any svg tag node.'
-    
+
     svg = node
     for node in svg.childNodes:
         if node.localName == 'defs' and node.namespaceURI == svgns:
@@ -946,14 +948,14 @@
 if __name__ == '__main__':
     from os import path
     import optparse
-    
+
     usage='usage: %prog [options] <SVG file> [<output>]'
     parser = optparse.OptionParser(usage=usage)
     parser.add_option('-s', '--stext', dest='stext',
                       action='store_true', default=False,
                       help='Use sh_stext instead of sh_text');
     options, args = parser.parse_args()
-    
+
     if len(args) == 2:
         svgfn = args[0]
         codefn = args[1]
@@ -964,7 +966,7 @@
         parser.print_help()
         sys.exit(1)
         pass
-    
+
     struct_name = path.basename(codefn).split('.')[0]
 
     if options.stext: