changeset 158:c1cdd3fcd28f

Postponing rdman_coord_free() and rdman_remove_shape(). rdman will access free memory if coords or shapes are free when it is dirty. The requests are postponed until rdman is clean.
author Thinker K.F. Li <thinker@branda.to>
date Fri, 03 Oct 2008 10:22:08 +0800
parents 5cd12609a5c7
children b90abd31a281
files src/event.c src/mb_types.h src/redraw_man.c src/redraw_man.h src/tools.h
diffstat 5 files changed, 210 insertions(+), 122 deletions(-) [+]
line wrap: on
line diff
--- a/src/event.c	Wed Oct 01 16:30:05 2008 +0800
+++ b/src/event.c	Fri Oct 03 10:22:08 2008 +0800
@@ -23,6 +23,8 @@
     return OK;
 }
 
+DARRAY_DEFINE(geos, geo_t *);
+
 /*! \brief Add a geo_t object to general geo list.
  *
  * General geo list can use to temporary keep a list of geo_t
@@ -31,21 +33,10 @@
  * from a redraw manager.
  */
 static int add_gen_geo(redraw_man_t *rdman, geo_t *geo) {
-    int max_gen_geos;
     int r;
 
-    if(rdman->n_gen_geos >= rdman->max_gen_geos) {
-	max_gen_geos = rdman->max_gen_geos + ARRAY_EXT_SZ;
-	r = extend_memblk((void **)&rdman->gen_geos,
-			  sizeof(geo_t *) * rdman->n_gen_geos,
-			  sizeof(geo_t *) * max_gen_geos);
-	if(r != OK)
-	    return ERR;
-	rdman->max_gen_geos = max_gen_geos;
-    }
-
-    rdman->gen_geos[rdman->n_gen_geos++] = geo;
-    return OK;
+    r = geos_add(&rdman->gen_geos, geo);
+    return r == 0? OK: ERR;
 }
 
 static int collect_shapes_at_point(redraw_man_t *rdman,
@@ -57,7 +48,7 @@
     if(r != OK)
 	return ERR;
 
-    rdman->n_gen_geos = 0;
+    rdman->gen_geos.num = 0;
 
     for(geo = rdman_geos(rdman, NULL);
 	geo != NULL;
@@ -94,9 +85,9 @@
     cairo_t *cr;
     int i;
 
-    geos = rdman->gen_geos;
+    geos = rdman->gen_geos.ds;
     cr = rdman->cr;
-    for(i = rdman->n_gen_geos - 1; i >= 0; i--) {
+    for(i = rdman->gen_geos.num - 1; i >= 0; i--) {
 	geo = geos[i];
 	if(geo->flags & GEF_HIDDEN)
 	    continue;
--- a/src/mb_types.h	Wed Oct 01 16:30:05 2008 +0800
+++ b/src/mb_types.h	Fri Oct 03 10:22:08 2008 +0800
@@ -53,6 +53,7 @@
 };
 #define GEF_DIRTY 0x1
 #define GEF_HIDDEN 0x2
+#define GEF_FREE 0x4
 
 extern int is_overlay(area_t *r1, area_t *r2);
 extern void area_init(area_t *area, int n_pos, co_aix pos[][2]);
@@ -120,6 +121,7 @@
 #define COF_SKIP_TRIVAL 0x8	/*!< Temporary skip descendants
 				 * when trivaling.
 				 */
+#define COF_FREE 0x10
 
 extern void coord_init(coord_t *co, coord_t *parent);
 extern void coord_trans_pos(coord_t *co, co_aix *x, co_aix *y);
@@ -160,22 +162,9 @@
 };
 enum { SHT_UNKNOW, SHT_PATH, SHT_TEXT, SHT_RECT };
 
-#define sh_attach_geo(sh, g)			\
-    do {					\
-	(sh)->geo = g;				\
-	(g)->shape = (shape_t *)(sh);		\
-    } while(0)
-#define sh_detach_geo(sh)			\
-    do {					\
-	(sh)->geo->shape = NULL;		\
-	(sh)->geo = NULL;			\
-    } while(0)
-#define sh_get_geo(sh) ((sh)->geo)
 #define sh_get_mouse_event_subject(sh) ((sh)->geo->mouse_event)
 #define sh_hide(sh) do { (sh)->geo->flags |= GEF_HIDDEN; } while(0)
 #define sh_show(sh) do { (sh)->geo->flags &= ~GEF_HIDDEN; } while(0)
-#define sh_attach_coord(sh, coord) do { (sh)->coord = coord; } while(0)
-#define sh_detach_coord(sh) do { (sh)->coord = NULL; } while(0)
 
 
 #endif /* __MB_TYPES_H_ */
--- a/src/redraw_man.c	Wed Oct 01 16:30:05 2008 +0800
+++ b/src/redraw_man.c	Fri Oct 03 10:22:08 2008 +0800
@@ -12,6 +12,24 @@
 /* NOTE: bounding box should also consider width of stroke.
  */
 
+#define sh_attach_geo(sh, g)			\
+    do {					\
+	(sh)->geo = g;				\
+	(g)->shape = (shape_t *)(sh);		\
+    } while(0)
+#define sh_detach_geo(sh)			\
+    do {					\
+	(sh)->geo->shape = NULL;		\
+	(sh)->geo = NULL;			\
+    } while(0)
+#define sh_get_geo(sh) ((sh)->geo)
+#define sh_attach_coord(sh, coord) do { (sh)->coord = coord; } while(0)
+#define sh_detach_coord(sh) do { (sh)->coord = NULL; } while(0)
+#define rdman_is_dirty(rdman)			\
+    ((rdman)->dirty_coords.num != 0 ||		\
+     (rdman)->dirty_geos.num != 0 ||		\
+     (rdman)->dirty_areas.num != 0)
+
 #define OK 0
 #define ERR -1
 
@@ -78,76 +96,38 @@
     }
 }
 
