xref: /plan9/sys/src/libscribble/graffiti.c (revision 4d44ba9b9ee4246ddbd96c7fcaf0918ab92ab35a)
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