From ce6b942ff12cbb7fe7da634004b8f7eed76eee3d Mon Sep 17 00:00:00 2001
From: cinap_lenrek <cinap_lenrek@rei2.9hal>
Date: Sat, 25 Feb 2012 23:47:09 +0100
Subject: [PATCH] vncs: /dev/kbd support by running kbdfs and forwarding
 keycodes thru kbdin

---
 sys/man/8/kbdfs               |  37 +++--
 sys/src/cmd/aux/kbdfs/kbdfs.c | 107 +++++++++++---
 sys/src/cmd/vnc/devcons.c     | 254 +---------------------------------
 sys/src/cmd/vnc/kbd.h         |   3 +-
 sys/src/cmd/vnc/kbds.c        | 181 ++++++------------------
 sys/src/cmd/vnc/kbdv.c        |   7 +-
 sys/src/cmd/vnc/latin1.c      |  76 ----------
 sys/src/cmd/vnc/latin1.h      | 100 -------------
 sys/src/cmd/vnc/mkfile        |   2 -
 sys/src/cmd/vnc/vncs.c        |  26 +++-
 10 files changed, 190 insertions(+), 603 deletions(-)
 delete mode 100644 sys/src/cmd/vnc/latin1.c
 delete mode 100644 sys/src/cmd/vnc/latin1.h

diff --git a/sys/man/8/kbdfs b/sys/man/8/kbdfs
index f97bf6f85..5327b68f2 100644
--- a/sys/man/8/kbdfs
+++ b/sys/man/8/kbdfs
@@ -20,6 +20,7 @@ kbdfs \- keyboard and console filesystem
 .B /dev/cons
 .B /dev/consctl
 .B /dev/kbd
+.B /dev/kbdin
 .B /dev/kbin
 .B /dev/kbmap
 .fi
@@ -34,6 +35,8 @@ translates raw keyboard scancodes from
 .IR kbd (3))
 and its
 .BR kbin
+and
+.BR kbdin
 file and optionaly reads console input from
 .I consfile
 to provide initial keyboard and console input.
@@ -42,6 +45,7 @@ It serves a one-level directory containing the files
 .BR cons,
 .BR consctl,
 .BR kbd,
+.BR kbdin,
 .BR kbin
 and
 .BR kbmap.
@@ -171,17 +175,9 @@ 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. 
+of the keys that are currently pressed down in unshifted form. 
 This includes all keys that have a keyboard mapping and modifier keys.
-Some keys may produce multiple characters like
-.B Shift
-and
-.B a
-will produce
-.B Shift,
-.B a,
-.B A
-in the string. The string following the
+The string following the
 .B c
 message contains the single character that would have been returned
 on the
@@ -198,6 +194,25 @@ file disables input processing on the
 .BR cons
 file until it is closed again.
 .PP
+.B K,
+.B k
+and
+.B c
+messages can be written to
+.BR kbdin
+and will forwarded to the reader of
+.BR cons
+or
+.BR kbd.
+Writing a
+.B r
+or
+.B R
+message followed by a
+.SM UTF
+encoded rune will simulate the press or
+release of that particular rune.
+.PP
 Raw scancodes can be written to the
 .BR kbin
 file for external keyboard input (used for USB keyboards).
@@ -241,6 +256,6 @@ to represent a control character.
 .IR utf (6),
 .IR kbd (3)
 .SH FILES
-.B /dev/lib/kbmap/*
+.B /sys/lib/kbmap/*
 .SH SOURCE
 .B /sys/src/cmd/aux/kbdfs
diff --git a/sys/src/cmd/aux/kbdfs/kbdfs.c b/sys/src/cmd/aux/kbdfs/kbdfs.c
index fb5c022db..2ffa2c72e 100644
--- a/sys/src/cmd/aux/kbdfs/kbdfs.c
+++ b/sys/src/cmd/aux/kbdfs/kbdfs.c
@@ -11,6 +11,7 @@ enum {
 
 	Qroot=	0,
 	Qkbd,
+	Qkbdin,
 	Qkbin,
 	Qkbmap,
 	Qcons,
@@ -58,6 +59,10 @@ struct Qtab {
 		0600,
 		0,
 
+	"kbdin",
+		0200,
+		0,
+
 	"kbin",
 		0200,	
 		0,
@@ -353,8 +358,7 @@ utfconv(Rune *r, int n)
 
 /*
  * 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.
+ * rawchan and keystate in kbdchan.
  */
 void
 keyproc(void *)
@@ -369,9 +373,6 @@ keyproc(void *)
 
 	nb = 0;
 	while(recv(keychan, &key) > 0){
-		if(key.down && key.r)
-			send(rawchan, &key.r);
-
 		rb[0] = 0;
 		for(i=0; i<nb && cb[i] != key.c; i++)
 			;
@@ -386,14 +387,14 @@ keyproc(void *)
 			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]){
+			if(kbdopen){
+				s = utfconv(rb, nb+1);
+				if(nbsendp(kbdchan, s) <= 0)
+					free(s);
+			}
 			if(mctlfd >= 0){
 				if(key.r == Kshift){
 					if(key.down){
@@ -405,12 +406,9 @@ keyproc(void *)
 				}
 				fprint(mctlfd, "twitch");
 			}
-			if(kbdopen){
-				s = utfconv(rb, nb+1);
-				if(nbsendp(kbdchan, s) <= 0)
-					free(s);
-			}
 		}
+		if(key.down && key.r)
+			send(rawchan, &key.r);
 	}
 }
 
