shithub: riscv

Download patch

ref: fbbb449cc0bd35ebb89b87d5b46ed2874246876e
parent: 98f4157b5a2f9bce37939e2ea807b631943d969c
author: cinap_lenrek <cinap_lenrek@centraldogma>
date: Sat May 28 04:16:01 EDT 2011

big /dev/kbd change, new format, support Alt+Stuff (key composing)

--- a/sys/man/8/kbdfs
+++ b/sys/man/8/kbdfs
@@ -152,26 +152,41 @@
 .SS Keyboard
 A read on the
 .BR kbd
-file returns a null terminated, variable-length,
+file returns the character
+.B k,
+.B K
+or
+.B c
+followed by a null terminated, variable-length,
 .SM UTF
-encoded string of all the keys that are currently pressed (key is
-down) on the keyboard.  This includes all keys that have a keyboard
-mapping and modifier keys.  No key is treated specially. A new event
-is generated on each state change or at keyboard repeat rate and put
-in a buffer. Each
-.IR read (2)
-will return a single event or block until there are new events
-available.  The read data is always terminated with a null-byte,
-so when all keys are released (all keys are up), a single
-null-byte will be returned.  Newly pressed keys are appended to the
-string before the null-byte.  Key releases remove the
-character from the string.  Change on a modifier key like
+encoded string. The
+.B k
+message is send when a key is pressed down
+and
+.B K
+when a key is released. The following string contains all the keycodes
+of the keys that are currently pressed down in decomposed form. 
+This includes all keys that have a keyboard mapping and modifier keys.
+Some keys may produce multiple characters like
 .B Shift
-or
-.B Num
-will not change
-the characters already present in the string, but will
-take effect on newly pressed keys. Opening the
+and
+.B a
+will produce
+.B Shift,
+.B a,
+.B A
+in the string. The string following the
+.B c
+message contains the single character that would have been appeared
+on the
+.BR cons
+file instead. The
+.B c
+message will be resent at the keyboard repeat rate.
+Each
+.IR read (2)
+will return a single message or block until there are new messages
+available. Opening the
 .BR kbd
 file disables input processing on the
 .BR cons
