changeset 122:17e97e92b76e

Encapsulate X_MB_runtime_t and support X keyboard events.
author Thinker K.F. Li <thinker@branda.to>
date Mon, 15 Sep 2008 20:33:06 +0800
parents 76ba6fd61c7d
children 9e2316dc6ecb
files examples/calculator/main.c examples/svg2code_ex/main.c examples/tank/tank_main.c src/X_supp.c src/X_supp.h src/animate.c src/observer.h
diffstat 7 files changed, 222 insertions(+), 54 deletions(-) [+]
line wrap: on
line diff
--- a/examples/calculator/main.c	Sun Sep 14 23:40:57 2008 +0800
+++ b/examples/calculator/main.c	Mon Sep 15 20:33:06 2008 +0800
@@ -61,22 +61,26 @@
 static void show_text(calc_data_t *calc_data, int num, int saved, int op,
 		      const char *suffix) {
     char buf[20];
+    redraw_man_t *rdman;
+
+    rdman = X_MB_rdman(calc_data->rt);
 
     sprintf(buf, "%d%s", num, suffix);
     sh_text_set_text(calc_data->code->screen_text, buf);
-    rdman_shape_changed(calc_data->rt->rdman, calc_data->code->screen_text);
+    rdman_shape_changed(rdman, calc_data->code->screen_text);
 
     if(op == 'n')
 	sprintf(buf, "None");
     else
 	sprintf(buf, "%d%c", saved, op);
     sh_text_set_text(calc_data->code->saved_text, buf);
-    rdman_shape_changed(calc_data->rt->rdman, calc_data->code->saved_text);
+    rdman_shape_changed(rdman, calc_data->code->saved_text);
 }
 
 static void compute(calc_data_t *calc_data, coord_t *tgt) {
     int i;
     coord_t **coord_p;
+    redraw_man_t *rdman;
     static int valid_num = 0;
     static int factor = 1;
     static int num = 0;
@@ -131,7 +135,8 @@
 	    break;
 	}
     }
-    rdman_redraw_changed(calc_data->rt->rdman);
+    rdman = X_MB_rdman(calc_data->rt);
+    rdman_redraw_changed(rdman);
 }
 
 static void buttons_handler(event_t *evt, void *arg) {
@@ -149,11 +154,13 @@
     ob_factory_t *factory;
     subject_t *subject;
     coord_t *coord;
+    redraw_man_t *rdman;
     int off;
     int i;
 
     calculator_scr = calc_data->code;
-    factory = rdman_get_ob_factory(calc_data->rt->rdman);
+    rdman = X_MB_rdman(calc_data->rt);
+    factory = rdman_get_ob_factory(rdman);
 
     for(i = 0; i < 16; i++) {
 	off = tgt_list[i].off;
@@ -164,23 +171,24 @@
 }
 
 int main(int argc, char * const argv[]) {
-    X_MB_runtime_t rt;
+    X_MB_runtime_t *rt;
+    redraw_man_t *rdman;
     calculator_scr_t *calculator_scr;
     calc_data_t calc_data;
-    int r;
 
-    r = X_MB_init(":0.0", 300, 400, &rt);
+    rt = X_MB_new(":0.0", 300, 400);
 
-    calculator_scr = calculator_scr_new(rt.rdman);
+    rdman = X_MB_rdman(rt);
+    calculator_scr = calculator_scr_new(rdman);
 
-    calc_data.rt = &rt;
+    calc_data.rt = rt;
     calc_data.code = calculator_scr;
     setup_observers(&calc_data);
 
-    X_MB_handle_connection(&rt);
+    X_MB_handle_connection(rt);
 
     calculator_scr_free(calculator_scr);
-    X_MB_destroy(&rt);
+    X_MB_free(rt);
 
     return 0;
 }
--- a/examples/svg2code_ex/main.c	Sun Sep 14 23:40:57 2008 +0800
+++ b/examples/svg2code_ex/main.c	Mon Sep 15 20:33:06 2008 +0800
@@ -21,54 +21,57 @@
     case EVT_MOUSE_BUT_PRESS:
 	coord_show(ex_rt->code->file_menu);
 	/* Tell redraw manager that a coord (group) is chagned. */
-	rdman_coord_changed(ex_rt->rt->rdman, ex_rt->code->file_menu);
+	rdman_coord_changed(X_MB_rdman(ex_rt->rt), ex_rt->code->file_menu);
 	/* Update changed part to UI. */
-	rdman_redraw_changed(ex_rt->rt->rdman);
+	rdman_redraw_changed(X_MB_rdman(ex_rt->rt));
 	break;
     }
 }
 
 static void file_menu_handler(event_t *evt, void *arg) {
     ex_rt_t *ex_rt = (ex_rt_t *)arg;
+    redraw_man_t *rdman;
 
+    rdman = X_MB_rdman(ex_rt->rt);
     switch(evt->type) {
     case EVT_MOUSE_BUT_PRESS:
 	coord_hide(ex_rt->code->file_menu);
 	/* Tell redraw manager that a coord (group) is chagned. */
-	rdman_coord_changed(ex_rt->rt->rdman, ex_rt->code->file_menu);
+	rdman_coord_changed(rdman, ex_rt->code->file_menu);
 	/* Update changed part to UI. */
-	rdman_redraw_changed(ex_rt->rt->rdman);
+	rdman_redraw_changed(rdman);
 	break;
     }
 }
 
 int main(int argc, char * const argv[]) {
-    X_MB_runtime_t rt;
+    X_MB_runtime_t *rt;
+    redraw_man_t *rdman;
     svg2code_ex_t *svg2code;
     ob_factory_t *factory;
     subject_t *subject;
     ex_rt_t ex_rt;
-    int r;
 
     /*
      * Initialize a runtime with XLib as backend.
      */
-    r = X_MB_init(":0.0", 800, 600, &rt);
+    rt = X_MB_new(":0.0", 800, 600);
 
     /*
      * Instantiate objects from a SVG file.
      */
-    svg2code = svg2code_ex_new(rt.rdman);
+    rdman = X_MB_rdman(rt);
+    svg2code = svg2code_ex_new(rdman);
 
     /*
      * Get observer factory
      */
-    factory = rdman_get_ob_factory(rt.rdman);
+    factory = rdman_get_ob_factory(rdman);
     /*
      * Register observers to subjects of events for objects.
      */
     subject = coord_get_mouse_event(svg2code->file_button);
-    ex_rt.rt = &rt;
+    ex_rt.rt = rt;
     ex_rt.code = svg2code;
     subject_add_observer(factory, subject, file_button_handler, &ex_rt);
     subject = coord_get_mouse_event(svg2code->file_menu);
@@ -78,13 +81,13 @@
      * Start handle connections, includes one to X server.
      * User start to interact with the application.
      */
-    X_MB_handle_connection(&rt);
+    X_MB_handle_connection(rt);
 
     /*
      * Clean
      */
     svg2code_ex_free(svg2code);
-    X_MB_destroy(&rt);
+    X_MB_free(rt);
 
     return 0;
 }