@@ -1084,8 +1082,10 @@ static void
 fswrite(Req *r)
 {
 	Fid *f;
-	char *p;
+	Scan *a;
+	char *p, *s;
 	int n, i;
+	Key k;
 
 	f = r->fid;
 	switch((ulong)f->qid.path){
@@ -1116,6 +1116,80 @@ fswrite(Req *r)
 		r->ofcall.count = n;
 		break;
 
+	case Qkbdin:
+		p = r->ifcall.data;
+		n = r->ifcall.count;
+		if(n <= 0)
+			n = 0;
+		r->ofcall.count = n;
+		if(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);
+			break;
+		}
+		switch(p[0]){
+		case 'R':
+		case 'r':
+			/* rune up/down */
+			chartorune(&k.r, p+1);
+			if(k.r == 0)
+				break;
+			k.b = k.r;
+			k.c = 0x100 + k.r;	/* fake */
+			k.down = (p[0] == 'r');
+			if(f->aux == nil){
+				f->aux = emalloc9p(sizeof(Scan));
+				memset(f->aux, 0, sizeof(Scan));
+			}
+			a = f->aux;
+			/*
+			 * handle ^X forms according to keymap,
+			 * assign base and scancode if any
+			 */
+			for(i=0; i<Nscan; i++){
+				if((a->shift && kbtabshift[i] == k.r) || (kbtab[i] == k.r)){
+					k.c = i;
+					k.b = kbtab[i];
+					if(a->shift)
+						k.r = kbtabshift[i];
+					else if(a->altgr)
+						k.r = kbtabaltgr[i];
+					else if(a->ctl)
+						k.r = kbtabctl[i];
+					break;
+				}
+			}
+			send(keychan, &k);
+			if(k.r == Kshift)
+				a->shift = k.down;
+			else if(k.r == Kaltgr)
+				a->altgr = k.down;
+			else if(k.r == Kctl)
+				a->ctl = k.down;
+			break;
+		default:
+			if(!kbdopen)
+				break;
+			s = emalloc9p(n);
+			memmove(s, p, n);
+			if(nbsendp(kbdchan, s) <= 0)
+				free(s);
+		}
+		break;
+
 	case Qkbin:
 		if(f->aux == nil){
 			f->aux = emalloc9p(sizeof(Scan));
@@ -1155,6 +1229,7 @@ fsdestroyfid(Fid *f)
 
 	if(f->omode != -1)
 		switch((ulong)f->qid.path){
+		case Qkbdin:
 		case Qkbin:
 		case Qkbmap:
 			if(p = f->aux){
diff --git a/sys/src/cmd/vnc/devcons.c b/sys/src/cmd/vnc/devcons.c
index 16804fce4..b328bdaaf 100644
--- a/sys/src/cmd/vnc/devcons.c
+++ b/sys/src/cmd/vnc/devcons.c
@@ -4,180 +4,10 @@
 #include	"kbd.h"
 #include	"error.h"
 
-typedef struct Queue	Queue;
-struct Queue
-{
-	QLock	qwait;
-	Rendez	rwait;
-
-	Lock	lock;
-	int	notempty;
-	char	buf[1024];
-	char	*w;
-	char	*r;
-	char	*e;
-};
-
-Queue*	kbdq;			/* unprocessed console input */
-Queue*	lineq;			/* processed console input */
 Snarf	snarf = {
 	.vers =	1
 };
 
-static struct
-{
-	QLock;
-	int	raw;		/* true if we shouldn't process input */
-	int	ctl;		/* number of opens to the control file */
-	int	x;		/* index into line */
-	char	line[1024];	/* current input line */
-} kbd;
-
-/*
- * cheapo fixed-length queues
- */
-static void
-qwrite(Queue *q, void *v, int n)
-{
-	char *buf, *next;
-	int i;
-
-	buf = v;
-	lock(&q->lock);
-	for(i = 0; i < n; i++){
-		next = q->w+1;
-		if(next >= q->e)
-			next = q->buf;
-		if(next == q->r)
-			break;
-		*q->w = buf[i];
-		q->w = next;
-	}
-	q->notempty = 1;
-	unlock(&q->lock);
-	rendwakeup(&q->rwait);
-}
-
-static int
-qcanread(void *vq)
-{
-	Queue *q;
-	int ne;
-
-	q = vq;
-	lock(&q->lock);
-	ne = q->notempty;
-	unlock(&q->lock);
-	return ne;
-}
-
-static int
-qread(Queue *q, void *v, int n)
-{
-	char *a;
-	int nn, notempty;
-
-	if(n == 0)
-		return 0;
-	a = v;
-	nn = 0;
-	for(;;){
-		lock(&q->lock);
-
-		while(nn < n && q->r != q->w){
-			a[nn++] = *q->r++;
-			if(q->r >= q->e)
-				q->r = q->buf;
-		}
-
-		notempty = q->notempty;
-		q->notempty = q->r != q->w;
-		unlock(&q->lock);
-		if(notempty)
-			break;
-
-		/*
-		 * wait for something to show up in the kbd buffer.
-		 */
-		qlock(&q->qwait);
-		if(waserror()){
-			qunlock(&q->qwait);
-			nexterror();
-		}
-		rendsleep(&q->rwait, qcanread, q);
-		qunlock(&q->qwait);
-		poperror();
-	}
-	return nn;
-}
-
-static Queue *
-mkqueue(void)
-{
-	Queue *q;
-
-	q = smalloc(sizeof(Queue));
-	q->r = q->buf;
-	q->w = q->r;
-	q->e = &q->buf[sizeof q->buf];
-	q->notempty = 0;
-	return q;
-}
-
-static void
-echoscreen(char *buf, int n)
-{
-	char *e, *p;
-	char ebuf[128];
-	int x;
-
-	p = ebuf;
-	e = ebuf + sizeof(ebuf) - 4;
-	while(n-- > 0){
-		if(p >= e){
-			screenputs(ebuf, p - ebuf);
-			p = ebuf;
-		}
-		x = *buf++;
-		if(x == 0x15){
-			*p++ = '^';
-			*p++ = 'U';
-			*p++ = '\n';
-		} else
-			*p++ = x;
-	}
-	if(p != ebuf)
-		screenputs(ebuf, p - ebuf);
-}
-
-/*
- *  Put character, possibly a rune, into read queue at interrupt time.
- *  Called at interrupt time to process a character.
- */
-void
-kbdputc(int ch)
-{
-	int n;
-	char buf[3];
-	Rune r;
-
-	r = ch;
-	n = runetochar(buf, &r);
-	qwrite(kbdq, buf, n);
-	if(!kbd.raw)
-		echoscreen(buf, n);
-}
-
-static void
-kbdputcinit(void)
-{
-	kbdq = mkqueue();
-	lineq = mkqueue();
-	kbd.raw = 0;
-	kbd.ctl = 0;
-	kbd.x = 0;
-}
-
 enum{
 	Qdir,
 	Qcons,
@@ -194,12 +24,6 @@ static Dirtab consdir[]={
 	"winname",	{Qwinname},	0,		0000,
 };
 
-static void
-consinit(void)
-{
-	kbdputcinit();
-}
-
 static Chan*
 consattach(char *spec)
 {
@@ -225,9 +49,6 @@ consopen(Chan *c, int omode)
 	c = devopen(c, omode, consdir, nelem(consdir), devgen);
 	switch((ulong)c->qid.path){
 	case Qconsctl:
-		qlock(&kbd);
-		kbd.ctl++;
-		qunlock(&kbd);
 		break;
 	case Qsnarf:
 		if((c->mode&3) == OWRITE || (c->mode&3) == ORDWR)
@@ -266,12 +87,6 @@ consclose(Chan *c)
 	switch((ulong)c->qid.path){
 	/* last close of control file turns off raw */
 	case Qconsctl:
-		if(c->flag&COPEN){
-			qlock(&kbd);
-			if(--kbd.ctl == 0)
-				kbd.raw = 0;
-			qunlock(&kbd);
-		}
 		break;
 	/* odd behavior but really ok: replace snarf buffer when /dev/snarf is closed */
 	case Qsnarf:
@@ -289,9 +104,6 @@ consclose(Chan *c)
 static long
 consread(Chan *c, void *buf, long n, vlong off)
 {
-	char ch;
-	int	send;
-
 	if(n <= 0)
 		return n;
 	switch((ulong)c->qid.path){
@@ -310,48 +122,8 @@ consread(Chan *c, void *buf, long n, vlong off)
 		return devdirread(c, buf, n, consdir, nelem(consdir), devgen);
 
 	case Qcons:
-		qlock(&kbd);
-		if(waserror()){
-			qunlock(&kbd);
-			nexterror();
-		}
-		while(!qcanread(lineq)){
-			qread(kbdq, &ch, 1);
-			send = 0;
-			if(ch == 0){
-				/* flush output on rawoff -> rawon */
-				if(kbd.x > 0)
-					send = !qcanread(kbdq);
-			}else if(kbd.raw){
-				kbd.line[kbd.x++] = ch;
-				send = !qcanread(kbdq);
-			}else{
-				switch(ch){
-				case '\b':
-					if(kbd.x > 0)
-						kbd.x--;
-					break;
-				case 0x15:	/* ^U */
-					kbd.x = 0;
-					break;
-				case '\n':
-				case 0x04:	/* ^D */
-					send = 1;
-				default:
-					if(ch != 0x04)
-						kbd.line[kbd.x++] = ch;
-					break;
-				}
-			}
-			if(send || kbd.x == sizeof kbd.line){
-				qwrite(lineq, kbd.line, kbd.x);
-				kbd.x = 0;
-			}
-		}
-		n = qread(lineq, buf, n);
-		qunlock(&kbd);
-		poperror();
-		return n;
+		error(Egreg);
+		return -1;
 
 	default:
 		print("consread 0x%llux\n", c->qid.path);
@@ -364,8 +136,7 @@ static long
 conswrite(Chan *c, void *va, long n, vlong)
 {
 	Snarf *t;
-	char buf[256], *a;
-	char ch;
+	char *a;
 
 	switch((ulong)c->qid.path){
 	case Qcons:
@@ -373,22 +144,7 @@ conswrite(Chan *c, void *va, long n, vlong)
 		break;
 
 	case Qconsctl:
-		if(n >= sizeof(buf))
-			n = sizeof(buf)-1;
-		strncpy(buf, va, n);
-		buf[n] = 0;
-		for(a = buf; a;){
-			if(strncmp(a, "rawon", 5) == 0){
-				kbd.raw = 1;
-				/* clumsy hack - wake up reader */
-				ch = 0;
-				qwrite(kbdq, &ch, 1);			
-			} else if(strncmp(a, "rawoff", 6) == 0){
-				kbd.raw = 0;
-			}
-			if(a = strchr(a, ' '))
-				a++;
-		}
+		error(Egreg);
 		break;
 
 	case Qsnarf:
@@ -416,7 +172,7 @@ Dev consdevtab = {
 	"cons",
 
 	devreset,
-	consinit,
+	devinit,
 	consattach,
 	conswalk,
 	consstat,
diff --git a/sys/src/cmd/vnc/kbd.h b/sys/src/cmd/vnc/kbd.h
index f8ee6136e..3bf0c7b5e 100644
--- a/sys/src/cmd/vnc/kbd.h
+++ b/sys/src/cmd/vnc/kbd.h
@@ -14,9 +14,8 @@ enum
 };
 
 extern	Snarf		snarf;
+extern	int		kbdin;
 
-long			latin1(Rune *k, int n);
-void			kbdputc(int c);
 void			screenputs(char*, int);
 void			vncputc(int, int);
 void			setsnarf(char *buf, int n, int *vers);
diff --git a/sys/src/cmd/vnc/kbds.c b/sys/src/cmd/vnc/kbds.c
index 89f7c80aa..867e0e2f2 100644
--- a/sys/src/cmd/vnc/kbds.c
+++ b/sys/src/cmd/vnc/kbds.c
@@ -1,81 +1,48 @@
 #include	<u.h>
 #include	<libc.h>
+#include	<keyboard.h>
 #include	"compat.h"
 #include	"kbd.h"
 #include   "ksym2utf.h"
 
-enum
-{
+enum {
 	VKSpecial = 0xff00,
-
-	/*
-	 * plan 9 key mappings
-	 */
-	Spec=		0xF800,
-
-	PF=		Spec|0x20,	/* num pad function key */
-	View=		Spec|0x00,	/* view (shift window up) */
-	KF=		0xF000,	/* function key (begin Unicode private space) */
-	Shift=		Spec|0x60,
-	Break=		Spec|0x61,
-	Ctrl=		Spec|0x62,
-	Latin=		Spec|0x63,
-	Caps=		Spec|0x64,
-	Num=		Spec|0x65,
-	Middle=		Spec|0x66,
-	No=		0x00,		/* peter */
-
-	Home=		KF|13,
-	Up=		KF|14,
-	Pgup=		KF|15,
-	Print=		KF|16,
-	Left=		KF|17,
-	Right=		KF|18,
-	End=		'\r',
-	Down=		View,
-	Pgdown=		KF|19,
-	Ins=		KF|20,
-	Del=		0x7F,
-	Scroll=		KF|21,
-
-	Esc = 0x1b,
-	Delete = 0x7f,
 };
 
 static Rune vnckeys[] =
 {
-[0x00]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x08]	'\b',	'\t',	'\r',	No,	No,	'\n',	No,	No,
-[0x10]	No,	No,	No,	No,	Scroll,	No,	No,	No,
-[0x18]	No,	No,	No,	Esc,	No,	No,	No,	No,
-[0x20]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x28]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x30]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x38]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x40]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x48]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x50]	Home,	Left,	Up,	Right,	Down,	Pgup,	Pgdown,	No,
-[0x58]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x60]	No,	Print,	No,	Ins,	No,	No,	No,	No,
-[0x68]	No,	No,	No,	Break,	No,	No,	No,	No,
-[0x70]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x78]	No,	No,	No,	No,	No,	No,	No,	Num,
-[0x80]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x88]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x90]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x98]	No,	No,	No,	No,	No,	No,	No,	No,
-[0xa0]	No,	No,	No,	No,	No,	No,	No,	No,
-[0xa8]	No,	No,	'*',	'+',	No,	'-',	'.',	'/',
+[0x00]	0,	0,	0,	0,	0,	0,	0,	0,
+[0x08]	'\b',	'\t',	'\r',	0,	0,	'\n',	0,	0,
+[0x10]	0,	0,	0,	0,	Kscroll,0,	0,	0,
+[0x18]	0,	0,	0,	Kesc,	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]	Khome,	Kleft,	Kup,	Kright,	Kdown,	Kpgup,	Kpgdown,0,
+[0x58]	0,	0,	0,	0,	0,	0,	0,	0,
+[0x60]	0,	Kprint,	0,	Kins,	0,	0,	0,	0,
+[0x68]	0,	0,	0,	Kbreak,	0,	0,	0,	0,
+[0x70]	0,	0,	0,	0,	0,	0,	0,	0,
+[0x78]	0,	0,	0,	0,	0,	0,	0,	Knum,
+[0x80]	0,	0,	0,	0,	0,	0,	0,	0,
+[0x88]	0,	0,	0,	0,	0,	0,	0,	0,
+[0x90]	0,	0,	0,	0,	0,	0,	0,	0,
+[0x98]	0,	0,	0,	0,	0,	0,	0,	0,
+[0xa0]	0,	0,	0,	0,	0,	0,	0,	0,
+[0xa8]	0,	0,	'*',	'+',	0,	'-',	'.',	'/',
 [0xb0]	'0',	'1',	'2',	'3',	'4',	'5',	'6',	'7',
-[0xb8]	'8',	'9',	No,	No,	No,	'=',	No,	No,
-[0xc0]	No,	No,	No,	No,	No,	No,	No,	No,
-[0xc8]	No,	No,	No,	No,	No,	No,	No,	No,
-[0xd0]	No,	No,	No,	No,	No,	No,	No,	No,
-[0xd8]	No,	No,	No,	No,	No,	No,	No,	No,
-[0xe0]	No,	Shift,	Shift,	Ctrl,	Ctrl,	Caps,	Caps,	No,
-[0xe8]	No,	Latin,	Latin,	No,	No,	No,	No,	No,
-[0xf0]	No,	No,	No,	No,	No,	No,	No,	No,
-[0xf8]	No,	No,	No,	No,	No,	No,	No,	Delete,
+[0xb8]	'8',	'9',	0,	0,	0,	'=',	0,	0,
+[0xc0]	0,	0,	0,	0,	0,	0,	0,	0,
+[0xc8]	0,	0,	0,	0,	0,	0,	0,	0,
+[0xd0]	0,	0,	0,	0,	0,	0,	0,	0,
+[0xd8]	0,	0,	0,	0,	0,	0,	0,	0,
+[0xe0]	0,	Kshift,	Kshift,	Kctl,	Kctl,	Kcaps,	Kcaps,	0,
+[0xe8]	0,	Kalt,	Kalt,	0,	0,	0,	0,	0,
+[0xf0]	0,	0,	0,	0,	0,	0,	0,	0,
+[0xf8]	0,	0,	0,	0,	0,	0,	0,	Kdel,
 };
 
 /*
@@ -84,90 +51,24 @@ static Rune vnckeys[] =
 void
 vncputc(int keyup, int c)
 {
-	int i;
-	static int esc1, esc2;
-	static int alt, caps, ctl, num, shift;
-	static int collecting, nk;
-	static Rune kc[5];
-
-	if(caps && c<='z' && c>='a')
-		c += 'A' - 'a';
+	char buf[16];
 
 	/*
  	 *  character mapping
 	 */
 	if((c & VKSpecial) == VKSpecial){
 		c = vnckeys[c & 0xff];
-		if(c == No)
+		if(c == 0)
 			return;
 	}
 	/*
 	 * map an xkeysym onto a utf-8 char
 	 */
 	if((c & 0xff00) && c < nelem(ksym2utf) && ksym2utf[c] != 0)
