changeset 30:e06a4a667ce2

Accept mouse/pointer event and hint the shape that the pointer is over. - add find_shape_at_pos()
author Thinker K.F. Li <thinker@branda.to>
date Tue, 05 Aug 2008 12:40:45 +0800
parents f56c96b035a8
children da770188a44d
files src/Makefile src/X_main.c src/event.c src/mb_types.h src/redraw_man.c src/redraw_man.h src/shape_path.c src/shape_text.c src/shapes.h src/tools.c
diffstat 10 files changed, 211 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/src/Makefile	Mon Aug 04 21:13:32 2008 +0800
+++ b/src/Makefile	Tue Aug 05 12:40:45 2008 +0800
@@ -1,4 +1,5 @@
-SRCS =	coord.c geo.c shape_path.c shape_text.c redraw_man.c paint.c tools.c
+SRCS =	coord.c geo.c shape_path.c shape_text.c redraw_man.c \
+	paint.c event.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	Mon Aug 04 21:13:32 2008 +0800
+++ b/src/X_main.c	Tue Aug 05 12:40:45 2008 +0800
@@ -11,6 +11,47 @@
 #include "paint.h"
 
 Display *display;
+Window win;
+
+void hint_shape(redraw_man_t *rdman, shape_t *shape) {
+    static shape_t *last_shape = NULL;
+    if(last_shape != shape) {
+	if(last_shape != NULL && last_shape != NULL) {
+	    last_shape->stroke_width -= 2;
+	    rdman_shape_changed(rdman, last_shape);
+	}
+	if(shape != NULL && shape->stroke != NULL) {
+	    shape->stroke_width += 2;
+	    rdman_shape_changed(rdman, shape);
+	    rdman_redraw_changed(rdman);
+	    XFlush(display);
+	}
+    }
+    last_shape = shape;
+}
+
+void event_interaction(Display *display,
+		       redraw_man_t *rdman, int w, int h) {
+    XEvent evt;
+    XMotionEvent *mevt;
+    int r;
+    co_aix x, y;
+    shape_t *shape = NULL;
+    int in_stroke;
+
+    XSelectInput(display, win, PointerMotionMask);
+    while((r = XNextEvent(display, &evt)) == 0) {
+	switch(evt.type) {
+	case MotionNotify:
+	    mevt = (XMotionEvent *)&evt;
+	    x = mevt->x;
+	    y = mevt->y;
+	    shape = find_shape_at_pos(rdman, x, y, &in_stroke);
+	    hint_shape(rdman, shape);
+	    break;
+	}
+    }
+}
 
 void draw_path(cairo_t *cr, int w, int h) {
     cairo_t *tmpcr;
@@ -168,6 +209,8 @@
 	XFlush(display);
     }
 
+    event_interaction(display, &rdman, w, h);
+
     fill1->free(fill1);
     fill2->free(fill2);
     stroke->free(stroke);
@@ -196,7 +239,6 @@
     Visual *visual;
     int screen;
     XSetWindowAttributes wattr;
-    Window win;
     int depth;
     cairo_surface_t *surface;
     int w, h;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/event.c	Tue Aug 05 12:40:45 2008 +0800