@@ -222,4 +237,4 @@
 .SH FILES
 .B /dev/lib/kbmap/*
 .SH SOURCE
-.B /sys/src/cmd/aux/kbdfs.c
+.B /sys/src/cmd/aux/kbdfs
--- a/sys/src/cmd/aux/kbdfs.c
+++ /dev/null
@@ -1,1179 +1,0 @@
-#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,
-	Qkbin,
-	Qkbmap,
-	Qcons,
-	Qconsctl,
-	Nqid,
-
-	Rawon=	0,
-	Rawoff,
-	Kbdflush,
-
-	STACK = 8*1024,
-};
-
-typedef struct Key Key;
-typedef struct Scan Scan;
-
-struct Key {
-	int	down;
-	int	c;
-	Rune	r;
-	Rune	b;
-};
-
-struct Scan {
-	int	esc1;
-	int	esc2;
-	int	caps;
-	int	num;
-	int	shift;
-	int	ctl;
-	int	alt;
-	int	altgr;
-	int	leds;
-};
-
-struct Qtab {
-	char *name;
-	int mode;
-	int type;
-} qtab[Nqid] = {
-	"/",
-		DMDIR|0500,
-		QTDIR,
-
-	"kbd",
-		0600,
-		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 Efront[] = "the front fell off";
-
-int scanfd;
-int ledsfd;
-int consfd;
-
-int kbdopen;
-int consopen;
-int consctlopen;
-
-int debug;
-
-Channel *keychan;	/* Key */
-
-Channel *reqchan;	/* Req* */
-Channel *ctlchan;	/* int */
-
-Channel *rawchan;	/* Rune */
-Channel *linechan;	/* char * */
-Channel *kbdchan;	/* char* */
-
-/*
- * The codes at 0x79 and 0x7b are produced by the PFU Happy Hacking keyboard.
- * A 'standard' keyboard doesn't produce anything above 0x58.
- */
-Rune kbtab[Nscan] = 
-{
-[0x00]	0,	0x1b,	'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,
-};
-
-Rune kbtabshift[Nscan] =
-{
-[0x00]	0,	0x1b,	'!',	'@',	'#',	'$',	'%',	'^',
-[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,	Kup,	0,	Kup,	0,	0,	0,	0,
-};
-
-Rune kbtabesc1[Nscan] =
-{
-[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,
-};
-
-Rune kbtabaltgr[Nscan] =
-{
-[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,
-};
-
-Rune kbtabctl[Nscan] =
-{
-[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,
-};
-
-void reboot(void);
-
-/*
- * Scan code processing
- */
-void
-kbdputsc(Scan *scan, int c)
-{
-	Key key;
-
-	/*
-	 *  e0's is the first of a 2 character sequence, e1 the first
-	 *  of a 3 character sequence (on the safari)
-	 */
-	if(c == 0xe0){
-		scan->esc1 = 1;
-		return;
-	} else if(c == 0xe1){
-		scan->esc2 = 2;
-		return;
-	}
-
-	key.down = (c & 0x80) == 0;
-	key.c = c & 0x7f;
-
-	if(key.c >= Nscan)
-		return;
-
-	if(scan->esc1)
-		key.r = kbtabesc1[key.c];
-	else if(scan->shift)
-		key.r = kbtabshift[key.c];
-	else if(scan->altgr)
-		key.r = kbtabaltgr[key.c];
-	else if(scan->ctl)
-		key.r = kbtabctl[key.c];
-	else
-		key.r = kbtab[key.c];
-
-	switch(key.r){
-	case Spec|0x60:
-		key.r = Kshift;
-		break;
-	case Spec|0x62:
-		key.r = Kctl;
-		break;
-	case Spec|0x63:
-		key.r = Kalt;
-		break;
-	}
-
-	if(scan->esc1)
-		key.b = key.r;
-	else
-		key.b = kbtab[key.c];
-
-	if(scan->caps && key.r<='z' && key.r>='a')
-		key.r += 'A' - 'a';
-
-	if(scan->ctl && scan->alt && key.r == Kdel)
-		reboot();
-
-	send(keychan, &key);
-
-	if(scan->esc1)
-		scan->esc1 = 0;
-	else if(scan->esc2)
-		scan->esc2--;
-
-	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 Kalt:
-		scan->alt = key.down;
-		break;
-	case Knum:
-		scan->num ^= key.down;
-		break;
-	case Kcaps:
-		scan->caps ^= key.down;
-		break;
-	}
-}
-
-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));
-	}
-}
-
-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. this way here is only
- * one global keystate even if multiple keyboards are used.
- */
-void
-keyproc(void *)
-{
-	Rune rb[Nscan*2];
-	int cb[Nscan];
-	Key key;
-	int i, nb;
-	char *s;
-
-	threadsetname("keyproc");
-
-	nb = 0;
-	while(recv(keychan, &key) > 0){
-		if(key.down){
-			switch(key.r){
-			case 0:
-			case Kcaps:
-			case Knum:
-			case Kshift:
-			case Kalt:
-			case Kctl:
-			case Kaltgr:
-				break;
-			default:
-				nbsend(rawchan, &key.r);
-			}
-		}
-
-		for(i=0; i<nb && cb[i] != key.c; i++)
-			;
-		if(!key.down){
-			while(i < nb && cb[i] == key.c){
-				memmove(cb+i, cb+i+1, (nb-i+1) * sizeof(cb[0]));
-				memmove(rb+i, rb+i+1, (nb-i+1) * sizeof(rb[0]));
-				nb--;
-			}
-		} else if(i == nb && nb < nelem(cb) && key.b){
-			cb[nb] = key.c;
-			rb[nb] = key.b;
-			nb++;
-			if(nb < nelem(cb) && key.r && key.b != key.r){
-				cb[nb] = key.c;
-				rb[nb] = key.r;
-				nb++;
-			}
-		}
-		s = utfconv(rb, nb);
-		if(nbsendp(kbdchan, s) <= 0)
-			free(s);
-	}
-}
-
-/*
- * 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 = buf + n;
-		while(p < x && fullrune(p, x - p)){
-			p += chartorune(&r, p);
-			if(r){
-				if(r == '\n' && cr){
-					cr = 0;
-					continue;
-				}
-				if(cr = (r == '\r'))
-					r = '\n';
-				send(rawchan, &r);
-			}
-		}
-		n = x - p;
-		memmove(buf, p, n);
-		p = buf + n;
-	}
-}
-
-/*
- * Cook lines for cons
- */
-void
-lineproc(void *aux)
-{
-	Rune rb[256], r;
-	Channel *cook;
-	int nr, done;
-	
-	cook = aux;
-
-	threadsetname("lineproc");
-
-	for(;;){
-		nr = 0;
-		done = 0;
-		do {
-			recv(cook, &r);
-			switch(r){
-			case '\0':	/* flush */
-				nr = 0;
-				continue;
-			case '\b':	/* backspace */
-			case Knack:	/* ^U */
-				while(nr > 0){
-					nr--;
-					fprint(1, "\b");
-					if(r == '\b')
-						break;
-				}
-				continue;
-			case Keof:	/* ^D */
-				done = 1;
-				break;
-			case '\n':
-				done = 1;
-				/* no break */
-			default:
-				rb[nr++] = r;
-				fprint(1, "%C", r);
-			}
-		} while(!done && nr < nelem(rb));
-		sendp(linechan, utfconv(rb, nr));
-	}
-}
-
-/*
- * Queue reads to cons and kbd, flushing and
- * relay data between 9p and rawchan / kbdchan.
- */
-void
-ctlproc(void *)
-{
-	struct {
-		Req *h;
-		Req **t;
-	} qcons, qkbd, *q;
-	enum { Areq, Actl, Araw, Aline, Akbd, Aend };
-	Alt a[Aend+1];
-	Req *req;
-	Fid *fid;
-	Rune r;
-	char *s, *b, *p, *e;
-	int c, n, raw;
-	Channel *cook;
-
-	threadsetname("ctlproc");
-
-	cook = chancreate(sizeof(Rune), 0);
-
-	if(scanfd >= 0)
-		proccreate(scanproc, nil, STACK);
-	if(consfd >= 0)
-		proccreate(consproc, nil, STACK);
-
-	threadcreate(keyproc, nil, STACK);
-	threadcreate(lineproc, cook, STACK);
-
-	raw = 0;
-
-	b = p = e = nil;
-
-	qcons.h = nil;
-	qcons.t = &qcons.h;
-	qkbd.h = nil;
-	qkbd.t = &qkbd.h;
-
-	memset(a, 0, sizeof a);
-
-	a[Areq].c = reqchan;
-	a[Areq].v = &req;
-	a[Areq].op = CHANRCV;
-
-	a[Actl].c = ctlchan;
-	a[Actl].v = &c;
-	a[Actl].op = CHANRCV;
-
-	a[Araw].c = rawchan;
-	a[Araw].v = &r;
-	a[Araw].op = CHANRCV;
-
-	a[Aline].c = linechan;
-	a[Aline].v = &s;
-	a[Aline].op = CHANRCV;
-
-	a[Akbd].c = kbdchan;
-	a[Akbd].v = &s;
-	a[Akbd].op = CHANRCV;
-
-	a[Aend].op = CHANEND;
-
-	for(;;){
-		s = nil;
-
-		a[Araw].op = (b == nil) ? CHANRCV : CHANNOP;
-		a[Aline].op = (b == nil) ? CHANRCV : CHANNOP;
-		a[Akbd].op = qkbd.h || !kbdopen ? CHANRCV : CHANNOP;
-
-		switch(alt(a)){
-		case Areq:
-			fid = req->fid;
-			if(req->ifcall.type == Tflush){
-				Req **rr;
-
-				fid = req->oldreq->fid;
-				q = fid->qid.path == Qcons ? &qcons : &qkbd;
-				for(rr = &q->h; *rr && *rr != req->oldreq; rr = &((*rr)->aux))
-					;
-				if(*rr == req->oldreq){
-					if((*rr = req->oldreq->aux) == nil)
-						q->t = rr;
-					req->oldreq->aux = nil;
-					respond(req->oldreq, "interrupted");
-				}
-				respond(req, nil);
-			} else if(req->ifcall.type == Tread){
-				q = fid->qid.path == Qcons ? &qcons : &qkbd;
-				req->aux = nil;
-				*q->t = req;
-				q->t = &req->aux;
-				goto Havereq;
-			} else
-				respond(req, Efront);
-			break;
-
-		case Actl:
-			switch(c){
-			case Rawoff:
-			case Rawon:
-				if(raw = (c == Rawon)){
-					while(s = nbrecvp(linechan))
-						free(s);
-					r = '\0';
-					send(cook, &r);
-					free(b);
-					b = nil;
-				}
-				break;
-			case Kbdflush:
-				while(s = nbrecvp(kbdchan))
-					free(s);
-				break;
-			}
-			break;
-
-		case Araw:
-			if(raw || kbdopen){
-				s = emalloc9p(UTFmax+1);
-				s[runetochar(s, &r)] = 0;
-			} else {
-				nbsend(cook, &r);
-				break;
-			}
-			/* no break */
-
-		case Aline:
-			b = s;
-			p = s;
-			e = s + strlen(s);
-
-		Havereq:
-			if(kbdopen){
-				free(b);
-				b = nil;
-				break;
-			}
-			while(b && (req = qcons.h)){
-				if((qcons.h = req->aux) == nil)
-					qcons.t = &qcons.h;
-				n = e - p;
-				if(req->ifcall.count < n)
-					n = req->ifcall.count;
-				req->ofcall.count = n;
-				memmove(req->ofcall.data, p, n);
-				respond(req, nil);
-				p += n;
-				if(p >= e){
-					free(b);
-					b = nil;
-				}
-			}
-			break;
-
-		case Akbd:
-			if(req = qkbd.h){
-				if((qkbd.h = req->aux) == nil)
-					qkbd.t = &qkbd.h;
-				n = strlen(s) + 1;
-				if(n > req->ifcall.count)
-					respond(req, Eshort);
-				else {
-					req->ofcall.count = n;
-					memmove(req->ofcall.data, s, n);
-					respond(req, nil);
-				}
-			}
-			free(s);
-			break;
-		}
-	}
-}
-
-/*
- * Keyboard layout maps
- */
-
-Rune*
-kbmapent(int t, int sc)
-{
-	if(sc < 0 || sc >= Nscan)
-		return nil;
-	switch(t){
-	default:
-		return nil;
-	case 0:
-		return &kbtab[sc];
-	case 1:
-		return &kbtabshift[sc];
-	case 2:
-		return &kbtabesc1[sc];
-	case 3:
-		return &kbtabaltgr[sc];
-	case 4:
-		return &kbtabctl[sc];
-	}
-}
-
-void
-kbmapread(Req *req)
-{
-	char tmp[3*12+1];
-	int t, sc, off, n;
-	Rune *rp;
-
-	off = req->ifcall.offset/(sizeof(tmp)-1);
-	t = off/Nscan;
-	sc = off%Nscan;
-	if(rp = kbmapent(t, sc))
-		sprint(tmp, "%11d %11d %11d\n", t, sc, *rp);
-	else
-		*tmp = 0;
-	n = strlen(tmp);
-	if(req->ifcall.count < n)
-		n = req->ifcall.count;
-	req->ofcall.count = n;
-	memmove(req->ofcall.data, tmp, n);
-	respond(req, nil);
-}
-
-void
-kbmapwrite(Req *req)
-{
-	char line[100], *lp, *b;
-	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;
-			}
-			if(*line == '\n' || *line == '#'){
-				lp = line;
-				continue;
-			}
-			lp = line;
-			while(*lp == ' ' || *lp == '\t')
-				lp++;
-			t = strtoul(line, &lp, 0);
-			sc = strtoul(lp, &lp, 0);
-			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 = 0xF900+lp[1]-'0';
-			else if(*lp>='0' && *lp<='9') /* includes 0x... */
-				r = strtoul(lp, &lp, 0);
-			else
-				goto Badarg;
-			*rp = 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 char*
-getauser(void)
-{
-	static char user[64];
-	int fd;
-	int n;
-
-	if(*user)
-		return user;
-	if((fd = open("/dev/user", OREAD)) < 0)
-		strcpy(user, "none");
-	else {
-		n = read(fd, user, (sizeof user)-1);
-		close(fd);
-		if(n < 0)
-			strcpy(user, "none");
-		else
-			user[n] = 0;
-	}
-	return user;
-}
-
-static int
-fillstat(ulong qid, Dir *d)
-{
-	struct Qtab *t;
-
-	memset(d, 0, sizeof *d);
-	d->uid = getauser();
-	d->gid = getauser();
-	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 Qkbd:
-			if(kbdopen){
-				respond(r, Einuse);
-				return;
-			}
-			kbdopen++;
-			sendul(ctlchan, Kbdflush);
-			break;
-		case Qcons:
-			consopen++;
-			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, Efront);
-		return;
-
-	case Qroot:
-		r->ofcall.count = readtopdir(f, (void*)r->ofcall.data, r->ifcall.offset,
-			r->ifcall.count, r->ifcall.count);
-		break;
-
-	case Qkbd:
-	case Qcons:
-		sendp(reqchan, 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;
-	switch((ulong)f->qid.path){
-	default:
-		respond(r, Efront);
-		return;
-
-	case Qcons:
-		n = r->ifcall.count;
-		if(write(1, r->ifcall.data, n) != n){
-			responderror(r);
-			return;
-		}
-		r->ofcall.count = n;
-		break;
-
-	case Qconsctl:
-		p = r->ifcall.data;
-		n = r->ifcall.count;
-		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;
-		}
-		r->ofcall.count = n;
-		break;
-
-	case Qkbin:
-		if(f->aux == nil){
-			f->aux = emalloc9p(sizeof(Scan));
-			memset(f->aux, 0, sizeof(Scan));
-		}
-		for(i=0; i<r->ifcall.count; i++)
-			kbdputsc((Scan*)f->aux, (uchar)r->ifcall.data[i]);
-		r->ofcall.count = i;
-		break;
-
-	case Qkbmap:
-		kbmapwrite(r);
-		return;
-
-	}
-	respond(r, nil);
-}
-
-static void
-fsflush(Req *r)
-{
-	switch((ulong)r->oldreq->fid->qid.path) {
-	case Qkbd:
-	case Qcons:
-		sendp(reqchan, r);
-		return;
-	}
-	respond(r, nil);
-}
-
-static void
-fsdestroyfid(Fid *f)
-{
-	void *p;
-
-	if(f->omode != -1)
-		switch((ulong)f->qid.path){
-		case Qkbin:
-		case Qkbmap:
-			if(p = f->aux){
-				f->aux = nil;
-				free(p);
-			}
-			break;
-		case Qkbd:
-			kbdopen--;
-			break;
-		case Qcons:
-			consopen--;
-			break;
-		case Qconsctl:
-			if(--consctlopen == 0)
-				sendul(ctlchan, Rawoff);
-			break;
-		}
-}
-
-static void
-fsend(Srv*)
-{
-	threadexitsall(nil);
-}
-
-Srv fs = {
-	.attach=			fsattach,
-	.walk1=			fswalk1,
-	.open=			fsopen,
-	.read=			fsread,
-	.write=			fswrite,
-	.stat=			fsstat,
-	.flush=			fsflush,
-	.destroyfid=		fsdestroyfid,
-	.end=			fsend,
-};
-
-void
-reboot(void)
-{
-	int fd;
-
-	if(debug)
-		return;
-
-	if((fd = open("/dev/reboot", OWRITE)) < 0){
-		fprint(2, "can't open /dev/reboot: %r\n");
-		return;
-	}
-	fprint(fd, "reboot\n");
-	close(fd);
-}
-
-void
-elevate(void)
-{
-	char buf[128];
-	Dir *d, nd;
-	int fd;
-
-	if(debug)
-		return;
-
-	snprint(buf, sizeof(buf), "/proc/%d/ctl", getpid());
-	if((fd = open(buf, OWRITE)) < 0){
-		fprint(2, "can't open %s: %r\n", buf);
-		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);
-	
-}
-
-void
-usage(void)
-{
-	fprint(2, "usage: %s [ -dD ] [ -s srv ] [ -m mntpnt ] [ file ]\n", argv0);
-	exits("usage");
-}
-
-void
-threadmain(int argc, char** argv)
-{
-	char *mtpt = "/dev";
-	char *srv = nil;
-
-	consfd = -1;
-
-	ARGBEGIN{
-	case 'd':
-		debug++;
-		break;
-	case 'D':
-		chatty9p++;
-		break;
-	case 's':
-		srv = EARGF(usage());
-		break;
-	case 'm':
-		mtpt = EARGF(usage());
-		break;
-	default:
-		usage();
-	}ARGEND
-
-	if((scanfd = open("/dev/scancode", OREAD)) < 0)
-		fprint(2, "%s: warning: can't open /dev/scancode: %r\n", argv0);
-	if((ledsfd = open("/dev/leds", OWRITE)) < 0)
-		fprint(2, "%s: warning: can't open /dev/leds: %r\n", argv0);
-
-	if(*argv)
-		if((consfd = open(*argv, OREAD)) < 0)
-			fprint(2, "%s: warning: can't open %s: %r\n", argv0, *argv);
-
-	keychan = chancreate(sizeof(Key), 8);
-	reqchan = chancreate(sizeof(Req*), 0);
-	ctlchan = chancreate(sizeof(int), 0);
-	rawchan = chancreate(sizeof(Rune), 32);
-	linechan = chancreate(sizeof(char*), 16);
-	kbdchan = chancreate(sizeof(char*), 16);
-
-	if(!(keychan && reqchan && ctlchan && rawchan && linechan && kbdchan))
-		sysfatal("allocating chans");
-
-	elevate();
-	procrfork(ctlproc, nil, STACK, RFNAMEG|RFNOTEG);
-	threadpostmountsrv(&fs, srv, mtpt, MBEFORE);
-}
--- /dev/null
+++ b/sys/src/cmd/aux/kbdfs/kbdfs.c
@@ -1,0 +1,1279 @@
+#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,
+	Qkbin,
+	Qkbmap,
+	Qcons,
+	Qconsctl,
+	Nqid,
+
+	Rawon=	0,
+	Rawoff,
+	Kbdflush,
+
+	STACK = 8*1024,
+};
+
+typedef struct Key Key;
+typedef struct Scan Scan;
+
+struct Key {
+	int	down;
+	int	c;
+	Rune	r;
+	Rune	b;
+};
+
+struct Scan {
+	int	esc1;
+	int	esc2;
+	int	caps;
+	int	num;
+	int	shift;
+	int	ctl;
+	int	alt;
+	int	altgr;
+	int	leds;
+};
+
+struct Qtab {
+	char *name;
+	int mode;
+	int type;
+} qtab[Nqid] = {
+	"/",
+		DMDIR|0500,
+		QTDIR,
+
+	"kbd",
+		0600,
+		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 Efront[] = "the front fell off";
+
+int scanfd;
+int ledsfd;
+int consfd;
+
+int kbdopen;
+int consopen;
+int consctlopen;
+
+int debug;
+
+Channel *keychan;	/* Key */
+
+Channel *reqchan;	/* Req* */
+Channel *ctlchan;	/* int */
+
+Channel *rawchan;	/* Rune */
+Channel *runechan;	/* Rune */
+Channel *linechan;	/* char * */
+Channel *kbdchan;	/* char* */
+
+/*
+ * The codes at 0x79 and 0x7b are produced by the PFU Happy Hacking keyboard.
+ * A 'standard' keyboard doesn't produce anything above 0x58.
+ */
+Rune kbtab[Nscan] = 
+{
+[0x00]	0,	0x1b,	'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,
+};
+
+Rune kbtabshift[Nscan] =
+{
+[0x00]	0,	0x1b,	'!',	'@',	'#',	'$',	'%',	'^',
+[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,	Kup,	0,	Kup,	0,	0,	0,	0,
+};
+
+Rune kbtabesc1[Nscan] =
+{
+[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,
+};
+
+Rune kbtabaltgr[Nscan] =
+{
+[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,
+};
+
+Rune kbtabctl[Nscan] =
+{
+[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,
+};
+
+void reboot(void);
+
+/*
+ * Scan code processing
+ */
+void
+kbdputsc(Scan *scan, int c)
+{
+	Key key;
+
+	/*
+	 *  e0's is the first of a 2 character sequence, e1 the first
+	 *  of a 3 character sequence (on the safari)
+	 */
+	if(c == 0xe0){
+		scan->esc1 = 1;
+		return;
+	} else if(c == 0xe1){
+		scan->esc2 = 2;
+		return;
+	}
+
+	key.down = (c & 0x80) == 0;
+	key.c = c & 0x7f;
+
+	if(key.c >= Nscan)
+		return;
+
+	if(scan->esc1)
+		key.r = kbtabesc1[key.c];
+	else if(scan->shift)
+		key.r = kbtabshift[key.c];
+	else if(scan->altgr)
+		key.r = kbtabaltgr[key.c];
+	else if(scan->ctl)
+		key.r = kbtabctl[key.c];
+	else
+		key.r = kbtab[key.c];
+
+	switch(key.r){
+	case Spec|0x60:
+		key.r = Kshift;
+		break;
+	case Spec|0x62:
+		key.r = Kctl;
+		break;
+	case Spec|0x63:
+		key.r = Kalt;
+		break;
+	}
+
+	if(scan->esc1)
+		key.b = key.r;
+	else
+		key.b = kbtab[key.c];
+
+	if(scan->caps && key.r<='z' && key.r>='a')
+		key.r += 'A' - 'a';
+
+	if(scan->ctl && scan->alt && key.r == Kdel)
+		reboot();
+
+	send(keychan, &key);
+
+	if(scan->esc1)
+		scan->esc1 = 0;
+	else if(scan->esc2)
+		scan->esc2--;
+
+	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 Kalt:
+		scan->alt = key.down;
+		break;
+	case Knum:
+		scan->num ^= key.down;
+		break;
+	case Kcaps:
+		scan->caps ^= key.down;
+		break;
+	}
+}
+
+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));
+	}
+}
+
+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. this way here is only
+ * one global keystate even if multiple keyboards are used.
+ */
+void
+keyproc(void *)
+{
+	Rune rb[Nscan*2+1];
+	int cb[Nscan];
+	Key key;
+	int i, nb;
+	char *s;
+
+	threadsetname("keyproc");
+
+	nb = 0;
+	while(recv(keychan, &key) > 0){
+		if(key.down && key.r)
+			nbsend(rawchan, &key.r);
+
+		rb[0] = 0;
+		for(i=0; i<nb && cb[i] != key.c; i++)
+			;
+		if(!key.down){
+			while(i < nb && cb[i] == key.c){
+				memmove(cb+i, cb+i+1, (nb-i+1) * sizeof(cb[0]));
+				memmove(rb+i+1, rb+i+2, (nb-i+1) * sizeof(rb[0]));
+				nb--;
+				rb[0] = 'K';
+			}
+		} else if(i == nb && nb < nelem(cb) && key.b){
+			cb[nb] = key.c;
+			rb[nb+1] = key.b;
+			nb++;
+			if(nb < nelem(cb) && key.r && key.b != key.r){
+				cb[nb] = key.c;
+				rb[nb+1] = key.r;
+				nb++;
+			}
+			rb[0] = 'k';
+		}
+		if(rb[0]){
+			s = utfconv(rb, nb+1);
+			if(nbsendp(kbdchan, s) <= 0)
+				free(s);
+		}
+	}
+}
+
+/*
+ * 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 = buf + n;
+		while(p < x && fullrune(p, x - p)){
+			p += chartorune(&r, p);
+			if(r){
+				if(r == '\n' && cr){
+					cr = 0;
+					continue;
+				}
+				if(cr = (r == '\r'))
+					r = '\n';
+				send(runechan, &r);
+			}
+		}
+		n = x - p;
+		memmove(buf, p, n);
+		p = buf + n;
+	}
+}
+
+static int
+nextrune(Channel *ch, Rune *r)
+{
+	while(recv(ch, r) > 0){
+		switch(*r){
+		case 0:
+		case Kcaps:
+		case Knum:
+		case Kshift:
+		case Kctl:
+		case Kaltgr:
+			/* ignore these special keys */
+			continue;
+
+		case Kalt:
+			/* latin escape! */
+			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;
+
+	threadsetname("runeproc");
+
+	while((i = nextrune(rawchan, &r)) >= 0){
+		if(i == 0){
+Forward:
+			send(runechan, &r);
+			continue;
+		}
+
+		/* latin sequence */
+		if(nextrune(rawchan, &r))
+			continue;
+
+		if(r == 'X'){
+			r = 0;
+			for(i = 0; i<4; i++){
+				if(nextrune(rawchan, &rr))
+					break;
+				r <<= 4;
+				if(rr >= '0' && rr <= '9')
+					r |= (rr - '0');
+				else if(rr >= 'a' && rr <= 'f')
+					r |= 10 + (rr - 'a');
+				else if(rr >= 'A' && rr <= 'F')
+					r |= 10 + (rr - 'A');
+				else
+					break;
+			}
+			if(i == 4 && r > 0)
+				goto Forward;
+		} else {
+			if(nextrune(rawchan, &rr))
+				continue;
+			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;
+			}
+		}
+	}
+}
+
+/*
+ * Cook lines for cons
+ */
+void
+lineproc(void *aux)
+{
+	Rune rb[256], r;
+	Channel *cook;
+	int nr, done;
+	
+	cook = aux;
+
+	threadsetname("lineproc");
+
+	for(;;){
+		nr = 0;
+		done = 0;
+		do {
+			recv(cook, &r);
+			switch(r){
+			case '\0':	/* flush */
+				nr = 0;
+				continue;
+			case '\b':	/* backspace */
+			case Knack:	/* ^U */
+				while(nr > 0){
+					nr--;
+					fprint(1, "\b");
+					if(r == '\b')
+						break;
+				}
+				continue;
+			case Keof:	/* ^D */
+				done = 1;
+				break;
+			case '\n':
+				done = 1;
+				/* no break */
+			default:
+				rb[nr++] = r;
+				fprint(1, "%C", r);
+			}
+		} while(!done && nr < nelem(rb));
+		sendp(linechan, utfconv(rb, nr));
+	}
+}
+
+/*
+ * Queue reads to cons and kbd, flushing and
+ * relay data between 9p and rawchan / kbdchan.
+ */
+void
+ctlproc(void *)
+{
+	struct {
+		Req *h;
+		Req **t;
+	} qcons, qkbd, *q;
+	enum { Areq, Actl, Arune, Aline, Akbd, Aend };
+	Alt a[Aend+1];
+	Req *req;
+	Fid *fid;
+	Rune r;
+	char *s, *b, *p, *e;
+	int c, n, raw;
+	Channel *cook;
+
+	threadsetname("ctlproc");
+
+	cook = chancreate(sizeof(Rune), 0);
+
+	if(scanfd >= 0)
+		proccreate(scanproc, nil, STACK);	/* scanfd -> keychan */
+	if(consfd >= 0)
+		proccreate(consproc, nil, STACK);	/* consfd -> runechan */
+
+	threadcreate(keyproc, nil, STACK);		/* keychan -> rawchan, kbdchan */
+	threadcreate(runeproc, nil, STACK);		/* rawchan -> runechan */
+	threadcreate(lineproc, cook, STACK);		/* cook -> linechan */
+
+	raw = 0;
+
+	b = p = e = nil;
+
+	qcons.h = nil;
+	qcons.t = &qcons.h;
+	qkbd.h = nil;
+	qkbd.t = &qkbd.h;
+
+	memset(a, 0, sizeof a);
+
+	a[Areq].c = reqchan;
+	a[Areq].v = &req;
+	a[Areq].op = CHANRCV;
+
+	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[Aline].c = linechan;
+	a[Aline].v = &s;
+	a[Aline].op = CHANRCV;
+
+	a[Akbd].c = kbdchan;
+	a[Akbd].v = &s;
+	a[Akbd].op = CHANRCV;
+
+	a[Aend].op = CHANEND;
+
+	for(;;){
+		s = nil;
+
+		if(kbdopen){
+			a[Arune].op = qkbd.h ? CHANRCV : CHANNOP;
+			a[Akbd].op = qkbd.h ? CHANRCV : CHANNOP;
+			a[Aline].op = CHANNOP;
+		}else{
+			a[Arune].op = (b == nil) ? CHANRCV : CHANNOP;
+			a[Akbd].op = CHANRCV;
+			a[Aline].op = (b == nil) ? CHANRCV : CHANNOP;
+		}
+
+		switch(alt(a)){
+		case Areq:
+			fid = req->fid;
+			if(req->ifcall.type == Tflush){
+				Req **rr;
+
+				fid = req->oldreq->fid;
+				q = fid->qid.path == Qcons ? &qcons : &qkbd;
+				for(rr = &q->h; *rr && *rr != req->oldreq; rr = &((*rr)->aux))
+					;
+				if(*rr == req->oldreq){
+					if((*rr = req->oldreq->aux) == nil)
+						q->t = rr;
+					req->oldreq->aux = nil;
+					respond(req->oldreq, "interrupted");
+				}
+				respond(req, nil);
+			} else if(req->ifcall.type == Tread){
+				q = fid->qid.path == Qcons ? &qcons : &qkbd;
+				req->aux = nil;
+				*q->t = req;
+				q->t = &req->aux;
+				goto Havereq;
+			} else
+				respond(req, Efront);
+			break;
+
+		case Actl:
+			switch(c){
+			case Rawoff:
+			case Rawon:
+				if(raw = (c == Rawon)){
+					while(s = nbrecvp(linechan))
+						free(s);
+					r = '\0';
+					send(cook, &r);
+					free(b);
+					b = nil;
+				}
+				break;
+			case Kbdflush:
+				while(s = nbrecvp(kbdchan))
+					free(s);
+				break;
+			}
+			break;
+
+		case Arune:
+			if(kbdopen){
+				s = emalloc9p(UTFmax+2);
+				s[0] = 'c';
+				s[1+runetochar(s+1, &r)] = 0;
+				goto Havekbd;
+			}
+
+			if(raw){
+				s = emalloc9p(UTFmax+1);
+				s[runetochar(s, &r)] = 0;
+			} else {
+				nbsend(cook, &r);
+				break;
+			}
+			/* no break */
+
+		case Aline:
+			b = s;
+			p = s;
+			e = s + strlen(s);
+
+		Havereq:
+			while(b && (req = qcons.h)){
+				if((qcons.h = req->aux) == nil)
+					qcons.t = &qcons.h;
+				n = e - p;
+				if(req->ifcall.count < n)
+					n = req->ifcall.count;
+				req->ofcall.count = n;
+				memmove(req->ofcall.data, p, n);
+				respond(req, nil);
+				p += n;
+				if(p >= e){
+					free(b);
+					b = nil;
+				}
+			}
+			break;
+
+		case Akbd:
+		Havekbd:
+			if(req = qkbd.h){
+				if((qkbd.h = req->aux) == nil)
+					qkbd.t = &qkbd.h;
+				n = strlen(s) + 1;
+				if(n > req->ifcall.count)
+					respond(req, Eshort);
+				else {
+					req->ofcall.count = n;
+					memmove(req->ofcall.data, s, n);
+					respond(req, nil);
+				}
+			}
+			free(s);
+			break;
+		}
+	}
+}
+
+/*
+ * Keyboard layout maps
+ */
+
+Rune*
+kbmapent(int t, int sc)
+{
+	if(sc < 0 || sc >= Nscan)
+		return nil;
+	switch(t){
+	default:
+		return nil;
+	case 0:
+		return &kbtab[sc];
+	case 1:
+		return &kbtabshift[sc];
+	case 2:
+		return &kbtabesc1[sc];
+	case 3:
+		return &kbtabaltgr[sc];
+	case 4:
+		return &kbtabctl[sc];
+	}
+}
+
+void
+kbmapread(Req *req)
+{
+	char tmp[3*12+1];
+	int t, sc, off, n;
+	Rune *rp;
+
+	off = req->ifcall.offset/(sizeof(tmp)-1);
+	t = off/Nscan;
+	sc = off%Nscan;
+	if(rp = kbmapent(t, sc))
+		sprint(tmp, "%11d %11d %11d\n", t, sc, *rp);
+	else
+		*tmp = 0;
+	n = strlen(tmp);
+	if(req->ifcall.count < n)
+		n = req->ifcall.count;
+	req->ofcall.count = n;
+	memmove(req->ofcall.data, tmp, n);
+	respond(req, nil);
+}
+
+void
+kbmapwrite(Req *req)
+{
+	char line[100], *lp, *b;
+	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;
+			}
+			if(*line == '\n' || *line == '#'){
+				lp = line;
+				continue;
+			}
+			lp = line;
+			while(*lp == ' ' || *lp == '\t')
+				lp++;
+			t = strtoul(line, &lp, 0);
+			sc = strtoul(lp, &lp, 0);
+			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 = 0xF900+lp[1]-'0';
+			else if(*lp>='0' && *lp<='9') /* includes 0x... */
+				r = strtoul(lp, &lp, 0);
+			else
+				goto Badarg;
+			*rp = 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 char*
+getauser(void)
+{
+	static char user[64];
+	int fd;
+	int n;
+
+	if(*user)
+		return user;
+	if((fd = open("/dev/user", OREAD)) < 0)
+		strcpy(user, "none");
+	else {
+		n = read(fd, user, (sizeof user)-1);
+		close(fd);
+		if(n < 0)
+			strcpy(user, "none");
+		else
+			user[n] = 0;
+	}
+	return user;
+}
+
+static int
+fillstat(ulong qid, Dir *d)
+{
+	struct Qtab *t;
+
+	memset(d, 0, sizeof *d);
+	d->uid = getauser();
+	d->gid = getauser();
+	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 Qkbd:
+			if(kbdopen){
+				respond(r, Einuse);
+				return;
+			}
+			kbdopen++;
+			sendul(ctlchan, Kbdflush);
+			break;
+		case Qcons:
+			consopen++;
+			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, Efront);
+		return;
+
+	case Qroot:
+		r->ofcall.count = readtopdir(f, (void*)r->ofcall.data, r->ifcall.offset,
+			r->ifcall.count, r->ifcall.count);
+		break;
+
+	case Qkbd:
+	case Qcons:
+		sendp(reqchan, 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;
+	switch((ulong)f->qid.path){
+	default:
+		respond(r, Efront);
+		return;
+
+	case Qcons:
+		n = r->ifcall.count;
+		if(write(1, r->ifcall.data, n) != n){
+			responderror(r);
+			return;
+		}
+		r->ofcall.count = n;
+		break;
+
+	case Qconsctl:
+		p = r->ifcall.data;
+		n = r->ifcall.count;
+		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;
+		}
+		r->ofcall.count = n;
+		break;
+
+	case Qkbin:
+		if(f->aux == nil){
+			f->aux = emalloc9p(sizeof(Scan));
+			memset(f->aux, 0, sizeof(Scan));
+		}
+		for(i=0; i<r->ifcall.count; i++)
+			kbdputsc((Scan*)f->aux, (uchar)r->ifcall.data[i]);
+		r->ofcall.count = i;
+		break;
+
+	case Qkbmap:
+		kbmapwrite(r);
+		return;
+
+	}
+	respond(r, nil);
+}
+
+static void
+fsflush(Req *r)
+{
+	switch((ulong)r->oldreq->fid->qid.path) {
+	case Qkbd:
+	case Qcons:
+		sendp(reqchan, r);
+		return;
+	}
+	respond(r, nil);
+}
+
+static void
+fsdestroyfid(Fid *f)
+{
+	void *p;
+
+	if(f->omode != -1)
+		switch((ulong)f->qid.path){
+		case Qkbin:
+		case Qkbmap:
+			if(p = f->aux){
+				f->aux = nil;
+				free(p);
+			}
+			break;
+		case Qkbd:
+			kbdopen--;
+			break;
+		case Qcons:
+			consopen--;
+			break;
+		case Qconsctl:
+			if(--consctlopen == 0)
+				sendul(ctlchan, Rawoff);
+			break;
+		}
+}
+
+static void
+fsend(Srv*)
+{
+	threadexitsall(nil);
+}
+
+Srv fs = {
+	.attach=			fsattach,
+	.walk1=			fswalk1,
+	.open=			fsopen,
+	.read=			fsread,
+	.write=			fswrite,
+	.stat=			fsstat,
+	.flush=			fsflush,
+	.destroyfid=		fsdestroyfid,
+	.end=			fsend,
+};
+
+void
+reboot(void)
+{
+	int fd;
+
+	if(debug)
+		return;
+
+	if((fd = open("/dev/reboot", OWRITE)) < 0){
+		fprint(2, "can't open /dev/reboot: %r\n");
+		return;
+	}
+	fprint(fd, "reboot\n");
+	close(fd);
+}
+
+void
+elevate(void)
+{
+	char buf[128];
+	Dir *d, nd;
+	int fd;
+
+	if(debug)
+		return;
+
+	snprint(buf, sizeof(buf), "/proc/%d/ctl", getpid());
+	if((fd = open(buf, OWRITE)) < 0){
+		fprint(2, "can't open %s: %r\n", buf);
+		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);
+	
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s [ -dD ] [ -s srv ] [ -m mntpnt ] [ file ]\n", argv0);
+	exits("usage");
+}
+
+void
+threadmain(int argc, char** argv)
+{
+	char *mtpt = "/dev";
+	char *srv = nil;
+
+	consfd = -1;
+
+	ARGBEGIN{
+	case 'd':
+		debug++;
+		break;
+	case 'D':
+		chatty9p++;
+		break;
+	case 's':
+		srv = EARGF(usage());
+		break;
+	case 'm':
+		mtpt = EARGF(usage());
+		break;
+	default:
+		usage();
+	}ARGEND
+
+	if((scanfd = open("/dev/scancode", OREAD)) < 0)
+		fprint(2, "%s: warning: can't open /dev/scancode: %r\n", argv0);
+	if((ledsfd = open("/dev/leds", OWRITE)) < 0)
+		fprint(2, "%s: warning: can't open /dev/leds: %r\n", argv0);
+
+	if(*argv)
+		if((consfd = open(*argv, OREAD)) < 0)
+			fprint(2, "%s: warning: can't open %s: %r\n", argv0, *argv);
+
+	keychan = chancreate(sizeof(Key), 8);
+	reqchan = chancreate(sizeof(Req*), 0);
+	ctlchan = chancreate(sizeof(int), 0);
+	rawchan = chancreate(sizeof(Rune), 16);
+	runechan = chancreate(sizeof(Rune), 32);
+	linechan = chancreate(sizeof(char*), 16);
+	kbdchan = chancreate(sizeof(char*), 16);
+
+	if(!(keychan && reqchan && ctlchan && rawchan && runechan && linechan && kbdchan))
+		sysfatal("allocating chans");
+
+	elevate();
+	procrfork(ctlproc, nil, STACK, RFNAMEG|RFNOTEG);
+	threadpostmountsrv(&fs, srv, mtpt, MBEFORE);
+}
--- /dev/null
+++ b/sys/src/cmd/aux/kbdfs/mkfile
@@ -1,0 +1,16 @@
+</$objtype/mkfile
+
+BIN=/$objtype/bin/aux
+
+TARG=kbdfs
+OFILES=kbdfs.$O
+HFILES=latin1.h
+CLEANFILES=latin1.h mklatin.$cputype
+
+</sys/src/cmd/mkone
+
+mklatin.$cputype:	mkfile.mklatin
+	@{objtype=$cputype mk -f $prereq $target}
+
+latin1.h:	mklatin.$cputype /lib/keyboard
+	$prereq >$target
--- /dev/null
+++ b/sys/src/cmd/aux/kbdfs/mkfile.mklatin
@@ -1,0 +1,7 @@
+</$objtype/mkfile
+
+mklatin.$cputype:	mklatin.$O
+	$LD $LDFLAGS -o $target $prereq
+
+</sys/src/cmd/mkone
+
--- /dev/null
+++ b/sys/src/cmd/aux/kbdfs/mklatin.c
@@ -1,0 +1,232 @@
+/*
+ * Parse /lib/keyboard to create latin1.h table for kernel.
+ * mklatinkbd -r prints an array of integers rather than a Rune string literal.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+
+int rflag;
+
+enum {
+	MAXLD = 2,	/* latin1.c assumes this is 2 */
+};
+
+char *head = ""
+"/*\n"
+" * This is automatically generated by %s from /lib/keyboard\n"
+" * Edit /lib/keyboard instead.\n"
+" */\n";
+
+/*
+ * latin1.c assumes that strlen(ld) is at most 2.
+ * It also assumes that latintab[i].ld can be a prefix of latintab[j].ld
+ * only when j < i.  We ensure this by sorting the output by prefix length.
+ * The so array is indexed by the character value.
+ */
+
+typedef struct Trie	Trie;
+struct Trie {
+	int n; /* of characters r */
+	char seq[MAXLD+1];
+	Rune r[256];
+	Trie *link[256];
+};
+
+Trie *root;
+
+Trie*
+mktrie(char *seq)
+{
+	uchar *q;
+	Trie **tp;
+
+	if(root == nil) {
+		root = malloc(sizeof *root);
+		memset(root, 0, sizeof *root);
+	}
+
+	assert(seq[0] != '\0');
+
+	tp = &root;
+	for(q=(uchar*)seq; *(q+1) != '\0'; q++) {
+		tp = &(*tp)->link[*q];
+		if(*tp == nil) {
+			*tp = malloc(sizeof(**tp));
+			assert(*tp != nil);
+			memset(*tp, 0, sizeof(**tp));
+			strcpy((*tp)->seq, seq);
+			(*tp)->seq[q+1-(uchar*)seq] = '\0';
+		}
+	}
+
+	assert(*tp != nil);
+	return *tp;
+}
+
+/* add character sequence s meaning rune r */
+void
+insert(char *s, Rune r)
+{
+	uchar lastc;
+	int len;
+	Trie *t;
+
+	len = strlen(s);
+	lastc = (uchar)s[len-1];
+
+	t = mktrie(s);
+	if(t->r[lastc]) {
+		fprint(2, "warning: table duplicate: %s is %C and %C\n", s, t->r[lastc], r);
+		return;
+	}
+	t->r[lastc] = r;
+	t->n++;
+}
+
+void
+cprintchar(Biobuf *b, int c)
+{
+	/* print a byte c safe for a C string. */
+	switch(c) {
+	case '\'':
+	case '\"':
+	case '\\':
+		Bprint(b, "\\%c", c);
+		break;
+	case '\t':
+		Bprint(b, "\\t");
+		break;
+	default:
+		if(isascii(c) && isprint(c))
+			Bprint(b, "%c", c);
+		else
+			Bprint(b, "\\x%.2x", c);
+		break;
+	}
+}
+
+void
+cprints(Biobuf *b, char *p)
+{
+	while(*p != '\0')
+		cprintchar(b, *p++);
+}
+
+
+void
+printtrie(Biobuf *b, Trie *t)
+{
+	int i;
+
+	for(i=0; i<256; i++)
+		if(t->link[i])
+			printtrie(b, t->link[i]);
+
+	if(t->n > 0) {
+		Bprint(b, "\t\"");
+		cprints(b, t->seq);
+		Bprint(b, "\", \"");
+		for(i=0; i<256; i++)
+			if(t->r[i])
+				cprintchar(b, i);
+		Bprint(b, "\",\t");
+		if(rflag) {
+			Bprint(b, "{");
+			for(i=0; i<256; i++)
+				if(t->r[i])
+					Bprint(b, " 0x%.4ux,", t->r[i]);
+			Bprint(b, " }");
+		} else {
+			Bprint(b, "L\"");
+			for(i=0; i<256; i++)
+				if(t->r[i])
+					Bprint(b, "%C", t->r[i]);
+			Bprint(b, "\"");
+		}
+		Bprint(b, ",\n");
+	}	
+}
+
+void
+readfile(char *fname)
+{
+	Biobuf *b;
+	char *line, *p;
+	char *seq;
+	int inseq;
+	int lineno;
+	Rune r;
+
+	if((b = Bopen(fname, OREAD)) == 0) {
+		fprint(2, "cannot open \"%s\": %r\n", fname);
+		exits("open");
+	}
+
+	lineno = 0;
+	while((line = Brdline(b, '\n')) != 0) {
+		lineno++;
+		if(line[0] == '#')
+			continue;
+
+		r = strtol(line, nil, 16);
+		p = strchr(line, ' ');
+		if(r == 0 || p != line+4 || p[0] != ' ' || p[1] != ' ') {
+			fprint(2, "%s:%d: cannot parse line\n", fname, lineno);
+			continue;
+		}
+
+		p = line+6;
+/*	00AE  Or rO       ®	registered trade mark sign	*/
+		for(inseq=1, seq=p; (uchar)*p < Runeself; p++) {
+			if(*p == '\0' || isspace(*p)) {
+				if(inseq && p-seq >= 2) {
+					*p = '\0';
+					inseq = 0;
+					insert(seq, r);
+					*p = ' ';
+				}
+				if(*p == '\0')
+					break;
+			} else {
+				if(!inseq) {
+					seq = p;
+					inseq = 1;
+				}
+			}
+		}
+	}
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: mklatinkbd [-r] [/lib/keyboard]\n");
+	exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+	Biobuf bout;
+
+	ARGBEGIN{
+	case 'r':	/* print rune values */
+		rflag = 1;
+		break;
+	default:
+		usage();
+	}ARGEND
+
+	if(argc > 1)
+		usage();
+
+	readfile(argc == 1 ? argv[0] : "/fd/0");
+
+	Binit(&bout, 1, OWRITE);
+	if(root)
+		printtrie(&bout, root);
+	exits(0);
+}
--- a/sys/src/cmd/aux/mkfile
+++ b/sys/src/cmd/aux/mkfile
@@ -17,11 +17,9 @@
 	disksim\
 	getflags\
 	icanhasmsi\
