diff options
Diffstat (limited to 'libstroke/stroke.c')
-rw-r--r-- | libstroke/stroke.c | 395 |
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; + } +} + |