@@ -0,0 +1,131 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <cairo.h>
+#include "mb_types.h"
+#include "redraw_man.h"
+#include "shapes.h"
+
+#define OK 0
+#define ERR -1
+
+
+static int extend_memblk(void **buf, int o_size, int n_size) {
+    void *new_buf;
+
+    new_buf = realloc(*buf, n_size);
+    if(new_buf == NULL)
+	return ERR;
+
+    *buf = new_buf;
+
+    return OK;
+}
+
+/*! \brief Add a geo_t object to general geo list.
+ *
+ * General geo list can use to temporary keep a list of geo_t
+ * objects for any purpose.  It supposed to be reused by
+ * different modules that need to select part of geo_t objects
+ * 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->n_geos;
+	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;
+}
+
+static int collect_shapes_at_point(redraw_man_t *rdman,
+				   co_aix x, co_aix y) {
+    geo_t *geo;
+    int r;
+    
+    r = rdman_force_clean(rdman);
+    if(r != OK)
+	return ERR;
+
+    rdman->n_gen_geos = 0;
+
+    for(geo = STAILQ_HEAD(rdman->all_geos);
+	geo != NULL;
+	geo = STAILQ_NEXT(geo_t, next, geo)) {
+	if(geo_pos_is_in(geo, x, y)) {
+	    r = add_gen_geo(rdman, geo);
+	    if(r != OK)
+		return ERR;
+	}
+    }
+
+    return OK;
+}
+
+static void draw_shape_path(shape_t *shape, cairo_t *cr) {
+    switch(shape->sh_type) {
+    case SHT_PATH:
+	sh_path_draw(shape, cr);
+	break;
+    case SHT_TEXT:
+	sh_text_draw(shape, cr);
+	break;
+    }
+}
+
+static geo_t *find_pos_in_geo(redraw_man_t *rdman,
+			      co_aix x, co_aix y, int *in_stroke) {
+    geo_t *geo;
+    geo_t **geos;
+    shape_t *shape;
+    cairo_t *cr;
+    int i;
+
+    geos = rdman->gen_geos;
+    cr = rdman->cr;
+    for(i = rdman->n_gen_geos - 1; i >= 0; i--) {
+	geo = geos[i];
+	shape = geo->shape;
+	draw_shape_path(shape, cr);
+	if(shape->fill) {
+	    if(cairo_in_fill(cr, x, y)) {
+		*in_stroke = 0;
+		cairo_new_path(cr);
+		return geo;
+	    }
+	}
+	if(shape->stroke) {
+	    if(cairo_in_stroke(cr, x, y)) {
+		*in_stroke = 1;
+		cairo_new_path(cr);
+		return geo;
+	    }
+	}
+    }
+
+    return NULL;
+}
+
+shape_t *find_shape_at_pos(redraw_man_t *rdman,
+			   co_aix x, co_aix y, int *in_stroke) {
+    geo_t *geo;
+    int r;
+
+    r = collect_shapes_at_point(rdman, x, y);
+    if(r != OK)
+	return NULL;
+
+    geo = find_pos_in_geo(rdman, x, y, in_stroke);
+    if(geo == NULL)
+	return NULL;
+
+    return geo->shape;
+}
--- a/src/mb_types.h	Mon Aug 04 21:13:32 2008 +0800
+++ b/src/mb_types.h	Tue Aug 05 12:40:45 2008 +0800
@@ -52,8 +52,8 @@
 #define geo_set_shape(g, sh) do {(g)->shape = sh;} while(0)
 #define _geo_is_in(a, s, w) ((a) >= (s) && (a) < ((s) + (w)))
 #define geo_pos_is_in(g, _x, _y)				\
-    (_geo_is_in(_x, (g)->cur_area.x, (g)->cur_area.w) &&	\
-     _geo_is_in(_y, (g)->cur_area.y, (g)->cur_area.h))
+    (_geo_is_in(_x, (g)->cur_area->x, (g)->cur_area->w) &&	\
+     _geo_is_in(_y, (g)->cur_area->y, (g)->cur_area->h))
 
 
 /*! \brief A coordination system.
--- a/src/redraw_man.c	Mon Aug 04 21:13:32 2008 +0800
+++ b/src/redraw_man.c	Tue Aug 05 12:40:45 2008 +0800
@@ -50,8 +50,7 @@
     if(new_buf == NULL)
 	return ERR;
 
-    if(new_buf != *buf)
-	*buf = new_buf;
+    *buf = new_buf;
 
     return OK;
 }
@@ -142,6 +141,8 @@
 	free(rdman->dirty_coords);
     if(rdman->dirty_geos)
 	free(rdman->dirty_geos);
+    if(rdman->gen_geos)
+	free(rdman->gen_geos);
 }
 
 
@@ -710,6 +711,14 @@
     return OK;
 }
 
+int rdman_force_clean(redraw_man_t *rdman) {
+    int r;
+
+    r = clean_rdman_dirties(rdman);
+
+    return r;
+}
+
 shnode_t *shnode_new(redraw_man_t *rdman, shape_t *shape) {
     shnode_t *node;
 
--- a/src/redraw_man.h	Mon Aug 04 21:13:32 2008 +0800
+++ b/src/redraw_man.h	Tue Aug 05 12:40:45 2008 +0800
@@ -47,6 +47,10 @@
     int n_dirty_areas;
     area_t **dirty_areas;
 
+    int max_gen_geos;
+    int n_gen_geos;
+    geo_t **gen_geos;		/* general geo list */
+
     cairo_t *cr;
     cairo_t *backend;
 } redraw_man_t;