-	kbdfs\
 	lines\
 	listen\
 	listen1\
-	mklatinkbd\
 	ms2\
 	msexceltables\
 	mswordstrings\
@@ -57,6 +55,7 @@
 DIRS=mnihongo\
 	flashfs\
 	gps\
+	kbdfs\
 	na\
 	vga\
 	realemu
--- a/sys/src/cmd/aux/mklatinkbd.c
+++ /dev/null
@@ -1,232 +1,0 @@
-/*
- * Parse /lib/keyboard to create latin1.h table for kernel.
- * mklatinkbd -r prints an array of integers rather than a Rune string literal.
- */
-
-#include <u.h>
-#include <libc.h>
-#include <bio.h>
-#include <ctype.h>
-
-int rflag;
-
-enum {
-	MAXLD = 2,	/* latin1.c assumes this is 2 */
-};
-
-char *head = ""
-"/*\n"
-" * This is automatically generated by %s from /lib/keyboard\n"
-" * Edit /lib/keyboard instead.\n"
-" */\n";
-
-/*
- * latin1.c assumes that strlen(ld) is at most 2.
- * It also assumes that latintab[i].ld can be a prefix of latintab[j].ld
- * only when j < i.  We ensure this by sorting the output by prefix length.
- * The so array is indexed by the character value.
- */
-
-typedef struct Trie	Trie;
-struct Trie {
-	int n; /* of characters r */
-	char seq[MAXLD+1];
-	Rune r[256];
-	Trie *link[256];
-};
-
-Trie *root;
-
-Trie*
-mktrie(char *seq)
-{
-	uchar *q;
-	Trie **tp;
-
-	if(root == nil) {
-		root = malloc(sizeof *root);
-		memset(root, 0, sizeof *root);
-	}
-
-	assert(seq[0] != '\0');
-
-	tp = &root;
-	for(q=(uchar*)seq; *(q+1) != '\0'; q++) {
-		tp = &(*tp)->link[*q];
-		if(*tp == nil) {
-			*tp = malloc(sizeof(**tp));
-			assert(*tp != nil);
-			memset(*tp, 0, sizeof(**tp));
-			strcpy((*tp)->seq, seq);
-			(*tp)->seq[q+1-(uchar*)seq] = '\0';
-		}
-	}
-
-	assert(*tp != nil);
-	return *tp;
-}
-
-/* add character sequence s meaning rune r */
-void
-insert(char *s, Rune r)
-{
-	uchar lastc;
-	int len;
-	Trie *t;
-
-	len = strlen(s);
-	lastc = (uchar)s[len-1];
-
-	t = mktrie(s);
-	if(t->r[lastc]) {
-		fprint(2, "warning: table duplicate: %s is %C and %C\n", s, t->r[lastc], r);
-		return;
-	}
-	t->r[lastc] = r;
-	t->n++;
-}
-
-void
-cprintchar(Biobuf *b, int c)
-{
-	/* print a byte c safe for a C string. */
-	switch(c) {
-	case '\'':
-	case '\"':
-	case '\\':
-		Bprint(b, "\\%c", c);
-		break;
-	case '\t':
-		Bprint(b, "\\t");
-		break;
-	default:
-		if(isascii(c) && isprint(c))
-			Bprint(b, "%c", c);
-		else
-			Bprint(b, "\\x%.2x", c);
-		break;
-	}
-}
-
-void
-cprints(Biobuf *b, char *p)
-{
-	while(*p != '\0')
-		cprintchar(b, *p++);
-}
-
-
-void
-printtrie(Biobuf *b, Trie *t)
-{
-	int i;
-
-	for(i=0; i<256; i++)
-		if(t->link[i])
-			printtrie(b, t->link[i]);
-
-	if(t->n > 0) {
-		Bprint(b, "\t\"");
-		cprints(b, t->seq);
-		Bprint(b, "\", \"");
-		for(i=0; i<256; i++)
-			if(t->r[i])
-				cprintchar(b, i);
-		Bprint(b, "\",\t");
-		if(rflag) {
-			Bprint(b, "{");
-			for(i=0; i<256; i++)
-				if(t->r[i])
-					Bprint(b, " 0x%.4ux,", t->r[i]);
-			Bprint(b, " }");
-		} else {
-			Bprint(b, "L\"");
-			for(i=0; i<256; i++)
-				if(t->r[i])
-					Bprint(b, "%C", t->r[i]);
-			Bprint(b, "\"");
-		}
-		Bprint(b, ",\n");
-	}	
-}
-
-void
-readfile(char *fname)
-{
-	Biobuf *b;
-	char *line, *p;
-	char *seq;
-	int inseq;
-	int lineno;
-	Rune r;
-
-	if((b = Bopen(fname, OREAD)) == 0) {
-		fprint(2, "cannot open \"%s\": %r\n", fname);
-		exits("open");
-	}
-
-	lineno = 0;
-	while((line = Brdline(b, '\n')) != 0) {
-		lineno++;
-		if(line[0] == '#')
-			continue;
-
-		r = strtol(line, nil, 16);
-		p = strchr(line, ' ');
-		if(r == 0 || p != line+4 || p[0] != ' ' || p[1] != ' ') {
-			fprint(2, "%s:%d: cannot parse line\n", fname, lineno);
-			continue;
-		}
-
-		p = line+6;
-/*	00AE  Or rO       ®	registered trade mark sign	*/
-		for(inseq=1, seq=p; (uchar)*p < Runeself; p++) {
-			if(*p == '\0' || isspace(*p)) {
-				if(inseq && p-seq >= 2) {
-					*p = '\0';
-					inseq = 0;
-					insert(seq, r);
-					*p = ' ';
-				}
-				if(*p == '\0')
-					break;
-			} else {
-				if(!inseq) {
-					seq = p;
-					inseq = 1;
-				}
-			}
-		}
-	}
-}
-
-void
-usage(void)
-{
-	fprint(2, "usage: mklatinkbd [-r] [/lib/keyboard]\n");
-	exits("usage");
-}
-
-void
-main(int argc, char **argv)
-{
-	Biobuf bout;
-
-	ARGBEGIN{
-	case 'r':	/* print rune values */
-		rflag = 1;
-		break;
-	default:
-		usage();
-	}ARGEND
-
-	if(argc > 1)
-		usage();
-
-	readfile(argc == 1 ? argv[0] : "/fd/0");
-
-	Binit(&bout, 1, OWRITE);
-	if(root)
-		printtrie(&bout, root);
-	exits(0);
-}
--- a/sys/src/cmd/rio/rio.c
+++ b/sys/src/cmd/rio/rio.c
@@ -1234,10 +1234,8 @@
 			e = p + n;
 			while(p < e && fullrune(p, e - p)){
 				p += chartorune(&r, p);
-				if(r){
-					chanprint(c, "%C", r);
-					chanprint(c, "");
-				}
+				if(r)
+					chanprint(c, "c%C", r);
 			}
 			n = e - p;
 			memmove(buf, p, n);