-static int extend_memblk(void **buf, int o_size, int n_size) {
-    void *new_buf;
+DARRAY_DEFINE(coords, coord_t *);
+DARRAY_DEFINE(geos, geo_t *);
+DARRAY_DEFINE(areas, area_t *);
 
-    new_buf = realloc(*buf, n_size);
-    if(new_buf == NULL)
-	return ERR;
+/*! Use \brief DARRAY to implement dirty & free lists.
+ */
+#define ADD_DATA(sttype, field, v)		\
+    int r;					\
+    r = sttype ## _add(&rdman->field, v);	\
+    return r == 0? OK: ERR;
 
-    *buf = new_buf;
-
-    return OK;
-}
 
 static int add_dirty_coord(redraw_man_t *rdman, coord_t *coord) {
-    int max_dirty_coords;
-    int r;
-    
-    if(rdman->n_dirty_coords >= rdman->max_dirty_coords) {
-	/* Max of dirty_coords is not big enough. */
-	max_dirty_coords = rdman->max_dirty_coords + 16;
-	
-	r = extend_memblk((void **)&rdman->dirty_coords,
-			  sizeof(coord_t *) * rdman->n_dirty_coords,
-			  sizeof(coord_t *) * max_dirty_coords);
-	if(r != OK)
-	    return ERR;
-	rdman->max_dirty_coords = max_dirty_coords;
-    }
-
-    rdman->dirty_coords[rdman->n_dirty_coords++] = coord;
     coord->flags |= COF_DIRTY;
-    return OK;
+    ADD_DATA(coords, dirty_coords, coord);
 }
 
 static int add_dirty_geo(redraw_man_t *rdman, geo_t *geo) {
-    int max_dirty_geos;
-    int r;
-
-    if(rdman->n_dirty_geos >= rdman->max_dirty_geos) {
-	max_dirty_geos = rdman->max_dirty_geos + ARRAY_EXT_SZ;
-	r = extend_memblk((void **)&rdman->dirty_geos,
-			  sizeof(geo_t *) * rdman->n_dirty_geos,
-			  sizeof(geo_t *) * max_dirty_geos);
-	if(r != OK)
-	    return ERR;
-	rdman->max_dirty_geos = max_dirty_geos;
-    }
-
-    rdman->dirty_geos[rdman->n_dirty_geos++] = geo;
-    return OK;
+    geo->flags |= GEF_DIRTY;
+    ADD_DATA(geos, dirty_geos, geo);
 }
 
 static int add_dirty_area(redraw_man_t *rdman, area_t *area) {
-    int max_dirty_areas;
-    int r;
+    ADD_DATA(areas, dirty_areas, area);
+}
 
-    if(rdman->n_dirty_areas >= rdman->max_dirty_areas) {
-	/* every geo object and coord object can contribute 2 areas.
-	 * rdman_draw_area() may also contribute 1 area.
-	 */
-	max_dirty_areas = rdman->max_dirty_areas + ARRAY_EXT_SZ;
-	r = extend_memblk((void **)&rdman->dirty_areas,
-			  sizeof(area_t *) * rdman->n_dirty_areas,
-			  sizeof(area_t *) * max_dirty_areas);
-	if(r != OK)
-	    return ERR;
-	rdman->max_dirty_areas = max_dirty_areas;
-    }
+static int add_free_coord(redraw_man_t *rdman, coord_t *coord) {
+    ADD_DATA(coords, free_coords, coord);
+}
 