@@ -66,6 +70,7 @@
 extern int rdman_shape_changed(redraw_man_t *rdman, shape_t *shape);
 extern int rdman_redraw_changed(redraw_man_t *rdman);
 extern int rdman_redraw_all(redraw_man_t *rdman);
+extern int rdman_force_clean(redraw_man_t *rdman);
 extern shnode_t *shnode_new(redraw_man_t *rdman, shape_t *shape);
 #define shnode_free(rdman, node) elmpool_elm_free((rdman)->shnode_pool, node)
 #define shnode_list_free(rdman, q)				\
@@ -103,5 +108,8 @@
     } while(0)
 extern int rdman_paint_changed(redraw_man_t *rdman, paint_t *paint);
 
+extern shape_t *find_shape_at_pos(redraw_man_t *rdman,
+				  co_aix x, co_aix y, int *in_stroke);
+
 
 #endif /* __REDRAW_MAN_H_ */
--- a/src/shape_path.c	Mon Aug 04 21:13:32 2008 +0800
+++ b/src/shape_path.c	Tue Aug 05 12:40:45 2008 +0800
@@ -544,6 +544,10 @@
     }
 }
 
+void sh_path_draw(shape_t *shape, cairo_t *cr) {
+    sh_path_path(shape, cr);
+}
+
 void sh_path_fill(shape_t *shape, cairo_t *cr) {
     sh_path_path(shape, cr);
     cairo_fill(cr);
--- a/src/shape_text.c	Mon Aug 04 21:13:32 2008 +0800
+++ b/src/shape_text.c	Tue Aug 05 12:40:45 2008 +0800
@@ -128,6 +128,10 @@
 }
 
 
+void sh_text_draw(shape_t *shape, cairo_t *cr) {
+    draw_text((sh_text_t *)shape, cr);
+}
+
 void sh_text_fill(shape_t *shape, cairo_t *cr) {
     sh_text_t *text = (sh_text_t *)shape;
 
--- a/src/shapes.h	Mon Aug 04 21:13:32 2008 +0800
+++ b/src/shapes.h	Tue Aug 05 12:40:45 2008 +0800
@@ -9,14 +9,18 @@
  * A shape must include
  * - *_new() and *_free()
  * - *_transform()
+ * - *_draw()
  * - *_fill()
  * - *_stroke()
  * - struct of shape must include an shape_t as type of first member.
  */
 
+/* TODO: remove *_fill() and *_stroke() */
+
 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_path_fill(shape_t *shape, cairo_t *cr);
 extern void sh_path_stroke(shape_t *shape, cairo_t *cr);
 
@@ -25,6 +29,7 @@
 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);
+extern void sh_text_draw(shape_t *shape, cairo_t *cr);
 extern void sh_text_fill(shape_t *shape, cairo_t *cr);
 extern void sh_text_stroke(shape_t *shape, cairo_t *cr);
 
--- a/src/tools.c	Mon Aug 04 21:13:32 2008 +0800
+++ b/src/tools.c	Tue Aug 05 12:40:45 2008 +0800
@@ -108,6 +108,7 @@
     }
 }
 
+
 #ifdef UNITTEST
 
 #include <CUnit/Basic.h>