plan9fox/sys/src/libdraw/event.c
cinap_lenrek 8556b8dae1 libevent: drop queued mouse events
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.
2013-09-18 01:54:22 +02:00

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;
}