changeset 73:9ab15ebc9061

Observer for mouse events
author Thinker K.F. Li <thinker@branda.to>
date Mon, 18 Aug 2008 01:59:26 +0800
parents 171a8cb7e4b5
children 3e3e074120a7
files examples/svg2code_ex/Makefile src/Makefile src/X_main.c src/mb_types.h src/observer.c src/observer.h src/redraw_man.c src/redraw_man.h src/shape_path.c src/shape_rect.c src/shape_text.c src/shapes.h src/testcase.c src/tools.h tools/mb_c_source.m4
diffstat 15 files changed, 384 insertions(+), 30 deletions(-) [+]
line wrap: on
line diff
--- a/examples/svg2code_ex/Makefile	Wed Aug 13 09:25:57 2008 +0800
+++ b/examples/svg2code_ex/Makefile	Mon Aug 18 01:59:26 2008 +0800
@@ -1,7 +1,12 @@
 SVG=svg2code_ex.svg
 TOOLSDIR=../../tools
+INCS=-I../../src
+CFLAGS=-g `pkg-config --cflags cairo` $(INCS) -Wall
 
-all: $(SVG:C/.svg/.mb/) $(SVG:C/.svg/.c/) $(SVG:C/.svg/.h/)
+all:	$(SVG:C/.svg/.o/)
+
+$(SVG:C/.svg/.o/): $(SVG:C/.svg/.c/) $(SVG:C/.svg/.h/)
+	$(CC) -c $(CFLAGS) -o $@ $(SVG:C/.svg/.c/)
 
 $(SVG:C/.svg/.mb/): $(SVG)
 	$(TOOLSDIR)/svg2code.py $(.ALLSRC) $@
@@ -13,7 +18,7 @@
 	m4 -I $(TOOLSDIR) mb_c_header.m4 $(.ALLSRC) > $@
 
 clean:
-	for i in *.mb *.o *~; do \
+	for i in *.mb *.o *~ $(SVG:C/.svg/.c/) $(SVG:C/.svg/.h/); do \
 		if [ -e "$$i" ]; then \
 			echo "delete $$i"; \
 			rm -f "$$i"; \
--- a/src/Makefile	Wed Aug 13 09:25:57 2008 +0800
+++ b/src/Makefile	Mon Aug 18 01:59:26 2008 +0800
@@ -1,5 +1,6 @@
 SRCS =	coord.c geo.c shape_path.c shape_text.c shape_rect.c \
-	redraw_man.c timer.c animate.c paint.c event.c tools.c
+	redraw_man.c timer.c animate.c paint.c event.c observer.c \
+	tools.c
 OBJS = ${SRCS:C/(.*)\.c/\1.o/g}
 TESTCASE_OBJS = ${SRCS:C/(.*)\.c/testcase-\1.o/g}
 CFLAGS+=	-Wall -I/usr/local/include `pkg-config --cflags cairo`
--- a/src/X_main.c	Wed Aug 13 09:25:57 2008 +0800
+++ b/src/X_main.c	Mon Aug 18 01:59:26 2008 +0800
@@ -250,10 +250,10 @@
     text_stroke->free(text_stroke);
     text_fill->free(text_fill);
     redraw_man_destroy(&rdman);
-    sh_path_free(path1);
-    sh_path_free(path2);
-    sh_rect_free(rect);
-    sh_text_free(text);
+    path1->free(path1);
+    path2->free(path2);
+    rect->free(rect);
+    text->free(text);
     cairo_destroy(tmpcr);
     cairo_surface_destroy(tmpsuf);
 }
--- a/src/mb_types.h	Wed Aug 13 09:25:57 2008 +0800
+++ b/src/mb_types.h	Mon Aug 18 01:59:26 2008 +0800
@@ -3,6 +3,7 @@
 
 #include <cairo.h>
 #include "tools.h"
+#include "observer.h"
 
 typedef float co_aix;
 typedef struct _shape shape_t;
@@ -47,6 +48,8 @@
 
     area_t *cur_area, *last_area;
     area_t areas[2];
+
+    subject_t *mouse_event;
 };
 #define GEF_DIRTY 0x1
 #define GEF_HIDDEN 0x2
@@ -96,6 +99,7 @@
     struct _coord *sibling;
 
     STAILQ(shape_t) members;	/*!< All shape_t objects in this coord. */
