git: 9front

Download patch

ref: 8f0174a92c00ebeec6dd766f2f7c1b457fdc3182
parent: bb85411a83ba9779849a9b7dade04ef6fcaf7513
author: cinap_lenrek <cinap_lenrek@centraldogma>
date: Wed May 11 01:55:48 EDT 2011

add /dev/kbd support to rio

--- a/sys/include/keyboard.h
+++ b/sys/include/keyboard.h
@@ -22,25 +22,42 @@
 enum {
 	KF=	0xF000,	/* Rune: beginning of private Unicode space */
 	Spec=	0xF800,
+	PF=	Spec|0x20,	/* num pad function key */
+	Kview=	Spec|0x00,	/* view (shift window up) */
 	/* KF|1, KF|2, ..., KF|0xC is F1, F2, ..., F12 */
 	Khome=	KF|0x0D,
 	Kup=	KF|0x0E,
+	Kdown=	Kview,
 	Kpgup=	KF|0x0F,
 	Kprint=	KF|0x10,
 	Kleft=	KF|0x11,
 	Kright=	KF|0x12,
-	Kdown=	Spec|0x00,
-	Kview=	Spec|0x00,
 	Kpgdown=	KF|0x13,
 	Kins=	KF|0x14,
-	Kend=	KF|0x18,
 
-	Kalt=		KF|0x15,
+	Kalt=	KF|0x15,
 	Kshift=	KF|0x16,
-	Kctl=		KF|0x17,
+	Kctl=	KF|0x17,
 
+	Kend=	KF|0x18,
+	Kscroll=	KF|0x19,
+	Kscrolloneup=	KF|0x20,
+	Kscrollonedown=	KF|0x21,
+
+	Ksoh=	0x01,
+	Keof=	0x04,
+	Kenq=	0x05,
+	Kack=	0x06,
 	Kbs=	0x08,
+	Knack=	0x15,
+	Ketb=	0x17,
 	Kdel=	0x7f,
 	Kesc=	0x1b,
-	Keof=	0x04,
+
+	Kbreak=	Spec|0x61,
+	Kcaps=	Spec|0x64,
+	Knum=	Spec|0x65,
+	Kmiddle=	Spec|0x66,
+	Kaltgr=	Spec|0x67,
+	Kmouse=	Spec|0x100,
 };
--- a/sys/src/cmd/aux/kbdfs.c
+++ b/sys/src/cmd/aux/kbdfs.c
@@ -3,43 +3,12 @@
 #include <auth.h>
 #include <fcall.h>
 #include <thread.h>