--- a/sys/src/cmd/rio/wind.c
+++ b/sys/src/cmd/rio/wind.c
@@ -292,20 +292,16 @@
 		}
 		switch(alt(alts)){
 		case WKbd:
-			if(utflen(kbds) >= utflen(kbdq[kbdqw] ? kbdq[kbdqw] : "")){
-				Rune r;
-
-				i = 0;
-				r = 0;
-				while(kbds[i])
-					i += chartorune(&r, kbds+i);
-				if(!w->kbdopen)
-					wkeyctl(w, r);
-			}
 			if(w->kbdopen){
 				i = (kbdqw+1) % nelem(kbdq);
 				if(i != kbdqr)
 					kbdqw = i;
+			} else if(*kbds == 'c'){
+				Rune r;
+
+				chartorune(&r, kbds+1);
+				if(r)
+					wkeyctl(w, r);
 			}
 			free(kbdq[kbdqw]);
 			kbdq[kbdqw] = kbds;
@@ -319,7 +315,7 @@
 				sendp(krm.ck, kbdq[i]);
 				kbdq[i] = nil;
 			}else
-				sendp(krm.ck, strdup(""));
+				sendp(krm.ck, strdup("K"));
 			continue;
 
 		case WMouse:
--- a/sys/src/games/doom/i_video.c
+++ b/sys/src/games/doom/i_video.c
@@ -217,32 +217,40 @@
 		sysfatal("can't open kbd: %r");
 
 	buf2[0] = 0;