+    subject_t *mouse_event;
 } coord_t;
 #define COF_DIRTY 0x1
 #define COF_HIDDEN 0x2
@@ -129,8 +133,9 @@
     shape_t *coord_mem_next;
     paint_t *fill, *stroke;
     co_aix stroke_width;
-    int stroke_linecap;
-    int stroke_linejoin;
+    int stroke_linecap:2;
+    int stroke_linejoin:2;
+    void (*free)(shape_t *shape);
 };
 enum { SHT_UNKNOW, SHT_PATH, SHT_TEXT, SHT_RECT };
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/observer.c	Mon Aug 18 01:59:26 2008 +0800
@@ -0,0 +1,149 @@
+#include <stdio.h>
+#include "redraw_man.h"
+#include "observer.h"
+#include "tools.h"
+
+subject_t *subject_new(ob_factory_t *factory, void *obj, int obj_type) {
+    subject_t *subject;
+
+    subject = factory->subject_alloc(factory);
+    if(subject == NULL)
+	return NULL;
+
+    subject->obj = obj;
+    subject->obj_type = obj_type;
+    subject->flags = 0;
+    STAILQ_INIT(subject->observers);
+
+    return subject;
+}
+
+void subject_free(ob_factory_t *factory, subject_t *subject) {
+    observer_t *observer;
+
+    while((observer = STAILQ_HEAD(subject->observers))) {
+	STAILQ_REMOVE(subject->observers, observer_t, next, observer);
+	factory->observer_free(factory, observer);
+    }
+    factory->subject_free(factory, subject);
+}
+
+
+void subject_notify(ob_factory_t *factory, subject_t *subject, event_t *evt) {
+    observer_t *observer;
+
+    while(subject) {
+	for(observer = STAILQ_HEAD(subject->observers);
+	    observer != NULL;
+	    observer = STAILQ_NEXT(observer_t, next, observer)) {
+	    observer->hdr(evt, observer->arg);
+	}
+
+	if(subject->flags & SUBF_STOP_PROPAGATE)
+	    break;
+
+	subject = factory->get_parent_subject(factory, subject);
+    }
+}
+
+observer_t *subject_add_observer(ob_factory_t *factory,
+				 subject_t *subject,
+				 evt_handler hdr, void *arg) {
+    observer_t *observer;
+
+    observer = factory->observer_alloc(factory);
+    if(observer == NULL)
+	return NULL;
+    observer->hdr = hdr;
+    observer->arg = arg;
+
+    STAILQ_INS_TAIL(subject->observers, observer_t, next, observer);
+
+    return observer;
+}
+
+void subject_remove_observer(ob_factory_t *factory,
+			     subject_t *subject,
+			     observer_t *observer) {
+    STAILQ_REMOVE(subject->observers, observer_t, next, observer);
+    factory->observer_free(factory, observer);
+}
+
+#ifdef UNITTEST
+
+#include <CUnit/Basic.h>
+#include <stdlib.h>
+
+static subject_t *test_subject_alloc(ob_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) {
+    free(subject);
+}
+
+static observer_t *test_observer_alloc(ob_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) {
+    free(observer);
+}
+
+static subject_t *test_get_parent_subject(ob_factory_t *factory,
+					  subject_t *subject) {
+    return NULL;
+}
+
+static ob_factory_t test_factory = {
+    test_subject_alloc,
+    test_subject_free,
+    test_observer_alloc,
+    test_observer_free,
+    test_get_parent_subject
+};
+
+static void handler(event_t *evt, void *arg) {
+    int *cnt = (int *)arg;
+
+    CU_ASSERT(evt->type == EVT_MOUSE_OVER);
+    (*cnt)++;
+}
+
+void test_observer(void) {
+    subject_t *subject;
+    observer_t *observer;
+    event_t evt;
+    int cnt = 0;
+
+    subject = subject_new(&test_factory, NULL, 0);
+    subject->flags |= SUBF_STOP_PROPAGATE;
+    observer = subject_add_observer(&test_factory, subject,
+				    handler, &cnt);
+
+    evt.type = EVT_MOUSE_OVER;
+    evt.tgt = NULL;
+    evt.cur_tgt = NULL;
+    subject_notify(&test_factory, subject, &evt);
+    CU_ASSERT(cnt == 1);
+
+    subject_remove_observer(&test_factory, subject, observer);
+    subject_free(&test_factory, subject);
+}
+
+CU_pSuite get_observer_suite(void) {
+    CU_pSuite suite;
+
+    suite = CU_add_suite("Suite_observer", NULL, NULL);
+    CU_ADD_TEST(suite, test_observer);
+
+    return suite;
+}
+
+#endif /* UNITTEST */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/observer.h	Mon Aug 18 01:59:26 2008 +0800
@@ -0,0 +1,71 @@
+#ifndef __OBSERVER_H_
+#define __OBSERVER_H_
+
+#include "tools.h"
+
+typedef struct _event event_t;
+typedef struct _observer observer_t;
+typedef struct _subject subject_t;
+typedef struct _mouse_event mouse_event_t;
+typedef struct _ob_factory ob_factory_t;
+typedef void (*evt_handler)(event_t *event, void *arg);
+
+struct _event {
+    int type;
+    subject_t *tgt, *cur_tgt;
+};
+
+struct _observer {
+    evt_handler hdr;
+    void *arg;
+    observer_t *next;
+};
+
+struct _subject {
+    int obj_type;
+    void *obj;
+    int flags;
+    STAILQ(observer_t) observers;
+};
+#define SUBF_STOP_PROPAGATE 0x1
+
+enum {OBJT_GEO, OBJT_COORD};
+
+struct _mouse_event {
+    event_t event;
+    int x, y;
+    int button;
+};
+
+/*! \brief Observer factory.
+ *
+ * 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);
+    /*! This is a strategy function to get subjects of parents. */
+    subject_t *(*get_parent_subject)(ob_factory_t *factory,
+				     subject_t *cur_subject);
+};
+
+enum {EVT_MOUSE_OVER, EVT_MOUSE_OUT, EVT_MOUSE_MOVE,
+      EVT_MOUSE_BUT_PRESS, EVT_MOUSE_BUT_RELEASE};
+
+extern subject_t *subject_new(ob_factory_t *factory,
+			      void *obj, int obj_type);
+extern void subject_free(ob_factory_t *factory, subject_t *subject);
+extern void subject_notify(ob_factory_t *factory,
+			   subject_t *subject, event_t *evt);
+extern observer_t *subject_add_observer(ob_factory_t *factory,
+					subject_t *subject,
+					evt_handler hdr, void *arg);
+extern void subject_remove_observer(ob_factory_t *factory,
+				    subject_t *subject,
+				    observer_t *observer);
+
+
+#endif /* __OBSERVER_H_ */
--- a/src/redraw_man.c	Wed Aug 13 09:25:57 2008 +0800
+++ b/src/redraw_man.c	Mon Aug 18 01:59:26 2008 +0800
@@ -6,6 +6,8 @@
 #include "shapes.h"
 #include "tools.h"
 #include "redraw_man.h"
