|  | /* 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> | 
|  |  | 
|  | extern Mouse _drawmouse; | 
|  | int chattydrawclient = 0; | 
|  |  | 
|  | 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){ | 
|  | char *devdraw; | 
|  | 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. | 
|  | * The NOLIBTHREADDAEMONIZE keeps devdraw from | 
|  | * forking before threadmain. OS X hates it when | 
|  | * guis fork. | 
|  | * | 
|  | * If client didn't use ARGBEGIN, argv0 == nil. | 
|  | * Can't send nil through because OS X expects | 
|  | * argv[0] to be non-nil.  Also, OS X apparently | 
|  | * expects argv[0] to be a valid executable name, | 
|  | * so "(argv0)" is not okay.  Use "devdraw" | 
|  | * instead. | 
|  | */ | 
|  | putenv("NOLIBTHREADDAEMONIZE", "1"); | 
|  | devdraw = getenv("DEVDRAW"); | 
|  | if(devdraw == nil) | 
|  | devdraw = "devdraw"; | 
|  | if(argv0 == nil) | 
|  | argv0 = devdraw; | 
|  | execl(devdraw, argv0, argv0, "(devdraw)", 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; | 
|  | _drawmouse = rx.mouse; | 
|  | *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; | 
|  | if(displayrpc(d, &tx, &rx, nil) < 0) | 
|  | return -1; | 
|  | _drawmouse.xy = p; | 
|  | return flushimage(d, 1); | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  |