ref: bf645afaac246967b9590ae7463f243c11d97480
dir: /sys/src/cmd/aux/kbdfs/kbdfs.c/
#include <u.h> #include <libc.h> #include <auth.h> #include <fcall.h> #include <thread.h> #include <keyboard.h> #include <9p.h> enum { Nscan= 128, Qroot= 0, Qkbd, Qkbdin, Qkbin, Qkbmap, Qcons, Qconsctl, Nqid, Lnone = 0, Lshift, Lesc1, Laltgr, Lctl, Lctrlesc1, Lshiftesc1, Lshiftaltgr, Lmod4, Laltgrmod4, Nlayers, Rawon= 0, Rawoff, STACK = 8*1024, }; typedef struct Key Key; typedef struct Scan Scan; struct Key { int down; Rune b; /* button, unshifted key */ Rune r; /* rune, shifted key */ }; struct Scan { int esc1; int esc2; int caps; int num; int shift; int ctl; int alt; int altgr; int mod4; int leds; }; struct Qtab { char *name; int mode; int type; } qtab[Nqid] = { "/", DMDIR|0500, QTDIR, "kbd", 0600, 0, "kbdin", 0200, 0, "kbin", 0200, 0, "kbmap", 0600, 0, "cons", 0600, 0, "consctl", 0600, 0, }; char Eshort[] = "read count too small"; char Ebadarg[] = "invalid argument"; char Eperm[] = "permission denied"; char Einuse[] = "file in use"; char Enonexist[] = "file does not exist"; char Ebadspec[] = "bad attach specifier"; char Ewalk[] = "walk in non directory"; char Ephase[] = "the front fell off"; char Eintr[] = "interrupted"; int kbdifd = -1; int scanfd = -1; int ledsfd = -1; int consfd = -1; int mctlfd = -1; int msinfd = -1; int notefd = -1; int killfd = -1; int kbdopen; int consctlopen; int quiet = 0; char *sname = nil; char *mntpt = "/dev"; char *user; int debug; Channel *keychan; /* chan(Key) */ Channel *mctlchan; /* chan(Key) */ Channel *kbdreqchan; /* chan(Req*) */ Channel *consreqchan; /* chan(Req*) */ Channel *ctlchan; /* chan(int) */ Channel *rawchan; /* chan(Rune) */ Channel *runechan; /* chan(Rune) */ Channel *conschan; /* chan(char*) */ Channel *kbdchan; /* chan(char*) */ Channel *intchan; /* chan(int) */ char *layertab[Nlayers] = { [Lnone] "none", [Lshift] "shift", [Lesc1] "esc", [Laltgr] "altgr", [Lctl] "ctl", [Lctrlesc1] "ctlesc", [Lshiftesc1] "shiftesc", [Lshiftaltgr] "shiftaltgr", [Lmod4] "mod4", [Laltgrmod4] "altgrmod4", }; /* /sys/lib/kbmap/ascii defaults */ Rune ascii[Nlayers][Nscan] = { /* * The codes at 0x79 and 0x7b are for the (無)変換 "(Mu)henkan" keys * used by OADG 109(A) keyboards. The PFU Happy Hacking keyboard * has only one layout and will produce these for otherwise unmarked * keys. The default mappings for the HHKB on other systems map * these to Kdown and Kup. The jp kbmap will instead map these * (along with 0x70) to control characters that ktrans understands. */ [Lnone] { [0x00] 0, Kesc, '1', '2', '3', '4', '5', '6', [0x08] '7', '8', '9', '0', '-', '=', '\b', '\t', [0x10] 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', [0x18] 'o', 'p', '[', ']', '\n', Kctl, 'a', 's', [0x20] 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', [0x28] '\'', '`', Kshift, '\\', 'z', 'x', 'c', 'v', [0x30] 'b', 'n', 'm', ',', '.', '/', Kshift, '*', [0x38] Kalt, ' ', Kctl, KF|1, KF|2, KF|3, KF|4, KF|5, [0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Knum, Kscroll,'7', [0x48] '8', '9', '-', '4', '5', '6', '+', '1', [0x50] '2', '3', '0', '.', 0, 0, 0, KF|11, [0x58] KF|12, 0, 0, 0, 0, 0, 0, 0, [0x60] 0, 0, 0, 0, 0, 0, 0, 0, [0x68] 0, 0, 0, 0, 0, 0, 0, 0, [0x70] 0, 0, 0, 0, 0, 0, 0, 0, [0x78] 0, Kdown, 0, Kup, 0, 0, 0, 0, }, [Lshift] { [0x00] 0, Kesc, '!', '@', '#', '$', '%', '^', [0x08] '&', '*', '(', ')', '_', '+', '\b', '\t', [0x10] 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', [0x18] 'O', 'P', '{', '}', '\n', Kctl, 'A', 'S', [0x20] 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', [0x28] '"', '~', Kshift, '|', 'Z', 'X', 'C', 'V', [0x30] 'B', 'N', 'M', '<', '>', '?', Kshift, '*', [0x38] Kalt, ' ', Kctl, KF|1, KF|2, KF|3, KF|4, KF|5, [0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Knum, Kscroll,'7', [0x48] '8', '9', '-', '4', '5', '6', '+', '1', [0x50] '2', '3', '0', '.', 0, 0, 0, KF|11, [0x58] KF|12, 0, 0, 0, 0, 0, 0, 0, [0x60] 0, 0, 0, 0, 0, 0, 0, 0, [0x68] 0, 0, 0, 0, 0, 0, 0, 0, [0x70] 0, 0, 0, 0, 0, 0, 0, 0, [0x78] 0, Kdown, 0, Kup, 0, 0, 0, 0, }, [Lesc1] { [0x00] 0, 0, 0, 0, 0, 0, 0, 0, [0x08] 0, 0, 0, 0, 0, 0, 0, 0, [0x10] Ksbwd, Kbrtdn, 0, 0, 0, 0, 0, 0, [0x18] 0, Ksfwd, Kbrtup, 0, '\n', Kctl, 0, 0, [0x20] Kmute, 0, Kpause, 0, 0, 0, 0, 0, [0x28] 0, 0, 0, 0, 0, 0, Kvoldn, 0, [0x30] Kvolup, 0, 0, 0, 0, '/', 0, Kprint, [0x38] Kaltgr, 0, 0, 0, 0, 0, 0, 0, [0x40] 0, 0, 0, 0, 0, 0, Kbreak, Khome, [0x48] Kup, Kpgup, 0, Kleft, 0, Kright, 0, Kend, [0x50] Kdown, Kpgdown,Kins, Kdel, 0, 0, 0, 0, [0x58] 0, 0, 0, Kmod4, 0, 0, 0, 0, [0x60] 0, 0, 0, 0, 0, 0, 0, 0, [0x68] 0, 0, 0, 0, 0, 0, 0, 0, [0x70] 0, 0, 0, 0, 0, 0, 0, 0, [0x78] 0, Kup, 0, 0, 0, 0, 0, 0, }, [Lshiftesc1] { [0x00] 0, 0, 0, 0, 0, 0, 0, 0, [0x08] 0, 0, 0, 0, 0, 0, 0, 0, [0x10] 0, 0, 0, 0, 0, 0, 0, 0, [0x18] 0, 0, 0, 0, 0, 0, 0, 0, [0x20] 0, 0, 0, 0, 0, 0, 0, 0, [0x28] 0, 0, 0, 0, 0, 0, 0, 0, [0x30] 0, 0, 0, 0, 0, 0, 0, 0, [0x38] 0, 0, 0, 0, 0, 0, 0, 0, [0x40] 0, 0, 0, 0, 0, 0, 0, 0, [0x48] Kup, 0, 0, 0, 0, 0, 0, 0, [0x50] 0, 0, 0, 0, 0, 0, 0, 0, [0x58] 0, 0, 0, 0, 0, 0, 0, 0, [0x60] 0, 0, 0, 0, 0, 0, 0, 0, [0x68] 0, 0, 0, 0, 0, 0, 0, 0, [0x70] 0, 0, 0, 0, 0, 0, 0, 0, [0x78] 0, Kup, 0, 0, 0, 0, 0, 0, }, [Lctrlesc1] { [0x00] 0, 0, 0, 0, 0, 0, 0, 0, [0x08] 0, 0, 0, 0, 0, 0, 0, 0, [0x10] 0, 0, 0, 0, 0, 0, 0, 0, [0x18] 0, 0, 0, 0, 0, 0, 0, 0, [0x20] 0, 0, 0, 0, 0, 0, 0, 0, [0x28] 0, 0, 0, 0, 0, 0, 0, 0, [0x30] 0, 0, 0, 0, 0, 0, 0, 0, [0x38] 0, 0, 0, 0, 0, 0, 0, 0, [0x40] 0, 0, 0, 0, 0, 0, 0, 0, [0x48] Kup, 0, 0, 0, 0, 0, 0, 0, [0x50] 0, 0, 0, 0, 0, 0, 0, 0, [0x58] 0, 0, 0, 0, 0, 0, 0, 0, [0x60] 0, 0, 0, 0, 0, 0, 0, 0, [0x68] 0, 0, 0, 0, 0, 0, 0, 0, [0x70] 0, 0, 0, 0, 0, 0, 0, 0, [0x78] 0, Kup, 0, 0, 0, 0, 0, 0, }, [Laltgr] { [0x00] 0, 0, 0, 0, 0, 0, 0, 0, [0x08] 0, 0, 0, 0, 0, 0, 0, 0, [0x10] 0, 0, 0, 0, 0, 0, 0, 0, [0x18] 0, 0, 0, 0, '\n', Kctl, 0, 0, [0x20] 0, 0, 0, 0, 0, 0, 0, 0, [0x28] 0, 0, Kshift, 0, 0, 0, 0, 0, [0x30] 0, 0, 0, 0, 0, '/', 0, Kprint, [0x38] Kaltgr, 0, 0, 0, 0, 0, 0, 0, [0x40] 0, 0, 0, 0, 0, 0, Kbreak, Khome, [0x48] Kup, Kpgup, 0, Kleft, 0, Kright, 0, Kend, [0x50] Kdown, Kpgdown,Kins, Kdel, 0, 0, 0, 0, [0x58] 0, 0, 0, 0, 0, 0, 0, 0, [0x60] 0, 0, 0, 0, 0, 0, 0, 0, [0x68] 0, 0, 0, 0, 0, 0, 0, 0, [0x70] 0, 0, 0, 0, 0, 0, 0, 0, [0x78] 0, Kup, 0, 0, 0, 0, 0, 0, }, [Lctl] { [0x00] 0, '', '', '', '', '', '', '', [0x08] '', '', '', '', ' ', '', '\b', '\t', [0x10] '', '', '', '', '', '', '', '\t', [0x18] '', '', '', '', '\n', Kctl, '', '', [0x20] '', '', '', '\b', '\n', '', '', '', [0x28] '', 0, Kshift, '', '', '', '', '', [0x30] '', '', ' ', '', '', '', Kshift, '\n', [0x38] Kalt, 0, Kctl, '', '', '', '', '', [0x40] '', '', '', ' ', '', '', '', '', [0x48] '', '', ' ', '', '', '', '', '', [0x50] '', '', '', '', 0, 0, 0, '', [0x58] '', 0, 0, 0, 0, 0, 0, 0, [0x60] 0, 0, 0, 0, 0, 0, 0, 0, [0x68] 0, 0, 0, 0, 0, 0, 0, 0, [0x70] 0, 0, 0, 0, 0, 0, 0, 0, [0x78] 0, '', 0, '\b', 0, 0, 0, 0, }, [Lshiftaltgr] { [0x00] 0, 0, 0, 0, 0, 0, 0, 0, [0x08] 0, 0, 0, 0, 0, 0, 0, 0, [0x10] 0, 0, 0, 0, 0, 0, 0, 0, [0x18] 0, 0, 0, 0, 0, 0, 0, 0, [0x20] 0, 0, 0, 0, 0, 0, 0, 0, [0x28] 0, 0, 0, 0, 0, 0, 0, 0, [0x30] 0, 0, 0, 0, 0, 0, 0, 0, [0x38] 0, 0, 0, 0, 0, 0, 0, 0, [0x40] 0, 0, 0, 0, 0, 0, 0, 0, [0x48] 0, 0, 0, 0, 0, 0, 0, 0, [0x50] 0, 0, 0, 0, 0, 0, 0, 0, [0x58] 0, 0, 0, 0, 0, 0, 0, 0, [0x60] 0, 0, 0, 0, 0, 0, 0, 0, [0x68] 0, 0, 0, 0, 0, 0, 0, 0, [0x70] 0, 0, 0, 0, 0, 0, 0, 0, [0x78] 0, 0, 0, 0, 0, 0, 0, 0, }, [Lmod4] { [0x00] 0, 0, 0, 0, 0, 0, 0, 0, [0x08] 0, 0, 0, 0, 0, 0, 0, 0, [0x10] 0, 0, 0, 0, 0, 0, 0, 0, [0x18] 0, 0, 0, 0, 0, 0, 0, 0, [0x20] 0, 0, 0, 0, 0, 0, 0, 0, [0x28] 0, 0, 0, 0, 0, 0, 0, 0, [0x30] 0, 0, 0, 0, 0, 0, 0, 0, [0x38] 0, 0, 0, 0, 0, 0, 0, 0, [0x40] 0, 0, 0, 0, 0, 0, 0, 0, [0x48] 0, 0, 0, 0, 0, 0, 0, 0, [0x50] 0, 0, 0, 0, 0, 0, 0, 0, [0x58] 0, 0, 0, 0, 0, 0, 0, 0, [0x60] 0, 0, 0, 0, 0, 0, 0, 0, [0x68] 0, 0, 0, 0, 0, 0, 0, 0, [0x70] 0, 0, 0, 0, 0, 0, 0, 0, [0x78] 0, 0, 0, 0, 0, 0, 0, 0, }, [Laltgrmod4] { [0x00] 0, 0, 0, 0, 0, 0, 0, 0, [0x08] 0, 0, 0, 0, 0, 0, 0, 0, [0x10] 0, 0, 0, 0, 0, 0, 0, 0, [0x18] 0, 0, 0, 0, 0, 0, 0, 0, [0x20] 0, 0, 0, 0, 0, 0, 0, 0, [0x28] 0, 0, 0, 0, 0, 0, 0, 0, [0x30] 0, 0, 0, 0, 0, 0, 0, 0, [0x38] 0, 0, 0, 0, 0, 0, 0, 0, [0x40] 0, 0, 0, 0, 0, 0, 0, 0, [0x48] 0, 0, 0, 0, 0, 0, 0, 0, [0x50] 0, 0, 0, 0, 0, 0, 0, 0, [0x58] 0, 0, 0, 0, 0, 0, 0, 0, [0x60] 0, 0, 0, 0, 0, 0, 0, 0, [0x68] 0, 0, 0, 0, 0, 0, 0, 0, [0x70] 0, 0, 0, 0, 0, 0, 0, 0, [0x78] 0, 0, 0, 0, 0, 0, 0, 0, }, }; Rune kbtabs[Nlayers][Nscan]; char* dev(char *file) { static char *buf = nil; free(buf); buf = smprint("%s/%s", mntpt, file); return buf; } int eopen(char *name, int mode) { int fd; fd = open(name, mode); if(fd < 0 && !quiet) fprint(2, "%s: warning: can't open %s: %r\n", argv0, name); return fd; } void reboot(void) { int fd; if(debug) return; if((fd = eopen(dev("reboot"), OWRITE)) < 0) return; fprint(fd, "reboot\n"); close(fd); } void shutdown(void) { if(notefd >= 0) write(notefd, "hangup", 6); if(killfd >= 0) write(killfd, "hangup", 6); threadexitsall(nil); } void shiftup(void) { Key key = { .down = 0, .r = Kshift, .b = Kshift }; send(keychan, &key); } /* * Scan code processing */ void kbdputsc(Scan *scan, int c) { Key key; /* * e0's is the first of a 2 character sequence, e1 and e2 the first * of a 3 character sequence (on the safari) */ if(scan->esc2){ scan->esc2--; return; } else if(c == 0xe1 || c == 0xe2){ scan->esc2 = 2; return; } else if(c == 0xe0){ scan->esc1 = 1; return; } key.down = (c & 0x80) == 0; c &= 0x7f; if(c >= Nscan) return; /* qemu workarround: emulate e0 for numpad */ if(c != 0 && strchr("GHIKMOPQRS", c) != nil) scan->esc1 |= !scan->num; if(scan->esc1 && scan->ctl && kbtabs[Lctrlesc1][c] != 0) key.r = kbtabs[Lctrlesc1][c]; else if(scan->esc1 && scan->shift && kbtabs[Lshiftesc1][c] != 0) key.r = kbtabs[Lshiftesc1][c]; else if(scan->esc1) key.r = kbtabs[Lesc1][c]; else if(scan->altgr && scan->mod4 && kbtabs[Laltgrmod4][c] != 0) key.r = kbtabs[Laltgrmod4][c]; else if(scan->mod4 && kbtabs[Lmod4][c] != 0) key.r = kbtabs[Lmod4][c]; else if(scan->shift && scan->altgr && kbtabs[Lshiftaltgr][c] != 0) key.r = kbtabs[Lshiftaltgr][c]; else if(scan->shift) key.r = kbtabs[Lshift][c]; else if(scan->altgr) key.r = kbtabs[Laltgr][c]; else if(scan->ctl) key.r = kbtabs[Lctl][c]; else key.r = kbtabs[Lnone][c]; if(scan->esc1 || kbtabs[Lnone][c] == 0) key.b = key.r; else key.b = kbtabs[Lnone][c]; if(scan->caps && key.r<='z' && key.r>='a') key.r += 'A' - 'a'; if(scan->ctl && scan->alt && key.r == Kdel){ if(scan->shift) shiftup(); else reboot(); } if(key.b) send(keychan, &key); switch(key.r){ case Kshift: scan->shift = key.down; break; case Kctl: scan->ctl = key.down; break; case Kaltgr: scan->altgr = key.down; break; case Kmod4: scan->mod4 = key.down; break; case Kalt: scan->alt = key.down; break; case Knum: scan->num ^= key.down; break; case Kcaps: scan->caps ^= key.down; break; } scan->esc1 = 0; } static void kbdin(Scan *a, char *p, int n) { char *s; Key k; int i; if(n > 0 && p[n-1] != 0){ /* * old format as used by bitsy keyboard: * just a string of characters, no keyup * information. */ s = emalloc9p(n+1); memmove(s, p, n); s[n] = 0; p = s; while(*p){ p += chartorune(&k.r, p); if(k.r) send(rawchan, &k.r); } free(s); return; } Nextmsg: if(n < 2) return; switch(p[0]){ case 'R': case 'r': /* rune up/down */ chartorune(&k.r, p+1); if(k.r == 0) break; k.b = 0; k.down = (p[0] == 'r'); for(i=0; i<Nscan; i++){ if(kbtabs[Lnone][i] == k.r || kbtabs[Lshift][i] == k.r || (i >= 16 && kbtabs[Lctl][i] == k.r)){ /* assign button from kbtab */ k.b = kbtabs[Lnone][i]; /* handle ^X forms */ if(k.r == kbtabs[Lnone][i] && kbtabs[Lctl][i] && !a->shift && !a->altgr && a->ctl) k.r = kbtabs[Lctl][i]; break; } } /* button unknown to kbtab, use rune if no modifier keys are active */ if(k.b == 0 && !a->shift && !a->altgr && !a->ctl) k.b = k.r; if(k.r == Kshift) a->shift = k.down; else if(k.r == Kaltgr) a->altgr = k.down; else if(k.r == Kmod4) a->mod4 = k.down; else if(k.r == Kctl) a->ctl = k.down; send(keychan, &k); break; case 'c': chartorune(&k.r, p+1); nbsend(runechan, &k.r); break; default: if(!kbdopen) break; i = strlen(p)+1; s = emalloc9p(i); memmove(s, p, i); if(nbsendp(kbdchan, s) <= 0) free(s); } i = strlen(p)+1; n -= i, p += i; goto Nextmsg; } void setleds(Scan *scan, int leds) { char buf[8]; if(ledsfd < 0 || scan->leds == leds) return; leds &= 7; snprint(buf, sizeof(buf), "%d", leds); pwrite(ledsfd, buf, strlen(buf), 0); scan->leds = leds; } /* * Read scan codes from scanfd */ void scanproc(void *) { uchar buf[64]; Scan scan; int i, n; threadsetname("scanproc"); memset(&scan, 0, sizeof scan); while((n = read(scanfd, buf, sizeof buf)) > 0){ for(i=0; i<n; i++) kbdputsc(&scan, buf[i]); setleds(&scan, (scan.num<<1) | (scan.caps<<2)); } shutdown(); } void kbdiproc(void *) { char buf[1024]; Scan a; int n; threadsetname("kbdiproc"); memset(&a, 0, sizeof(a)); while((n = read(kbdifd, buf, sizeof buf)) > 0) kbdin(&a, buf, n); shutdown(); } char* utfconv(Rune *r, int n) { char *s, *p; int l; l = runenlen(r, n) + 1; s = emalloc9p(l); for(p = s; n > 0; r++, n--) p += runetochar(p, r); *p = 0; return s; } /* * Read key events from keychan and produce characters to * rawchan and keystate in kbdchan. */ void keyproc(void *) { Rune rb[Nscan+1]; Key key; int i, nb; char *s; threadsetname("keyproc"); nb = 0; while(recv(keychan, &key) > 0){ if(key.r >= Kmouse+1 && key.r <= Kmouse+5){ if(msinfd >= 0) send(mctlchan, &key); continue; } rb[0] = 0; if(key.b){ for(i=0; i<nb && rb[i+1] != key.b; i++) ; if(!key.down){ while(i < nb && rb[i+1] == key.b){ memmove(rb+i+1, rb+i+2, (nb-i+1) * sizeof(rb[0])); nb--; rb[0] = 'K'; } } else if(i == nb && nb < nelem(rb)-1 && key.b){ rb[++nb] = key.b; rb[0] = 'k'; } } if(rb[0]){ if(kbdopen){ s = utfconv(rb, nb+1); if(nbsendp(kbdchan, s) <= 0) free(s); } if(mctlfd >= 0) send(mctlchan, &key); } if(key.down && key.r) send(rawchan, &key.r); } } /* * Read characters from consfd (serial console) */ void consproc(void *) { char *p, *e, *x, buf[64]; int n, cr; Rune r; threadsetname("consproc"); cr = 0; p = buf; e = buf + sizeof(buf); while((n = read(consfd, p, e - p)) > 0){ x = p + n; p = buf; while((n = x - p) > 0){ if(!fullrune(p, n)){ memmove(buf, p, n); break; } p += chartorune(&r, p); if(r == 021 || r == 023) /* XON/XOFF */ continue; if(r == 0 || r == Runeerror){ cr = 0; continue; } if(r == '\n' && cr){ cr = 0; continue; } if(cr = (r == '\r')) r = '\n'; send(runechan, &r); } if(n < 0) n = 0; p = buf + n; } shutdown(); } static int nextrune(Channel *ch, Rune *r) { while(recv(ch, r) > 0){ switch(*r){ case 0: case Kcaps: case Knum: case Kshift: case Kaltgr: case Kmod4: /* ignore modifiers */ continue; case Kctl: case Kalt: /* composing escapes */ return 1; } return 0; } return -1; } /* * Read runes from rawchan, possibly compose special characters * and output the new runes to runechan */ void runeproc(void *) { static struct { char *ld; /* must be seen before using this conversion */ char *si; /* options for last input characters */ Rune *so; /* the corresponding Rune for each si entry */ } tab[] = { #include "latin1.h" }; Rune r, rr; int i, j; int ctl; threadsetname("runeproc"); ctl = 0; while((i = nextrune(rawchan, &r)) >= 0){ if(i == 0){ ctl = 0; Forward: send(runechan, &r); continue; } if(r == Kctl){ ctl = 1; continue; } /* * emulators like qemu and vmware use Ctrl+Alt to lock * keyboard input so dont confuse them for a compose * sequence. */ if(r != Kalt || ctl) continue; if(nextrune(rawchan, &r)) continue; if(r == 'x' || r == 'X'){ i = (r == 'X') ? 4 : 6; j = i; r = 0; do { if(nextrune(rawchan, &rr)) break; if(rr >= '0' && rr <= '9') r = (r << 4) | (rr - '0'); else if(rr >= 'a' && rr <= 'f') r = (r << 4) | (10 + (rr - 'a')); else if(rr >= 'A' && rr <= 'F') r = (r << 4) | (10 + (rr - 'A')); else{ if(i == j && rr != ';'){ r = j == 6 ? 'x' : 'X'; goto Nothex; } break; } } while(--i > 0); if((i == 0 || rr == ';') && r != 0 && r <= Runemax) goto Forward; } else { if(nextrune(rawchan, &rr)) continue; Nothex: for(i = 0; i<nelem(tab); i++){ if(tab[i].ld[0] != r) continue; if(tab[i].ld[1] == 0) break; if(tab[i].ld[1] == rr){ nextrune(rawchan, &rr); break; } } if(i == nelem(tab) || rr == 0) continue; for(j = 0; tab[i].si[j]; j++){ if(tab[i].si[j] != rr) continue; r = tab[i].so[j]; goto Forward; } } } } /* * Need to do this in a separate proc because if process we're interrupting * is dying and trying to print tombstone, kernel is blocked holding p->debug lock. */ void intrproc(void *) { threadsetname("intrproc"); while(recv(intchan, nil) > 0) write(notefd, "interrupt", 9); } /* * Process Kmouse keys and mouse button swap on shift, * unblank screen by twiching. */ void mctlproc(void *) { Key key; int i, mouseb = 0; threadsetname("mctlproc"); for(;;){ if(nbrecv(mctlchan, &key) <= 0){ if(mctlfd >= 0) fprint(mctlfd, "twitch"); if(recv(mctlchan, &key) <= 0) break; } if(mctlfd >= 0 && key.r == Kshift){ if(key.down){ fprint(mctlfd, "buttonmap 132"); } else { fprint(mctlfd, "swap"); fprint(mctlfd, "swap"); } continue; } if(msinfd >= 0 && key.r >= Kmouse+1 && key.r <= Kmouse+5){ i = 1<<(key.r-(Kmouse+1)); if(key.down) mouseb |= i; else mouseb &= ~i; fprint(msinfd, "m%11d %11d %11d", 0, 0, mouseb); continue; } } } /* * Cook lines for cons */ void lineproc(void *aux) { Rune rb[256], r; Channel *cook; int nr, done; char *s; cook = aux; threadsetname("lineproc"); for(;;){ nr = 0; done = 0; do { recv(cook, &r); switch(r){ case Kdel: if(nbsend(intchan, ¬efd) <= 0) continue; /* no break */ case '\0': /* flush */ nr = 0; continue; case Kbs: /* ^H: erase character */ case Knack: /* ^U: erase line */ case Ketb: /* ^W: erase word */ while(nr > 0){ nr--; fprint(1, "\b"); if(r == Kbs) break; if(r == Ketb && utfrune(" \t", rb[nr])) break; } continue; case Keof: /* ^D: eof */ done = 1; break; case '\n': done = 1; /* no break */ default: rb[nr++] = r; fprint(1, "%C", r); } } while(!done && nr < nelem(rb)); s = utfconv(rb, nr); if(nbsendp(conschan, s) <= 0) free(s); } } /* * Reads Tread and Tflush requests from reqchan and responds * to them with data received on the string channel. */ void reqproc(void *aux) { enum { AREQ, ASTR, AEND }; Alt a[AEND+1]; Channel **ac; Req *r, *q, **qq; char *s, *p, *e; int n, m; threadsetname("reqproc"); e = nil; s = nil; p = nil; q = nil; qq = &q; ac = aux; a[AREQ].op = CHANRCV; a[AREQ].c = ac[0]; /* chan(Req*) */ a[AREQ].v = &r; a[ASTR].c = ac[1]; /* chan(char*) */ a[ASTR].v = &s; a[AEND].op = CHANEND; for(;;){ a[ASTR].op = s != nil ? CHANNOP : CHANRCV; switch(alt(a)){ case AREQ: if(r->ifcall.type == Tflush){ Req **rr, **xx; for(rr = &q; *rr; rr=xx){ xx = &((*rr)->aux); if(*rr == r->oldreq){ if((*rr = *xx) == nil) qq = rr; respond(r->oldreq, Eintr); break; } } respond(r, nil); continue; } else if(r->ifcall.type != Tread){ respond(r, Ephase); continue; } r->aux = nil; *qq = r; qq = &r->aux; if(0){ case ASTR: p = s; } while(s != nil && q != nil){ r = q; if((q = q->aux) == nil) qq = &q; r->ofcall.count = 0; if(s == p){ More: e = s + strlen(s); if(r->fid->qid.path == Qkbd) e++; /* send terminating \0 if its kbd file */ } n = e - p; m = r->ifcall.count - r->ofcall.count; if(n > m){ if(r->ofcall.count > 0){ respond(r, nil); continue; } n = m; } memmove((char*)r->ofcall.data + r->ofcall.count, p, n); r->ofcall.count += n; p += n; if(p >= e){ free(s); s = nbrecvp(a[ASTR].c); if(s != nil){ p = s; goto More; } } respond(r, nil); } } } } /* * Keep track of rawing state and distribute the runes from * runechan to the right channels depending on the state. */ void ctlproc(void *) { Channel *cook, *aconsr[2], *akbdr[2]; enum { ACTL, ARUNE, AEND }; Alt a[AEND+1]; Rune r; int c, raw; char *s; threadsetname("ctlproc"); if(kbdifd >= 0) proccreate(kbdiproc, nil, STACK); /* kbdifd -> kbdin() */ if(mctlfd >= 0 || msinfd >= 0) proccreate(mctlproc, nil, STACK); /* mctlchan -> mctlfd, msinfd */ if(scanfd >= 0) proccreate(scanproc, nil, STACK); /* scanfd -> keychan */ if(consfd >= 0) proccreate(consproc, nil, STACK); /* consfd -> runechan */ if(notefd >= 0) proccreate(intrproc, nil, STACK); /* intchan -> notefd */ threadcreate(keyproc, nil, STACK); /* keychan -> mctlchan, rawchan, kbdchan */ threadcreate(runeproc, nil, STACK); /* rawchan -> runechan */ aconsr[0] = consreqchan; aconsr[1] = conschan; threadcreate(reqproc, aconsr, STACK); /* consreqchan,conschan -> respond */ akbdr[0] = kbdreqchan; akbdr[1] = kbdchan; threadcreate(reqproc, akbdr, STACK); /* kbdreqchan,kbdchan -> respond */ cook = chancreate(sizeof(Rune), 0); threadcreate(lineproc, cook, STACK); /* cook -> conschan */ raw = 0; a[ACTL].c = ctlchan; a[ACTL].v = &c; a[ACTL].op = CHANRCV; a[ARUNE].c = runechan; a[ARUNE].v = &r; a[ARUNE].op = CHANRCV; a[AEND].op = CHANEND; for(;;){ switch(alt(a)){ case ACTL: switch(c){ case Rawoff: case Rawon: if(raw = (c == Rawon)){ r = 0; nbsend(cook, &r); } } break; case ARUNE: if(kbdopen){ s = emalloc9p(UTFmax+2); s[0] = 'c'; s[1+runetochar(s+1, &r)] = 0; if(nbsendp(kbdchan, s) <= 0) free(s); break; } if(raw){ s = emalloc9p(UTFmax+1); s[runetochar(s, &r)] = 0; if(nbsendp(conschan, s) <= 0) free(s); break; } nbsend(cook, &r); break; } } } /* * Keyboard layout maps */ Rune* kbmapent(int t, int sc) { if(t >= 0 && t < nelem(kbtabs) && sc >= 0 && sc < Nscan) return &kbtabs[t][sc]; return nil; } void kbmapread(Req *req) { char tmp[3*12+1]; int t, sc, soff, off, n; Rune *rp; off = req->ifcall.offset/(sizeof(tmp)-1); soff = req->ifcall.offset%(sizeof(tmp)-1); t = off/Nscan; sc = off%Nscan; if(rp = kbmapent(t, sc)){ sprint(tmp, "%11s %11d %11d\n", layertab[t], sc, *rp); n = strlen(&tmp[soff]); if(req->ifcall.count < n) n = req->ifcall.count; req->ofcall.count = n; memmove(req->ofcall.data, &tmp[soff], n); }else req->ofcall.count = 0; respond(req, nil); } Rune kbcompat(Rune r) { static Rune o = Spec|0x60, tab[] = { Kshift, Kbreak, Kctl, Kalt, Kcaps, Knum, Kmiddle, Kaltgr, Kmod4, }; if(r >= o && r < o+nelem(tab)) return tab[r - o]; return r; } void kbmapwrite(Req *req) { char line[100], *lp, *b, *e; Rune r, *rp; int sc, t, l; Fid *f; f = req->fid; b = req->ifcall.data; l = req->ifcall.count; lp = line; if(f->aux){ strcpy(line, f->aux); lp = line+strlen(line); free(f->aux); f->aux = nil; } while(--l >= 0) { *lp++ = *b++; if(lp[-1] == '\n' || lp == &line[sizeof(line)-1]) { *lp = 0; if(*line == 0){ Badarg: respond(req, Ebadarg); return; } lp = line; if(*line == '\n' || *line == '#') continue; while(*lp == ' ' || *lp == '\t') lp++; for(e = lp; strchr("\t ", *e) == nil; e++) ; if(e == lp || *e == '\0') goto Badarg; *e = '\0'; for(t = 0; t < nelem(layertab); t++){ if(strcmp(lp, layertab[t]) != 0) continue; break; } if(t == nelem(layertab)){ t = strtoul(lp, &e, 0); if(e == lp) goto Badarg; } e++; sc = strtoul(e, &lp, 0); if(e == lp) goto Badarg; while(*lp == ' ' || *lp == '\t') lp++; if((rp = kbmapent(t, sc)) == nil) goto Badarg; r = 0; if(*lp == '\'' && lp[1]) chartorune(&r, lp+1); else if(*lp == '^' && lp[1]){ chartorune(&r, lp+1); if(0x40 <= r && r < 0x60) r -= 0x40; else goto Badarg; }else if(*lp == 'M' && ('1' <= lp[1] && lp[1] <= '5')) r = Kmouse+lp[1]-'0'; else if(*lp>='0' && *lp<='9') /* includes 0x... */ r = strtoul(lp, &lp, 0); else goto Badarg; *rp = kbcompat(r); lp = line; } } if(lp != line){ l = lp-line; f->aux = lp = emalloc9p(l+1); memmove(lp, line, l); lp[l] = 0; } req->ofcall.count = req->ifcall.count; respond(req, nil); } /* * Filesystem */ static int fillstat(ulong qid, Dir *d) { struct Qtab *t; memset(d, 0, sizeof *d); d->uid = d->gid = user; d->muid = ""; d->qid = (Qid){qid, 0, 0}; d->atime = time(0); t = qtab + qid; d->name = t->name; d->qid.type = t->type; d->mode = t->mode; return 1; } static void fsattach(Req *r) { char *spec; spec = r->ifcall.aname; if(spec && spec[0]){ respond(r, Ebadspec); return; } r->fid->qid = (Qid){Qroot, 0, QTDIR}; r->ofcall.qid = r->fid->qid; respond(r, nil); } static void fsstat(Req *r) { fillstat((ulong)r->fid->qid.path, &r->d); r->d.name = estrdup9p(r->d.name); r->d.uid = estrdup9p(r->d.uid); r->d.gid = estrdup9p(r->d.gid); r->d.muid = estrdup9p(r->d.muid); respond(r, nil); } static char* fswalk1(Fid *fid, char *name, Qid *qid) { int i; ulong path; path = fid->qid.path; switch(path){ case Qroot: if (strcmp(name, "..") == 0) { *qid = (Qid){Qroot, 0, QTDIR}; fid->qid = *qid; return nil; } for(i = fid->qid.path; i<Nqid; i++){ if(strcmp(name, qtab[i].name) != 0) continue; *qid = (Qid){i, 0, 0}; fid->qid = *qid; return nil; } return Enonexist; default: return Ewalk; } } static void fsopen(Req *r) { Fid *f; static int need[4] = { 4, 2, 6, 1 }; struct Qtab *t; int n; f = r->fid; t = qtab + f->qid.path; n = need[r->ifcall.mode & 3]<<6; if((n & t->mode) != n) respond(r, Eperm); else{ f->aux = nil; switch((ulong)f->qid.path){ case Qkbmap: if(r->ifcall.mode & OTRUNC) memcpy(kbtabs, ascii, sizeof kbtabs); break; case Qkbd: if(kbdopen){ respond(r, Einuse); return; } kbdopen++; break; case Qconsctl: consctlopen++; break; } respond(r, nil); } } static int readtopdir(Fid*, uchar *buf, long off, int cnt, int blen) { int i, m, n; long pos; Dir d; n = 0; pos = 0; for (i = 1; i < Nqid; i++){ fillstat(i, &d); m = convD2M(&d, &buf[n], blen-n); if(off <= pos){ if(m <= BIT16SZ || m > cnt) break; n += m; cnt -= m; } pos += m; } return n; } static void fsread(Req *r) { Fid *f; f = r->fid; switch((ulong)f->qid.path){ default: respond(r, Ephase); return; case Qroot: r->ofcall.count = readtopdir(f, (void*)r->ofcall.data, r->ifcall.offset, r->ifcall.count, r->ifcall.count); break; case Qkbd: sendp(kbdreqchan, r); return; case Qcons: sendp(consreqchan, r); return; case Qkbmap: kbmapread(r); return; } respond(r, nil); } static void fswrite(Req *r) { Fid *f; char *p; int n, i; f = r->fid; p = r->ifcall.data; n = r->ifcall.count; switch((ulong)f->qid.path){ default: respond(r, Ephase); return; case Qcons: if(write(1, p, n) != n){ responderror(r); return; } break; case Qconsctl: if(n >= 5 && memcmp(p, "rawon", 5) == 0) sendul(ctlchan, Rawon); else if(n >= 6 && memcmp(p, "rawoff", 6) == 0) sendul(ctlchan, Rawoff); else { respond(r, Ebadarg); return; } break; case Qkbdin: case Qkbin: if(f->aux == nil){ f->aux = emalloc9p(sizeof(Scan)); memset(f->aux, 0, sizeof(Scan)); } if(f->qid.path == Qkbin){ for(i=0; i<n; i++) kbdputsc((Scan*)f->aux, (uchar)p[i]); } else { kbdin((Scan*)f->aux, p, n); } break; case Qkbmap: kbmapwrite(r); return; } r->ofcall.count = n; respond(r, nil); } static void fsflush(Req *r) { switch((ulong)r->oldreq->fid->qid.path) { case Qkbd: sendp(kbdreqchan, r); return; case Qcons: sendp(consreqchan, r); return; } respond(r, nil); } static void fsdestroyfid(Fid *f) { void *p; if(f->omode != -1) switch((ulong)f->qid.path){ case Qkbdin: case Qkbin: case Qkbmap: if(p = f->aux){ f->aux = nil; free(p); } break; case Qkbd: kbdopen--; break; case Qconsctl: if(--consctlopen == 0) sendul(ctlchan, Rawoff); break; } } static int procopen(int pid, char *name, int mode) { char buf[128]; snprint(buf, sizeof(buf), "/proc/%d/%s", pid, name); return eopen(buf, mode); } static void elevate(void) { Dir *d, nd; int fd; if(debug) return; if((fd = procopen(getpid(), "ctl", OWRITE)) < 0) return; /* get higher than normal priority */ fprint(fd, "pri 16\n"); /* always present in physical memory */ fprint(fd, "noswap\n"); /* dont let anybody kill us */ if(d = dirfstat(fd)){ nulldir(&nd); nd.mode = d->mode & ~0222; dirfwstat(fd, &nd); free(d); } close(fd); } static void fsstart(Srv*) { killfd = procopen(getpid(), "notepg", OWRITE); elevate(); memcpy(kbtabs, ascii, sizeof kbtabs); proccreate(ctlproc, nil, STACK); } static void fsend(Srv*) { shutdown(); } Srv fs = { .start= fsstart, .attach= fsattach, .walk1= fswalk1, .open= fsopen, .read= fsread, .write= fswrite, .stat= fsstat, .flush= fsflush, .destroyfid= fsdestroyfid, .end= fsend, }; void usage(void) { fprint(2, "usage: %s [ -qdD ] [ -s sname ] [ -m mntpnt ] [ file ]\n", argv0); exits("usage"); } void threadmain(int argc, char** argv) { ARGBEGIN{ case 'd': debug++; break; case 'D': chatty9p++; break; case 's': sname = EARGF(usage()); break; case 'm': mntpt = EARGF(usage()); break; case 'q': quiet++; break; default: usage(); }ARGEND if(*argv) consfd = eopen(*argv, OREAD); kbdifd = open(dev("kbd"), OREAD); if(kbdifd < 0){ scanfd = eopen(dev("scancode"), OREAD); ledsfd = eopen(dev("leds"), OWRITE); } mctlfd = eopen(dev("mousectl"), OWRITE); msinfd = eopen(dev("mousein"), OWRITE); notefd = procopen(getpid(), "notepg", OWRITE); consreqchan = chancreate(sizeof(Req*), 0); kbdreqchan = chancreate(sizeof(Req*), 0); keychan = chancreate(sizeof(Key), 64); mctlchan = chancreate(sizeof(Key), 64); ctlchan = chancreate(sizeof(int), 0); rawchan = chancreate(sizeof(Rune), 0); runechan = chancreate(sizeof(Rune), 256); conschan = chancreate(sizeof(char*), 128); kbdchan = chancreate(sizeof(char*), 128); intchan = chancreate(sizeof(int), 0); user = getuser(); threadpostmountsrv(&fs, sname, mntpt, MBEFORE); threadexits(0); }