+#include <keyboard.h>
 #include <9p.h>
 
 enum {
-	Spec=		0xF800,		/* Unicode private space */
-	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,
-	Altgr=		Spec|0x67,
-	Kmouse=		Spec|0x100,
-	No=		0x00,		/* peter */
-
-	Home=		KF|13,
-	Up=		KF|14,
-	Pgup=		KF|15,
-	Print=		KF|16,
-	Left=		KF|17,
-	Right=		KF|18,
-	End=		KF|24,
-	Down=		View,
-	Pgdown=		KF|19,
-	Ins=		KF|20,
-	Del=		0x7F,
-	Scroll=		KF|21,
-
 	Nscan=	128,
 
-	Int=	0,			/* scans indices */
-	Ext,
-	Nscans,
-
 	Qroot=	0,
 	Qkbd,
 	Qkbin,
@@ -138,102 +107,102 @@
  */
 Rune kbtab[Nscan] = 
 {
-[0x00]	No,	0x1b,	'1',	'2',	'3',	'4',	'5',	'6',
+[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',	Ctrl,	'a',	's',
+[0x18]	'o',	'p',	'[',	']',	'\n',	Kctl,	'a',	's',
 [0x20]	'd',	'f',	'g',	'h',	'j',	'k',	'l',	';',
-[0x28]	'\'',	'`',	Shift,	'\\',	'z',	'x',	'c',	'v',
-[0x30]	'b',	'n',	'm',	',',	'.',	'/',	Shift,	'*',
-[0x38]	Latin,	' ',	Ctrl,	KF|1,	KF|2,	KF|3,	KF|4,	KF|5,
-[0x40]	KF|6,	KF|7,	KF|8,	KF|9,	KF|10,	Num,	Scroll,	'7',
+[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',	'.',	No,	No,	No,	KF|11,
-[0x58]	KF|12,	No,	No,	No,	No,	No,	No,	No,
-[0x60]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x68]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x70]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x78]	No,	View,	No,	Up,	No,	No,	No,	No,
+[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]	No,	0x1b,	'!',	'@',	'#',	'$',	'%',	'^',
+[0x00]	0,	0x1b,	'!',	'@',	'#',	'$',	'%',	'^',
 [0x08]	'&',	'*',	'(',	')',	'_',	'+',	'\b',	'\t',
 [0x10]	'Q',	'W',	'E',	'R',	'T',	'Y',	'U',	'I',
-[0x18]	'O',	'P',	'{',	'}',	'\n',	Ctrl,	'A',	'S',
+[0x18]	'O',	'P',	'{',	'}',	'\n',	Kctl,	'A',	'S',
 [0x20]	'D',	'F',	'G',	'H',	'J',	'K',	'L',	':',
-[0x28]	'"',	'~',	Shift,	'|',	'Z',	'X',	'C',	'V',
-[0x30]	'B',	'N',	'M',	'<',	'>',	'?',	Shift,	'*',
-[0x38]	Latin,	' ',	Ctrl,	KF|1,	KF|2,	KF|3,	KF|4,	KF|5,
-[0x40]	KF|6,	KF|7,	KF|8,	KF|9,	KF|10,	Num,	Scroll,	'7',
+[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',	'.',	No,	No,	No,	KF|11,
-[0x58]	KF|12,	No,	No,	No,	No,	No,	No,	No,
-[0x60]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x68]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x70]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x78]	No,	Up,	No,	Up,	No,	No,	No,	No,
+[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]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x08]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x10]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x18]	No,	No,	No,	No,	'\n',	Ctrl,	No,	No,
-[0x20]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x28]	No,	No,	Shift,	No,	No,	No,	No,	No,
-[0x30]	No,	No,	No,	No,	No,	'/',	No,	Print,
-[0x38]	Altgr,	No,	No,	No,	No,	No,	No,	No,
-[0x40]	No,	No,	No,	No,	No,	No,	Break,	Home,
-[0x48]	Up,	Pgup,	No,	Left,	No,	Right,	No,	End,
-[0x50]	Down,	Pgdown,	Ins,	Del,	No,	No,	No,	No,
-[0x58]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x60]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x68]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x70]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x78]	No,	Up,	No,	No,	No,	No,	No,	No,
+[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]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x08]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x10]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x18]	No,	No,	No,	No,	'\n',	Ctrl,	No,	No,
-[0x20]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x28]	No,	No,	Shift,	No,	No,	No,	No,	No,
-[0x30]	No,	No,	No,	No,	No,	'/',	No,	Print,
-[0x38]	Altgr,	No,	No,	No,	No,	No,	No,	No,
-[0x40]	No,	No,	No,	No,	No,	No,	Break,	Home,
-[0x48]	Up,	Pgup,	No,	Left,	No,	Right,	No,	End,
-[0x50]	Down,	Pgdown,	Ins,	Del,	No,	No,	No,	No,
-[0x58]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x60]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x68]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x70]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x78]	No,	Up,	No,	No,	No,	No,	No,	No,
+[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 kbtabctrl[Nscan] =
 {
-[0x00]	No,	'', 	'', 	'', 	'', 	'', 	'', 	'', 
+[0x00]	0,	'', 	'', 	'', 	'', 	'', 	'', 	'', 
 [0x08]	'', 	'', 	'', 	'', 	'
', 	'', 	'\b',	'\t',
 [0x10]	'', 	'', 	'', 	'', 	'', 	'', 	'', 	'\t',
-[0x18]	'', 	'', 	'', 	'', 	'\n',	Ctrl,	'', 	'', 
+[0x18]	'', 	'', 	'', 	'', 	'\n',	Kctl,	'', 	'', 
 [0x20]	'', 	'', 	'', 	'\b',	'\n',	'', 	'', 	'', 
-[0x28]	'', 	No, 	Shift,	'', 	'', 	'', 	'', 	'', 
-[0x30]	'', 	'', 	'
', 	'', 	'', 	'', 	Shift,	'\n',
-[0x38]	Latin,	No, 	Ctrl,	'', 	'', 	'', 	'', 	'', 
+[0x28]	'', 	0, 	Kshift,	'', 	'', 	'', 	'', 	'', 
+[0x30]	'', 	'', 	'
', 	'', 	'', 	'', 	Kshift,	'\n',
+[0x38]	Kalt,	0, 	Kctl,	'', 	'', 	'', 	'', 	'', 
 [0x40]	'', 	'', 	'', 	'
', 	'', 	'', 	'', 	'', 
 [0x48]	'', 	'', 	'
', 	'', 	'', 	'', 	'', 	'', 
-[0x50]	'', 	'', 	'', 	'', 	No,	No,	No,	'', 
-[0x58]	'', 	No,	No,	No,	No,	No,	No,	No,
-[0x60]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x68]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x70]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x78]	No,	'', 	No,	'\b',	No,	No,	No,	No,
+[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);
@@ -278,7 +247,7 @@
 	if(scan->caps && key.r<='z' && key.r>='a')
 		key.r += 'A' - 'a';
 
