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 .SS Keyboard
A read on the A read on the
.BR kbd .BR kbd
file returns a null terminated, variable-length, file returns the character
.SM UTF .B k,
encoded string of all the keys that are currently pressed (key is .B K
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
or or
.B Num .B c
will not change followed by a null terminated, variable-length,
the characters already present in the string, but will .SM UTF
take effect on newly pressed keys. Opening the 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 .BR kbd
file disables input processing on the file disables input processing on the
.BR cons .BR cons
@ -222,4 +237,4 @@ to represent a control character.
.SH FILES .SH FILES
.B /dev/lib/kbmap/* .B /dev/lib/kbmap/*
.SH SOURCE .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 *ctlchan; /* int */
Channel *rawchan; /* Rune */ Channel *rawchan; /* Rune */
Channel *runechan; /* Rune */
Channel *linechan; /* char * */ Channel *linechan; /* char * */
Channel *kbdchan; /* char* */ Channel *kbdchan; /* char* */
@ -354,7 +355,7 @@ utfconv(Rune *r, int n)
void void
keyproc(void *) keyproc(void *)
{ {
Rune rb[Nscan*2]; Rune rb[Nscan*2+1];
int cb[Nscan]; int cb[Nscan];
Key key; Key key;
int i, nb; int i, nb;
@ -364,42 +365,35 @@ keyproc(void *)
nb = 0; nb = 0;
while(recv(keychan, &key) > 0){ while(recv(keychan, &key) > 0){
if(key.down){ if(key.down && key.r)
switch(key.r){ nbsend(rawchan, &key.r);
case 0:
case Kcaps:
case Knum:
case Kshift:
case Kalt:
case Kctl:
case Kaltgr:
break;
default:
nbsend(rawchan, &key.r);
}
}
rb[0] = 0;
for(i=0; i<nb && cb[i] != key.c; i++) for(i=0; i<nb && cb[i] != key.c; i++)
; ;
if(!key.down){ if(!key.down){
while(i < nb && cb[i] == key.c){ while(i < nb && cb[i] == key.c){
memmove(cb+i, cb+i+1, (nb-i+1) * sizeof(cb[0])); 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--; nb--;
rb[0] = 'K';
} }
} else if(i == nb && nb < nelem(cb) && key.b){ } else if(i == nb && nb < nelem(cb) && key.b){
cb[nb] = key.c; cb[nb] = key.c;
rb[nb] = key.b; rb[nb+1] = key.b;
nb++; nb++;
if(nb < nelem(cb) && key.r && key.b != key.r){ if(nb < nelem(cb) && key.r && key.b != key.r){
cb[nb] = key.c; cb[nb] = key.c;
rb[nb] = key.r; rb[nb+1] = key.r;
nb++; 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')) if(cr = (r == '\r'))
r = '\n'; r = '\n';
send(rawchan, &r); send(runechan, &r);
} }
} }
n = x - p; 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 * Cook lines for cons
*/ */
@ -496,7 +585,7 @@ ctlproc(void *)
Req *h; Req *h;
Req **t; Req **t;
} qcons, qkbd, *q; } qcons, qkbd, *q;
enum { Areq, Actl, Araw, Aline, Akbd, Aend }; enum { Areq, Actl, Arune, Aline, Akbd, Aend };
Alt a[Aend+1]; Alt a[Aend+1];
Req *req; Req *req;
Fid *fid; Fid *fid;
@ -510,12 +599,13 @@ ctlproc(void *)
cook = chancreate(sizeof(Rune), 0); cook = chancreate(sizeof(Rune), 0);
if(scanfd >= 0) if(scanfd >= 0)
proccreate(scanproc, nil, STACK); proccreate(scanproc, nil, STACK); /* scanfd -> keychan */
if(consfd >= 0) if(consfd >= 0)
proccreate(consproc, nil, STACK); proccreate(consproc, nil, STACK); /* consfd -> runechan */
threadcreate(keyproc, nil, STACK); threadcreate(keyproc, nil, STACK); /* keychan -> rawchan, kbdchan */
threadcreate(lineproc, cook, STACK); threadcreate(runeproc, nil, STACK); /* rawchan -> runechan */
threadcreate(lineproc, cook, STACK); /* cook -> linechan */
raw = 0; raw = 0;
@ -536,9 +626,9 @@ ctlproc(void *)
a[Actl].v = &c; a[Actl].v = &c;
a[Actl].op = CHANRCV; a[Actl].op = CHANRCV;
a[Araw].c = rawchan; a[Arune].c = runechan;
a[Araw].v = &r; a[Arune].v = &r;
a[Araw].op = CHANRCV; a[Arune].op = CHANRCV;
a[Aline].c = linechan; a[Aline].c = linechan;
a[Aline].v = &s; a[Aline].v = &s;
@ -553,9 +643,15 @@ ctlproc(void *)
for(;;){ for(;;){
s = nil; s = nil;
a[Araw].op = (b == nil) ? CHANRCV : CHANNOP; if(kbdopen){
a[Aline].op = (b == nil) ? CHANRCV : CHANNOP; a[Arune].op = qkbd.h ? CHANRCV : CHANNOP;
a[Akbd].op = qkbd.h || !kbdopen ? 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)){ switch(alt(a)){
case Areq: case Areq:
@ -604,8 +700,15 @@ ctlproc(void *)
} }
break; break;
case Araw: case Arune:
if(raw || kbdopen){ 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 = emalloc9p(UTFmax+1);
s[runetochar(s, &r)] = 0; s[runetochar(s, &r)] = 0;
} else { } else {
@ -620,11 +723,6 @@ ctlproc(void *)
e = s + strlen(s); e = s + strlen(s);
Havereq: Havereq:
if(kbdopen){
free(b);
b = nil;
break;
}
while(b && (req = qcons.h)){ while(b && (req = qcons.h)){
if((qcons.h = req->aux) == nil) if((qcons.h = req->aux) == nil)
qcons.t = &qcons.h; qcons.t = &qcons.h;
@ -643,6 +741,7 @@ ctlproc(void *)
break; break;
case Akbd: case Akbd:
Havekbd:
if(req = qkbd.h){ if(req = qkbd.h){
if((qkbd.h = req->aux) == nil) if((qkbd.h = req->aux) == nil)
qkbd.t = &qkbd.h; qkbd.t = &qkbd.h;
@ -1166,11 +1265,12 @@ threadmain(int argc, char** argv)
keychan = chancreate(sizeof(Key), 8); keychan = chancreate(sizeof(Key), 8);
reqchan = chancreate(sizeof(Req*), 0); reqchan = chancreate(sizeof(Req*), 0);
ctlchan = chancreate(sizeof(int), 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); linechan = chancreate(sizeof(char*), 16);
kbdchan = 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"); sysfatal("allocating chans");
elevate(); 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\ disksim\
getflags\ getflags\
icanhasmsi\ icanhasmsi\
kbdfs\
lines\ lines\
listen\ listen\
listen1\ listen1\
mklatinkbd\
ms2\ ms2\
msexceltables\ msexceltables\
mswordstrings\ mswordstrings\
@ -57,6 +55,7 @@ UPDATE=\
DIRS=mnihongo\ DIRS=mnihongo\
flashfs\ flashfs\
gps\ gps\
kbdfs\
na\ na\
vga\ vga\
realemu realemu

View file

@ -1234,10 +1234,8 @@ kbdproc(void *arg)
e = p + n; e = p + n;
while(p < e && fullrune(p, e - p)){ while(p < e && fullrune(p, e - p)){
p += chartorune(&r, p); p += chartorune(&r, p);
if(r){ if(r)
chanprint(c, "%C", r); chanprint(c, "c%C", r);
chanprint(c, "");
}
} }
n = e - p; n = e - p;
memmove(buf, p, n); memmove(buf, p, n);

View file

@ -292,20 +292,16 @@ winctl(void *arg)
} }
switch(alt(alts)){ switch(alt(alts)){
case WKbd: 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){ if(w->kbdopen){
i = (kbdqw+1) % nelem(kbdq); i = (kbdqw+1) % nelem(kbdq);
if(i != kbdqr) if(i != kbdqr)
kbdqw = i; kbdqw = i;
} else if(*kbds == 'c'){
Rune r;
chartorune(&r, kbds+1);
if(r)
wkeyctl(w, r);
} }
free(kbdq[kbdqw]); free(kbdq[kbdqw]);
kbdq[kbdqw] = kbds; kbdq[kbdqw] = kbds;
@ -319,7 +315,7 @@ winctl(void *arg)
sendp(krm.ck, kbdq[i]); sendp(krm.ck, kbdq[i]);
kbdq[i] = nil; kbdq[i] = nil;
}else }else
sendp(krm.ck, strdup("")); sendp(krm.ck, strdup("K"));
continue; continue;
case WMouse: case WMouse:

View file

@ -217,32 +217,40 @@ kbdproc(void *)
sysfatal("can't open kbd: %r"); sysfatal("can't open kbd: %r");
buf2[0] = 0; buf2[0] = 0;
buf2[1] = 0;
while((n = read(kfd, buf, sizeof(buf))) > 0){ while((n = read(kfd, buf, sizeof(buf))) > 0){
buf[n-1] = 0; buf[n-1] = 0;
s = buf; switch(buf[0]){
while(*s){ case 'k':
s += chartorune(&r, s); s = buf+1;
if(utfrune(buf2, r) == nil){ while(*s){
e.type = ev_keydown; s += chartorune(&r, s);
if(e.data1 = runetokey(r)){ if(utfrune(buf2+1, r) == nil){
e.data2 = *s == 0 ? e.data1 : -1; if(e.data1 = runetokey(r)){
e.data3 = *s ? e.data1 : -1; e.data2 = *s == 0 ? e.data1 : -1;
D_PostEvent(&e); e.data3 = -1;
e.type = ev_keydown;
D_PostEvent(&e);
}
} }
} }
} break;
s = buf2; case 'K':
while(*s){ s = buf2+1;
s += chartorune(&r, s); while(*s){
if(utfrune(buf, r) == nil){ s += chartorune(&r, s);
e.type = ev_keyup; if(utfrune(buf+1, r) == nil){
if(e.data1 = runetokey(r)){ if(e.data1 = runetokey(r)){
e.data2 = -1; e.data2 = e.data3 = -1;
e.data3 = -1; e.type = ev_keyup;
D_PostEvent(&e); D_PostEvent(&e);
}
} }
} }
break;
default:
continue;
} }
strcpy(buf2, buf); strcpy(buf2, buf);
} }