summaryrefslogtreecommitdiff
path: root/libstroke/stroke.c
diff options
context:
space:
mode:
Diffstat (limited to 'libstroke/stroke.c')
-rw-r--r--libstroke/stroke.c395
1 files changed, 395 insertions, 0 deletions
diff --git a/libstroke/stroke.c b/libstroke/stroke.c
new file mode 100644
index 0000000..4fccd01
--- /dev/null
+++ b/libstroke/stroke.c
@@ -0,0 +1,395 @@
+/*
+ libstroke - an X11 stroke interface library
+ Copyright (c) 1996,1997,1998,1999 Mark F. Willey, ETLA Technical
+
+ See the files COPYRIGHT and LICENSE for distribution information.
+*/
+
+#include "config.h"
+#include "stroke.h"
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+#ifdef STROKE_MOUSE_FOOTPRINTS
+#include <X11/Xutil.h>
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+
+/* structure for holding point data */
+typedef struct s_point *p_point;
+
+static struct s_point {
+ int x;
+ int y;
+ p_point next;
+} point;
+
+/* point list head and tail */
+static p_point point_list_head;
+static p_point point_list_tail;
+
+
+
+/* determine which bin a point falls in */
+static int stroke_bin (p_point point_p, int bound_x_1, int bound_x_2,
+ int bound_y_1, int bound_y_2);
+
+
+
+/* point list head and tail */
+static p_point point_list_head=NULL;
+static p_point point_list_tail=NULL;
+
+/* metrics fo input stroke */
+static int min_x = 10000;
+static int min_y = 10000;
+static int max_x = -1;
+static int max_y = -1;
+static int point_count = 0;
+
+#ifdef STROKE_MOUSE_FOOTPRINTS
+/*FIXME: Maybe these should be put in a structure, and not static...*/
+static Display * stroke_disp;
+static Window stroke_window;
+static GC stroke_gc;
+
+static int use_mouse_footprints; /* True if using mouse footprints */
+#endif
+
+static void init_stroke_data (void)
+{
+ while (point_list_head != NULL) {
+ point_list_tail = point_list_head;
+ point_list_head = point_list_head->next;
+ free (point_list_tail);
+ }
+ point_list_tail = NULL;
+}
+
+#ifdef STROKE_MOUSE_FOOTPRINTS
+void stroke_init_with_mouse_footprints (Display *disp, Window wind)
+{
+ XSetWindowAttributes w_attr;
+ XWindowAttributes orig_w_attr;
+ unsigned long mask, col_border, col_background;
+ unsigned int border_width;
+ int screen = DefaultScreen (disp);
+ XSizeHints hints;
+
+ stroke_disp = disp;
+
+ XGetWindowAttributes (stroke_disp, wind, &orig_w_attr);
+ hints.x = orig_w_attr.x;
+ hints.y = orig_w_attr.y;
+ hints.width = orig_w_attr.width;
+ hints.height = orig_w_attr.height;
+
+ /* Let the X server take care of whats underneath this window,
+ rather than the client applications, it'll also prevent needless
+ Expose events. NOTE: Not all X servers support this */
+ mask = CWSaveUnder;
+ w_attr.save_under = True;
+
+ /* inhibit all the decorations */
+ mask |= CWOverrideRedirect;
+ w_attr.override_redirect = True;
+
+ /* Don't set a background, transparent window */
+ mask |= CWBackPixmap;
+ w_attr.background_pixmap = None;
+
+ /* Default input window look */
+ col_background = WhitePixel (stroke_disp, screen);
+
+ /* no border for the window */
+ border_width = 0;
+ col_border = BlackPixel (stroke_disp, screen);
+
+ stroke_window = XCreateSimpleWindow (stroke_disp, wind,
+ hints.x, hints.y,
+ hints.width, hints.height,
+ border_width,
+ col_border, col_background);
+
+ stroke_gc = XCreateGC (stroke_disp, stroke_window, 0, NULL);
+
+ XSetFunction (stroke_disp, stroke_gc, GXinvert);
+
+ XChangeWindowAttributes (stroke_disp, stroke_window, mask, &w_attr);
+
+ XSetLineAttributes (stroke_disp, stroke_gc, 2, LineSolid,
+ CapButt, JoinMiter);
+
+ /*FIXME: is this call really needed? If yes, does it need the real
+ argc and argv? */
+ hints.flags = PPosition | PSize;
+ XSetStandardProperties (stroke_disp, stroke_window, "stroke_test", NULL,
+ (Pixmap)NULL, NULL, 0, &hints);
+
+
+ /* Receive the close window client message */
+ {
+ /* FIXME: is this really needed? If yes, something should be done
+ with wmdelete...*/
+ Atom wmdelete = XInternAtom (stroke_disp, "WM_DELETE_WINDOW",
+ False);
+ XSetWMProtocols (stroke_disp, stroke_window, &wmdelete, True);
+ }
+ use_mouse_footprints = 1;
+ init_stroke_data ();
+}
+#endif
+
+void stroke_init (void)
+{
+#ifdef STROKE_MOUSE_FOOTPRINTS
+ use_mouse_footprints = 0;
+#endif
+ init_stroke_data ();
+}
+
+
+
+int stroke_trans (char *sequence)
+{
+ /* number of bins recorded in the stroke */
+ int sequence_count = 0;
+
+ /* points-->sequence translation scratch variables */
+ int prev_bin = 0;
+ int current_bin = 0;
+ int bin_count = 0;
+
+ /* flag indicating the start of a stroke - always count it in the sequence */
+ int first_bin = TRUE;
+
+ /* bin boundary and size variables */
+ int delta_x, delta_y;
+ int bound_x_1, bound_x_2;
+ int bound_y_1, bound_y_2;
+
+#ifdef STROKE_MOUSE_FOOTPRINTS
+ if (use_mouse_footprints)
+ {
+ XUnmapWindow (stroke_disp, stroke_window);
+ XFlush (stroke_disp);
+ }
+#endif
+
+ /* determine size of grid */
+ delta_x = max_x - min_x;
+ delta_y = max_y - min_y;
+
+ /* calculate bin boundary positions */
+ bound_x_1 = min_x + (delta_x / 3);
+ bound_x_2 = min_x + 2 * (delta_x / 3);
+
+ bound_y_1 = min_y + (delta_y / 3);
+ bound_y_2 = min_y + 2 * (delta_y / 3);
+
+ if (delta_x > STROKE_SCALE_RATIO * delta_y) {
+ bound_y_1 = (max_y + min_y - delta_x) / 2 + (delta_x / 3);
+ bound_y_2 = (max_y + min_y - delta_x) / 2 + 2 * (delta_x / 3);
+ } else if (delta_y > STROKE_SCALE_RATIO * delta_x) {
+ bound_x_1 = (max_x + min_x - delta_y) / 2 + (delta_y / 3);
+ bound_x_2 = (max_x + min_x - delta_y) / 2 + 2 * (delta_y / 3);
+ }
+
+ /*
+ printf ("DEBUG:: point count: %d\n",point_count);
+ printf ("DEBUG:: min_x: %d\n",min_x);
+ printf ("DEBUG:: max_x: %d\n",max_x);
+ printf ("DEBUG:: min_y: %d\n",min_y);
+ printf ("DEBUG:: max_y: %d\n",max_y);
+ printf ("DEBUG:: delta_x: %d\n",delta_x);
+ printf ("DEBUG:: delta_y: %d\n",delta_y);
+ printf ("DEBUG:: bound_x_1: %d\n",bound_x_1);
+ printf ("DEBUG:: bound_x_2: %d\n",bound_x_2);
+ printf ("DEBUG:: bound_y_1: %d\n",bound_y_1);
+ printf ("DEBUG:: bound_y_2: %d\n",bound_y_2);
+ */
+
+ /*
+ build string by placing points in bins, collapsing bins and
+ discarding those with too few points...
+ */
+
+ while (point_list_head != NULL) {
+
+ /* figure out which bin the point falls in */
+ current_bin = stroke_bin(point_list_head,bound_x_1, bound_x_2,
+ bound_y_1, bound_y_2);
+ /* if this is the first point, consider it the previous bin,
+ too. */
+ prev_bin = (prev_bin == 0) ? current_bin : prev_bin;
+ /* printf ("DEBUG:: current bin: %d\n",current_bin); */
+
+ if (prev_bin == current_bin)
+ bin_count++;
+ else { /* we are moving to a new bin -- consider adding to the
+ sequence */
+ if ((bin_count > (point_count * STROKE_BIN_COUNT_PERCENT))
+ || (first_bin == TRUE)) {
+ first_bin = FALSE;
+ sequence[sequence_count++] = '0' + prev_bin;
+ /* printf ("DEBUG:: adding sequence: %d\n",prev_bin);
+ */
+ }
+
+ /* restart counting points in the new bin */
+ bin_count=0;
+ prev_bin = current_bin;
+ }
+
+ /* move to next point, freeing current point from list */
+ point_list_tail = point_list_head;
+ point_list_head = point_list_head->next;
+ free (point_list_tail);
+ }
+ point_list_tail = NULL;
+
+ /* add the last run of points to the sequence */
+ sequence[sequence_count++] = '0' + current_bin;
+ /* printf ("DEBUG:: adding final sequence: %d\n",current_bin); */
+
+ /* bail out on error cases */
+ if ((point_count < STROKE_MIN_POINTS)
+ || (sequence_count > STROKE_MAX_SEQUENCE)) {
+ point_count = 0;
+ strcpy (sequence,"0");
+ return FALSE;
+ }
+
+ /* add null termination and leave */
+ point_count = 0;
+ sequence[sequence_count] = '\0';
+ return TRUE;
+}
+
+/* figure out which bin the point falls in */
+static int stroke_bin (p_point point_p, int bound_x_1, int bound_x_2,
+ int bound_y_1, int bound_y_2)
+{
+ int bin_num = 1;
+ if (point_p->x > bound_x_1) bin_num += 1;
+ if (point_p->x > bound_x_2) bin_num += 1;
+ if (point_p->y > bound_y_1) bin_num += 3;
+ if (point_p->y > bound_y_2) bin_num += 3;
+
+ return bin_num;
+}
+
+void stroke_record (int x, int y)
+{
+ p_point new_point_p;
+ int delx, dely;
+ float ix, iy;
+
+ if (point_count < STROKE_MAX_POINTS) {
+ new_point_p = (p_point) malloc (sizeof(point));
+
+ if (point_list_tail == NULL) {
+
+ /* first point in list - initialize list and metrics */
+ point_list_head = point_list_tail = new_point_p;
+ min_x = 10000;
+ min_y = 10000;
+ max_x = -1;
+ max_y = -1;
+ point_count = 0;
+
+ } else {
+#ifdef STROKE_MOUSE_FOOTPRINTS
+ if (use_mouse_footprints)
+ {
+ XMapRaised(stroke_disp, stroke_window);
+ XDrawLine (stroke_disp, stroke_window, stroke_gc,
+ x, y, point_list_tail->x, point_list_tail->y);
+ XFlush (stroke_disp);
+ }
+#endif
+
+ /* interpolate between last and current point */
+ delx = x - point_list_tail->x;
+ dely = y - point_list_tail->y;
+
+ /* step by the greatest delta direction */
+ if (abs(delx) > abs(dely)) {
+ iy = point_list_tail->y;
+
+ /* go from the last point to the current, whatever direction it
+ may be */
+ for (ix = point_list_tail->x;
+ (delx > 0) ? (ix < x) : (ix > x);
+ ix += (delx > 0) ? 1 : -1) {
+
+ /* step the other axis by the correct increment */
+ iy += fabs(((float) dely
+ / (float) delx)) * (float) ((dely < 0) ? -1.0 : 1.0);
+
+ /* add the interpolated point */
+ point_list_tail->next = new_point_p;
+ point_list_tail = new_point_p;
+ new_point_p->x = ix;
+ new_point_p->y = iy;
+ new_point_p->next = NULL;
+
+ /* update metrics */
+ if (((int) ix) < min_x) min_x = (int) ix;
+ if (((int) ix) > max_x) max_x = (int) ix;
+ if (((int) iy) < min_y) min_y = (int) iy;
+ if (((int) iy) > max_y) max_y = (int) iy;
+ point_count++;
+
+ new_point_p = (p_point) malloc (sizeof(point));
+ }
+ } else { /* same thing, but for dely larger than delx case... */
+ ix = point_list_tail->x;
+
+ /* go from the last point to the current, whatever direction
+ it may be */
+ for (iy = point_list_tail->y; (dely > 0) ? (iy < y) : (iy > y);
+ iy += (dely > 0) ? 1 : -1) {
+
+ /* step the other axis by the correct increment */
+ ix += fabs(((float) delx / (float) dely))
+ * (float) ((delx < 0) ? -1.0 : 1.0);
+
+ /* add the interpolated point */
+ point_list_tail->next = new_point_p;
+ point_list_tail = new_point_p;
+ new_point_p->y = iy;
+ new_point_p->x = ix;
+ new_point_p->next = NULL;
+
+ /* update metrics */
+ if (((int) ix) < min_x) min_x = (int) ix;
+ if (((int) ix) > max_x) max_x = (int) ix;
+ if (((int) iy) < min_y) min_y = (int) iy;
+ if (((int) iy) > max_y) max_y = (int) iy;
+ point_count++;
+
+ new_point_p = (p_point) malloc (sizeof(point));
+ }
+ }
+
+ /* add the sampled point */
+ point_list_tail->next = new_point_p;
+ point_list_tail = new_point_p;
+ }
+
+ /* record the sampled point values */
+ new_point_p->x = x;
+ new_point_p->y = y;
+ new_point_p->next = NULL;
+ }
+}
+