+#include "observer.h"
+
 
 /* NOTE: bounding box should also consider width of stroke.
  */
@@ -13,7 +15,6 @@
 #define OK 0
 #define ERR -1
 
-#define OFFSET(type, field) ((void *)&((type *)NULL)->field - (void *)NULL)
 #define SWAP(a, b, t) do { t c;  c = a; a = b; b = c; } while(0)
 
 #ifdef UNITTEST
@@ -23,6 +24,14 @@
 extern void sh_dummy_fill(shape_t *, cairo_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);
+
+
 /*! \brief Sort a list of element by a unsigned integer.
  *
  * The result is in ascend order.  The unsigned integers is
@@ -123,11 +132,37 @@
 	return ERR;
     }
 
+    rdman->observer_pool = elmpool_new(sizeof(observer_t), 16);
+    if(rdman->observer_pool == NULL) {
+	elmpool_free(rdman->geo_pool);
+	elmpool_free(rdman->coord_pool);
+	elmpool_free(rdman->shnode_pool);
+	return ERR;
+    }
+
+    rdman->subject_pool = elmpool_new(sizeof(subject_t), 16);
+    if(rdman->subject_pool == NULL) {
+	elmpool_free(rdman->geo_pool);
+	elmpool_free(rdman->coord_pool);
+	elmpool_free(rdman->shnode_pool);
+	elmpool_free(rdman->observer_pool);
+	return 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->root_coord = elmpool_elm_alloc(rdman->coord_pool);
     if(rdman->root_coord == NULL)
 	redraw_man_destroy(rdman);
     rdman->n_coords = 1;
     coord_init(rdman->root_coord, NULL);
+    rdman->root_coord->mouse_event = subject_new(&rdman->ob_factory,
+						 rdman->root_coord,
+						 OBJT_COORD);
 
     rdman->cr = cr;
     rdman->backend = backend;
@@ -139,6 +174,8 @@
     elmpool_free(rdman->coord_pool);
     elmpool_free(rdman->geo_pool);
     elmpool_free(rdman->shnode_pool);
+    elmpool_free(rdman->observer_pool);
+    elmpool_free(rdman->subject_pool);
     if(rdman->dirty_coords)
 	free(rdman->dirty_coords);
     if(rdman->dirty_geos)
@@ -226,6 +263,7 @@
 	return ERR;
     
     geo_init(geo);
+    geo->mouse_event = subject_new(&rdman->ob_factory, geo, OBJT_GEO);
 
     sh_attach_geo(shape, geo);
     STAILQ_INS_TAIL(rdman->all_geos, geo_t, next, geo);
@@ -261,6 +299,7 @@
  */
 int rdman_remove_shape(redraw_man_t *rdman, shape_t *shape) {
     STAILQ_REMOVE(rdman->all_geos, geo_t, next, shape->geo);
+    subject_free(&rdman->ob_factory, shape->geo->mouse_event);
     elmpool_elm_free(rdman->geo_pool, shape->geo);
     sh_detach_geo(shape);
     rdman->n_geos--;
@@ -277,6 +316,9 @@
 	return NULL;
 
     coord_init(coord, parent);
+    coord->mouse_event = subject_new(&rdman->ob_factory,
+				     coord,
+				     OBJT_COORD);
     rdman->n_coords++;
 
     coord->order = ++rdman->next_coord_order;
@@ -313,6 +355,7 @@
 	return ERR;
 
     STAILQ_REMOVE(parent->children, coord_t, sibling, coord);
+    subject_free(&rdman->ob_factory, coord->mouse_event);
     elmpool_elm_free(rdman->coord_pool, coord);
     rdman->n_coords--;
 
@@ -857,7 +900,71 @@
  * - redraw changed
  */
 
+/* Implment factory and strategy functions for observers and subjects.
+ */
+static subject_t *ob_subject_alloc(ob_factory_t *factory) {
+    redraw_man_t *rdman;
+    subject_t *subject;
+
+    rdman = MEM2OBJ(factory, redraw_man_t, ob_factory);
+    subject = elmpool_elm_alloc(rdman->subject_pool);
+
+    return subject;
+}
+
+static void ob_subject_free(ob_factory_t *factory, subject_t *subject) {
+    redraw_man_t *rdman;
+
+    rdman = MEM2OBJ(factory, redraw_man_t, ob_factory);
+    elmpool_elm_free(rdman->subject_pool, subject);
+}
+
+static observer_t *ob_observer_alloc(ob_factory_t *factory) {
+    redraw_man_t *rdman;
+    observer_t *observer;
+
+    rdman = MEM2OBJ(factory, redraw_man_t, ob_factory);
+    observer = elmpool_elm_alloc(rdman->observer_pool);
+
+    return observer;
+}
+
+static void ob_observer_free(ob_factory_t *factory, observer_t *observer) {
+    redraw_man_t *rdman;
+
+    rdman = MEM2OBJ(factory, redraw_man_t, ob_factory);
+    elmpool_elm_free(rdman->observer_pool, observer);
+}
+
+static subject_t *ob_get_parent_subject(ob_factory_t *factory,
+					subject_t *cur_subject) {
+    redraw_man_t *rdman;
+    coord_t *coord;
+    geo_t *geo;
+    subject_t *parent;
+
+    rdman = MEM2OBJ(factory, redraw_man_t, ob_factory);
+    switch(cur_subject->obj_type) {
+    case OBJT_GEO:
+	geo = (geo_t *)cur_subject->obj;
+	coord = geo->shape->coord;
+	parent = coord->mouse_event;
+	break;
+    case OBJT_COORD:
+	coord = (coord_t *)cur_subject->obj;
+	coord = coord->parent;
+	parent = coord->mouse_event;
+	break;
+    default:
+	parent = NULL;
+	break;
+    }
+
+    return parent;
+}
+
 #ifdef UNITTEST
+/* Test cases */
 
 #include <CUnit/Basic.h>
 #include "paint.h"
--- a/src/redraw_man.h	Wed Aug 13 09:25:57 2008 +0800
+++ b/src/redraw_man.h	Mon Aug 18 01:59:26 2008 +0800
@@ -4,6 +4,7 @@
 #include <cairo.h>
 #include "tools.h"
 #include "mb_types.h"
+#include "observer.h"
 
 /*! \brief Manage redrawing of shapes (graphic elements).
  *
@@ -34,6 +35,8 @@
     elmpool_t *geo_pool;
     elmpool_t *coord_pool;
     elmpool_t *shnode_pool;
+    elmpool_t *observer_pool;
+    elmpool_t *subject_pool;
 
     int max_dirty_coords;
     int n_dirty_coords;
@@ -53,6 +56,8 @@
 
     cairo_t *cr;
     cairo_t *backend;
+
+    ob_factory_t ob_factory;
 } redraw_man_t;
 
 extern int redraw_man_init(redraw_man_t *rdman, cairo_t *cr,
@@ -112,6 +117,7 @@
 
 extern shape_t *find_shape_at_pos(redraw_man_t *rdman,
 				  co_aix x, co_aix y, int *in_stroke);
+#define rdman_get_ob_factory(rdman) (&(rdman)->ob_factory)
 
 
 #endif /* __REDRAW_MAN_H_ */
--- a/src/shape_path.c	Wed Aug 13 09:25:57 2008 +0800
+++ b/src/shape_path.c	Mon Aug 18 01:59:26 2008 +0800
@@ -37,7 +37,7 @@
 #define OK 0
 #define ERR -1
 
-void sh_path_free(shape_t *shape) {
+static void sh_path_free(shape_t *shape) {
     sh_path_t *path = (sh_path_t *)shape;
     if(path->user_data)
 	free(path->user_data);
@@ -382,6 +382,8 @@
     }
     memcpy(path->dev_data, path->user_data, cmd_cnt);
 
+    path->shape.free = sh_path_free;
+
     return (shape_t *)path;
 }
 
@@ -592,6 +594,7 @@
     CU_ASSERT(strcmp(path->user_data, "MLCLZ") == 0);
     CU_ASSERT(strcmp(path->dev_data, "MLCLZ") == 0);
 
+    geo_init(&geo);
     path->shape.geo = &geo;
     geo.shape = (shape_t *)path;
 
--- a/src/shape_rect.c	Wed Aug 13 09:25:57 2008 +0800
+++ b/src/shape_rect.c	Mon Aug 18 01:59:26 2008 +0800
@@ -12,6 +12,10 @@
     co_aix poses[12][2];
 } sh_rect_t;
 