-	if(scan->ctrl && scan->latin && key.r == Del)
+	if(scan->ctrl && scan->latin && key.r == Kdel)
 		reboot();
 
 	send(keychan, &key);
@@ -289,22 +258,22 @@
 		scan->esc2--;
 
 	switch(key.r){
-	case Shift:
+	case Kshift:
 		scan->shift = key.down;
 		break;
-	case Ctrl:
+	case Kctl:
 		scan->ctrl = key.down;
 		break;
-	case Altgr:
+	case Kaltgr:
 		scan->altgr = key.down;
 		break;
-	case Latin:
+	case Kalt:
 		scan->latin = key.down;
 		break;
-	case Num:
+	case Knum:
 		scan->num ^= key.down;
 		break;
-	case Caps:
+	case Kcaps:
 		scan->caps ^= key.down;
 		break;
 	}
@@ -377,20 +346,13 @@
 	while(recv(keychan, &key) > 0){
 		if(key.down){
 			switch(key.r){
-			case No:
-			case Caps:
-			case Num:
-			case Shift:
-			case Latin:
-			case Ctrl:
-			case Altgr:
-			case Kmouse|1:
-			case Kmouse|2:
-			case Kmouse|3:
-			case Kmouse|4:
-			case Kmouse|5:
-			case KF|11:
-			case KF|12:
+			case 0:
+			case Kcaps:
+			case Knum:
+			case Kshift:
+			case Kalt:
+			case Kctl:
+			case Kaltgr:
 				break;
 			default:
 				nbsend(rawchan, &key.r);
@@ -397,7 +359,6 @@
 			}
 		}
 
-		s = nil;
 		for(i=0; i<nb && cb[i] != key.c; i++)
 			;
 		if(!key.down){
@@ -405,15 +366,14 @@
 				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--;
-				s = utfconv(rb, nb);
 			}
 		} else if(i == nb && nb < nelem(cb) && key.r){
 			cb[nb] = key.c;
 			rb[nb] = key.r;
 			nb++;
-			s = utfconv(rb, nb);
 		}
-		if(s && nbsendp(kbdchan, s) <= 0)
+		s = utfconv(rb, nb);
+		if(nbsendp(kbdchan, s) <= 0)
 			free(s);
 	}
 }
@@ -480,7 +440,7 @@
 				}
 				write(echofd, "\b", 1);
 				continue;
-			case 0x04:	/* eof */
+			case Keof:
 				p = l;
 				done = 1;
 				break;
--- a/sys/src/cmd/rio/dat.h
+++ b/sys/src/cmd/rio/dat.h
@@ -9,6 +9,7 @@
 	Qwinname,
 	Qkbdin,
 	Qlabel,
