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

This commit is contained in:
cinap_lenrek 2011-05-28 08:16:01 +00:00
parent 98f4157b5a
commit fbbb449cc0
9 changed files with 237 additions and 98 deletions

View file

@ -152,26 +152,41 @@ This is used on serial consoles.
.SS Keyboard
A read on the
.BR kbd
file returns 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
.B Shift
file returns the character
.B k,
.B K
or
.B Num
will not change
the characters already present in the string, but will
take effect on newly pressed keys. Opening the
.B c
followed by a null terminated, variable-length,
.SM UTF
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
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 @@ to represent a control character.
.SH FILES
.B /dev/lib/kbmap/*
.SH SOURCE
.B /sys/src/cmd/aux/kbdfs.c
.B /sys/src/cmd/aux/kbdfs

View file

@ -101,6 +101,7 @@ Channel *reqchan; /* Req* */
Channel *ctlchan; /* int */
Channel *rawchan; /* Rune */
Channel *runechan; /* Rune */
Channel *linechan; /* char * */
Channel *kbdchan; /* char* */
@ -354,7 +355,7 @@ utfconv(Rune *r, int n)
void
keyproc(void *)
{
Rune rb[Nscan*2];
Rune rb[Nscan*2+1];
int cb[Nscan];
Key key;
int i, nb;
@ -364,42 +365,35 @@ keyproc(void *)
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);
}
}
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, rb+i+1, (nb-i+1) * sizeof(rb[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] = key.b;
rb[nb+1] = key.b;
nb++;
if(nb < nelem(cb) && key.r && key.b != key.r){
cb[nb] = key.c;
rb[nb] = key.r;
rb[nb+1] = key.r;
nb++;
}
rb[0] = 'k';
}
if(rb[0]){
s = utfconv(rb, nb+1);
if(nbsendp(kbdchan, s) <= 0)
free(s);
}
s = utfconv(rb, nb);
if(nbsendp(kbdchan, s) <= 0)
free(s);
}
}
@ -429,7 +423,7 @@ consproc(void *)
}
if(cr = (r == '\r'))
r = '\n';
send(rawchan, &r);
send(runechan, &r);
}
}
n = x - p;
@ -438,6 +432,101 @@ consproc(void *)
}
}
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
*/
@ -496,7 +585,7 @@ ctlproc(void *)
Req *h;
Req **t;
} qcons, qkbd, *q;
enum { Areq, Actl, Araw, Aline, Akbd, Aend };
enum { Areq, Actl, Arune, Aline, Akbd, Aend };
Alt a[Aend+1];
Req *req;
Fid *fid;
@ -510,12 +599,13 @@ ctlproc(void *)
cook = chancreate(sizeof(Rune), 0);
if(scanfd >= 0)
proccreate(scanproc, nil, STACK);
proccreate(scanproc, nil, STACK); /* scanfd -> keychan */
if(consfd >= 0)
proccreate(consproc, nil, STACK);
proccreate(consproc, nil, STACK); /* consfd -> runechan */
threadcreate(keyproc, nil, STACK);
threadcreate(lineproc, cook, STACK);
threadcreate(keyproc, nil, STACK); /* keychan -> rawchan, kbdchan */
threadcreate(runeproc, nil, STACK); /* rawchan -> runechan */
threadcreate(lineproc, cook, STACK); /* cook -> linechan */
raw = 0;
@ -536,9 +626,9 @@ ctlproc(void *)
a[Actl].v = &c;
a[Actl].op = CHANRCV;
a[Araw].c = rawchan;
a[Araw].v = &r;
a[Araw].op = CHANRCV;
a[Arune].c = runechan;
a[Arune].v = &r;
a[Arune].op = CHANRCV;
a[Aline].c = linechan;
a[Aline].v = &s;
@ -553,9 +643,15 @@ ctlproc(void *)
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;
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:
@ -604,8 +700,15 @@ ctlproc(void *)
}
break;
case Araw:
if(raw || kbdopen){
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 {
@ -620,11 +723,6 @@ ctlproc(void *)
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;
@ -643,6 +741,7 @@ ctlproc(void *)
break;
case Akbd:
Havekbd:
if(req = qkbd.h){
if((qkbd.h = req->aux) == nil)
qkbd.t = &qkbd.h;
@ -1166,11 +1265,12 @@ threadmain(int argc, char** argv)
keychan = chancreate(sizeof(Key), 8);
reqchan = chancreate(sizeof(Req*), 0);
ctlchan = chancreate(sizeof(int), 0);
rawchan = chancreate(sizeof(Rune), 32);
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 && linechan && kbdchan))
if(!(keychan && reqchan && ctlchan && rawchan && runechan && linechan && kbdchan))
sysfatal("allocating chans");
elevate();

View file

@ -0,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

View file

@ -0,0 +1,7 @@
</$objtype/mkfile
mklatin.$cputype: mklatin.$O
$LD $LDFLAGS -o $target $prereq
</sys/src/cmd/mkone

View file

@ -17,11 +17,9 @@ TARG=\
disksim\
getflags\
icanhasmsi\
kbdfs\
lines\
listen\
listen1\
mklatinkbd\
ms2\
msexceltables\
mswordstrings\
@ -57,6 +55,7 @@ UPDATE=\
DIRS=mnihongo\
flashfs\
gps\
kbdfs\
na\
vga\
realemu

View file

@ -1234,10 +1234,8 @@ kbdproc(void *arg)
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);

View file

@ -292,20 +292,16 @@ winctl(void *arg)
}
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 @@ winctl(void *arg)
sendp(krm.ck, kbdq[i]);
kbdq[i] = nil;
}else
sendp(krm.ck, strdup(""));
sendp(krm.ck, strdup("K"));
continue;
case WMouse:

View file

@ -217,32 +217,40 @@ kbdproc(void *)
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);
}