-    rdman->dirty_areas[rdman->n_dirty_areas++] = area;
-    return OK;
+static int add_free_geo(redraw_man_t *rdman, geo_t *geo) {
+    ADD_DATA(geos, free_geos, geo);
 }
 
 static void area_to_positions(area_t *area, co_aix (*poses)[2]) {
@@ -294,13 +274,23 @@
 
 void redraw_man_destroy(redraw_man_t *rdman) {
     coord_t *coord, *saved_coord;
+    geo_t *member;
 
     coord = postorder_coord_subtree(rdman->root_coord, NULL);
     while(coord) {
 	saved_coord = coord;
 	coord = postorder_coord_subtree(rdman->root_coord, coord);
+	FORMEMBERS(saved_coord, member) {
+	    rdman_remove_shape(rdman, member->shape);
+	}
 	rdman_coord_free(rdman, saved_coord);
     }
+    FORMEMBERS(saved_coord, member) {
+	rdman_remove_shape(rdman, member->shape);
+    }
+    /* Resources of root_coord is free by elmpool_free() or
+     * caller; for canvas
+     */
 
     elmpool_free(rdman->coord_pool);
     elmpool_free(rdman->geo_pool);
@@ -308,12 +298,13 @@
     elmpool_free(rdman->observer_pool);
     elmpool_free(rdman->subject_pool);
     elmpool_free(rdman->paint_color_pool);
-    if(rdman->dirty_coords)
-	free(rdman->dirty_coords);
-    if(rdman->dirty_geos)
-	free(rdman->dirty_geos);
-    if(rdman->gen_geos)
-	free(rdman->gen_geos);
+
+    DARRAY_DESTROY(&rdman->dirty_coords);
+    DARRAY_DESTROY(&rdman->dirty_geos);
+    DARRAY_DESTROY(&rdman->dirty_areas);
+    DARRAY_DESTROY(&rdman->gen_geos);
+    DARRAY_DESTROY(&rdman->free_coords);
+    DARRAY_DESTROY(&rdman->free_geos);
 }
 
 
@@ -354,7 +345,6 @@
     geo_attach_coord(geo, coord);
 
     /* New one should be dirty to recompute it when drawing. */
-    geo->flags |= GEF_DIRTY;
     r = add_dirty_geo(rdman, geo);
     if(r != OK)
 	return ERR;
@@ -368,15 +358,31 @@
 /*! \brief Remove a shape object from redraw manager.
  *
  * \note Shapes should be removed after redrawing or when rdman is in clean.
+ * \note Removing shapes or coords when a rdman is dirty, removing
+ *       is postponsed.
  * \todo redraw shape objects that overlaid with removed one.
- * \todo To allow shapes be removed at anytime.
  */
 int rdman_remove_shape(redraw_man_t *rdman, shape_t *shape) {
     geo_t *geo;
     coord_t *coord;
+    int r;
 
     geo = shape->geo;
     coord = shape->coord;
+
+    if(rdman_is_dirty(rdman)) {
+	geo->flags |= GEF_FREE | GEF_HIDDEN;
+	if(!(geo->flags & GEF_DIRTY)) {
+	    r = add_dirty_geo(rdman, geo);
+	    if(r != OK)
+		return ERR;
+	}
+	r = add_free_geo(rdman, geo);
+	if(r != OK)
+	    return ERR;
+	return OK;
+    }
+
     geo_detach_coord(geo, coord);
     subject_free(&rdman->ob_factory, geo->mouse_event);
     sh_detach_geo(shape);
@@ -428,20 +434,49 @@
  *
  * \param coord is a coord_t without children and members.
  * \return 0 for successful, -1 for error.
+ *
+ * \note Removing coords when the rdman is dirty, the removing is postponsed.
  */
 int rdman_coord_free(redraw_man_t *rdman, coord_t *coord) {
     coord_t *parent;
+    coord_t *child;
+    geo_t *member;
+    int r;
 
     parent = coord->parent;
     if(parent == NULL)
 	return ERR;
 
+    if(rdman_is_dirty(rdman)) {
+	FORCHILDREN(coord, child) {
+	    if(!(child->flags & COF_FREE))
+		return ERR;
+	}
+	FORMEMBERS(coord, member) {
+	    if(!(member->flags & GEF_FREE))
+		return ERR;
+	}
+	coord->flags |= COF_FREE | COF_HIDDEN;
+	if(!(coord->flags & COF_DIRTY)) {
+	    r = add_dirty_coord(rdman, coord);
+	    if(r != OK)
+		return ERR;
+	}
+	r = add_free_coord(rdman, coord);
+	if(r != OK)
+	    return ERR;
+	return OK;
+    }
+
     if(FIRST_MEMBER(coord) != NULL)
 	return ERR;
 
     if(FIRST_CHILD(coord) != NULL)
 	return ERR;
 
+    if(coord->flags & COF_FREE)
+	return ERR;
+
     /* Free canvas (\ref redraw) */
     if(coord->flags & COF_OWN_CANVAS)
 	free_canvas(coord->canvas);
@@ -517,7 +552,6 @@
     r = add_dirty_geo(rdman, geo);
     if(r == ERR)
 	return ERR;
-    geo->flags |= GEF_DIRTY;
 
     return OK;
 }
@@ -655,9 +689,9 @@
     int n_dirty_coords;
     int i, r;
 
-    n_dirty_coords = rdman->n_dirty_coords;
+    n_dirty_coords = rdman->dirty_coords.num;
     if(n_dirty_coords > 0) {
-	dirty_coords = rdman->dirty_coords;
+	dirty_coords = rdman->dirty_coords.ds;
 	_insert_sort((void **)dirty_coords, n_dirty_coords,
 		     OFFSET(coord_t, order));
 	for(i = 0; i < n_dirty_coords; i++) {
@@ -671,7 +705,7 @@
 	    add_dirty_area(rdman, &coord->areas[0]);
 	    add_dirty_area(rdman, &coord->areas[1]);
 	}
-	rdman->n_dirty_coords = 0;
+	rdman->dirty_coords.num = 0;
     }
     return OK;
 }
@@ -682,9 +716,9 @@
     geo_t **dirty_geos;
     geo_t *visit_geo;
 
-    n_dirty_geos = rdman->n_dirty_geos;
+    n_dirty_geos = rdman->dirty_geos.num;
     if(n_dirty_geos > 0) {
-	dirty_geos = rdman->dirty_geos;
+	dirty_geos = rdman->dirty_geos.ds;
 	for(i = 0; i < n_dirty_geos; i++) {
 	    visit_geo = dirty_geos[i];
 	    if(!(visit_geo->flags & GEF_DIRTY))
@@ -695,7 +729,7 @@
 	    add_dirty_area(rdman, visit_geo->cur_area);
 	    add_dirty_area(rdman, visit_geo->last_area);
 	}
-	rdman->n_dirty_geos = 0;
+	rdman->dirty_geos.num = 0;
     }    
 
     return OK;
@@ -952,24 +986,41 @@
     event_t event;
     ob_factory_t *factory;
     subject_t *redraw;
+    geo_t *geo;
+    coord_t *coord;
+    int i;
 
     r = clean_rdman_dirties(rdman);
     if(r != OK)
 	return ERR;
 
-    n_dirty_areas = rdman->n_dirty_areas;
-    dirty_areas = rdman->dirty_areas;
+    n_dirty_areas = rdman->dirty_areas.num;
+    dirty_areas = rdman->dirty_areas.ds;
     if(n_dirty_areas > 0) {
 	/*! \brief Draw shapes in preorder of coord tree and support opacity
 	 * rules.
 	 */
 	clean_canvas(rdman->cr);
 	draw_shapes_in_areas(rdman, n_dirty_areas, dirty_areas);
-	copy_cr_2_backend(rdman, rdman->n_dirty_areas, rdman->dirty_areas);
-	rdman->n_dirty_areas = 0;
+	copy_cr_2_backend(rdman, rdman->dirty_areas.num,
+			  rdman->dirty_areas.ds);
+	rdman->dirty_areas.num = 0;
 	reset_clip(rdman);
     }
-    rdman->n_dirty_areas = 0;
+    rdman->dirty_areas.num = 0;
+
+    /* Free postponsed removing */
+    for(i = 0; i < rdman->free_geos.num; i++) {
+	geo = rdman->free_geos.ds[i];
+	rdman_remove_shape(rdman, geo->shape);
+    }
+    DARRAY_CLEAN(&rdman->free_geos);
+
+    for(i = 0; i < rdman->free_coords.num; i++) {
+	coord = rdman->free_coords.ds[i];
+	rdman_remove_shape(rdman, coord);
+    }
+    DARRAY_CLEAN(&rdman->free_coords);
 
     factory = rdman_get_ob_factory(rdman);
     redraw = rdman_get_redraw_subject(rdman);
@@ -1003,7 +1054,7 @@
  * \sa
  * - rdman_redraw_all()
  * - rdman_redraw_changed()
- * = draw_shapes_in_areas()
+ * - draw_shapes_in_areas()
  */
 
 int rdman_redraw_all(redraw_man_t *rdman) {
--- a/src/redraw_man.h	Wed Oct 01 16:30:05 2008 +0800
+++ b/src/redraw_man.h	Fri Oct 03 10:22:08 2008 +0800
@@ -6,6 +6,10 @@
 #include "mb_types.h"
 #include "observer.h"
 
+DARRAY(coords, coord_t *);
+DARRAY(geos, geo_t *);
+DARRAY(areas, area_t *);
+
 /*! \brief Manage redrawing of shapes (graphic elements).
  *
  * Every coord_t and geo_t object is assigned with a unique
@@ -33,23 +37,14 @@
     elmpool_t *subject_pool;
     elmpool_t *paint_color_pool;
 
-    int max_dirty_coords;
-    int n_dirty_coords;
-    coord_t **dirty_coords;	/*!< coordinates their transform
-				 * matric are chagned.
-				 */
+    coords_t dirty_coords;
+    geos_t dirty_geos;
+    areas_t dirty_areas;
 
-    int max_dirty_geos;
-    int n_dirty_geos;
-    geo_t **dirty_geos;		/*!< geometries that need re-computed */
+    geos_t gen_geos;
 
-    int max_dirty_areas;
-    int n_dirty_areas;
-    area_t **dirty_areas;	/*!< \brief are areas need to redraw. */
-
-    int max_gen_geos;
-    int n_gen_geos;
-    geo_t **gen_geos;		/* general geo list (for temporary store) */
+    coords_t free_coords;
+    geos_t free_geos;
 
     cairo_t *cr;
     cairo_t *backend;
--- a/src/tools.h	Wed Oct 01 16:30:05 2008 +0800
+++ b/src/tools.h	Fri Oct 03 10:22:08 2008 +0800
@@ -62,6 +62,68 @@
 	}						\
     } while(0)
 