+	Qkbd,
 	Qmouse,
 	Qnew,
 	Qscreen,
@@ -22,16 +23,11 @@
 	QMAX,
 };
 
-enum
-{
-	Kscrolloneup = KF|0x20,
-	Kscrollonedown = KF|0x21,
-};
-
 #define	STACK	8192
 
 typedef	struct	Consreadmesg Consreadmesg;
 typedef	struct	Conswritemesg Conswritemesg;
+typedef struct	Kbdreadmesg Kbdreadmesg;
 typedef	struct	Stringpair Stringpair;
 typedef	struct	Dirtab Dirtab;
 typedef	struct	Fid Fid;
@@ -98,6 +94,11 @@
 	Channel	*cm;		/* chan(Mouse) */
 };
 
+struct Kbdreadmesg
+{
+	Channel *ck;		/* chan(char*) */
+};
+
 struct Stringpair	/* rune and nrune or byte and nbyte */
 {
 	void		*s;
@@ -129,12 +130,13 @@
 	Image		*i;
 	Mousectl		mc;
 	Mouseinfo	mouse;
-	Channel		*ck;			/* chan(Rune[10]) */
+	Channel		*ck;			/* chan(char*) */
 	Channel		*cctl;		/* chan(Wctlmesg)[20] */
 	Channel		*conswrite;	/* chan(Conswritemesg) */
 	Channel		*consread;	/* chan(Consreadmesg) */
 	Channel		*mouseread;	/* chan(Mousereadmesg) */
 	Channel		*wctlread;		/* chan(Consreadmesg) */
+	Channel		*kbdread;	/* chan(Kbdreadmesg) */
 	uint			nr;			/* number of runes in window */
 	uint			maxr;		/* number of runes allocated in r */
 	Rune			*r;
@@ -167,6 +169,7 @@
 	uchar		wctlopen;
 	uchar		deleted;
 	uchar		mouseopen;
+	uchar		kbdopen;
 	char			*label;
 	int			pid;
 	char			*dir;
@@ -303,7 +306,6 @@
 Font		*font;
 Mousectl	*mousectl;
 Mouse	*mouse;
-Keyboardctl	*keyboardctl;
 Display	*display;
 Image	*view;
 Screen	*wscreen;
--- a/sys/src/cmd/rio/fsys.c
+++ b/sys/src/cmd/rio/fsys.c
@@ -32,6 +32,7 @@
 	{ "winname",	QTFILE,	Qwinname,	0400 },
 	{ "kbdin",		QTFILE,	Qkbdin,		0200 },
 	{ "label",		QTFILE,	Qlabel,		0600 },
+	{ "kbd",	QTFILE,	Qkbd,		0600 },
 	{ "mouse",	QTFILE,	Qmouse,		0600 },
 	{ "screen",		QTFILE,	Qscreen,		0400 },
 	{ "snarf",		QTFILE,	Qsnarf,		0600 },
--- a/sys/src/cmd/rio/rio.c
+++ b/sys/src/cmd/rio/rio.c
@@ -33,6 +33,7 @@
 void		resized(void);
 Channel	*exitchan;	/* chan(int) */
 Channel	*winclosechan; /* chan(Window*); */
+Channel *kbdchan;	/* chan(char*); */
 Rectangle	viewr;
 int		threadrforkflag = 0;	/* should be RFENVG but that hides rio from plumber */
 
@@ -41,6 +42,7 @@
 void winclosethread(void*);
 void deletethread(void*);
 void	initcmd(void*);
+Channel* initkbd(void);
 
 char		*fontname;
 int		mainpid;
@@ -189,8 +191,8 @@
 	if(mousectl == nil)
 		error("can't find mouse");
 	mouse = mousectl;
-	keyboardctl = initkeyboard(nil);
-	if(keyboardctl == nil)
+	kbdchan = initkbd();
+	if(kbdchan == nil)
 		error("can't find keyboard");
 	wscreen = allocscreen(screen, background, 0);
 	if(wscreen == nil)
@@ -332,21 +334,12 @@
 void
 keyboardthread(void*)
 {
-	Rune buf[2][20], *rp;
-	int n, i;
+	char *s;
 
 	threadsetname("keyboardthread");
-	n = 0;
-	for(;;){
-		rp = buf[n];
-		n = 1-n;
-		recv(keyboardctl->c, rp);
-		for(i=1; i<nelem(buf[0])-1; i++)
-			if(nbrecv(keyboardctl->c, rp+i) <= 0)
-				break;
-		rp[i] = L'\0';
-		if(input != nil)
-			sendp(input->ck, rp);
+	while(s = recvp(kbdchan)){
+		if(input == nil || sendp(input->ck, s) <= 0)
+			free(s);
 	}
 }
 
@@ -356,15 +349,22 @@
 void
 keyboardsend(char *s, int cnt)
 {
-	Rune *r;
-	int i, nb, nr;
+	if(cnt <= 0)
+		return;
+	if(s[cnt-1] == 0)
+		chanprint(kbdchan, "%s", s);
+	else {
+		Rune *r;
+		int i, nb, nr;
 
-	r = runemalloc(cnt);
-	/* BUGlet: partial runes will be converted to error runes */
-	cvttorunes(s, cnt, r, &nb, &nr, nil);
-	for(i=0; i<nr; i++)
-		send(keyboardctl->c, &r[i]);
-	free(r);
+		r = runemalloc(cnt);
+		cvttorunes(s, cnt, r, &nb, &nr, nil);
+		for(i=0; i<nr; i++){
+			chanprint(kbdchan, "%C", r[i]);
+			chanprint(kbdchan, "");
+		}
+		free(r);
+	}
 }
 
 int
@@ -1141,7 +1141,7 @@
 	if(i == nil)
 		return nil;
 	cm = chancreate(sizeof(Mouse), 0);
-	ck = chancreate(sizeof(Rune*), 0);
+	ck = chancreate(sizeof(char*), 0);
 	cctl = chancreate(sizeof(Wctlmesg), 4);
 	cpid = chancreate(sizeof(int), 0);
 	if(cm==nil || ck==nil || cctl==nil)
@@ -1188,4 +1188,79 @@
 		w->dir = estrdup(dir);
 	chanfree(cpid);
 	return w;
+}
+
+static void
+kbdioproc(void *arg)
+{
+	Channel *c = arg;
+	char buf[128], *p, *e;
+	int fd, cfd, kfd, n;
+
+	threadsetname("kbdioproc");
+
+	if((fd = open("/dev/cons", OREAD)) < 0){
+		chanprint(c, "%r");
+		return;
+	}
+	if((cfd = open("/dev/consctl", OWRITE)) < 0){
+		chanprint(c, "%r");
+		return;
+	}
+	fprint(cfd, "rawon");
+
+	if(sendp(c, nil) <= 0){
+		close(cfd);
+		close(fd);
+		return;
+	}
+
+	if((kfd = open("/dev/kbd", OREAD)) >= 0){
+		close(fd);
+
+		/* read kbd state */
+		while((n = read(kfd, buf, sizeof(buf))) > 0)
+			chanprint(c, "%.*s", n, buf);
+		close(kfd);
+	} else {
+		/* read single characters */
+		p = buf;
+		for(;;){
+			Rune r;
+
+			e = buf + sizeof(buf);
+			if((n = read(fd, p, e-p)) <= 0)
+				break;
+			e = p + n;
+			while(p < e && fullrune(p, e - p)){
+				p += chartorune(&r, p);
+				chanprint(c, "%C", r);
+				chanprint(c, "");
+			}
+			n = e - p;
+			if(n > 0){
+				memmove(buf, p, n);
+				p = buf + n;
+			}
+		}
+		close(fd);
+	}
+	close(cfd);
+}
+
+Channel*
+initkbd(void)
+{
+	Channel *c;
+	char *err;
+
+	c = chancreate(sizeof(char*), 16);
+	proccreate(kbdioproc, c, STACK);
+	if(err = recvp(c)){
+		chanfree(c);
+		werrstr(err);
+		free(err);
+		return nil;
+	}
+	return c;
 }
--- a/sys/src/cmd/rio/wind.c
+++ b/sys/src/cmd/rio/wind.c
@@ -65,6 +65,7 @@
 	w->cursorp = nil;
 	w->conswrite = chancreate(sizeof(Conswritemesg), 0);
 	w->consread =  chancreate(sizeof(Consreadmesg), 0);
+	w->kbdread =  chancreate(sizeof(Kbdreadmesg), 0);
 	w->mouseread =  chancreate(sizeof(Mousereadmesg), 0);
 	w->wctlread =  chancreate(sizeof(Consreadmesg), 0);
 	w->scrollr = r;
@@ -184,21 +185,23 @@
 void
 winctl(void *arg)
 {
-	Rune *rp, *bp, *tp, *up, *kbdr;
+	Rune *rp, *bp, *tp, *up;
 	uint qh;
 	int nr, nb, c, wid, i, npart, initial, lastb;
 	char *s, *t, part[3];
 	Window *w;
 	Mousestate *mp, m;
-	enum { WKey, WMouse, WMouseread, WCtl, WCwrite, WCread, WWread, NWALT };
+	enum { WKbd, WKbdread, WMouse, WMouseread, WCtl, WCwrite, WCread, WWread, NWALT };
 	Alt alts[NWALT+1];
 	Mousereadmesg mrm;
+	Kbdreadmesg krm;
 	Conswritemesg cwm;
 	Consreadmesg crm;
 	Consreadmesg cwrm;
 	Stringpair pair;
 	Wctlmesg wcm;
-	char buf[4*12+1];
+	char buf[4*12+1], *kbdq[8], *kbds;
+	int kbdqr, kbdqw;
 
 	w = arg;
 	snprint(buf, sizeof buf, "winctl-id%d", w->id);
@@ -205,6 +208,7 @@
 	threadsetname(buf);
 
 	mrm.cm = chancreate(sizeof(Mouse), 0);
+	krm.ck = chancreate(sizeof(char*), 0);
 	cwm.cw = chancreate(sizeof(Stringpair), 0);
 	crm.c1 = chancreate(sizeof(Stringpair), 0);
 	crm.c2 = chancreate(sizeof(Stringpair), 0);
@@ -211,10 +215,12 @@
 	cwrm.c1 = chancreate(sizeof(Stringpair), 0);
 	cwrm.c2 = chancreate(sizeof(Stringpair), 0);
 	
-
-	alts[WKey].c = w->ck;
-	alts[WKey].v = &kbdr;
-	alts[WKey].op = CHANRCV;
+	alts[WKbd].c = w->ck;
+	alts[WKbd].v = &kbds;
+	alts[WKbd].op = CHANRCV;
+	alts[WKbdread].c = w->kbdread;
+	alts[WKbdread].v = &krm;
+	alts[WKbdread].op = CHANSND;
 	alts[WMouse].c = w->mc.c;
 	alts[WMouse].v = &w->mc.Mouse;
 	alts[WMouse].op = CHANRCV;
@@ -235,21 +241,20 @@
 	alts[WWread].op = CHANSND;
 	alts[NWALT].op = CHANEND;
 
+	memset(kbdq, 0, sizeof(kbdq));
+	kbdqr = kbdqw = 0;
 	npart = 0;
 	lastb = -1;
 	for(;;){
-		if(w->mouseopen && w->mouse.counter != w->mouse.lastcounter)
-			alts[WMouseread].op = CHANSND;
-		else
-			alts[WMouseread].op = CHANNOP;
-		if(!w->scrolling && !w->mouseopen && w->qh>w->org+w->nchars)
-			alts[WCwrite].op = CHANNOP;
-		else
-			alts[WCwrite].op = CHANSND;
-		if(w->deleted || !w->wctlready)
-			alts[WWread].op = CHANNOP;
-		else
-			alts[WWread].op = CHANSND;
+		alts[WKbdread].op = (w->kbdopen && kbdqw != kbdqr) ?
+			CHANSND : CHANNOP;
+		alts[WMouseread].op = (w->mouseopen && w->mouse.counter != w->mouse.lastcounter) ? 
+			CHANSND : CHANNOP;
+		alts[WCwrite].op = (!w->scrolling && !w->mouseopen && w->qh>w->org+w->nchars) ?
+			CHANNOP : CHANSND;
+		alts[WWread].op = (w->deleted || !w->wctlready) ?
+			CHANNOP : CHANSND;
+
 		/* this code depends on NL and EOT fitting in a single byte */
 		/* kind of expensive for each loop; worth precomputing? */
 		if(w->holding)
@@ -267,13 +272,36 @@
 			}
 		}
 		switch(alt(alts)){
-		case WKey:
-			for(i=0; kbdr[i]!=L'\0'; i++)
-				wkeyctl(w, kbdr[i]);
-//			wkeyctl(w, r);
-///			while(nbrecv(w->ck, &r))
-//				wkeyctl(w, r);
+		case WKbd:
+			if(utflen(kbds) >= utflen(kbdq[kbdqw] ? kbdq[kbdqw] : "")){
+				Rune r;
+
+				i = 0;
+				r = 0;
+				while(kbds[i])
+					i += chartorune(&r, kbds+i);
+				wkeyctl(w, r);
+			}
+			if(w->kbdopen){
+				i = (kbdqw+1) % nelem(kbdq);
+				if(i != kbdqr)
+					kbdqw = i;
+			}
+			free(kbdq[kbdqw]);
+			kbdq[kbdqw] = kbds;
 			break;
+
+		case WKbdread:
+			i = (kbdqr+1) % nelem(kbdq);
+			if(kbdqr != kbdqw)
+				kbdqr = i;
+			if(kbdq[i]){
+				sendp(krm.ck, kbdq[i]);
+				kbdq[i] = nil;
+			}else
+				sendp(krm.ck, strdup(""));
+			continue;
+
 		case WMouse:
 			if(w->mouseopen) {
 				w->mouse.counter++;
@@ -309,9 +337,12 @@
 			continue;
 		case WCtl:
 			if(wctlmesg(w, wcm.type, wcm.r, wcm.image) == Exited){
+				for(i=0; i<nelem(kbdq); i++)
+					free(kbdq[i]);
 				chanfree(crm.c1);
 				chanfree(crm.c2);
 				chanfree(mrm.cm);
+				chanfree(krm.ck);
 				chanfree(cwm.cw);
 				chanfree(cwrm.c1);
 				chanfree(cwrm.c2);
@@ -560,8 +591,17 @@
 	Rune *rp;
 	int *notefd;
 
-	if(r == 0)
+	switch(r){
+	case 0:
+	case Kcaps:
+	case Knum:
+	case Kshift:
+	case Kalt:
+	case Kctl:
+	case Kaltgr:
 		return;
+	}
+
 	if(w->deleted)
 		return;
 	/* navigation keys work only when mouse is not open */
@@ -615,7 +655,7 @@
 		case Kend:
 			wshow(w, w->nr);
 			return;
-		case 0x01:	/* ^A: beginning of line */
+		case Ksoh:	/* ^A: beginning of line */
 			if(w->q0==0 || w->q0==w->qh || w->r[w->q0-1]=='\n')
 				return;
 			nb = wbswidth(w, 0x15 /* ^U */);
@@ -622,7 +662,7 @@
 			wsetselect(w, w->q0-nb, w->q0-nb);
 			wshow(w, w->q0);
 			return;
-		case 0x05:	/* ^E: end of line */
+		case Kenq:	/* ^E: end of line */
 			q0 = w->q0;
 			while(q0 < w->nr && w->r[q0]!='\n')
 				q0++;
@@ -634,21 +674,21 @@
 		waddraw(w, &r, 1);
 		return;
 	}
-	if(r==0x1B || (w->holding && r==0x7F)){	/* toggle hold */
+	if(r==Kesc || (w->holding && r==Kdel)){	/* toggle hold */
 		if(w->holding)
 			--w->holding;
 		else
 			w->holding++;
 		wrepaint(w);
-		if(r == 0x1B)
+		if(r == Kesc)
 			return;
 	}
-	if(r != 0x7F){
+	if(r != Kdel){
 		wsnarf(w);
 		wcut(w);
 	}
 	switch(r){
-	case 0x7F:		/* send interrupt */
+	case Kdel:	/* send interrupt */
 		w->qh = w->nr;
 		wshow(w, w->qh);
 		notefd = emalloc(sizeof(int));
@@ -655,8 +695,8 @@
 		*notefd = w->notefd;
 		proccreate(interruptproc, notefd, 4096);
 		return;
-	case 0x06:	/* ^F: file name completion */
-	case Kins:		/* Insert: file name completion */
+	case Kack:	/* ^F: file name completion */
+	case Kins:	/* Insert: file name completion */
 		rp = namecomplete(w);
 		if(rp == nil)
 			return;
@@ -666,9 +706,9 @@
 		wshow(w, q0+nr);
 		free(rp);
 		return;
-	case 0x08:	/* ^H: erase character */
-	case 0x15:	/* ^U: erase line */
-	case 0x17:	/* ^W: erase word */
+	case Kbs:	/* ^H: erase character */
+	case Knack:	/* ^U: erase line */
+	case Ketb:	/* ^W: erase word */
 		if(w->q0==0 || w->q0==w->qh)
 			return;
 		nb = wbswidth(w, r);
@@ -1122,6 +1162,7 @@
 		chanfree(w->consread);
 		chanfree(w->mouseread);
 		chanfree(w->wctlread);
+		chanfree(w->kbdread);
 		free(w->raw);
 		free(w->r);
 		free(w->dir);
--- a/sys/src/cmd/rio/xfid.c
+++ b/sys/src/cmd/rio/xfid.c
@@ -243,6 +243,13 @@
 			return;
 		}
 		break;
+	case Qkbd:
+		if(w->kbdopen){
+			filsysrespond(x->fs, x, &t, Einuse);
+			return;
+		}
+		w->kbdopen = TRUE;
+		break;
 	case Qmouse:
 		if(w->mouseopen){
 			filsysrespond(x->fs, x, &t, Einuse);
@@ -318,6 +325,9 @@
 		w->cursorp = nil;
 		wsetcursor(w, FALSE);
 		break;
+	case Qkbd:
+		w->kbdopen = FALSE;
+		break;
 	case Qmouse:
 		w->resized = FALSE;
 		w->mouseopen = FALSE;
@@ -587,6 +597,7 @@
 	Consreadmesg crm;
 	Mousereadmesg mrm;
 	Consreadmesg cwrm;
+	Kbdreadmesg krm;
 	Stringpair pair;
 	enum { CRdata, CRflush, NCR };
 	enum { MRdata, MRflush, NMR };
@@ -694,6 +705,42 @@
 		fc.count = min(n, cnt);
 		filsysrespond(x->fs, x, &fc, nil);
 		qunlock(&x->active);
+		break;
+
+	case Qkbd:
+		x->flushtag = x->tag;
+
+		alts[MRdata].c = w->kbdread;
+		alts[MRdata].v = &krm;
+		alts[MRdata].op = CHANRCV;
+		alts[MRflush].c = x->flushc;
+		alts[MRflush].v = nil;
+		alts[MRflush].op = CHANRCV;
+		alts[NMR].op = CHANEND;
+
+		switch(alt(alts)){
+		case MRdata:
+			break;
+		case MRflush:
+			filsyscancel(x);
+			return;
+		}
+
+		/* received data */
+		t = recvp(krm.ck);
+		x->flushtag = -1;
+		if(x->flushing){
+			free(t);		/* wake up window and toss data */
+			recv(x->flushc, nil);		/* wake up flushing xfid */
+			filsyscancel(x);
+			return;
+		}
+		qlock(&x->active);
+		fc.data = t;
+		fc.count = strlen(t)+1;
+		filsysrespond(x->fs, x, &fc, nil);
+		qunlock(&x->active);
+		free(t);
 		break;
 
 	case Qcursor:
--