-			c = ksym2utf[c];
-
-	/*
-	 *  keyup only important for shifts
-	 */
-	if(keyup){
-		switch(c){
-		case Latin:
-			alt = 0;
-			break;
-		case Shift:
-			shift = 0;
-			break;
-		case Ctrl:
-			ctl = 0;
-			break;
-		}
-		return;
-	}
-
-	/*
- 	 *  normal character
-	 */
-	if(!(c & (Spec|KF))){
-		if(ctl){
-			c &= 0x1f;
-		}
-		if(!collecting){
-			kbdputc(c);
-			return;
-		}
-		kc[nk++] = c;
-		c = latin1(kc, nk);
-		if(c < -1)	/* need more keystrokes */
-			return;
-		if(c != -1)	/* valid sequence */
-			kbdputc(c);
-		else	/* dump characters */
-			for(i=0; i<nk; i++)
-				kbdputc(kc[i]);
-		nk = 0;
-		collecting = 0;
-		return;
-	}else{
-		switch(c){
-		case Caps:
-			caps ^= 1;
-			return;
-		case Num:
-			num ^= 1;
-			return;
-		case Shift:
-			shift = 1;
-			return;
-		case Latin:
-			alt = 1;
-			collecting = 1;
-			nk = 0;
-			return;
-		case Ctrl:
-			ctl = 1;
-			return;
-		}
-	}
-	kbdputc(c);
+		c = ksym2utf[c];
+	snprint(buf, sizeof(buf), "r%C", c);
+	if(keyup)
+		buf[0] = 'R';
+	if(kbdin >= 0)
+		write(kbdin, buf, strlen(buf)+1);
 }