--- a/examples/tank/tank_main.c	Sun Sep 14 23:40:57 2008 +0800
+++ b/examples/tank/tank_main.c	Mon Sep 15 20:33:06 2008 +0800
@@ -54,6 +54,7 @@
 void
 initial_tank(tank_rt_t *tank_rt, X_MB_runtime_t *mb_rt) {
     redraw_man_t *rdman;
+    mb_tman_t *tman;
     mud_t *mud;
     brick_t *brick;
     rock_t *rock;
@@ -63,7 +64,7 @@
     mb_timeval_t mbtv;
     int i, j;
 
-    rdman = mb_rt->rdman;
+    rdman = X_MB_rdman(mb_rt);
 
     tank_rt->mb_rt = mb_rt;
     for(i = 0; i < 12; i++) {
@@ -119,20 +120,21 @@
     mb_shift_new(0, 150, tank_rt->tank1->root_coord, word);
     mb_shift_new(0, 150, tank_rt->tank2->root_coord, word);
 
+    tman = X_MB_tman(mb_rt);
     get_now(&mbtv);
-    mb_progm_start(tank_rt->tank1_progm, mb_rt->tman, &mbtv);
+    mb_progm_start(tank_rt->tank1_progm, tman, &mbtv);
 }
 
 int
 main(int argc, char *const argv[]) {
-    X_MB_runtime_t rt;
+    X_MB_runtime_t *rt;
     tank_rt_t tank_rt;
 
-    X_MB_init(":0.0", 800, 600, &rt);
+    rt = X_MB_new(":0.0", 800, 600);
 
-    initial_tank(&tank_rt, &rt);
+    initial_tank(&tank_rt, rt);
     
-    X_MB_handle_connection(&rt);
+    X_MB_handle_connection(rt);
 
-    X_MB_destroy(&rt);
+    X_MB_free(rt);
 }
--- a/src/X_supp.c	Sun Sep 14 23:40:57 2008 +0800
+++ b/src/X_supp.c	Mon Sep 15 20:33:06 2008 +0800
@@ -9,6 +9,108 @@
 #include "mb_timer.h"
 #include "X_supp.h"
 
+#define ERR -1
+#define OK 0
+
+/*! \ingroup xkb
+ * @{
+ */
+struct _X_kb_info {
+    int keycode_min, keycode_max;
+    int ksym_per_code;
+    KeySym *syms;
+    subject_t *kbevents;
+    ob_factory_t *ob_factory;
+};
+
+/* @} */
+
+struct _X_MB_runtime {
+    Display *display;
+    Window win;
+    Visual *visual;
+    cairo_surface_t *surface, *backend_surface;
+    cairo_t *cr, *backend_cr;
+    redraw_man_t *rdman;
+    mb_tman_t *tman;
+    int w, h;
+
+    X_kb_info_t kbinfo;
+
+    /* States */
+    shape_t *last;
+};
+
+/*! \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 int X_kb_init(X_kb_info_t *kbinfo, Display *display,
+		     redraw_man_t *rdman) {
+    int n_syms;
+    ob_factory_t *factory;
+    int r;
+
+    r = XDisplayKeycodes(display,
+			 &kbinfo->keycode_min,
+			 &kbinfo->keycode_max);
+    if(r == 0)
+	return ERR;
+
+    n_syms = kbinfo->keycode_max - kbinfo->keycode_min + 1;
+    kbinfo->syms = XGetKeyboardMapping(display, kbinfo->keycode_min,
+				       n_syms,
+				       &kbinfo->ksym_per_code);
+    if(kbinfo->syms == NULL)
+	return ERR;
+
+    factory = rdman_get_ob_factory(rdman);
+    kbinfo->kbevents = subject_new(factory, kbinfo, OBJT_KB);
+    if(kbinfo->kbevents == NULL)
+	return ERR;
+    kbinfo->ob_factory = factory;
+
+    return OK;
+}
+
+static void X_kb_destroy(X_kb_info_t *kbinfo) {
+    subject_free(kbinfo->ob_factory, kbinfo->kbevents);
+    XFree(kbinfo->syms);
+}
+
+/*! \brief Accept X keyboard events from handle_x_event() and dispatch it.
+ */
+static void X_kb_handle_event(X_kb_info_t *kbinfo, XKeyEvent *xkey) {
+    unsigned int code;
+    int sym;
+    X_kb_event_t event;
+
+    code = xkey->keycode;
+    sym = keycode2sym(kbinfo, code);
+    if(xkey->type == KeyPress)
+	event.event.type = EVT_KB_PRESS;
+    else if(xkey->type == KeyRelease)
+	event.event.type = EVT_KB_RELEASE;
+    event.event.tgt = event.event.cur_tgt = kbinfo->kbevents;
+    event.keycode = code;
+    event.sym = sym;
+
+    subject_notify(kbinfo->ob_factory, kbinfo->kbevents, &event.event);
+}
+
+/* @} */
 
 static unsigned int get_button_state(unsigned int state) {
     unsigned int but = 0;
@@ -72,6 +174,7 @@
     XMotionEvent *mevt;
     XButtonEvent *bevt;
     XExposeEvent *eevt;
+    XKeyEvent *xkey;
     co_aix x, y, w, h;
 
     int eflag = 0;
@@ -145,6 +248,12 @@
 	    }
 	    break;
 
+	case KeyPress:
+	case KeyRelease:
+	    xkey = &evt.xkey;
+	    X_kb_handle_event(&rt->kbinfo, xkey);
+	    break;
+
 	case Expose:
 	    eevt = &evt.xexpose;
 	    x = eevt->x;
@@ -273,7 +382,8 @@
     }
 
     XSelectInput(display, win, PointerMotionMask | ExposureMask |
-		 ButtonPressMask | ButtonReleaseMask);
+		 ButtonPressMask | ButtonReleaseMask |
+		 KeyPressMask | KeyReleaseMask);
     XFlush(display);
 
     *displayp = display;
