xref: /plan9/sys/src/libcontrol/keyboard.c (revision ac57dd0bdfb9d49ce3ebb32937bb07f2cec3da6c)
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <thread.h>
5 #include <mouse.h>
6 #include <keyboard.h>
7 #include <control.h>
8 
9 typedef struct Keyboard Keyboard;
10 
11 enum{
12 	SRegular	= 0,
13 	SShift	= 1,
14 	SCaps	= 2,
15 	SMask	= 3,
16 	Nstate	= 4,
17 	SControl	= 4,
18 };
19 
20 struct Keyboard
21 {
22 	Control;
23 	CImage	*image;
24 	CImage	*mask;
25 	CImage	*light;
26 	CImage	*textcolor;
27 	CImage	*bordercolor;
28 	CFont	*font;
29 	CFont	*ctlfont;
30 	Image	*im[Nstate];
31 	int		border;
32 	int		lastbut;
33 	int		state;
34 	char		*key;
35 };
36 
37 enum{
38 	EBorder,
39 	EBordercolor,
40 	EFocus,
41 	EFont,
42 	EFormat,
43 	EHide,
44 	EImage,
45 	ELight,
46 	EMask,
47 	ERect,
48 	EReveal,
49 	EShow,
50 	ESize,
51 };
52 
53 static char *cmds[] = {
54 	[EBorder] =	"border",
55 	[EBordercolor] = "bordercolor",
56 	[EFocus] = 	"focus",
57 	[EFont] =		"font",
58 	[EFormat] = 	"format",
59 	[EHide] =		"hide",
60 	[EImage] =	"image",
61 	[ELight] =		"light",
62 	[EMask] =		"mask",
63 	[ERect] =		"rect",
64 	[EReveal] =	"reveal",
65 	[EShow] =		"show",
66 	[ESize] =		"size",
67 	nil
68 };
69 
70 enum
71 {
72 	Nrow = 5
73 };
74 
75 static uchar wid [Nrow][16] = {
76 	{16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 30, },
77 	{24, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 24, },
78 	{32, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, },
79 	{40, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, },
80 	{30, 30, 80, 40, 42, 24, },
81 };
82 
83 static char *keyregular[Nrow] = {
84 	"`\0001\0002\0003\0004\0005\0006\0007\0008\0009\0000\0-\0=\0\\\0<-\0\0",
85 	"->\0q\0w\0e\0r\0t\0y\0u\0i\0o\0p\0[\0]\0Del\0\0",
86 	"Caps\0a\0s\0d\0f\0g\0h\0j\0k\0l\0;\0'\0Enter\0\0",
87 	"Shift\0z\0x\0c\0v\0b\0n\0m\0,\0.\0/\0Shift\0\0",
88 	"Ctrl\0Alt\0 \0Scrib\0Menu\0Esc\0\0"
89 };
90 
91 static char *keyshift[Nrow] = {
92 	"~\0!\0@\0#\0$\0%\0^\0&\0*\0(\0)\0_\0+\0|\0<-\0\0",
93 	"->\0Q\0W\0E\0R\0T\0Y\0U\0I\0O\0P\0{\0}\0Del\0\0",
94 	"Caps\0A\0S\0D\0F\0G\0H\0J\0K\0L\0:\0\"\0Enter\0\0",
95 	"Shift\0Z\0X\0C\0V\0B\0N\0M\0<\0>\0?\0Shift\0\0",
96 	"Ctrl\0Alt\0 \0Scrib\0Menu\0Esc\0\0"
97 };
98 
99 static char *keycaps[Nrow] = {
100 	"`\0001\0002\0003\0004\0005\0006\0007\0008\0009\0000\0-\0=\0\\\0<-\0\0",
101 	"->\0Q\0W\0E\0R\0T\0Y\0U\0I\0O\0P\0[\0]\0Del\0\0",
102 	"Caps\0A\0S\0D\0F\0G\0H\0J\0K\0L\0;\0'\0Enter\0\0",
103 	"Shift\0Z\0X\0C\0V\0B\0N\0M\0,\0.\0/\0Shift\0\0",
104 	"Ctrl\0Alt\0 \0Scrib\0Menu\0Esc\0\0"
105 };
106 
107 static char *keycapsshift[Nrow] = {
108 	"~\0!\0@\0#\0$\0%\0^\0&\0*\0(\0)\0_\0+\0|\0<-\0\0",
109 	"->\0q\0w\0e\0r\0t\0y\0u\0i\0o\0p\0{\0}\0Del\0\0",
110 	"Caps\0a\0s\0d\0f\0g\0h\0j\0k\0l\0:\0\"\0Enter\0\0",
111 	"Shift\0z\0x\0c\0v\0b\0n\0m\0<\0>\0?\0Shift\0\0",
112 	"Ctrl\0Alt\0 \0Scrib\0Menu\0Esc\0\0"
113 };
114 
115 struct{
116 	char	*name;
117 	int	val;
118 }keytab[] = {
119 	"Shift",	0,
120 	"Ctrl",	0,
121 	"Alt",		0,
122 	"Caps",	0,
123 	"Del",	'\177',
124 	"Enter",	'\n',
125 	"Esc",	'\033',
126 	"<-",		'\b',
127 	"->",		'\t',
128 	"Scrib",	0x10000,
129 	"Menu",	0x10001,
130 	nil,		0,
131 };
132 
133 static char **keyset[Nstate] = {
134 	keyregular,
135 	keyshift,
136 	keycaps,
137 	keycapsshift,
138 };
139 
140 static void	keyboardshow(Keyboard*);
141 static void	keyup(Keyboard*, Point);
142 static void	keydown(Keyboard*, Point);
143 static void	keyresize(Keyboard*);
144 
145 static void
keyboardmouse(Control * c,Mouse * m)146 keyboardmouse(Control *c, Mouse *m)
147 {
148 	Keyboard *k;
149 
150 	k = (Keyboard *)c;
151 	if(m->buttons==1)
152 		keydown(k, m->xy);
153 	else if(k->lastbut==1 && m->buttons==0)
154 		keyup(k, m->xy);
155 	k->lastbut = m->buttons;
156 }
157 
158 static void
keyboardfree(Control * c)159 keyboardfree(Control *c)
160 {
161 	int i;
162 	Keyboard *k;
163 
164 	k = (Keyboard *)c;
165 	_putctlimage(k->image);
166 	_putctlimage(k->mask);
167 	_putctlimage(k->light);
168 	_putctlimage(k->textcolor);
169 	_putctlimage(k->bordercolor);
170 	_putctlfont(k->font);
171 	_putctlfont(k->ctlfont);
172 	for(i=0; i<nelem(k->im); i++)
173 		freeimage(k->im[i]);
174 	free(k->format);
175 }
176 
177 static int
keyboardy(Keyboard * k,int row)178 keyboardy(Keyboard *k, int row)
179 {
180 	int dy;
181 
182 	if(row >= Nrow)
183 		return k->rect.max.y-k->border;
184 	dy = Dy(k->rect)-2*k->border;
185 	return k->rect.min.y+k->border+(row*dy+Nrow-1)/Nrow;
186 }
187 
188 static char*
whichkey(Keyboard * k,Point p,int * rowp,int * colp,Rectangle * rp)189 whichkey(Keyboard *k, Point p, int *rowp, int *colp, Rectangle *rp)
190 {
191 	uchar *wp;
192 	char *kp;
193 	int row, col, dx, dy, x, n, maxx;
194 	Rectangle r;
195 
196 	r = insetrect(k->rect, k->border);
197 	if(!ptinrect(p, r))
198 		return nil;
199 	maxx = r.max.x;
200 	dx = Dx(r);
201 	dy = Dy(r);
202 	row = (p.y - r.min.y)*Nrow/dy;
203 	if(row >= Nrow)
204 		row = Nrow-1;
205 	r.min.y = keyboardy(k, row);
206 	r.max.y = keyboardy(k, row+1);
207 	x = r.min.x;
208 	kp = keyset[k->state&SMask][row];
209 	wp = wid[row];
210 	for(col=0; *kp; col++,kp+=n+1){
211 		n = strlen(kp);
212 		r.min.x = x;
213 		r.max.x = x + (wp[col]*dx+255)/256;
214 		if(kp[n+1] == '\0')
215 			r.max.x = maxx;
216 		if(r.max.x > p.x)
217 			break;
218 		x = r.max.x;
219 	}
220 	*rp = insetrect(r, 1);
221 	*rowp = row;
222 	*colp = col;
223 	return kp;
224 }
225 
226 static Rectangle
keyrect(Keyboard * k,int row,int col)227 keyrect(Keyboard *k, int row, int col)
228 {
229 	uchar *wp;
230 	char *kp;
231 	int i, x, n, dx;
232 	Rectangle r;
233 	Point p;
234 
235 	r = insetrect(k->rect, k->border);
236 	p = r.min;
237 	dx = Dx(r);
238 	r.min.y = keyboardy(k, row);
239 	r.max.y = keyboardy(k, row+1);
240 	x = r.min.x;
241 	kp = keyset[0][row];
242 	wp = wid[row];
243 	for(i=0; *kp; i++,kp+=n+1){
244 		n = strlen(kp);
245 		r.min.x = x;
246 		r.max.x = x + (wp[i]*dx+255)/256;
247 		if(kp[n+1] == '\0')
248 			r.max.x = p.x+dx;
249 		if(i >= col)
250 			break;
251 		x = r.max.x;
252 	}
253 	return insetrect(r, 1);
254 }
255 
256 static void
keydraw(Keyboard * k,int state)257 keydraw(Keyboard *k, int state)
258 {
259 	Point p, q;
260 	int row, col, x, dx, dy, nexty, n;
261 	uchar *wp;
262 	char *kp;
263 	Rectangle r;
264 	Font *f, *f1, *f2;
265 	Image *im;
266 
267 	freeimage(k->im[state]);
268 	k->im[state] = nil;
269 	if(Dx(k->rect)-2*k->border <= 0)
270 		return;
271 
272 	im = allocimage(display, k->rect, screen->chan, 0, ~0);
273 	if(im == nil)
274 		return;
275 	k->im[state] = im;
276 
277 	r = insetrect(k->rect, k->border);
278 	border(im, k->rect, k->border, k->bordercolor->image, ZP);
279 	draw(im, r, k->image->image, nil, ZP);
280 	dx = Dx(r);
281 	dy = Dy(r);
282 	p = r.min;
283 	f1 = k->font->font;
284 	f2 = k->ctlfont->font;
285 	nexty = p.y;
286 	for(row=0; row<Nrow; row++){
287 		x = p.x;
288 		kp = keyset[state][row];
289 		wp = wid[row];
290 		r.min.y = nexty;
291 		nexty = keyboardy(k, row+1);
292 		r.max.y = nexty;
293 		for(col=0; *kp; col++,kp+=n+1){
294 			r.min.x = x;
295 			r.max.x = x + (wp[col]*dx+255)/256;
296 			n = strlen(kp);
297 			if(kp[n+1] == '\0')
298 				r.max.x = p.x+dx;
299 			if(row == Nrow-1)
300 				r.max.y = p.y+dy;
301 			if(n > 1)
302 				f = f2;
303 			else
304 				f = f1;
305 			q = _ctlalignpoint(r, stringnwidth(f, kp, n), f->height, Acenter);
306 			_string(im, q, k->textcolor->image,
307 				ZP, f, kp, nil, n, r,
308 				nil, ZP, SoverD);
309 			x = r.max.x;
310 			if(kp[n+1])
311 				draw(im, Rect(x, r.min.y, x+1, r.max.y),
312 					k->textcolor->image, nil, ZP);
313 		}
314 		if(row != Nrow-1)
315 			draw(im, Rect(p.x, r.max.y, p.x+dx, r.max.y+1),
316 				k->textcolor->image, nil, ZP);
317 	}
318 }
319 
320 static void
keyresize(Keyboard * k)321 keyresize(Keyboard *k)
322 {
323 	int i;
324 
325 	for(i=0; i<Nstate; i++)
326 		keydraw(k, i);
327 }
328 
329 static void
keyboardshow(Keyboard * k)330 keyboardshow(Keyboard *k)
331 {
332 	Rectangle r;
333 
334 	if (k->hidden)
335 		return;
336 	if(k->im[0]==nil || !eqrect(k->im[0]->r, k->rect))
337 		keyresize(k);
338 	if(k->im[k->state&SMask] == nil)
339 		return;
340 	draw(k->screen, k->rect, k->im[k->state&SMask], nil, k->rect.min);
341 	if(k->state & SShift){
342 		r = keyrect(k, 3, 0);
343 		draw(k->screen, r, k->light->image, k->mask->image, ZP);
344 		r = keyrect(k, 3, 11);
345 		draw(k->screen, r, k->light->image, k->mask->image, ZP);
346 	}
347 	if(k->state & SCaps){
348 		r = keyrect(k, 2, 0);
349 		draw(k->screen, r, k->light->image, k->mask->image, ZP);
350 	}
351 	if(k->state & SControl){
352 		r = keyrect(k, 4, 0);
353 		draw(k->screen, r, k->light->image, k->mask->image, ZP);
354 	}
355 	flushimage(display, 1);
356 }
357 
358 static void
keydown(Keyboard * k,Point p)359 keydown(Keyboard *k, Point p)
360 {
361 	int row, col;
362 	Rectangle r;
363 	char *s;
364 
365 	s = whichkey(k, p, &row, &col, &r);
366 	if(s == k->key)
367 		return;
368 	keyboardshow(k);
369 	if(s != nil)
370 		draw(k->screen, r, k->light->image, k->mask->image, ZP);
371 	flushimage(display, 1);
372 	k->key = s;
373 }
374 
375 static int
keylookup(char * s)376 keylookup(char *s)
377 {
378 	int i;
379 
380 	for(i=0; keytab[i].name; i++)
381 		if(strcmp(s, keytab[i].name) == 0)
382 			return keytab[i].val;
383 	return s[0];
384 }
385 
386 static void
keyup(Keyboard * k,Point p)387 keyup(Keyboard *k, Point p)
388 {
389 	int row, col;
390 	Rectangle r;
391 	char *s;
392 	int val;
393 
394 	s = whichkey(k, p, &row, &col, &r);
395 	if(s == nil)
396 		return;
397 	val = keylookup(s);
398 	if(k->state & SControl)
399 		if(' '<val && val<0177)
400 			val &= ~0x60;
401 	if(strcmp(s, "Alt") == 0)
402 		{;}
403 	if(strcmp(s, "Ctrl") == 0){
404 		k->state ^= SControl;
405 	}else
406 		k->state &= ~SControl;
407 	if(strcmp(s, "Shift")==0 || strcmp(s, "Caps")==0){
408 		if(strcmp(s, "Shift") == 0)
409 			k->state ^= SShift;
410 		if(strcmp(s, "Caps") == 0)
411 			k->state ^= SCaps;
412 	}else
413 		k->state &= ~SShift;
414 	keyboardshow(k);
415 	if(val)
416 		chanprint(k->event, k->format, k->name, val);
417 	k->key = nil;
418 }
419 
420 static void
keyboardctl(Control * c,CParse * cp)421 keyboardctl(Control *c, CParse *cp)
422 {
423 	int cmd;
424 	Rectangle r;
425 	Keyboard *k;
426 
427 	k = (Keyboard*)c;
428 	cmd = _ctllookup(cp->args[0], cmds, nelem(cmds));
429 	switch(cmd){
430 	default:
431 		ctlerror("%q: unrecognized message '%s'", k->name, cp->str);
432 		break;
433 	case EBorder:
434 		_ctlargcount(k, cp, 2);
435 		if(cp->iargs[1] < 0)
436 			ctlerror("%q: bad border: %c", k->name, cp->str);
437 		k->border = cp->iargs[1];
438 		break;
439 	case EBordercolor:
440 		_ctlargcount(k, cp, 2);
441 		_setctlimage(k, &k->bordercolor, cp->args[1]);
442 		break;
443 	case EFocus:
444 		/* ignore focus change */
445 		break;
446 	case EFont:
447 		if(cp->nargs!=2 && cp->nargs!=3)
448 			ctlerror("%q: bad font message '%s'", k->name, cp->str);
449 		_setctlfont(k, &k->font, cp->args[1]);
450 		if(cp->nargs == 3)
451 			_setctlfont(k, &k->ctlfont, cp->args[2]);
452 		else
453 			_setctlfont(k, &k->ctlfont, cp->args[1]);
454 		break;
455 	case EFormat:
456 		_ctlargcount(k, cp, 2);
457 		k->format = ctlstrdup(cp->args[1]);
458 		break;
459 	case EHide:
460 		_ctlargcount(k, cp, 1);
461 		k->hidden = 1;
462 		break;
463 	case EImage:
464 		_ctlargcount(k, cp, 2);
465 		_setctlimage(k, &k->image, cp->args[1]);
466 		break;
467 	case ELight:
468 		_ctlargcount(k, cp, 2);
469 		_setctlimage(k, &k->light, cp->args[1]);
470 		break;
471 	case EMask:
472 		_ctlargcount(k, cp, 2);
473 		_setctlimage(k, &k->mask, cp->args[1]);
474 		break;
475 	case ERect:
476 		_ctlargcount(k, cp, 5);
477 		r.min.x = cp->iargs[1];
478 		r.min.y = cp->iargs[2];
479 		r.max.x = cp->iargs[3];
480 		r.max.y = cp->iargs[4];
481 		if(Dx(r)<0 || Dy(r)<0)
482 			ctlerror("%q: bad rectangle: %s", k->name, cp->str);
483 		k->rect = r;
484 		keyboardshow(k);
485 		break;
486 	case EReveal:
487 		_ctlargcount(k, cp, 1);
488 		k->hidden = 0;
489 		keyboardshow(k);
490 		break;
491 	case EShow:
492 		_ctlargcount(k, cp, 1);
493 		keyboardshow(k);
494 		break;
495 	case ESize:
496 		if (cp->nargs == 3)
497 			r.max = Pt(0x7fffffff, 0x7fffffff);
498 		else{
499 			_ctlargcount(k, cp, 5);
500 			r.max.x = cp->iargs[3];
501 			r.max.y = cp->iargs[4];
502 		}
503 		r.min.x = cp->iargs[1];
504 		r.min.y = cp->iargs[2];
505 		if(r.min.x<=0 || r.min.y<=0 || r.max.x<=0 || r.max.y<=0 || r.max.x < r.min.x || r.max.y < r.min.y)
506 			ctlerror("%q: bad sizes: %s", k->name, cp->str);
507 		k->size.min = r.min;
508 		k->size.max = r.max;
509 		break;
510 	}
511 }
512 
513 Control*
createkeyboard(Controlset * cs,char * name)514 createkeyboard(Controlset *cs, char *name)
515 {
516 	Keyboard *k;
517 
518 	k = (Keyboard *)_createctl(cs, "keyboard", sizeof(Keyboard), name);
519 	k->image = _getctlimage("white");
520 	k->mask = _getctlimage("opaque");
521 	k->light = _getctlimage("yellow");
522 	k->bordercolor = _getctlimage("black");
523 	k->textcolor = _getctlimage("black");
524 	k->font = _getctlfont("font");
525 	k->ctlfont = _getctlfont("font");
526 	k->format = ctlstrdup("%q: value 0x%x");
527 	k->border = 0;
528 	k->lastbut = 0;
529 	k->key = nil;
530 	k->state = SRegular;
531 	k->ctl = keyboardctl;
532 	k->mouse = keyboardmouse;
533 	k->exit = keyboardfree;
534 	k->size = Rect(246, 2 + 5 * (k->font->font->height + 1), 512, 256);
535 	return k;
536 }
537