8556b8dae1
the changeset r541ead66e8af: "libdraw: make ebread() return buffer immidiately if available" makes mouse sluggish when the program cant keep up as mouse events queue up. this more or less restores the original behaviour but only for mouse events.
475 lines
8.6 KiB
C
475 lines
8.6 KiB
C
#include <u.h>
|
|
#include <libc.h>
|
|
#include <draw.h>
|
|
#include <cursor.h>
|
|
#include <event.h>
|
|
|
|
typedef struct Slave Slave;
|
|
typedef struct Ebuf Ebuf;
|
|
|
|
struct Slave
|
|
{
|
|
int pid;
|
|
Ebuf *head; /* queue of messages for this descriptor */
|
|
Ebuf *tail;
|
|
int (*fn)(int, Event*, uchar*, int);
|
|
};
|
|
|
|
struct Ebuf
|
|
{
|
|
Ebuf *next;
|
|
int n; /* number of bytes in buf */
|
|
uchar buf[EMAXMSG];
|
|
};
|
|
|
|
static Slave eslave[MAXSLAVE];
|
|
static int Skeyboard = -1;
|
|
static int Smouse = -1;
|
|
static int Stimer = -1;
|
|
static int logfid;
|
|
|
|
static int nslave;
|
|
static int parentpid;
|
|
static int epipe[2];
|
|
|
|
static int eforkslave(ulong);
|
|
static void extract(void);
|
|
static void ekill(void);
|
|
static int enote(void *, char *);
|
|
|
|
static int mousefd;
|
|
static int cursorfd;
|
|
|
|
static
|
|
Ebuf*
|
|
ebread(Slave *s)
|
|
{
|
|
Ebuf *eb;
|
|
|
|
while((eb = s->head) == 0)
|
|
extract();
|
|
s->head = eb->next;
|
|
if(s->head == 0)
|
|
s->tail = 0;
|
|
return eb;
|
|
}
|
|
|
|
ulong
|
|
event(Event *e)
|
|
{
|
|
return eread(~0UL, e);
|
|
}
|
|
|
|
ulong
|
|
eread(ulong keys, Event *e)
|
|
{
|
|
Ebuf *eb;
|
|
int i, id;
|
|
|
|
if(keys == 0)
|
|
return 0;
|
|
for(;;){
|
|
for(i=0; i<nslave; i++)
|
|
if((keys & (1<<i)) && eslave[i].head){
|
|
id = 1<<i;
|
|
if(i == Smouse)
|
|
e->mouse = emouse();
|
|
else if(i == Skeyboard)
|
|
e->kbdc = ekbd();
|
|
else if(i == Stimer)
|
|
eslave[i].head = 0;
|
|
else{
|
|
eb = ebread(&eslave[i]);
|
|
e->n = eb->n;
|
|
if(eslave[i].fn)
|
|
id = (*eslave[i].fn)(id, e, eb->buf, eb->n);
|
|
else
|
|
memmove(e->data, eb->buf, eb->n);
|
|
free(eb);
|
|
}
|
|
return id;
|
|
}
|
|
extract();
|
|
}
|
|
}
|
|
|
|
int
|
|
ecanmouse(void)
|
|
{
|
|
if(Smouse < 0)
|
|
drawerror(display, "events: mouse not initialized");
|
|
return ecanread(Emouse);
|
|
}
|
|
|
|
int
|
|
ecankbd(void)
|
|
{
|
|
if(Skeyboard < 0)
|
|
drawerror(display, "events: keyboard not initialzed");
|
|
return ecanread(Ekeyboard);
|
|
}
|
|
|
|
int
|
|
ecanread(ulong keys)
|
|
{
|
|
Dir *d;
|
|
int i;
|
|
ulong l;
|
|
|
|
for(;;){
|
|
for(i=0; i<nslave; i++)
|
|
if((keys & (1<<i)) && eslave[i].head)
|
|
return 1;
|
|
d = dirfstat(epipe[0]);
|
|
if(d == nil)
|
|
drawerror(display, "events: ecanread stat error");
|
|
l = d->length;
|
|
free(d);
|
|
if(l == 0)
|
|
return 0;
|
|
extract();
|
|
}
|
|
}
|
|
|
|
ulong
|
|
estartfn(ulong key, int fd, int n, int (*fn)(int, Event*, uchar*, int))
|
|
{
|
|
char buf[EMAXMSG+1];
|
|
int i, r;
|
|
|
|
if(fd < 0)
|
|
drawerror(display, "events: bad file descriptor");
|
|
if(n <= 0 || n > EMAXMSG)
|
|
n = EMAXMSG;
|
|
i = eforkslave(key);
|
|
if(i < MAXSLAVE){
|
|
eslave[i].fn = fn;
|
|
return 1<<i;
|
|
}
|
|
buf[0] = i - MAXSLAVE;
|
|
while((r = read(fd, buf+1, n))>0)
|
|
if(write(epipe[1], buf, r+1)!=r+1)
|
|
break;
|
|
buf[0] = MAXSLAVE;
|
|
write(epipe[1], buf, 1);
|
|
_exits(0);
|
|
return 0;
|
|
}
|
|
|
|
ulong
|
|
estart(ulong key, int fd, int n)
|
|
{
|
|
return estartfn(key, fd, n, nil);
|
|
}
|
|
|
|
ulong
|
|
etimer(ulong key, int n)
|
|
{
|
|
char t[2];
|
|
|
|
if(Stimer != -1)
|
|
drawerror(display, "events: timer started twice");
|
|
Stimer = eforkslave(key);
|
|
if(Stimer < MAXSLAVE)
|
|
return 1<<Stimer;
|
|
if(n <= 0)
|
|
n = 1000;
|
|
t[0] = t[1] = Stimer - MAXSLAVE;
|
|
do
|
|
sleep(n);
|
|
while(write(epipe[1], t, 2) == 2);
|
|
t[0] = MAXSLAVE;
|
|
write(epipe[1], t, 1);
|
|
_exits(0);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
ekeyslave(int fd)
|
|
{
|
|
Rune r;
|
|
char t[1+UTFmax], k[10];
|
|
int kr, kn, w;
|
|
|
|
if(eforkslave(Ekeyboard) < MAXSLAVE)
|
|
return;
|
|
kn = 0;
|
|
t[0] = Skeyboard;
|
|
for(;;){
|
|
while(!fullrune(k, kn)){
|
|
kr = read(fd, k+kn, sizeof k - kn);
|
|
if(kr <= 0)
|
|
goto breakout;
|
|
kn += kr;
|
|
}
|
|
w = chartorune(&r, k);
|
|
kn -= w;
|
|
memmove(t+1, k, w);
|
|
memmove(k, &k[w], kn);
|
|
if(write(epipe[1], t, sizeof(t)) != sizeof(t))
|
|
break;
|
|
}
|
|
breakout:;
|
|
t[0] = MAXSLAVE;
|
|
write(epipe[1], t, 1);
|
|
_exits(0);
|
|
}
|
|
|
|
void
|
|
einit(ulong keys)
|
|
{
|
|
int ctl, fd;
|
|
char buf[256];
|
|
|
|
parentpid = getpid();
|
|
if(pipe(epipe) < 0)
|
|
drawerror(display, "events: einit pipe");
|
|
atexit(ekill);
|
|
atnotify(enote, 1);
|
|
snprint(buf, sizeof buf, "%s/mouse", display->devdir);
|
|
mousefd = open(buf, ORDWR|OCEXEC);
|
|
if(mousefd < 0)
|
|
drawerror(display, "einit: can't open mouse\n");
|
|
snprint(buf, sizeof buf, "%s/cursor", display->devdir);
|
|
cursorfd = open(buf, ORDWR|OCEXEC);
|
|
if(cursorfd < 0)
|
|
drawerror(display, "einit: can't open cursor\n");
|
|
if(keys&Ekeyboard){
|
|
snprint(buf, sizeof buf, "%s/cons", display->devdir);
|
|
fd = open(buf, OREAD);
|
|
if(fd < 0)
|
|
drawerror(display, "events: can't open console");
|
|
snprint(buf, sizeof buf, "%s/consctl", display->devdir);
|
|
ctl = open("/dev/consctl", OWRITE|OCEXEC);
|
|
if(ctl < 0)
|
|
drawerror(display, "events: can't open consctl");
|
|
write(ctl, "rawon", 5);
|
|
for(Skeyboard=0; Ekeyboard & ~(1<<Skeyboard); Skeyboard++)
|
|
;
|
|
ekeyslave(fd);
|
|
}
|
|
if(keys&Emouse){
|
|
estart(Emouse, mousefd, 1+4*12);
|
|
for(Smouse=0; Emouse & ~(1<<Smouse); Smouse++)
|
|
;
|
|
}
|
|
}
|
|
|
|
static void
|
|
extract(void)
|
|
{
|
|
Slave *s;
|
|
Ebuf *eb;
|
|
int i, n;
|
|
uchar ebuf[EMAXMSG+1];
|
|
|
|
/* avoid generating a message if there's nothing to show. */
|
|
/* this test isn't perfect, though; could do flushimage(display, 0) then call extract */
|
|
/* also: make sure we don't interfere if we're multiprocessing the display */
|
|
if(display->locking){
|
|
/* if locking is being done by program, this means it can't depend on automatic flush in emouse() etc. */
|
|
if(canqlock(&display->qlock)){
|
|
if(display->bufp > display->buf)
|
|
flushimage(display, 1);
|
|
unlockdisplay(display);
|
|
}
|
|
}else
|
|
if(display->bufp > display->buf)
|
|
flushimage(display, 1);
|
|
loop:
|
|
if((n=read(epipe[0], ebuf, EMAXMSG+1)) < 0
|
|
|| ebuf[0] >= MAXSLAVE)
|
|
drawerror(display, "eof on event pipe");
|
|
if(n == 0)
|
|
goto loop;
|
|
i = ebuf[0];
|
|
if(i >= nslave || n <= 1)
|
|
drawerror(display, "events: protocol error: short read");
|
|
s = &eslave[i];
|
|
if(i == Stimer){
|
|
s->head = (Ebuf *)1;
|
|
return;
|
|
}
|
|
if(i == Skeyboard && n != (1+UTFmax))
|
|
drawerror(display, "events: protocol error: keyboard");
|
|
if(i == Smouse){
|
|
if(n < 1+1+2*12)
|
|
drawerror(display, "events: protocol error: mouse");
|
|
if(ebuf[1] == 'r')
|
|
eresized(1);
|
|
/* squash extraneous mouse events */
|
|
if((eb=s->tail) && memcmp(eb->buf+1+2*12, ebuf+1+1+2*12, 12)==0){
|
|
memmove(eb->buf, &ebuf[1], n - 1);
|
|
return;
|
|
}
|
|
}
|
|
/* try to save space by only allocating as much buffer as we need */
|
|
eb = malloc(sizeof(*eb) - sizeof(eb->buf) + n - 1);
|
|
if(eb == 0)
|
|
drawerror(display, "events: protocol error 4");
|
|
eb->n = n - 1;
|
|
memmove(eb->buf, &ebuf[1], n - 1);
|
|
eb->next = 0;
|
|
if(s->head)
|
|
s->tail->next = eb;
|
|
else
|
|
s->head = eb;
|
|
s->tail = eb;
|
|
}
|
|
|
|
static int
|
|
eforkslave(ulong key)
|
|
{
|
|
int i, pid;
|
|
|
|
for(i=0; i<MAXSLAVE; i++)
|
|
if((key & ~(1<<i)) == 0 && eslave[i].pid == 0){
|
|
if(nslave <= i)
|
|
nslave = i + 1;
|
|
/*
|
|
* share the file descriptors so the last child
|
|
* out closes all connections to the window server.
|
|
*/
|
|
switch(pid = rfork(RFPROC)){
|
|
case 0:
|
|
return MAXSLAVE+i;
|
|
case -1:
|
|
fprint(2, "events: fork error\n");
|
|
exits("fork");
|
|
}
|
|
eslave[i].pid = pid;
|
|
eslave[i].head = eslave[i].tail = 0;
|
|
return i;
|
|
}
|
|
drawerror(display, "events: bad slave assignment");
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
enote(void*, char *s)
|
|
{
|
|
int i, pid;
|
|
|
|
if(strncmp(s, "sys:", 4) == 0 || strcmp(s, "alarm") == 0)
|
|
return 0;
|
|
pid = getpid();
|
|
for(i=0; i<nslave; i++)
|
|
if(pid == eslave[i].pid)
|
|
return 1;
|
|
if(pid != parentpid)
|
|
return 0;
|
|
exits("killed");
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
ekill(void)
|
|
{
|
|
int i, pid;
|
|
|
|
pid = getpid();
|
|
for(i=0; i<nslave; i++){
|
|
if(pid == eslave[i].pid)
|
|
continue; /* don't kill myself */
|
|
postnote(PNPROC, eslave[i].pid, "die");
|
|
}
|
|
}
|
|
|
|
Mouse
|
|
emouse(void)
|
|
{
|
|
Mouse m;
|
|
Ebuf *eb;
|
|
static but[2];
|
|
int b;
|
|
|
|
if(Smouse < 0)
|
|
drawerror(display, "events: mouse not initialized");
|
|
for(;;){
|
|
eb = ebread(&eslave[Smouse]);
|
|
if(!ecanmouse())
|
|
break;
|
|
free(eb); /* drop queued mouse events */
|
|
}
|
|
m.xy.x = atoi((char*)eb->buf+1+0*12);
|
|
m.xy.y = atoi((char*)eb->buf+1+1*12);
|
|
b = atoi((char*)eb->buf+1+2*12);
|
|
m.buttons = b;
|
|
m.msec = atoi((char*)eb->buf+1+3*12);
|
|
if (logfid)
|
|
fprint(logfid, "b: %d xy: %P\n", m.buttons, m.xy);
|
|
free(eb);
|
|
return m;
|
|
}
|
|
|
|
int
|
|
ekbd(void)
|
|
{
|
|
Ebuf *eb;
|
|
Rune r;
|
|
|
|
if(Skeyboard < 0)
|
|
drawerror(display, "events: keyboard not initialzed");
|
|
eb = ebread(&eslave[Skeyboard]);
|
|
chartorune(&r, (char*)eb->buf);
|
|
free(eb);
|
|
return r;
|
|
}
|
|
|
|
void
|
|
emoveto(Point pt)
|
|
{
|
|
char buf[2*12+2];
|
|
int n;
|
|
|
|
n = sprint(buf, "m%d %d", pt.x, pt.y);
|
|
write(mousefd, buf, n);
|
|
}
|
|
|
|
void
|
|
esetcursor(Cursor *c)
|
|
{
|
|
uchar curs[2*4+2*2*16];
|
|
|
|
if(c == 0)
|
|
write(cursorfd, curs, 0);
|
|
else{
|
|
BPLONG(curs+0*4, c->offset.x);
|
|
BPLONG(curs+1*4, c->offset.y);
|
|
memmove(curs+2*4, c->clr, 2*2*16);
|
|
write(cursorfd, curs, sizeof curs);
|
|
}
|
|
}
|
|
|
|
int
|
|
ereadmouse(Mouse *m)
|
|
{
|
|
int n;
|
|
char buf[128];
|
|
|
|
do{
|
|
n = read(mousefd, buf, sizeof(buf));
|
|
if(n < 0) /* probably interrupted */
|
|
return -1;
|
|
n = eatomouse(m, buf, n);
|
|
}while(n == 0);
|
|
return n;
|
|
}
|
|
|
|
int
|
|
eatomouse(Mouse *m, char *buf, int n)
|
|
{
|
|
if(n != 1+4*12){
|
|
werrstr("atomouse: bad count");
|
|
return -1;
|
|
}
|
|
|
|
if(buf[0] == 'r')
|
|
eresized(1);
|
|
m->xy.x = atoi(buf+1+0*12);
|
|
m->xy.y = atoi(buf+1+1*12);
|
|
m->buttons = atoi(buf+1+2*12);
|
|
m->msec = atoi(buf+1+3*12);
|
|
return n;
|
|
}
|