+static void sh_rect_free(shape_t *shape) {
+    free(shape);
+}
+
 shape_t *sh_rect_new(co_aix x, co_aix y, co_aix w, co_aix h,
 		    co_aix rx, co_aix ry) {
     sh_rect_t *rect;
@@ -29,14 +33,11 @@
     rect->h = h;
     rect->rx = rx;
     rect->ry = ry;
+    rect->shape.free = sh_rect_free;
 
     return (shape_t *)rect;
 }
 
-void sh_rect_free(shape_t *shape) {
-    free(shape);
-}
-
 void sh_rect_set(shape_t *shape, co_aix x, co_aix y,
 		 co_aix w, co_aix h, co_aix rx, co_aix ry) {
     sh_rect_t *rect = (sh_rect_t *)shape;
--- a/src/shape_text.c	Wed Aug 13 09:25:57 2008 +0800
+++ b/src/shape_text.c	Mon Aug 18 01:59:26 2008 +0800
@@ -23,6 +23,14 @@
 
 #define TXF_SCALE_DIRTY 0x1
 
+static void sh_text_free(shape_t *shape) {
+    sh_text_t *text = (sh_text_t *)shape;
+
+    if(text->scaled_font)
+	cairo_scaled_font_destroy(text->scaled_font);
+    cairo_font_face_destroy(text->face);
+}
+
 shape_t *sh_text_new(const char *txt, co_aix x, co_aix y,
 		     co_aix font_size, cairo_font_face_t *face) {
     sh_text_t *text;
@@ -45,15 +53,9 @@
     text->face = face;
     text->flags |= TXF_SCALE_DIRTY;
 
-    return (shape_t *)text;
-}
+    text->shape.free = sh_text_free;
 
-void sh_text_free(shape_t *shape) {
-    sh_text_t *text = (sh_text_t *)shape;
-
-    if(text->scaled_font)
-	cairo_scaled_font_destroy(text->scaled_font);
-    cairo_font_face_destroy(text->face);
+    return (shape_t *)text;
 }
 
 static int get_extents(sh_text_t *text, cairo_text_extents_t *extents) {
--- a/src/shapes.h	Wed Aug 13 09:25:57 2008 +0800
+++ b/src/shapes.h	Mon Aug 18 01:59:26 2008 +0800
@@ -20,13 +20,11 @@
  */
 
 
-extern void sh_path_free(shape_t *path);
 extern shape_t *sh_path_new(char *data);
 extern void sh_path_transform(shape_t *shape);
 extern void sh_path_draw(shape_t *shape, cairo_t *cr);
 
 
-extern void sh_text_free(shape_t *text);
 extern shape_t *sh_text_new(const char *txt, co_aix x, co_aix y,
 			    co_aix font_size, cairo_font_face_t *face);
 extern void sh_text_transform(shape_t *shape);
@@ -34,7 +32,6 @@
 
 extern shape_t *sh_rect_new(co_aix x, co_aix y, co_aix w, co_aix h,
 			    co_aix rx, co_aix ry);
-extern void sh_rect_free(shape_t *shape);
 extern void sh_rect_transform(shape_t *shape);
 extern void sh_rect_draw(shape_t *shape, cairo_t *cr);
 extern void sh_rect_set(shape_t *shape, co_aix x, co_aix y,
--- a/src/testcase.c	Wed Aug 13 09:25:57 2008 +0800
+++ b/src/testcase.c	Mon Aug 18 01:59:26 2008 +0800
@@ -6,6 +6,7 @@
 extern CU_pSuite get_shape_path_suite(void);
 extern CU_pSuite get_redraw_man_suite(void);
 extern CU_pSuite get_animate_suite(void);
+extern CU_pSuite get_observer_suite(void);
 
 int
 main(int argc, char * const argv[]) {
@@ -32,6 +33,9 @@
     suite = get_animate_suite();
     if(suite == NULL)
 	return CU_get_error();
+    suite = get_observer_suite();
+    if(suite == NULL)
+	return CU_get_error();
 
     CU_basic_set_mode(CU_BRM_VERBOSE);
     CU_basic_run_tests();
--- a/src/tools.h	Wed Aug 13 09:25:57 2008 +0800
+++ b/src/tools.h	Mon Aug 18 01:59:26 2008 +0800
@@ -66,4 +66,7 @@
 
 #define O_ALLOC(type) ((type *)malloc(sizeof(type)))
 
+#define OFFSET(type, mem) (((void *)&((type *)NULL)->mem) - NULL)
+#define MEM2OBJ(var, type, mem) ((type *)((void *)var - OFFSET(type, mem)))
+
 #endif /* __TOOLS_H_ */
--- a/tools/mb_c_source.m4	Wed Aug 13 09:25:57 2008 +0800
+++ b/tools/mb_c_source.m4	Mon Aug 18 01:59:26 2008 +0800
@@ -32,8 +32,8 @@
 DIMPORT([ADD_LINEAR_PAINT])
 DIMPORT([ADD_RADIAL_PAINT])
 DIMPORT([COLOR_STOP])
-define([REF_STOPS],)
-define([ADD_PATH],)
+define([REF_STOPS])
+define([ADD_PATH])
 define([ADD_RECT])
 define([ADD_COORD])
 define([FILL_SHAPE])
@@ -127,11 +127,11 @@
 ]])
 
 define([F_ADD_PATH],[[
-    sh_path_free(obj->$1);
+    obj->$1->free(obj->$1);
 ]]);
 
 define([F_ADD_RECT],[[
-    sh_rect_free(obj->$1);
+    obj->$1->free(obj->$1);
 ]]);
 
 define([F_FILL_SHAPE],[[