diff --git a/sys/src/cmd/vnc/kbdv.c b/sys/src/cmd/vnc/kbdv.c
index 7e6ac926c..acd58ec44 100644
--- a/sys/src/cmd/vnc/kbdv.c
+++ b/sys/src/cmd/vnc/kbdv.c
@@ -229,11 +229,12 @@ readkbd(Vnc *v)
 			}
 			break;
 		case 'c':
-			chartorune(&r, buf+1);
 			if(utfrune(buf2+1, Kctl) || utfrune(buf2+1, Kalt) || utfrune(buf2+1, Kaltgr))
 				continue;
-			if(utfrune(buf2+1, r))
-				keyevent(v, runetovnc(r), 1);
+			chartorune(&r, buf+1);
+			keyevent(v, runetovnc(r), 1);
+			if(utfrune(buf2+1, r) == nil)
+				keyevent(v, runetovnc(r), 0);
 		default:
 			continue;
 		}
diff --git a/sys/src/cmd/vnc/latin1.c b/sys/src/cmd/vnc/latin1.c
deleted file mode 100644
index 8650f7d82..000000000
--- a/sys/src/cmd/vnc/latin1.c
+++ /dev/null
@@ -1,76 +0,0 @@
-#include <u.h>
-
-/*
- * The code makes two assumptions: strlen(ld) is 1 or 2; latintab[i].ld can be a
- * prefix of latintab[j].ld only when j<i.
- */
-struct cvlist
-{
-	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 */
-} latintab[] = {
-#include "latin1.h"
-	0,	0,		0
-};
-
-/*
- * Given 5 characters k[0]..k[4], find the rune or return -1 for failure.
- */
-long
-unicode(Rune *k)
-{
-	long i, c;
-
-	k++;	/* skip 'X' */
-	c = 0;
-	for(i=0; i<4; i++,k++){
-		c <<= 4;
-		if('0'<=*k && *k<='9')
-			c += *k-'0';
-		else if('a'<=*k && *k<='f')
-			c += 10 + *k-'a';
-		else if('A'<=*k && *k<='F')
-			c += 10 + *k-'A';
-		else
-			return -1;
-	}
-	return c;
-}
-
-/*
- * Given n characters k[0]..k[n-1], find the corresponding rune or return -1 for
- * failure, or something < -1 if n is too small.  In the latter case, the result
- * is minus the required n.
- */
-long
-latin1(Rune *k, int n)
-{
-	struct cvlist *l;
-	int c;
-	char* p;
-
-	if(k[0] == 'X')
-		if(n>=5)
-			return unicode(k);
-		else
-			return -5;
-	for(l=latintab; l->ld!=0; l++)
-		if(k[0] == l->ld[0]){
-			if(n == 1)
-				return -2;
-			if(l->ld[1] == 0)
-				c = k[1];
-			else if(l->ld[1] != k[1])
-				continue;
-			else if(n == 2)
-				return -3;
-			else
-				c = k[2];
-			for(p=l->si; *p!=0; p++)
-				if(*p == c)
-					return l->so[p - l->si];
-			return -1;
-		}
-	return -1;
-}
diff --git a/sys/src/cmd/vnc/latin1.h b/sys/src/cmd/vnc/latin1.h
deleted file mode 100644
index 76a95bf7a..000000000
--- a/sys/src/cmd/vnc/latin1.h
+++ /dev/null
@@ -1,100 +0,0 @@
-	" ", " i",	L"␣ı",
-	"!~", "-=~",	L"≄≇≉",
-	"!", "!<=>?bmp",	L"¡≮≠≯‽⊄∉⊅",
-	"\"*", "IUiu",	L"ΪΫϊϋ",
-	"\"", "\"AEIOUYaeiouy",	L"¨ÄËÏÖÜŸäëïöüÿ",
-	"$*", "fhk",	L"ϕϑϰ",
-	"$", "BEFHILMRVaefglopv",	L"ℬℰℱℋℐℒℳℛƲɑℯƒℊℓℴ℘ʋ",
-	"\'\"", "Uu",	L"Ǘǘ",
-	"\'", "\'ACEILNORSUYZacegilnorsuyz",	L"´ÁĆÉÍĹŃÓŔŚÚÝŹáćéģíĺńóŕśúýź",
-	"*", "*ABCDEFGHIKLMNOPQRSTUWXYZabcdefghiklmnopqrstuwxyz",	L"∗ΑΒΞΔΕΦΓΘΙΚΛΜΝΟΠΨΡΣΤΥΩΧΗΖαβξδεφγθικλμνοπψρστυωχηζ",
-	"+", "-O",	L"±⊕",
-	",", ",ACEGIKLNORSTUacegiklnorstu",	L"¸ĄÇĘĢĮĶĻŅǪŖŞŢŲąçęģįķļņǫŗşţų",
-	"-*", "l",	L"ƛ",
-	"-", "+-2:>DGHILOTZbdghiltuz~",	L"∓­ƻ÷→ÐǤĦƗŁ⊖ŦƵƀðǥℏɨłŧʉƶ≂",
-	".", ".CEGILOZceglz",	L"·ĊĖĠİĿ⊙Żċėġŀż",
-	"/", "Oo",	L"Øø",
-	"1", ".234568",	L"․½⅓¼⅕⅙⅛",
-	"2", "-.35",	L"ƻ‥⅔⅖",
-	"3", ".458",	L"…¾⅗⅜",
-	"4", "5",	L"⅘",
-	"5", "68",	L"⅚⅝",
-	"7", "8",	L"⅞",
-	":", "()-=",	L"☹☺÷≔",
-	"<!", "=~",	L"≨⋦",
-	"<", "-<=>~",	L"←«≤≶≲",
-	"=", ":<=>OV",	L"≕⋜≡⋝⊜⇒",
-	">!", "=~",	L"≩⋧",
-	">", "<=>~",	L"≷≥»≳",
-	"?", "!?",	L"‽¿",
-	"@\'", "\'",	L"ъ",
-	"@@", "\'EKSTYZekstyz",	L"ьЕКСТЫЗекстыз",
-	"@C", "Hh",	L"ЧЧ",
-	"@E", "Hh",	L"ЭЭ",
-	"@K", "Hh",	L"ХХ",
-	"@S", "CHch",	L"ЩШЩШ",
-	"@T", "Ss",	L"ЦЦ",
-	"@Y", "AEOUaeou",	L"ЯЕЁЮЯЕЁЮ",
-	"@Z", "Hh",	L"ЖЖ",
-	"@c", "h",	L"ч",
-	"@e", "h",	L"э",
-	"@k", "h",	L"х",
-	"@s", "ch",	L"щш",
-	"@t", "s",	L"ц",
-	"@y", "aeou",	L"яеёю",
-	"@z", "h",	L"ж",
-	"@", "ABDFGIJLMNOPRUVXabdfgijlmnopruvx",	L"АБДФГИЙЛМНОПРУВХабдфгийлмнопрувх",
-	"A", "E",	L"Æ",
-	"C", "ACU",	L"⋂ℂ⋃",
-	"Dv", "Zz",	L"DŽDž",
-	"D", "-e",	L"Ð∆",
-	"G", "-",	L"Ǥ",
-	"H", "-H",	L"Ħℍ",
-	"I", "-J",	L"ƗIJ",
-	"L", "&-Jj|",	L"⋀ŁLJLj⋁",
-	"M", "#48bs",	L"♮♩♪♭♯",
-	"N", "JNj",	L"NJℕNj",
-	"O", "*+-./=EIcoprx",	L"⊛⊕⊖⊙⊘⊜ŒƢ©⊚℗®⊗",
-	"P", "P",	L"ℙ",
-	"Q", "Q",	L"ℚ",
-	"R", "R",	L"ℝ",
-	"S", "S",	L"§",
-	"T", "-u",	L"Ŧ⊨",
-	"V", "=",	L"⇐",
-	"Y", "R",	L"Ʀ",
-	"Z", "-ACSZ",	L"Ƶℤ",
-	"^", "ACEGHIJOSUWYaceghijosuwy",	L"ÂĈÊĜĤÎĴÔŜÛŴŶâĉêĝĥîĵôŝûŵŷ",
-	"_\"", "AUau",	L"ǞǕǟǖ",
-	"_,", "Oo",	L"Ǭǭ",
-	"_.", "Aa",	L"Ǡǡ",
-	"_", "AEIOU_aeiou",	L"ĀĒĪŌŪ¯āēīōū",
-	"`\"", "Uu",	L"Ǜǜ",
-	"`", "AEIOUaeiou",	L"ÀÈÌÒÙàèìòù",
-	"a", "ben",	L"↔æ∠",
-	"b", "()+-0123456789=bknpqru",	L"₍₎₊₋₀₁₂₃₄₅₆₇₈₉₌♝♚♞♟♛♜•",
-	"c", "$Oagu",	L"¢©∩≅∪",
-	"dv", "z",	L"dž",
-	"d", "-adegz",	L"ð↓‡°†ʣ",
-	"e", "$lmns",	L"€⋯—–∅",
-	"f", "a",	L"∀",
-	"g", "$-r",	L"¤ǥ∇",
-	"h", "-v",	L"ℏƕ",
-	"i", "-bfjps",	L"ɨ⊆∞ij⊇∫",
-	"l", "\"$&\'-jz|",	L"“£∧‘łlj⋄∨",
-	"m", "iou",	L"µ∈×",
-	"n", "jo",	L"nj¬",
-	"o", "AOUaeiu",	L"Å⊚Ůåœƣů",
-	"p", "Odgrt",	L"℗∂¶∏∝",
-	"r", "\"\'O",	L"”’®",
-	"s", "()+-0123456789=abnoprstu",	L"⁽⁾⁺⁻⁰¹²³⁴⁵⁶⁷⁸⁹⁼ª⊂ⁿº⊃√ß∍∑",
-	"t", "-efmsu",	L"ŧ∃∴™ς⊢",
-	"u", "-AEGIOUaegiou",	L"ʉĂĔĞĬŎŬ↑ĕğĭŏŭ",
-	"v\"", "Uu",	L"Ǚǚ",
-	"v", "ACDEGIKLNORSTUZacdegijklnorstuz",	L"ǍČĎĚǦǏǨĽŇǑŘŠŤǓŽǎčďěǧǐǰǩľňǒřšťǔž",
-	"w", "bknpqr",	L"♗♔♘♙♕♖",
-	"x", "O",	L"⊗",
-	"y", "$",	L"¥",
-	"z", "-",	L"ƶ",
-	"|", "Pp|",	L"Þþ¦",
-	"~!", "=",	L"≆",
-	"~", "-=AINOUainou~",	L"≃≅ÃĨÑÕŨãĩñõũ≈",
diff --git a/sys/src/cmd/vnc/mkfile b/sys/src/cmd/vnc/mkfile
index 8b1c84946..41e3139ec 100644
--- a/sys/src/cmd/vnc/mkfile
+++ b/sys/src/cmd/vnc/mkfile
@@ -18,7 +18,6 @@ SOFILES=\
 	compat.$O\
 	exportfs.$O\
 	kbds.$O\