+/*! \defgroup darray Dynamic Array
+ *
+ * DARRAY is a dynamic sized array/list, it's length is a variable.
+ * It is extended, automatically, if it is full and more elemnts are
+ * putted in.
+ *
+ * Users of DARRAY must declare a new type to store data.  The way to
+ * declear a new type is to invoke DARRAY() with paramters of name of
+ * type and type of data to be stored in.  The new storage type is named
+ * with foo_t where foo is the name you pass in.
+ *
+ * DARRAY_DEFINE() is inovked to define foo_add() function; foo is name
+ * of storage type.  You can call foo_add() to add a data element
+ * into a storage object.
+ *
+ * Get ith element in a storage object, use
+ * \code
+ * obj->ds[i]
+ * \endcode
+ *
+ * To loop over elements in a storage object, us
+ * \code
+ * for(i = 0; i < obj->num; i++) {
+ *	v = obj->ds[i];
+ *	......
+ * }
+ * \endcode
+ * @{
+ */
+/*! \brief Declare a DARRAY storage type.
+ *
+ * \param name is name of storage type.
+ * \param type is type of data elements that will be stored in.
+ *
+ * Type of <name>_t is defined by the macro.  It is used to define a
+ * storage object to contain data elements.
+ */
+#define DARRAY(name, type)				\
+    struct _ ## name {					\
+	int max, num;					\
+	type *ds;					\
+    };							\
+    typedef struct _ ## name name ## _t
+#define DARRAY_DEFINE(name, type)			\
+    static int name ## _add(name ## _t *da, type v) {	\
+	type *new_ds;					\
+	int max;					\
+	if(da->num >= (da)->max) {			\
+	    max = (da)->max + 32;			\
+	    new_ds = realloc(da->ds,			\
+			     max * sizeof(type));	\
+	    if(new_ds == NULL) return -1;		\
+	    da->ds = new_ds;				\
+	    da->max = max;				\
+	}						\
+	da->ds[da->num++] = v;				\
+	return 0;					\
+    }
+#define DARRAY_CLEAN(da) do { (da)->num = 0; } while(0)
+#define DARRAY_INIT(da) do { (da)->num = (da)->max = 0; (da)->ds = NULL; }
+#define DARRAY_DESTROY(da) do { if((da)->ds) free((da)->ds); } while(0)
+/* @} */
 
 #include <stdlib.h>