@@ -288,7 +398,7 @@
  * It setups a runtime environment to run MadButterfly with Xlib.
  * Users should specify width and height of the opening window.
  */
-int X_MB_init(const char *display_name,
+static int X_MB_init(const char *display_name,
 	      int w, int h, X_MB_runtime_t *xmb_rt) {
     memset(xmb_rt, 0, sizeof(X_MB_runtime_t));
 
@@ -318,10 +428,12 @@
 
     xmb_rt->last = NULL;
 
+    X_kb_init(&xmb_rt->kbinfo, xmb_rt->display, xmb_rt->rdman);
+
     return OK;
 }
 
-void X_MB_destroy(X_MB_runtime_t *xmb_rt) {
+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);
@@ -342,5 +454,38 @@
 
     if(xmb_rt->display)
 	XCloseDisplay(xmb_rt->display);
+
+    X_kb_destroy(&xmb_rt->kbinfo);
 }
 
+X_MB_runtime_t *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(display_name, w, h, rt);
+    if(r != OK)
+	return NULL;
+
+    return rt;
+}
+
+void X_MB_free(X_MB_runtime_t *rt) {
+    X_MB_destroy(rt);
+    free(rt);
+}
+
+subject_t *X_MB_kbevents(X_MB_runtime_t *xmb_rt) {
+    return xmb_rt->kbinfo.kbevents;
+}
+
+redraw_man_t *X_MB_rdman(X_MB_runtime_t *xmb_rt) {
+    return xmb_rt->rdman;
+}
+
+mb_tman_t *X_MB_tman(X_MB_runtime_t *xmb_rt) {
+    return xmb_rt->tman;
+}
--- a/src/X_supp.h	Sun Sep 14 23:40:57 2008 +0800
+++ b/src/X_supp.h	Mon Sep 15 20:33:06 2008 +0800
@@ -6,25 +6,28 @@
 #include "mb_timer.h"
 #include "redraw_man.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;
