distinguish between "cannot receive without blocking" and "EOF on connection". In libmux, do not elect async guys muxers, so that synchronous RPC calls run in the main event loop (e.g., in eresized) do not get stuck. Fixes problem reported by Lu Xuxiao, namely that jpg etc. would spin at 100% cpu usage.
399 lines
6.7 KiB
C
399 lines
6.7 KiB
C
/* Copyright (c) 2006 Russ Cox */
|
|
|
|
#include <u.h>
|
|
#include <sys/select.h>
|
|
#include <libc.h>
|
|
#include <draw.h>
|
|
#include <mouse.h>
|
|
#include <cursor.h>
|
|
#include <drawfcall.h>
|
|
#include <mux.h>
|
|
|
|
int chattydrawclient;
|
|
|
|
static int drawgettag(Mux *mux, void *vmsg);
|
|
static void* drawrecv(Mux *mux);
|
|
static int drawnbrecv(Mux *mux, void**);
|
|
static int drawsend(Mux *mux, void *vmsg);
|
|
static int drawsettag(Mux *mux, void *vmsg, uint tag);
|
|
static int canreadfd(int);
|
|
|
|
int
|
|
_displayconnect(Display *d)
|
|
{
|
|
int pid, p[2];
|
|
|
|
fmtinstall('W', drawfcallfmt);
|
|
fmtinstall('H', encodefmt);
|
|
|
|
if(pipe(p) < 0)
|
|
return -1;
|
|
if((pid=fork()) < 0){
|
|
close(p[0]);
|
|
close(p[1]);
|
|
return -1;
|
|
}
|
|
if(pid == 0){
|
|
close(p[0]);
|
|
dup(p[1], 0);
|
|
dup(p[1], 1);
|
|
/* execl("strace", "strace", "-o", "drawsrv.out", "drawsrv", nil); */
|
|
/*
|
|
* The argv0 has no meaning to devdraw.
|
|
* Pass it along only so that the various
|
|
* devdraws in psu -a can be distinguished.
|
|
*/
|
|
execl("devdraw", "devdraw", argv0, nil);
|
|
sysfatal("exec devdraw: %r");
|
|
}
|
|
close(p[1]);
|
|
d->srvfd = p[0];
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
_displaymux(Display *d)
|
|
{
|
|
if((d->mux = mallocz(sizeof(*d->mux), 1)) == nil)
|
|
return -1;
|
|
|
|
d->mux->mintag = 1;
|
|
d->mux->maxtag = 255;
|
|
d->mux->send = drawsend;
|
|
d->mux->recv = drawrecv;
|
|
d->mux->nbrecv = drawnbrecv;
|
|
d->mux->gettag = drawgettag;
|
|
d->mux->settag = drawsettag;
|
|
d->mux->aux = d;
|
|
muxinit(d->mux);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
drawsend(Mux *mux, void *vmsg)
|
|
{
|
|
int n;
|
|
uchar *msg;
|
|
Display *d;
|
|
|
|
msg = vmsg;
|
|
GET(msg, n);
|
|
d = mux->aux;
|
|
return write(d->srvfd, msg, n);
|
|
}
|
|
|
|
static int
|
|
_drawrecv(Mux *mux, int canblock, void **vp)
|
|
{
|
|
int n;
|
|
uchar buf[4], *p;
|
|
Display *d;
|
|
|
|
d = mux->aux;
|
|
*vp = nil;
|
|
if(!canblock && !canreadfd(d->srvfd))
|
|
return 0;
|
|
if((n=readn(d->srvfd, buf, 4)) != 4)
|
|
return 1;
|
|
GET(buf, n);
|
|
p = malloc(n);
|
|
if(p == nil){
|
|
fprint(2, "out of memory allocating %d in drawrecv\n", n);
|
|
return 1;
|
|
}
|
|
memmove(p, buf, 4);
|
|
if(readn(d->srvfd, p+4, n-4) != n-4){
|
|
free(p);
|
|
return 1;
|
|
}
|
|
*vp = p;
|
|
return 1;
|
|
}
|
|
|
|
static void*
|
|
drawrecv(Mux *mux)
|
|
{
|
|
void *p;
|
|
_drawrecv(mux, 1, &p);
|
|
return p;
|
|
}
|
|
|
|
static int
|
|
drawnbrecv(Mux *mux, void **vp)
|
|
{
|
|
return _drawrecv(mux, 0, vp);
|
|
}
|
|
|
|
static int
|
|
drawgettag(Mux *mux, void *vmsg)
|
|
{
|
|
uchar *msg;
|
|
USED(mux);
|
|
|
|
msg = vmsg;
|
|
return msg[4];
|
|
}
|
|
|
|
static int
|
|
drawsettag(Mux *mux, void *vmsg, uint tag)
|
|
{
|
|
uchar *msg;
|
|
USED(mux);
|
|
|
|
msg = vmsg;
|
|
msg[4] = tag;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
displayrpc(Display *d, Wsysmsg *tx, Wsysmsg *rx, void **freep)
|
|
{
|
|
int n, nn;
|
|
void *tpkt, *rpkt;
|
|
|
|
n = sizeW2M(tx);
|
|
tpkt = malloc(n);
|
|
if(freep)
|
|
*freep = nil;
|
|
if(tpkt == nil)
|
|
return -1;
|
|
tx->tag = 0;
|
|
if(chattydrawclient)
|
|
fprint(2, "<- %W\n", tx);
|
|
nn = convW2M(tx, tpkt, n);
|
|
if(nn != n){
|
|
free(tpkt);
|
|
werrstr("drawclient: sizeW2M convW2M mismatch");
|
|
fprint(2, "%r\n");
|
|
return -1;
|
|
}
|
|
/*
|
|
* This is the only point where we might reschedule.
|
|
* Muxrpc might need to acquire d->mux->lk, which could
|
|
* be held by some other proc (e.g., the one reading from
|
|
* the keyboard via Trdkbd messages). If we need to wait
|
|
* for the lock, don't let other threads from this proc
|
|
* run. This keeps up the appearance that writes to /dev/draw
|
|
* don't cause rescheduling. If you *do* allow rescheduling
|
|
* here, then flushimage(display, 1) happening in two different
|
|
* threads in the same proc can cause a buffer of commands
|
|
* to be written out twice, leading to interesting results
|
|
* on the screen.
|
|
*
|
|
* Threadpin and threadunpin were added to the thread library
|
|
* to solve exactly this problem. Be careful! They are dangerous.
|
|
*
|
|
* _pin and _unpin are aliases for threadpin and threadunpin
|
|
* in a threaded program and are no-ops in unthreaded programs.
|
|
*/
|
|
_pin();
|
|
rpkt = muxrpc(d->mux, tpkt);
|
|
_unpin();
|
|
free(tpkt);
|
|
if(rpkt == nil){
|
|
werrstr("muxrpc: %r");
|
|
return -1;
|
|
}
|
|
GET((uchar*)rpkt, n);
|
|
nn = convM2W(rpkt, n, rx);
|
|
if(nn != n){
|
|
free(rpkt);
|
|
werrstr("drawclient: convM2W packet size mismatch %d %d %.*H", n, nn, n, rpkt);
|
|
fprint(2, "%r\n");
|
|
return -1;
|
|
}
|
|
if(chattydrawclient)
|
|
fprint(2, "-> %W\n", rx);
|
|
if(rx->type == Rerror){
|
|
werrstr("%s", rx->error);
|
|
free(rpkt);
|
|
return -1;
|
|
}
|
|
if(rx->type != tx->type+1){
|
|
werrstr("packet type mismatch -- tx %d rx %d",
|
|
tx->type, rx->type);
|
|
free(rpkt);
|
|
return -1;
|
|
}
|
|
if(freep)
|
|
*freep = rpkt;
|
|
else
|
|
free(rpkt);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
_displayinit(Display *d, char *label, char *winsize)
|
|
{
|
|
Wsysmsg tx, rx;
|
|
|
|
tx.type = Tinit;
|
|
tx.label = label;
|
|
tx.winsize = winsize;
|
|
return displayrpc(d, &tx, &rx, nil);
|
|
}
|
|
|
|
int
|
|
_displayrdmouse(Display *d, Mouse *m, int *resized)
|
|
{
|
|
Wsysmsg tx, rx;
|
|
|
|
tx.type = Trdmouse;
|
|
if(displayrpc(d, &tx, &rx, nil) < 0)
|
|
return -1;
|
|
*m = rx.mouse;
|
|
*resized = rx.resized;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
_displayrdkbd(Display *d, Rune *r)
|
|
{
|
|
Wsysmsg tx, rx;
|
|
|
|
tx.type = Trdkbd;
|
|
if(displayrpc(d, &tx, &rx, nil) < 0)
|
|
return -1;
|
|
*r = rx.rune;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
_displaymoveto(Display *d, Point p)
|
|
{
|
|
Wsysmsg tx, rx;
|
|
|
|
tx.type = Tmoveto;
|
|
tx.mouse.xy = p;
|
|
return displayrpc(d, &tx, &rx, nil);
|
|
}
|
|
|
|
int
|
|
_displaycursor(Display *d, Cursor *c)
|
|
{
|
|
Wsysmsg tx, rx;
|
|
|
|
tx.type = Tcursor;
|
|
if(c == nil){
|
|
memset(&tx.cursor, 0, sizeof tx.cursor);
|
|
tx.arrowcursor = 1;
|
|
}else{
|
|
tx.arrowcursor = 0;
|
|
tx.cursor = *c;
|
|
}
|
|
return displayrpc(d, &tx, &rx, nil);
|
|
}
|
|
|
|
int
|
|
_displaybouncemouse(Display *d, Mouse *m)
|
|
{
|
|
Wsysmsg tx, rx;
|
|
|
|
tx.type = Tbouncemouse;
|
|
tx.mouse = *m;
|
|
return displayrpc(d, &tx, &rx, nil);
|
|
}
|
|
|
|
int
|
|
_displaylabel(Display *d, char *label)
|
|
{
|
|
Wsysmsg tx, rx;
|
|
|
|
tx.type = Tlabel;
|
|
tx.label = label;
|
|
return displayrpc(d, &tx, &rx, nil);
|
|
}
|
|
|
|
char*
|
|
_displayrdsnarf(Display *d)
|
|
{
|
|
void *p;
|
|
char *s;
|
|
Wsysmsg tx, rx;
|
|
|
|
tx.type = Trdsnarf;
|
|
if(displayrpc(d, &tx, &rx, &p) < 0)
|
|
return nil;
|
|
s = strdup(rx.snarf);
|
|
free(p);
|
|
return s;
|
|
}
|
|
|
|
int
|
|
_displaywrsnarf(Display *d, char *snarf)
|
|
{
|
|
Wsysmsg tx, rx;
|
|
|
|
tx.type = Twrsnarf;
|
|
tx.snarf = snarf;
|
|
return displayrpc(d, &tx, &rx, nil);
|
|
}
|
|
|
|
int
|
|
_displayrddraw(Display *d, void *v, int n)
|
|
{
|
|
void *p;
|
|
Wsysmsg tx, rx;
|
|
|
|
tx.type = Trddraw;
|
|
tx.count = n;
|
|
if(displayrpc(d, &tx, &rx, &p) < 0)
|
|
return -1;
|
|
memmove(v, rx.data, rx.count);
|
|
free(p);
|
|
return rx.count;
|
|
}
|
|
|
|
int
|
|
_displaywrdraw(Display *d, void *v, int n)
|
|
{
|
|
Wsysmsg tx, rx;
|
|
|
|
tx.type = Twrdraw;
|
|
tx.count = n;
|
|
tx.data = v;
|
|
if(displayrpc(d, &tx, &rx, nil) < 0)
|
|
return -1;
|
|
return rx.count;
|
|
}
|
|
|
|
int
|
|
_displaytop(Display *d)
|
|
{
|
|
Wsysmsg tx, rx;
|
|
|
|
tx.type = Ttop;
|
|
return displayrpc(d, &tx, &rx, nil);
|
|
}
|
|
|
|
int
|
|
_displayresize(Display *d, Rectangle r)
|
|
{
|
|
Wsysmsg tx, rx;
|
|
|
|
tx.type = Tresize;
|
|
tx.rect = r;
|
|
return displayrpc(d, &tx, &rx, nil);
|
|
}
|
|
|
|
static int
|
|
canreadfd(int fd)
|
|
{
|
|
fd_set rs, ws, xs;
|
|
struct timeval tv;
|
|
|
|
FD_ZERO(&rs);
|
|
FD_ZERO(&ws);
|
|
FD_ZERO(&xs);
|
|
FD_SET(fd, &rs);
|
|
FD_SET(fd, &xs);
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = 0;
|
|
if(select(fd+1, &rs, &ws, &xs, &tv) < 0)
|
|
return 0;
|
|
if(FD_ISSET(fd, &rs) || FD_ISSET(fd, &xs))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|