+	buf2[1] = 0;
 	while((n = read(kfd, buf, sizeof(buf))) > 0){
 		buf[n-1] = 0;
 
-		s = buf;
-		while(*s){
-			s += chartorune(&r, s);
-			if(utfrune(buf2, r) == nil){
-				e.type = ev_keydown;
-				if(e.data1 = runetokey(r)){
-					e.data2 = *s == 0 ? e.data1 : -1;
-					e.data3 = *s ? e.data1 : -1;
-					D_PostEvent(&e);
+		switch(buf[0]){
+		case 'k':
+			s = buf+1;
+			while(*s){
+				s += chartorune(&r, s);
+				if(utfrune(buf2+1, r) == nil){
+					if(e.data1 = runetokey(r)){
+						e.data2 = *s == 0 ? e.data1 : -1;
+						e.data3 = -1;
+						e.type = ev_keydown;
+						D_PostEvent(&e);
+					}
 				}
 			}
-		}
-		s = buf2;
-		while(*s){
-			s += chartorune(&r, s);
-			if(utfrune(buf, r) == nil){
-				e.type = ev_keyup;
-				if(e.data1 = runetokey(r)){
-					e.data2 = -1;
-					e.data3 = -1;
-					D_PostEvent(&e);
+			break;
+		case 'K':
+			s = buf2+1;
+			while(*s){
+				s += chartorune(&r, s);
+				if(utfrune(buf+1, r) == nil){
+					if(e.data1 = runetokey(r)){
+						e.data2 = e.data3 = -1;
+						e.type = ev_keyup;
+						D_PostEvent(&e);
+					}
 				}
 			}
+			break;
+		default:
+			continue;
 		}
 		strcpy(buf2, buf);
 	}