-struct _X_MB_runtime {
-    Display *display;
-    Window win;
-    Visual *visual;
-    cairo_surface_t *surface, *backend_surface;
-    cairo_t *cr, *backend_cr;
-    redraw_man_t *rdman;
-    mb_tman_t *tman;
-    int w, h;
-
-    /* States */
-    shape_t *last;
-};
 
 extern void X_MB_handle_connection(X_MB_runtime_t *rt);
-extern int X_MB_init(const char *display_name,
-		     int w, int h, X_MB_runtime_t *xmb_rt);
-extern void X_MB_destroy(X_MB_runtime_t *xmb_rt);
+extern X_MB_runtime_t *X_MB_new(const char *display_name, int w, int h);
+extern void X_MB_free(X_MB_runtime_t *xmb_rt);
 
+extern subject_t *X_MB_kbevents(X_MB_runtime_t *xmb_rt);
+extern redraw_man_t *X_MB_rdman(X_MB_runtime_t *xmb_rt);
+extern mb_tman_t *X_MB_tman(X_MB_runtime_t *xmb_rt);
 
 #endif
--- a/src/animate.c	Sun Sep 14 23:40:57 2008 +0800
+++ b/src/animate.c	Mon Sep 15 20:33:06 2008 +0800
@@ -74,7 +74,7 @@
 #include "animate.h"
 
 
-#define STEP_INTERVAL 50000
+#define STEP_INTERVAL 90000
 #define ASSERT(x)
 
 /*! \brief A word is a set of concurrent actions in a program.
@@ -201,9 +201,15 @@
     }
 }
 
-/*
+/*! \brief Time stepping for a program.
+ *
  * Any two actions in concurrent words never change the same attribute.
  * Start time of words in a program are specified in incremental order.
+ *
+ * \note Program will take a big step at last monent.  It is because
+ *	mb_progm_step() running mb_word_stop() if the word being stop
+ *	between now and next_tmo.  It is not obviously if time stepping
+ *	small.
  */
 static void mb_progm_step(const mb_timeval_t *tmo,
 			  const mb_timeval_t *now,
--- a/src/observer.h	Sun Sep 14 23:40:57 2008 +0800
+++ b/src/observer.h	Mon Sep 15 20:33:06 2008 +0800
@@ -11,7 +11,7 @@
 typedef void (*evt_handler)(event_t *event, void *arg);
 
 struct _event {
-    int type;
+    int type;			/*!< event type (a.k.a. EVT_*  */
     subject_t *tgt, *cur_tgt;
 };
 
@@ -40,7 +40,7 @@
 /*! \brief Flag that make a subject to propagate events to parents. */
 #define SUBF_STOP_PROPAGATE 0x1
 
-enum {OBJT_GEO, OBJT_COORD};
+enum {OBJT_GEO, OBJT_COORD, OBJT_KB};
 
 struct _mouse_event {
     event_t event;
@@ -69,7 +69,8 @@
 };
 
 enum {EVT_MOUSE_OVER, EVT_MOUSE_OUT, EVT_MOUSE_MOVE,
-      EVT_MOUSE_BUT_PRESS, EVT_MOUSE_BUT_RELEASE};
+      EVT_MOUSE_BUT_PRESS, EVT_MOUSE_BUT_RELEASE,
+      EVT_KB_PRESS, EVT_KB_RELEASE};
 
 extern subject_t *subject_new(ob_factory_t *factory,
 			      void *obj, int obj_type);