diff src/redraw_man.c @ 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 6ce68c1f7405
children b90abd31a281
line wrap: on
line diff
--- 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) {