1 /* 2 * Graffiti.c is based on the file Scribble.c copyrighted 3 * by Keith Packard: 4 * 5 * Copyright © 1999 Keith Packard 6 * 7 * Permission to use, copy, modify, distribute, and sell this software and its 8 * documentation for any purpose is hereby granted without fee, provided that 9 * the above copyright notice appear in all copies and that both that 10 * copyright notice and this permission notice appear in supporting 11 * documentation, and that the name of Keith Packard not be used in 12 * advertising or publicity pertaining to distribution of the software without 13 * specific, written prior permission. Keith Packard makes no 14 * representations about the suitability of this software for any purpose. It 15 * is provided "as is" without express or implied warranty. 16 * 17 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 18 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 19 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR 20 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 21 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 22 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 23 * PERFORMANCE OF THIS SOFTWARE. 24 */ 25 26 #include <u.h> 27 #include <libc.h> 28 #include <draw.h> 29 #include <scribble.h> 30 31 #include "scribbleimpl.h" 32 #include "graffiti.h" 33 34 int ScribbleDebug; 35 36 char *cl_name[3] = { 37 DEFAULT_LETTERS_FILE, 38 DEFAULT_DIGITS_FILE, 39 DEFAULT_PUNC_FILE 40 }; 41 42 Rune 43 recognize (Scribble *s) 44 { 45 struct graffiti *graf = s->graf; 46 Stroke *ps = &s->ps; 47 Rune rune; 48 int c; 49 int nr; 50 rec_alternative *ret; 51 52 if (ps->npts == 0) 53 return '\0'; 54 55 c = recognizer_translate( 56 graf->rec[s->puncShift ? CS_PUNCTUATION : s->curCharSet], 57 1, ps, false, &nr, &ret); 58 if (c != -1) 59 delete_rec_alternative_array(nr, ret, false); 60 61 rune = '\0'; 62 63 switch (c) { 64 case '\0': 65 if(ScribbleDebug)fprint(2, "(case '\\0')\n"); 66 break; 67 case 'A': /* space */ 68 rune = ' '; 69 if(ScribbleDebug)fprint(2, "(case A) character = ' %C' (0x%x)\n", rune, rune); 70 break; 71 case 'B': /* backspace */ 72 rune = '\b'; 73 if(ScribbleDebug)fprint(2, "(case B) character = \\b (0x%x)\n", rune); 74 break; 75 case 'N': /* numlock */ 76 if(ScribbleDebug)fprint(2, "(case N)\n"); 77 if (s->curCharSet == CS_DIGITS) { 78 s->curCharSet = CS_LETTERS; 79 } else { 80 s->curCharSet = CS_DIGITS; 81 } 82 s->tmpShift = 0; 83 s->puncShift = 0; 84 s->ctrlShift = 0; 85 break; 86 case 'P': /* usually puncshift, but we'll make it CTRL */ 87 if(ScribbleDebug)fprint(2, "(case P)\n"); 88 s->ctrlShift = !s->ctrlShift; 89 s->tmpShift = 0; 90 s->puncShift = 0; 91 break; 92 case 'R': /* newline */ 93 rune = '\n'; 94 if(ScribbleDebug)fprint(2, "(case R) character = \\n (0x%x)\n", rune); 95 break; 96 case 'S': /* shift */ 97 if(ScribbleDebug)fprint(2, "(case S)\n"); 98 s->puncShift = 0; 99 s->ctrlShift = 0; 100 if (s->capsLock) { 101 s->capsLock = 0; 102 s->tmpShift = 0; 103 break; 104 } 105 if (s->tmpShift == 0) { 106 s->tmpShift++; 107 break; 108 } 109 /* fall through */ 110 case 'L': /* caps lock */ 111 if(ScribbleDebug)fprint(2, "(case L)\n"); 112 s->capsLock = !s->capsLock; 113 break; 114 case '.': /* toggle punctuation mode */ 115 if (s->puncShift) { 116 s->puncShift = 0; 117 } else { 118 s->puncShift = 1; 119 s->ctrlShift = 0; 120 s->tmpShift = 0; 121 return rune; 122 } 123 rune = '.'; 124 if(0)fprint(2, "(case .) character = %c (0x%x)\n", rune, rune); 125 break; 126 default: 127 if ('A' <= c && c <= 'Z') { 128 if(ScribbleDebug)fprint(2, "(bad case?) character = %c (0x%x)\n", c, c); 129 return rune; 130 } 131 rune = c; 132 if (s->ctrlShift) 133 { 134 if (c < 'a' || 'z' < c) 135 { 136 if(ScribbleDebug)fprint(2, "(default) character = %c (0x%x)\n", rune, rune); 137 return rune; 138 } 139 rune = rune & 0x1f; 140 } else if ((s->capsLock && !s->tmpShift) || 141 (!s->capsLock && s->tmpShift)) 142 { 143 if (rune < 0xff) 144 rune = toupper(rune); 145 } 146 s->tmpShift = 0; 147 s->puncShift = 0; 148 s->ctrlShift = 0; 149 if(ScribbleDebug)fprint(2, "(default) character = %c (0x%x)\n", rune, rune); 150 } 151 return rune; 152 } 153 154 /* This procedure is called to initialize pg by loading the three 155 * recognizers, loading the initial set of three classifiers, and 156 * loading & verifying the recognizer extension functions. If the 157 * directory $HOME/.recognizers exists, the classifier files will be 158 * loaded from that directory. If not, or if there is an error, the 159 * default files (directory specified in Makefile) will be loaded 160 * instead. Returns non-zero on success, 0 on failure. (Adapted from 161 * package tkgraf/src/GraffitiPkg.c. 162 */ 163 164 static int 165 graffiti_load_recognizers(struct graffiti *pg) 166 { 167 bool usingDefault; 168 char* homedir; 169 int i; 170 rec_fn *fns; 171 172 /* First, load the recognizers... */ 173 /* call recognizer_unload if an error ? */ 174 for (i = 0; i < NUM_RECS; i++) { 175 /* Load the recognizer itself... */ 176 pg->rec[i] = recognizer_load(DEFAULT_REC_DIR, "", nil); 177 if (pg->rec[i] == nil) { 178 fprint(2,"Error loading recognizer from %s.", DEFAULT_REC_DIR); 179 return 0; 180 } 181 if ((* (int *)(pg->rec[i])) != 0xfeed) { 182 fprint(2,"Error in recognizer_magic."); 183 return 0; 184 } 185 } 186 187 /* ...then figure out where the classifiers are... */ 188 if ( (homedir = (char*)getenv("home")) == nil ) { 189 if(0)fprint(2, "no homedir, using = %s\n", REC_DEFAULT_USER_DIR); 190 strecpy(pg->cldir, pg->cldir+sizeof pg->cldir, REC_DEFAULT_USER_DIR); 191 usingDefault = true; 192 } else { 193 if(0)fprint(2, "homedir = %s\n", homedir); 194 snprint(pg->cldir, sizeof pg->cldir, "%s/%s", homedir, CLASSIFIER_DIR); 195 usingDefault = false; 196 } 197 198 /* ...then load the classifiers... */ 199 for (i = 0; i < NUM_RECS; i++) { 200 int rec_return; 201 char *s; 202 203 rec_return = recognizer_load_state(pg->rec[i], pg->cldir, cl_name[i]); 204 if ((rec_return == -1) && (usingDefault == false)) { 205 if(0)fprint(2, "Unable to load custom classifier file %s/%s.\nTrying default classifier file instead.\nOriginal error: %s\n ", 206 pg->cldir, cl_name[i], 207 (s = recognizer_error(pg->rec[i])) ? s : "(none)"); 208 rec_return = recognizer_load_state(pg->rec[i], 209 REC_DEFAULT_USER_DIR, cl_name[i]); 210 } 211 if (rec_return == -1) { 212 fprint(2, "Unable to load default classifier file %s.\nOriginal error: %s\n", 213 cl_name[i], 214 (s = recognizer_error(pg->rec[i])) ? s : "(none)"); 215 return 0; 216 } 217 } 218 219 /* We have recognizers and classifiers now. */ 220 /* Get the vector of LIextension functions.. */ 221 fns = recognizer_get_extension_functions(pg->rec[CS_LETTERS]); 222 if (fns == nil) { 223 fprint(2, "LI Recognizer Training:No extension functions!"); 224 return 0; 225 } 226 227 /* ... and make sure the training & get-classes functions are okay. */ 228 if( (pg->rec_train = (li_recognizer_train)fns[LI_TRAIN]) == nil ) { 229 fprint(2, 230 "LI Recognizer Training:li_recognizer_train() not found!"); 231 if (fns != nil) { 232 free(fns); 233 } 234 return 0; 235 } 236 237 if( (pg->rec_getClasses = (li_recognizer_getClasses)fns[LI_GET_CLASSES]) == nil ) { 238 fprint(2, 239 "LI Recognizer Training:li_recognizer_getClasses() not found!"); 240 if (fns != nil) { 241 free(fns); 242 } 243 return 0; 244 } 245 free(fns); 246 return 1; 247 } 248 249 Scribble * 250 scribblealloc(void) 251 { 252 Scribble *s; 253 254 s = mallocz(sizeof(Scribble), 1); 255 if (s == nil) 256 sysfatal("Initialize: %r"); 257 s->curCharSet = CS_LETTERS; 258 259 s->graf = mallocz(sizeof(struct graffiti), 1); 260 if (s->graf == nil) 261 sysfatal("Initialize: %r"); 262 263 graffiti_load_recognizers(s->graf); 264 265 return s; 266 } 267