/*
|
/*
|
* Copyright (c) 2000 Greg Haerr <greg@censoft.com>
|
* Copyright (c) 2000 Greg Haerr <greg@censoft.com>
|
* Copyright (c) 2000 Century Software <embedded.centurysoftware.com>
|
* Copyright (c) 2000 Century Software <embedded.centurysoftware.com>
|
* Scribble Handwriting Recognition for Nano-X!
|
* Scribble Handwriting Recognition for Nano-X!
|
* Scribble object routines
|
* Scribble object routines
|
*
|
*
|
* Copyright © 1999 Keith Packard
|
* Copyright © 1999 Keith Packard
|
*
|
*
|
* Permission to use, copy, modify, distribute, and sell this software and its
|
* Permission to use, copy, modify, distribute, and sell this software and its
|
* documentation for any purpose is hereby granted without fee, provided that
|
* documentation for any purpose is hereby granted without fee, provided that
|
* the above copyright notice appear in all copies and that both that
|
* the above copyright notice appear in all copies and that both that
|
* copyright notice and this permission notice appear in supporting
|
* copyright notice and this permission notice appear in supporting
|
* documentation, and that the name of Keith Packard not be used in
|
* documentation, and that the name of Keith Packard not be used in
|
* advertising or publicity pertaining to distribution of the software without
|
* advertising or publicity pertaining to distribution of the software without
|
* specific, written prior permission. Keith Packard makes no
|
* specific, written prior permission. Keith Packard makes no
|
* representations about the suitability of this software for any purpose. It
|
* representations about the suitability of this software for any purpose. It
|
* is provided "as is" without express or implied warranty.
|
* is provided "as is" without express or implied warranty.
|
*
|
*
|
* KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
* KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
* EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
* EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
* PERFORMANCE OF THIS SOFTWARE.
|
* PERFORMANCE OF THIS SOFTWARE.
|
*/
|
*/
|
|
|
#include <stdio.h>
|
#include <stdio.h>
|
#include <string.h>
|
#include <string.h>
|
#include "scrib.h"
|
#include "scrib.h"
|
|
|
static ScribbleRec scrib; /* static object instance*/
|
static ScribbleRec scrib; /* static object instance*/
|
static char *curmsg = NULL;
|
static char *curmsg = NULL;
|
|
|
static char *cl_name[3] = {DEFAULT_LETTERS_FILE,
|
static char *cl_name[3] = {DEFAULT_LETTERS_FILE,
|
DEFAULT_DIGITS_FILE,
|
DEFAULT_DIGITS_FILE,
|
DEFAULT_PUNC_FILE};
|
DEFAULT_PUNC_FILE};
|
|
|
static int graffiti_load_recognizers(struct graffiti *pg);
|
static int graffiti_load_recognizers(struct graffiti *pg);
|
static void Recognize (ScribbleWidget w);
|
static void Recognize (ScribbleWidget w);
|
static void ShowMode (ScribbleWidget w);
|
static void ShowMode (ScribbleWidget w);
|
|
|
static void
|
static void
|
ResetStroke (ScribbleWidget w)
|
ResetStroke (ScribbleWidget w)
|
{
|
{
|
w->ps.ps_npts = 0;
|
w->ps.ps_npts = 0;
|
w->ps.ps_nstate = 0;
|
w->ps.ps_nstate = 0;
|
w->ps.ps_trans = 0;
|
w->ps.ps_trans = 0;
|
w->ps.ps_state = 0;
|
w->ps.ps_state = 0;
|
w->lastchar = 0;
|
w->lastchar = 0;
|
curmsg = NULL;
|
curmsg = NULL;
|
ShowMode(w);
|
ShowMode(w);
|
}
|
}
|
|
|
static void
|
static void
|
DisplayStroke (ScribbleWidget w)
|
DisplayStroke (ScribbleWidget w)
|
{
|
{
|
GrDrawLines(w->win, w->gc, w->pt, w->ps.ps_npts);
|
GrDrawLines(w->win, w->gc, w->pt, w->ps.ps_npts);
|
}
|
}
|
|
|
static void
|
static void
|
DisplayLast (ScribbleWidget w)
|
DisplayLast (ScribbleWidget w)
|
{
|
{
|
int npt;
|
int npt;
|
|
|
npt = w->ps.ps_npts;
|
npt = w->ps.ps_npts;
|
if (npt > 2)
|
if (npt > 2)
|
npt = 2;
|
npt = 2;
|
GrDrawLines(w->win, w->gc, w->pt + (w->ps.ps_npts - npt), npt);
|
GrDrawLines(w->win, w->gc, w->pt + (w->ps.ps_npts - npt), npt);
|
}
|
}
|
|
|
static void
|
static void
|
AddPoint (ScribbleWidget w, int x, int y)
|
AddPoint (ScribbleWidget w, int x, int y)
|
{
|
{
|
pen_point *ppa;
|
pen_point *ppa;
|
GR_POINT *pt;
|
GR_POINT *pt;
|
int ppasize;
|
int ppasize;
|
|
|
if (w->ps.ps_npts == w->ppasize)
|
if (w->ps.ps_npts == w->ppasize)
|
{
|
{
|
ppasize = w->ppasize + 100;
|
ppasize = w->ppasize + 100;
|
ppa = malloc ((sizeof (pen_point) + sizeof (GR_POINT)) * ppasize);
|
ppa = malloc ((sizeof (pen_point) + sizeof (GR_POINT)) * ppasize);
|
if (!ppa)
|
if (!ppa)
|
return;
|
return;
|
pt = (GR_POINT *) (ppa + ppasize);
|
pt = (GR_POINT *) (ppa + ppasize);
|
memcpy (ppa, w->ps.ps_pts, w->ppasize * sizeof (pen_point));
|
memcpy (ppa, w->ps.ps_pts, w->ppasize * sizeof (pen_point));
|
memcpy (pt, w->pt, w->ppasize * sizeof (GR_POINT));
|
memcpy (pt, w->pt, w->ppasize * sizeof (GR_POINT));
|
free (w->ps.ps_pts);
|
free (w->ps.ps_pts);
|
w->ps.ps_pts = ppa;
|
w->ps.ps_pts = ppa;
|
w->pt = pt;
|
w->pt = pt;
|
w->ppasize = ppasize;
|
w->ppasize = ppasize;
|
}
|
}
|
ppa = &w->ps.ps_pts[w->ps.ps_npts];
|
ppa = &w->ps.ps_pts[w->ps.ps_npts];
|
ppa->x = x;
|
ppa->x = x;
|
ppa->y = y;
|
ppa->y = y;
|
|
|
pt = &w->pt[w->ps.ps_npts];
|
pt = &w->pt[w->ps.ps_npts];
|
pt->x = x;
|
pt->x = x;
|
pt->y = y;
|
pt->y = y;
|
|
|
w->ps.ps_npts++;
|
w->ps.ps_npts++;
|
|
|
DisplayLast (w);
|
DisplayLast (w);
|
}
|
}
|
|
|
ScribbleWidget
|
ScribbleWidget
|
create_scribble(void)
|
create_scribble(void)
|
{
|
{
|
ScribbleWidget new = (ScribbleWidget)&scrib;
|
ScribbleWidget new = (ScribbleWidget)&scrib;
|
GR_WM_PROPERTIES props;
|
GR_WM_PROPERTIES props;
|
|
|
new->capsLock = 0;
|
new->capsLock = 0;
|
new->puncShift = 0;
|
new->puncShift = 0;
|
new->tmpShift = 0;
|
new->tmpShift = 0;
|
new->ctrlShift = 0;
|
new->ctrlShift = 0;
|
new->curCharSet = CS_LETTERS;
|
new->curCharSet = CS_LETTERS;
|
new->lastchar = 0;
|
new->lastchar = 0;
|
new->down = GR_FALSE;
|
new->down = GR_FALSE;
|
/*new->lastfocusid = 0;*/
|
/*new->lastfocusid = 0;*/
|
|
|
graffiti_load_recognizers (&new->graf);
|
graffiti_load_recognizers (&new->graf);
|
|
|
new->ppasize = 0;
|
new->ppasize = 0;
|
new->ps.ps_pts = 0;
|
new->ps.ps_pts = 0;
|
new->pt = 0;
|
new->pt = 0;
|
|
|
new->win = GrNewWindow(GR_ROOT_WINDOW_ID,
|
new->win = GrNewWindow(GR_ROOT_WINDOW_ID,
|
350, 20, 200, 150,
|
350, 20, 200, 150,
|
0, GrGetSysColor(GR_COLOR_APPWINDOW), BLACK);
|
0, GrGetSysColor(GR_COLOR_APPWINDOW), BLACK);
|
/* set title, disallow focus on input window*/
|
/* set title, disallow focus on input window*/
|
props.flags = GR_WM_FLAGS_TITLE | GR_WM_FLAGS_PROPS;
|
props.flags = GR_WM_FLAGS_TITLE | GR_WM_FLAGS_PROPS;
|
props.props = GR_WM_PROPS_NOFOCUS | GR_WM_PROPS_BORDER |
|
props.props = GR_WM_PROPS_NOFOCUS | GR_WM_PROPS_BORDER |
|
GR_WM_PROPS_CAPTION | GR_WM_PROPS_CLOSEBOX;
|
GR_WM_PROPS_CAPTION | GR_WM_PROPS_CLOSEBOX;
|
props.title = "nxScribble";
|
props.title = "nxScribble";
|
GrSetWMProperties(new->win, &props);
|
GrSetWMProperties(new->win, &props);
|
|
|
GrSelectEvents(new->win, GR_EVENT_MASK_BUTTON_DOWN |
|
GrSelectEvents(new->win, GR_EVENT_MASK_BUTTON_DOWN |
|
GR_EVENT_MASK_BUTTON_UP | GR_EVENT_MASK_MOUSE_MOTION |
|
GR_EVENT_MASK_BUTTON_UP | GR_EVENT_MASK_MOUSE_MOTION |
|
GR_EVENT_MASK_KEY_DOWN | /*GR_EVENT_MASK_FOCUS_IN |*/
|
GR_EVENT_MASK_KEY_DOWN | /*GR_EVENT_MASK_FOCUS_IN |*/
|
GR_EVENT_MASK_EXPOSURE | GR_EVENT_MASK_CLOSE_REQ);
|
GR_EVENT_MASK_EXPOSURE | GR_EVENT_MASK_CLOSE_REQ);
|
GrMapWindow(new->win);
|
GrMapWindow(new->win);
|
|
|
new->gc = GrNewGC();
|
new->gc = GrNewGC();
|
GrSetGCForeground(new->gc, GrGetSysColor(GR_COLOR_APPTEXT));
|
GrSetGCForeground(new->gc, GrGetSysColor(GR_COLOR_APPTEXT));
|
GrSetGCBackground(new->gc, GrGetSysColor(GR_COLOR_APPWINDOW));
|
GrSetGCBackground(new->gc, GrGetSysColor(GR_COLOR_APPWINDOW));
|
GrSetGCFont(new->gc, GrCreateFont(GR_FONT_OEM_FIXED, 0, NULL));
|
GrSetGCFont(new->gc, GrCreateFont(GR_FONT_OEM_FIXED, 0, NULL));
|
|
|
ResetStroke (new);
|
ResetStroke (new);
|
return new;
|
return new;
|
}
|
}
|
|
|
void
|
void
|
destroy_scribble(ScribbleWidget w)
|
destroy_scribble(ScribbleWidget w)
|
{
|
{
|
GrDestroyWindow(w->win);
|
GrDestroyWindow(w->win);
|
GrDestroyGC(w->gc);
|
GrDestroyGC(w->gc);
|
free (w->ps.ps_pts);
|
free (w->ps.ps_pts);
|
}
|
}
|
|
|
void
|
void
|
Redisplay (ScribbleWidget w)
|
Redisplay (ScribbleWidget w)
|
{
|
{
|
/*DisplayStroke (w);*/
|
/*DisplayStroke (w);*/
|
ShowMode(w);
|
ShowMode(w);
|
}
|
}
|
|
|
void
|
void
|
ActionStart (ScribbleWidget w, int x, int y)
|
ActionStart (ScribbleWidget w, int x, int y)
|
{
|
{
|
GrRaiseWindow(w->win);
|
GrRaiseWindow(w->win);
|
ResetStroke (w);
|
ResetStroke (w);
|
w->down = GR_TRUE;
|
w->down = GR_TRUE;
|
AddPoint (w, x, y);
|
AddPoint (w, x, y);
|
}
|
}
|
|
|
void
|
void
|
ActionMove (ScribbleWidget w, int x, int y)
|
ActionMove (ScribbleWidget w, int x, int y)
|
{
|
{
|
if (w->down)
|
if (w->down)
|
AddPoint (w, x, y);
|
AddPoint (w, x, y);
|
}
|
}
|
|
|
void
|
void
|
ActionEnd (ScribbleWidget w, int x, int y)
|
ActionEnd (ScribbleWidget w, int x, int y)
|
{
|
{
|
AddPoint (w, x, y);
|
AddPoint (w, x, y);
|
w->down = GR_FALSE;
|
w->down = GR_FALSE;
|
Recognize (w);
|
Recognize (w);
|
}
|
}
|
|
|
|
|
static void
|
static void
|
SendKey(ScribbleWidget w, int ch)
|
SendKey(ScribbleWidget w, int ch)
|
{
|
{
|
GR_WINDOW_ID win = GrGetFocus();
|
GR_WINDOW_ID win = GrGetFocus();
|
|
|
/* FIXME: modifiers are incorrect*/
|
/* FIXME: modifiers are incorrect*/
|
GrInjectKeyboardEvent(win, ch, 0, 0, 1);
|
GrInjectKeyboardEvent(win, ch, 0, 0, 1);
|
GrInjectKeyboardEvent(win, ch, 0, 0, 0);
|
GrInjectKeyboardEvent(win, ch, 0, 0, 0);
|
}
|
}
|
|
|
/* This procedure is called to initialize pg by loading the three
|
/* This procedure is called to initialize pg by loading the three
|
recognizers, loading the initial set of three classifiers, and
|
recognizers, loading the initial set of three classifiers, and
|
loading & verifying the recognizer extension functions. If the
|
loading & verifying the recognizer extension functions. If the
|
directory $HOME/.recognizers exists, the classifier files will be
|
directory $HOME/.recognizers exists, the classifier files will be
|
loaded from that directory. If not, or if there is an error, the
|
loaded from that directory. If not, or if there is an error, the
|
default files (directory specified in Makefile) will be loaded
|
default files (directory specified in Makefile) will be loaded
|
instead. Returns non-zero on success, 0 on failure. (Adapted from
|
instead. Returns non-zero on success, 0 on failure. (Adapted from
|
package tkgraf/src/GraffitiPkg.c. */
|
package tkgraf/src/GraffitiPkg.c. */
|
|
|
static int
|
static int
|
graffiti_load_recognizers(struct graffiti *pg)
|
graffiti_load_recognizers(struct graffiti *pg)
|
{
|
{
|
bool usingDefault;
|
bool usingDefault;
|
#if 0
|
#if 0
|
char* homedir;
|
char* homedir;
|
#endif
|
#endif
|
int i;
|
int i;
|
rec_fn *fns;
|
rec_fn *fns;
|
|
|
/* First, load the recognizers... */
|
/* First, load the recognizers... */
|
/* call recognizer_unload if an error ? */
|
/* call recognizer_unload if an error ? */
|
for (i = 0; i < NUM_RECS; i++) {
|
for (i = 0; i < NUM_RECS; i++) {
|
/* Load the recognizer itself... */
|
/* Load the recognizer itself... */
|
pg->rec[i] = recognizer_load(DEFAULT_REC_DIR, rec_name, NULL);
|
pg->rec[i] = recognizer_load(DEFAULT_REC_DIR, rec_name, NULL);
|
if (pg->rec[i] == NULL) {
|
if (pg->rec[i] == NULL) {
|
fprintf(stderr,"Error loading recognizer from %s.", DEFAULT_REC_DIR);
|
fprintf(stderr,"Error loading recognizer from %s.", DEFAULT_REC_DIR);
|
return 0;
|
return 0;
|
}
|
}
|
if ((* (int *)(pg->rec[i])) != 0xfeed) {
|
if ((* (int *)(pg->rec[i])) != 0xfeed) {
|
fprintf(stderr,"Error in recognizer_magic.");
|
fprintf(stderr,"Error in recognizer_magic.");
|
return 0;
|
return 0;
|
}
|
}
|
}
|
}
|
|
|
/* ...then figure out where the classifiers are... */
|
/* ...then figure out where the classifiers are... */
|
#if 0
|
#if 0
|
if ( (homedir = (char*)getenv("HOME")) == NULL ) {
|
if ( (homedir = (char*)getenv("HOME")) == NULL ) {
|
#endif
|
#endif
|
strcpy(pg->cldir, REC_DEFAULT_USER_DIR);
|
strcpy(pg->cldir, REC_DEFAULT_USER_DIR);
|
usingDefault = true;
|
usingDefault = true;
|
#if 0
|
#if 0
|
} else {
|
} else {
|
strcpy(pg->cldir, homedir);
|
strcpy(pg->cldir, homedir);
|
strcat(pg->cldir, "/");
|
strcat(pg->cldir, "/");
|
strcat(pg->cldir, CLASSIFIER_DIR);
|
strcat(pg->cldir, CLASSIFIER_DIR);
|
usingDefault = false;
|
usingDefault = false;
|
}
|
}
|
#endif
|
#endif
|
|
|
/* ...then load the classifiers... */
|
/* ...then load the classifiers... */
|
for (i = 0; i < NUM_RECS; i++) {
|
for (i = 0; i < NUM_RECS; i++) {
|
int rec_return;
|
int rec_return;
|
char *s;
|
char *s;
|
|
|
rec_return = recognizer_load_state(pg->rec[i], pg->cldir, cl_name[i]);
|
rec_return = recognizer_load_state(pg->rec[i], pg->cldir, cl_name[i]);
|
if ((rec_return == -1) && (usingDefault == false)) {
|
if ((rec_return == -1) && (usingDefault == false)) {
|
fprintf(stderr,
|
fprintf(stderr,
|
"Unable to load custom classifier file %s/%s.\nTrying default classifier file instead.\nOriginal error: %s\n ",
|
"Unable to load custom classifier file %s/%s.\nTrying default classifier file instead.\nOriginal error: %s\n ",
|
pg->cldir, cl_name[i],
|
pg->cldir, cl_name[i],
|
(s = recognizer_error(pg->rec[i])) ? s : "(none)");
|
(s = recognizer_error(pg->rec[i])) ? s : "(none)");
|
rec_return = recognizer_load_state(pg->rec[i],
|
rec_return = recognizer_load_state(pg->rec[i],
|
REC_DEFAULT_USER_DIR, cl_name[i]);
|
REC_DEFAULT_USER_DIR, cl_name[i]);
|
}
|
}
|
if (rec_return == -1) {
|
if (rec_return == -1) {
|
fprintf(stderr, "Unable to load default classifier file %s.\nOriginal error: %s\n",
|
fprintf(stderr, "Unable to load default classifier file %s.\nOriginal error: %s\n",
|
cl_name[i],
|
cl_name[i],
|
(s = recognizer_error(pg->rec[i])) ? s : "(none)");
|
(s = recognizer_error(pg->rec[i])) ? s : "(none)");
|
return 0;
|
return 0;
|
}
|
}
|
}
|
}
|
|
|
/* We have recognizers and classifiers now. */
|
/* We have recognizers and classifiers now. */
|
/* Get the vector of LIextension functions.. */
|
/* Get the vector of LIextension functions.. */
|
fns = recognizer_get_extension_functions(pg->rec[CS_LETTERS]);
|
fns = recognizer_get_extension_functions(pg->rec[CS_LETTERS]);
|
if (fns == NULL) {
|
if (fns == NULL) {
|
fprintf(stderr, "LI Recognizer Training:No extension functions!");
|
fprintf(stderr, "LI Recognizer Training:No extension functions!");
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* ... and make sure the training & get-classes functions are okay. */
|
/* ... and make sure the training & get-classes functions are okay. */
|
if( (pg->rec_train = (li_recognizer_train)fns[LI_TRAIN]) == NULL ) {
|
if( (pg->rec_train = (li_recognizer_train)fns[LI_TRAIN]) == NULL ) {
|
fprintf(stderr,
|
fprintf(stderr,
|
"LI Recognizer Training:li_recognizer_train() not found!");
|
"LI Recognizer Training:li_recognizer_train() not found!");
|
if (fns != NULL) {
|
if (fns != NULL) {
|
free(fns);
|
free(fns);
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
if( (pg->rec_getClasses = (li_recognizer_getClasses)fns[LI_GET_CLASSES]) == NULL ) {
|
if( (pg->rec_getClasses = (li_recognizer_getClasses)fns[LI_GET_CLASSES]) == NULL ) {
|
fprintf(stderr,
|
fprintf(stderr,
|
"LI Recognizer Training:li_recognizer_getClasses() not found!");
|
"LI Recognizer Training:li_recognizer_getClasses() not found!");
|
if (fns != NULL) {
|
if (fns != NULL) {
|
free(fns);
|
free(fns);
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
free(fns);
|
free(fns);
|
return 1;
|
return 1;
|
}
|
}
|
|
|
static void
|
static void
|
msg(char *str)
|
msg(char *str)
|
{
|
{
|
curmsg = str;
|
curmsg = str;
|
}
|
}
|
|
|
static void
|
static void
|
ShowMode (ScribbleWidget w)
|
ShowMode (ScribbleWidget w)
|
{
|
{
|
char *mode;
|
char *mode;
|
char buf[32];
|
char buf[32];
|
|
|
if (w->ctrlShift)
|
if (w->ctrlShift)
|
mode = "^C";
|
mode = "^C";
|
else if (w->puncShift)
|
else if (w->puncShift)
|
mode = "#&^";
|
mode = "#&^";
|
else if (w->curCharSet == CS_DIGITS)
|
else if (w->curCharSet == CS_DIGITS)
|
mode = "123";
|
mode = "123";
|
else if (w->capsLock)
|
else if (w->capsLock)
|
mode = "ABC";
|
mode = "ABC";
|
else if (w->tmpShift)
|
else if (w->tmpShift)
|
mode = "Abc";
|
mode = "Abc";
|
else
|
else
|
mode = "abc";
|
mode = "abc";
|
|
|
if (curmsg)
|
if (curmsg)
|
sprintf(buf, "%s %s", mode, curmsg);
|
sprintf(buf, "%s %s", mode, curmsg);
|
else if (w->lastchar > ' ')
|
else if (w->lastchar > ' ')
|
sprintf(buf, "%s %c", mode, w->lastchar);
|
sprintf(buf, "%s %c", mode, w->lastchar);
|
else sprintf(buf, "%s", mode);
|
else sprintf(buf, "%s", mode);
|
GrClearWindow(w->win, GR_FALSE);
|
GrClearWindow(w->win, GR_FALSE);
|
GrText(w->win, w->gc, 70, 0, buf, strlen(buf), GR_TFTOP);
|
GrText(w->win, w->gc, 70, 0, buf, strlen(buf), GR_TFTOP);
|
}
|
}
|
|
|
static char
|
static char
|
do_recognize(struct graffiti *pg, pen_stroke *ps, int charset)
|
do_recognize(struct graffiti *pg, pen_stroke *ps, int charset)
|
{
|
{
|
int rec_char;
|
int rec_char;
|
int nret;
|
int nret;
|
rec_alternative *ret;
|
rec_alternative *ret;
|
|
|
rec_char = recognizer_translate(pg->rec[charset], 1, ps, false,
|
rec_char = recognizer_translate(pg->rec[charset], 1, ps, false,
|
&nret, &ret);
|
&nret, &ret);
|
if (rec_char != -1) {
|
if (rec_char != -1) {
|
delete_rec_alternative_array(nret, ret, false);
|
delete_rec_alternative_array(nret, ret, false);
|
}
|
}
|
return rec_char;
|
return rec_char;
|
}
|
}
|
|
|
typedef int KeySym;
|
typedef int KeySym;
|
|
|
static void
|
static void
|
Recognize (ScribbleWidget w)
|
Recognize (ScribbleWidget w)
|
{
|
{
|
struct graffiti *graf = &w->graf;
|
struct graffiti *graf = &w->graf;
|
pen_stroke *ps = &w->ps;
|
pen_stroke *ps = &w->ps;
|
KeySym keysym;
|
KeySym keysym;
|
GR_BOOL control;
|
GR_BOOL control;
|
char c;
|
char c;
|
|
|
if (ps->ps_npts == 0)
|
if (ps->ps_npts == 0)
|
return;
|
return;
|
|
|
w->lastchar = 0;
|
w->lastchar = 0;
|
|
|
c = do_recognize(graf, ps, w->puncShift ? CS_PUNCTUATION : w->curCharSet);
|
c = do_recognize(graf, ps, w->puncShift ? CS_PUNCTUATION : w->curCharSet);
|
|
|
/*printf("class %c (%d)\n", c, c);*/
|
/*printf("class %c (%d)\n", c, c);*/
|
|
|
switch (c) {
|
switch (c) {
|
case '\000':
|
case '\000':
|
msg("[Error]");
|
msg("[Error]");
|
w->tmpShift = 0;
|
w->tmpShift = 0;
|
w->puncShift = 0;
|
w->puncShift = 0;
|
w->ctrlShift = 0;
|
w->ctrlShift = 0;
|
ShowMode (w);
|
ShowMode (w);
|
break;
|
break;
|
case 'L': /* caps lock */
|
case 'L': /* caps lock */
|
msg("[Capslock]");
|
msg("[Capslock]");
|
w->capsLock = !w->capsLock;
|
w->capsLock = !w->capsLock;
|
ShowMode (w);
|
ShowMode (w);
|
break;
|
break;
|
case 'N': /* numlock */
|
case 'N': /* numlock */
|
if (w->curCharSet == CS_DIGITS) {
|
if (w->curCharSet == CS_DIGITS) {
|
w->curCharSet = CS_LETTERS;
|
w->curCharSet = CS_LETTERS;
|
msg("[Letters]");
|
msg("[Letters]");
|
} else {
|
} else {
|
w->curCharSet = CS_DIGITS;
|
w->curCharSet = CS_DIGITS;
|
msg("[Digits]");
|
msg("[Digits]");
|
}
|
}
|
w->tmpShift = 0;
|
w->tmpShift = 0;
|
w->puncShift = 0;
|
w->puncShift = 0;
|
w->ctrlShift = 0;
|
w->ctrlShift = 0;
|
ShowMode (w);
|
ShowMode (w);
|
break;
|
break;
|
case 'P': /* usually puncshift, but we'll make it CTRL */
|
case 'P': /* usually puncshift, but we'll make it CTRL */
|
msg("[Ctrlshift]");
|
msg("[Ctrlshift]");
|
w->ctrlShift = !w->ctrlShift;
|
w->ctrlShift = !w->ctrlShift;
|
w->tmpShift = 0;
|
w->tmpShift = 0;
|
w->puncShift = 0;
|
w->puncShift = 0;
|
ShowMode (w);
|
ShowMode (w);
|
break;
|
break;
|
case 'S': /* shift */
|
case 'S': /* shift */
|
w->tmpShift = !w->tmpShift;
|
w->tmpShift = !w->tmpShift;
|
if (w->tmpShift) msg("[Shift]"); else msg("[Unshift]");
|
if (w->tmpShift) msg("[Shift]"); else msg("[Unshift]");
|
w->puncShift = 0;
|
w->puncShift = 0;
|
w->ctrlShift = 0;
|
w->ctrlShift = 0;
|
ShowMode (w);
|
ShowMode (w);
|
break;
|
break;
|
default:
|
default:
|
control = GR_FALSE;
|
control = GR_FALSE;
|
switch (c) {
|
switch (c) {
|
case 'A':
|
case 'A':
|
msg("[Space]");
|
msg("[Space]");
|
keysym = ' ';
|
keysym = ' ';
|
break;
|
break;
|
case 'B':
|
case 'B':
|
msg("[Backspace]");
|
msg("[Backspace]");
|
keysym = '\b';
|
keysym = '\b';
|
break;
|
break;
|
case 'R':
|
case 'R':
|
msg("[Return]");
|
msg("[Return]");
|
keysym = '\r';
|
keysym = '\r';
|
break;
|
break;
|
case '.':
|
case '.':
|
if (! w->puncShift) {
|
if (! w->puncShift) {
|
msg("[Puncshift]");
|
msg("[Puncshift]");
|
w->puncShift = 1;
|
w->puncShift = 1;
|
w->ctrlShift = 0;
|
w->ctrlShift = 0;
|
w->tmpShift = 0;
|
w->tmpShift = 0;
|
ShowMode (w);
|
ShowMode (w);
|
return;
|
return;
|
} else {
|
} else {
|
w->puncShift = 0;
|
w->puncShift = 0;
|
ShowMode (w);
|
ShowMode (w);
|
}
|
}
|
keysym = '.';
|
keysym = '.';
|
break;
|
break;
|
default:
|
default:
|
if ('A' <= c && c <= 'Z') {
|
if ('A' <= c && c <= 'Z') {
|
msg("[Notimp]");
|
msg("[Notimp]");
|
ShowMode (w);
|
ShowMode (w);
|
return;
|
return;
|
}
|
}
|
keysym = (KeySym) c;
|
keysym = (KeySym) c;
|
if (w->ctrlShift)
|
if (w->ctrlShift)
|
{
|
{
|
control = GR_TRUE;
|
control = GR_TRUE;
|
w->ctrlShift = 0;
|
w->ctrlShift = 0;
|
if (c < 'a' || 'z' < c)
|
if (c < 'a' || 'z' < c)
|
{
|
{
|
ShowMode (w);
|
ShowMode (w);
|
return;
|
return;
|
}
|
}
|
}
|
}
|
else if ((w->capsLock && !w->tmpShift) ||
|
else if ((w->capsLock && !w->tmpShift) ||
|
(!w->capsLock && w->tmpShift))
|
(!w->capsLock && w->tmpShift))
|
{
|
{
|
keysym = keysym-'a'+'A';
|
keysym = keysym-'a'+'A';
|
}
|
}
|
w->tmpShift = 0;
|
w->tmpShift = 0;
|
w->puncShift = 0;
|
w->puncShift = 0;
|
ShowMode(w);
|
ShowMode(w);
|
}
|
}
|
|
|
if (control)
|
if (control)
|
keysym &= 0x1f;
|
keysym &= 0x1f;
|
w->lastchar = keysym;
|
w->lastchar = keysym;
|
ShowMode(w);
|
ShowMode(w);
|
SendKey(w, keysym);
|
SendKey(w, keysym);
|
break;
|
break;
|
}
|
}
|
}
|
}
|
|
|