-	latin1.$O\
 	rre.$O\
 	rlist.$O\
 
@@ -34,7 +33,6 @@ HFILES=\
 	compat.h\
 	errstr.h\
 	kbd.h\
-	latin1.h\
 	vncv.h\
 	vncs.h\
 
diff --git a/sys/src/cmd/vnc/vncs.c b/sys/src/cmd/vnc/vncs.c
index cb4bbd66f..2b3f7b0ac 100644
--- a/sys/src/cmd/vnc/vncs.c
+++ b/sys/src/cmd/vnc/vncs.c
@@ -55,6 +55,8 @@ struct {
 int	shared;
 int	sleeptime = 5;
 int	verbose = 0;
+int	kbdin = -1;
+
 char *cert;
 char *pixchan = "r5g6b5";
 static int	cmdpid;
@@ -88,6 +90,7 @@ main(int argc, char **argv)
 	int altnet, baseport, cfd, display, exnum, fd, h, killing, w;
 	char adir[NETPATHLEN], ldir[NETPATHLEN];
 	char net[NETPATHLEN], *p;
+	char *kbdfs[] = { "/bin/aux/kbdfs", "-dq", nil };
 	char *rc[] = { "/bin/rc", "-i", nil };
 	Vncs *v;
 
@@ -190,7 +193,7 @@ main(int argc, char **argv)
 	bind("#c", "/dev", MREPL);
 
 	/* run the command */
-	switch(cmdpid = rfork(RFPROC|RFFDG|RFNOTEG|RFNAMEG|RFREND)){
+	switch(cmdpid = rfork(RFPROC|RFFDG|RFNOTEG)){
 	case -1:
 		sysfatal("rfork: %r");
 		break;
@@ -198,12 +201,20 @@ main(int argc, char **argv)
 		if(mounter("/dev", MBEFORE, fd, exnum) < 0)
 			sysfatal("mounter: %r");
 		close(exportfd);
-		close(0);
 		close(1);
 		close(2);
+		open("/dev/cons", OWRITE);
+		open("/dev/cons", OWRITE);
+		if(rfork(RFPROC|RFMEM|RFFDG) == 0){
+			exec(kbdfs[0], kbdfs);
+			_exits("kbdfs");
+		}
+		waitpid();
+		rfork(RFNAMEG);
+		rendezvous(&cmdpid, 0);
+		rfork(RFREND);
+		close(0);
 		open("/dev/cons", OREAD);
-		open("/dev/cons", OWRITE);
-		open("/dev/cons", OWRITE);
 		exec(argv[0], argv);
 		fprint(2, "exec %s: %r\n", argv[0]);
 		_exits(nil);
@@ -212,6 +223,11 @@ main(int argc, char **argv)
 		break;
 	}
 
+	rendezvous(&cmdpid, 0);
+	kbdin = open("/dev/kbdin", OWRITE);
+	unmount(nil, "/dev");
+	bind("#c", "/dev", MREPL);
+
 	/* run the service */
 	srvfd = vncannounce(net, display, adir, baseport);
 	if(srvfd < 0)
@@ -385,6 +401,8 @@ killall(void)
 	srvfd = -1;
 	close(exportfd);
 	exportfd = -1;
+	close(kbdin);
+	kbdin = -1;
 	postnote(PNGROUP, getpid(), killkin);
 }