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
recognize(Scribble * s)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
graffiti_load_recognizers(struct graffiti * pg)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 *
scribblealloc(void)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