ref: d010b87b1814a3bf4df3f13d0c4131fbd885b270
dir: /sys/src/libcontrol/keyboard.c/
#include <u.h> #include <libc.h> #include <draw.h> #include <thread.h> #include <mouse.h> #include <keyboard.h> #include <control.h> typedef struct Keyboard Keyboard; enum{ SRegular = 0, SShift = 1, SCaps = 2, SMask = 3, Nstate = 4, SControl = 4, }; struct Keyboard { Control; CImage *image; CImage *mask; CImage *light; CImage *textcolor; CImage *bordercolor; CFont *font; CFont *ctlfont; Image *im[Nstate]; int border; int lastbut; int state; char *key; }; enum{ EBorder, EBordercolor, EFocus, EFont, EFormat, EHide, EImage, ELight, EMask, ERect, EReveal, EShow, ESize, }; static char *cmds[] = { [EBorder] = "border", [EBordercolor] = "bordercolor", [EFocus] = "focus", [EFont] = "font", [EFormat] = "format", [EHide] = "hide", [EImage] = "image", [ELight] = "light", [EMask] = "mask", [ERect] = "rect", [EReveal] = "reveal", [EShow] = "show", [ESize] = "size", nil }; enum { Nrow = 5 }; static uchar wid [Nrow][16] = { {16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 30, }, {24, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 24, }, {32, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, }, {40, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, }, {30, 30, 80, 40, 42, 24, }, }; static char *keyregular[Nrow] = { "`\0001\0002\0003\0004\0005\0006\0007\0008\0009\0000\0-\0=\0\\\0<-\0\0", "->\0q\0w\0e\0r\0t\0y\0u\0i\0o\0p\0[\0]\0Del\0\0", "Caps\0a\0s\0d\0f\0g\0h\0j\0k\0l\0;\0'\0Enter\0\0", "Shift\0z\0x\0c\0v\0b\0n\0m\0,\0.\0/\0Shift\0\0", "Ctrl\0Alt\0 \0Scrib\0Menu\0Esc\0\0" }; static char *keyshift[Nrow] = { "~\0!\0@\0#\0$\0%\0^\0&\0*\0(\0)\0_\0+\0|\0<-\0\0", "->\0Q\0W\0E\0R\0T\0Y\0U\0I\0O\0P\0{\0}\0Del\0\0", "Caps\0A\0S\0D\0F\0G\0H\0J\0K\0L\0:\0\"\0Enter\0\0", "Shift\0Z\0X\0C\0V\0B\0N\0M\0<\0>\0?\0Shift\0\0", "Ctrl\0Alt\0 \0Scrib\0Menu\0Esc\0\0" }; static char *keycaps[Nrow] = { "`\0001\0002\0003\0004\0005\0006\0007\0008\0009\0000\0-\0=\0\\\0<-\0\0", "->\0Q\0W\0E\0R\0T\0Y\0U\0I\0O\0P\0[\0]\0Del\0\0", "Caps\0A\0S\0D\0F\0G\0H\0J\0K\0L\0;\0'\0Enter\0\0", "Shift\0Z\0X\0C\0V\0B\0N\0M\0,\0.\0/\0Shift\0\0", "Ctrl\0Alt\0 \0Scrib\0Menu\0Esc\0\0" }; static char *keycapsshift[Nrow] = { "~\0!\0@\0#\0$\0%\0^\0&\0*\0(\0)\0_\0+\0|\0<-\0\0", "->\0q\0w\0e\0r\0t\0y\0u\0i\0o\0p\0{\0}\0Del\0\0", "Caps\0a\0s\0d\0f\0g\0h\0j\0k\0l\0:\0\"\0Enter\0\0", "Shift\0z\0x\0c\0v\0b\0n\0m\0<\0>\0?\0Shift\0\0", "Ctrl\0Alt\0 \0Scrib\0Menu\0Esc\0\0" }; struct{ char *name; int val; }keytab[] = { "Shift", 0, "Ctrl", 0, "Alt", 0, "Caps", 0, "Del", '\177', "Enter", '\n', "Esc", '\033', "<-", '\b', "->", '\t', "Scrib", 0x10000, "Menu", 0x10001, nil, 0, }; static char **keyset[Nstate] = { keyregular, keyshift, keycaps, keycapsshift, }; static void keyboardshow(Keyboard*); static void keyup(Keyboard*, Point); static void keydown(Keyboard*, Point); static void keyresize(Keyboard*); static void keyboardmouse(Control *c, Mouse *m) { Keyboard *k; k = (Keyboard *)c; if(m->buttons==1) keydown(k, m->xy); else if(k->lastbut==1 && m->buttons==0) keyup(k, m->xy); k->lastbut = m->buttons; } static void keyboardfree(Control *c) { int i; Keyboard *k; k = (Keyboard *)c; _putctlimage(k->image); _putctlimage(k->mask); _putctlimage(k->light); _putctlimage(k->textcolor); _putctlimage(k->bordercolor); _putctlfont(k->font); _putctlfont(k->ctlfont); for(i=0; i<nelem(k->im); i++) freeimage(k->im[i]); free(k->format); } static int keyboardy(Keyboard *k, int row) { int dy; if(row >= Nrow) return k->rect.max.y-k->border; dy = Dy(k->rect)-2*k->border; return k->rect.min.y+k->border+(row*dy+Nrow-1)/Nrow; } static char* whichkey(Keyboard *k, Point p, int *rowp, int *colp, Rectangle *rp) { uchar *wp; char *kp; int row, col, dx, dy, x, n, maxx; Rectangle r; r = insetrect(k->rect, k->border); if(!ptinrect(p, r)) return nil; maxx = r.max.x; dx = Dx(r); dy = Dy(r); row = (p.y - r.min.y)*Nrow/dy; if(row >= Nrow) row = Nrow-1; r.min.y = keyboardy(k, row); r.max.y = keyboardy(k, row+1); x = r.min.x; kp = keyset[k->state&SMask][row]; wp = wid[row]; for(col=0; *kp; col++,kp+=n+1){ n = strlen(kp); r.min.x = x; r.max.x = x + (wp[col]*dx+255)/256; if(kp[n+1] == '\0') r.max.x = maxx; if(r.max.x > p.x) break; x = r.max.x; } *rp = insetrect(r, 1); *rowp = row; *colp = col; return kp; } static Rectangle keyrect(Keyboard *k, int row, int col) { uchar *wp; char *kp; int i, x, n, dx; Rectangle r; Point p; r = insetrect(k->rect, k->border); p = r.min; dx = Dx(r); r.min.y = keyboardy(k, row); r.max.y = keyboardy(k, row+1); x = r.min.x; kp = keyset[0][row]; wp = wid[row]; for(i=0; *kp; i++,kp+=n+1){ n = strlen(kp); r.min.x = x; r.max.x = x + (wp[i]*dx+255)/256; if(kp[n+1] == '\0') r.max.x = p.x+dx; if(i >= col) break; x = r.max.x; } return insetrect(r, 1); } static void keydraw(Keyboard *k, int state) { Point p, q; int row, col, x, dx, dy, nexty, n; uchar *wp; char *kp; Rectangle r; Font *f, *f1, *f2; Image *im; freeimage(k->im[state]); k->im[state] = nil; if(Dx(k->rect)-2*k->border <= 0) return; im = allocimage(display, k->rect, screen->chan, 0, ~0); if(im == nil) return; k->im[state] = im; r = insetrect(k->rect, k->border); border(im, k->rect, k->border, k->bordercolor->image, ZP); draw(im, r, k->image->image, nil, ZP); dx = Dx(r); dy = Dy(r); p = r.min; f1 = k->font->font; f2 = k->ctlfont->font; nexty = p.y; for(row=0; row<Nrow; row++){ x = p.x; kp = keyset[state][row]; wp = wid[row]; r.min.y = nexty; nexty = keyboardy(k, row+1); r.max.y = nexty; for(col=0; *kp; col++,kp+=n+1){ r.min.x = x; r.max.x = x + (wp[col]*dx+255)/256; n = strlen(kp); if(kp[n+1] == '\0') r.max.x = p.x+dx; if(row == Nrow-1) r.max.y = p.y+dy; if(n > 1) f = f2; else f = f1; q = _ctlalignpoint(r, stringnwidth(f, kp, n), f->height, Acenter); _string(im, q, k->textcolor->image, ZP, f, kp, nil, n, r, nil, ZP, SoverD); x = r.max.x; if(kp[n+1]) draw(im, Rect(x, r.min.y, x+1, r.max.y), k->textcolor->image, nil, ZP); } if(row != Nrow-1) draw(im, Rect(p.x, r.max.y, p.x+dx, r.max.y+1), k->textcolor->image, nil, ZP); } } static void keyresize(Keyboard *k) { int i; for(i=0; i<Nstate; i++) keydraw(k, i); } static void keyboardshow(Keyboard *k) { Rectangle r; if (k->hidden) return; if(k->im[0]==nil || !eqrect(k->im[0]->r, k->rect)) keyresize(k); if(k->im[k->state&SMask] == nil) return; draw(k->screen, k->rect, k->im[k->state&SMask], nil, k->rect.min); if(k->state & SShift){ r = keyrect(k, 3, 0); draw(k->screen, r, k->light->image, k->mask->image, ZP); r = keyrect(k, 3, 11); draw(k->screen, r, k->light->image, k->mask->image, ZP); } if(k->state & SCaps){ r = keyrect(k, 2, 0); draw(k->screen, r, k->light->image, k->mask->image, ZP); } if(k->state & SControl){ r = keyrect(k, 4, 0); draw(k->screen, r, k->light->image, k->mask->image, ZP); } flushimage(display, 1); } static void keydown(Keyboard *k, Point p) { int row, col; Rectangle r; char *s; s = whichkey(k, p, &row, &col, &r); if(s == k->key) return; keyboardshow(k); if(s != nil) draw(k->screen, r, k->light->image, k->mask->image, ZP); flushimage(display, 1); k->key = s; } static int keylookup(char *s) { int i; for(i=0; keytab[i].name; i++) if(strcmp(s, keytab[i].name) == 0) return keytab[i].val; return s[0]; } static void keyup(Keyboard *k, Point p) { int row, col; Rectangle r; char *s; int val; s = whichkey(k, p, &row, &col, &r); if(s == nil) return; val = keylookup(s); if(k->state & SControl) if(' '<val && val<0177) val &= ~0x60; if(strcmp(s, "Alt") == 0) {;} if(strcmp(s, "Ctrl") == 0){ k->state ^= SControl; }else k->state &= ~SControl; if(strcmp(s, "Shift")==0 || strcmp(s, "Caps")==0){ if(strcmp(s, "Shift") == 0) k->state ^= SShift; if(strcmp(s, "Caps") == 0) k->state ^= SCaps; }else k->state &= ~SShift; keyboardshow(k); if(val) chanprint(k->event, k->format, k->name, val); k->key = nil; } static void keyboardctl(Control *c, CParse *cp) { int cmd; Rectangle r; Keyboard *k; k = (Keyboard*)c; cmd = _ctllookup(cp->args[0], cmds, nelem(cmds)); switch(cmd){ default: ctlerror("%q: unrecognized message '%s'", k->name, cp->str); break; case EBorder: _ctlargcount(k, cp, 2); if(cp->iargs[1] < 0) ctlerror("%q: bad border: %c", k->name, cp->str); k->border = cp->iargs[1]; break; case EBordercolor: _ctlargcount(k, cp, 2); _setctlimage(k, &k->bordercolor, cp->args[1]); break; case EFocus: /* ignore focus change */ break; case EFont: if(cp->nargs!=2 && cp->nargs!=3) ctlerror("%q: bad font message '%s'", k->name, cp->str); _setctlfont(k, &k->font, cp->args[1]); if(cp->nargs == 3) _setctlfont(k, &k->ctlfont, cp->args[2]); else _setctlfont(k, &k->ctlfont, cp->args[1]); break; case EFormat: _ctlargcount(k, cp, 2); k->format = ctlstrdup(cp->args[1]); break; case EHide: _ctlargcount(k, cp, 1); k->hidden = 1; break; case EImage: _ctlargcount(k, cp, 2); _setctlimage(k, &k->image, cp->args[1]); break; case ELight: _ctlargcount(k, cp, 2); _setctlimage(k, &k->light, cp->args[1]); break; case EMask: _ctlargcount(k, cp, 2); _setctlimage(k, &k->mask, cp->args[1]); break; case ERect: _ctlargcount(k, cp, 5); r.min.x = cp->iargs[1]; r.min.y = cp->iargs[2]; r.max.x = cp->iargs[3]; r.max.y = cp->iargs[4]; if(Dx(r)<0 || Dy(r)<0) ctlerror("%q: bad rectangle: %s", k->name, cp->str); k->rect = r; keyboardshow(k); break; case EReveal: _ctlargcount(k, cp, 1); k->hidden = 0; keyboardshow(k); break; case EShow: _ctlargcount(k, cp, 1); keyboardshow(k); break; case ESize: if (cp->nargs == 3) r.max = Pt(0x7fffffff, 0x7fffffff); else{ _ctlargcount(k, cp, 5); r.max.x = cp->iargs[3]; r.max.y = cp->iargs[4]; } r.min.x = cp->iargs[1]; r.min.y = cp->iargs[2]; 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) ctlerror("%q: bad sizes: %s", k->name, cp->str); k->size.min = r.min; k->size.max = r.max; break; } } Control* createkeyboard(Controlset *cs, char *name) { Keyboard *k; k = (Keyboard *)_createctl(cs, "keyboard", sizeof(Keyboard), name); k->image = _getctlimage("white"); k->mask = _getctlimage("opaque"); k->light = _getctlimage("yellow"); k->bordercolor = _getctlimage("black"); k->textcolor = _getctlimage("black"); k->font = _getctlfont("font"); k->ctlfont = _getctlfont("font"); k->format = ctlstrdup("%q: value 0x%x"); k->border = 0; k->lastbut = 0; k->key = nil; k->state = SRegular; k->ctl = keyboardctl; k->mouse = keyboardmouse; k->exit = keyboardfree; k->size = Rect(246, 2 + 5 * (k->font->font->height + 1), 512, 256); return k; }