diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/vnc/vncs.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/vnc/vncs.c')
-rwxr-xr-x | sys/src/cmd/vnc/vncs.c | 1103 |
1 files changed, 1103 insertions, 0 deletions
diff --git a/sys/src/cmd/vnc/vncs.c b/sys/src/cmd/vnc/vncs.c new file mode 100755 index 000000000..cb4bbd66f --- /dev/null +++ b/sys/src/cmd/vnc/vncs.c @@ -0,0 +1,1103 @@ +#define Image IMAGE +#include "vnc.h" +#include "vncs.h" +#include "compat.h" +#include <cursor.h> +#include "screen.h" +#include "kbd.h" + +#include <mp.h> +#include <libsec.h> + +extern Dev drawdevtab; +extern Dev mousedevtab; +extern Dev consdevtab; + +Dev *devtab[] = +{ + &drawdevtab, + &mousedevtab, + &consdevtab, + nil +}; + +static char *msgname[] = { + [MPixFmt] = "MPixFmt", + [MFixCmap] = "MFixCmap", + [MSetEnc] = "MSetEnc", + [MFrameReq] = "MFrameReq", + [MKey] = "MKey", + [MMouse] = "MMouse", + [MCCut] = "MCCut", +}; + +static char *encname[] = { + [EncRaw] = "raw", + [EncCopyRect] = "copy rect", + [EncRre] = "rre", + [EncCorre] = "corre", + [EncHextile] = "hextile", + [EncZlib] = "zlib", + [EncTight] = "zlibtight", + [EncZHextile] = "zhextile", + [EncMouseWarp] = "mousewarp", + +}; + +/* + * list head. used to hold the list, the lock, dim, and pixelfmt + */ +struct { + QLock; + Vncs *head; +} clients; + +int shared; +int sleeptime = 5; +int verbose = 0; +char *cert; +char *pixchan = "r5g6b5"; +static int cmdpid; +static int srvfd; +static int exportfd; +static Vncs **vncpriv; + +static int parsedisplay(char*); +static void vnckill(char*, int, int); +static int vncannounce(char *net, int display, char *adir, int base); +static void noteshutdown(void*, char*); +static void vncaccept(Vncs*); +static int vncsfmt(Fmt*); +static void getremote(char*, char*); +static void vncname(char*, ...); +#pragma varargck argpos vncname 1 + +#pragma varargck type "V" Vncs* + +void +usage(void) +{ + fprint(2, "usage: vncs [-v] [-c cert] [-d :display] [-g widthXheight] [-p pixelfmt] [cmd [args]...]\n"); + fprint(2, "\tto kill a server: vncs [-v] -k :display\n"); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + int altnet, baseport, cfd, display, exnum, fd, h, killing, w; + char adir[NETPATHLEN], ldir[NETPATHLEN]; + char net[NETPATHLEN], *p; + char *rc[] = { "/bin/rc", "-i", nil }; + Vncs *v; + + fmtinstall('V', vncsfmt); + display = -1; + killing = 0; + altnet = 0; + w = 1024; + h = 768; + baseport = 5900; + setnetmtpt(net, sizeof net, nil); + ARGBEGIN{ + default: + usage(); + case 'c': + cert = EARGF(usage()); + baseport = 35729; + break; + case 'd': + if(display != -1) + usage(); + display = parsedisplay(EARGF(usage())); + break; + case 'g': + p = EARGF(usage()); + w = strtol(p, &p, 10); + if(*p != 'x' && *p != 'X' && *p != ' ' && *p != ' ') + usage(); + h = strtol(p+1, &p, 10); + if(*p != 0) + usage(); + break; + case 'k': + if(display != -1) + usage(); + display = parsedisplay(EARGF(usage())); + killing = 1; + break; + case 'p': + pixchan = EARGF(usage()); + break; +/* DEBUGGING + case 's': + sleeptime = atoi(EARGF(usage())); + break; +*/ + case 'v': + verbose++; + break; + case 'x': + p = EARGF(usage()); + setnetmtpt(net, sizeof net, p); + altnet = 1; + break; + }ARGEND + + if(killing){ + vnckill(net, display, baseport); + exits(nil); + } + + if(altnet && !cert) + sysfatal("announcing on alternate network requires TLS (-c)"); + + if(argc == 0) + argv = rc; + + /* easy exit */ + if(access(argv[0], AEXEC) < 0) + sysfatal("access %s for exec: %r", argv[0]); + + /* background ourselves */ + switch(rfork(RFPROC|RFNAMEG|RFFDG|RFNOTEG)){ + case -1: + sysfatal("rfork: %r"); + default: + exits(nil); + case 0: + break; + } + + vncpriv = privalloc(); + if(vncpriv == nil) + sysfatal("privalloc: %r"); + + /* start screen */ + initcompat(); + if(waserror()) + sysfatal("screeninit %dx%d %s: %s", w, h, pixchan, up->error); + if(verbose) + fprint(2, "geometry is %dx%d\n", w, h); + screeninit(w, h, pixchan); + poperror(); + + /* start file system device slaves */ + exnum = exporter(devtab, &fd, &exportfd); + + /* rebuild /dev because the underlying connection might go away (ick) */ + unmount(nil, "/dev"); + bind("#c", "/dev", MREPL); + + /* run the command */ + switch(cmdpid = rfork(RFPROC|RFFDG|RFNOTEG|RFNAMEG|RFREND)){ + case -1: + sysfatal("rfork: %r"); + break; + case 0: + if(mounter("/dev", MBEFORE, fd, exnum) < 0) + sysfatal("mounter: %r"); + close(exportfd); + close(0); + close(1); + close(2); + open("/dev/cons", OREAD); + open("/dev/cons", OWRITE); + open("/dev/cons", OWRITE); + exec(argv[0], argv); + fprint(2, "exec %s: %r\n", argv[0]); + _exits(nil); + default: + close(fd); + break; + } + + /* run the service */ + srvfd = vncannounce(net, display, adir, baseport); + if(srvfd < 0) + sysfatal("announce failed"); + if(verbose) + fprint(2, "announced in %s\n", adir); + + atexit(shutdown); + notify(noteshutdown); + for(;;){ + vncname("listener"); + cfd = listen(adir, ldir); + if(cfd < 0) + break; + if(verbose) + fprint(2, "call in %s\n", ldir); + fd = accept(cfd, ldir); + if(fd < 0){ + close(cfd); + continue; + } + v = mallocz(sizeof(Vncs), 1); + if(v == nil){ + close(cfd); + close(fd); + continue; + } + v->ctlfd = cfd; + v->datafd = fd; + v->nproc = 1; + v->ndead = 0; + getremote(ldir, v->remote); + strcpy(v->netpath, ldir); + qlock(&clients); + v->next = clients.head; + clients.head = v; + qunlock(&clients); + vncaccept(v); + } + exits(0); +} + +static int +parsedisplay(char *p) +{ + int n; + + if(*p != ':') + usage(); + if(*p == 0) + usage(); + n = strtol(p+1, &p, 10); + if(*p != 0) + usage(); + return n; +} + +static void +getremote(char *ldir, char *remote) +{ + char buf[NETPATHLEN]; + int fd, n; + + snprint(buf, sizeof buf, "%s/remote", ldir); + strcpy(remote, "<none>"); + if((fd = open(buf, OREAD)) < 0) + return; + n = readn(fd, remote, NETPATHLEN-1); + close(fd); + if(n < 0) + return; + remote[n] = 0; + if(n>0 && remote[n-1] == '\n') + remote[n-1] = 0; +} + +static int +vncsfmt(Fmt *fmt) +{ + Vncs *v; + + v = va_arg(fmt->args, Vncs*); + return fmtprint(fmt, "[%d] %s %s", getpid(), v->remote, v->netpath); +} + +/* + * We register exiting as an atexit handler in each proc, so that + * client procs need merely exit when something goes wrong. + */ +static void +vncclose(Vncs *v) +{ + Vncs **l; + + /* remove self from client list if there */ + qlock(&clients); + for(l=&clients.head; *l; l=&(*l)->next) + if(*l == v){ + *l = v->next; + break; + } + qunlock(&clients); + + /* if last proc, free v */ + vnclock(v); + if(++v->ndead < v->nproc){ + vncunlock(v); + return; + } + + freerlist(&v->rlist); + vncterm(v); + if(v->ctlfd >= 0) + close(v->ctlfd); + if(v->datafd >= 0) + close(v->datafd); + if(v->image) + freememimage(v->image); + free(v); +} + +static void +exiting(void) +{ + vncclose(*vncpriv); +} + +void +vnchungup(Vnc *v) +{ + if(verbose) + fprint(2, "%V: hangup\n", (Vncs*)v); + exits(0); /* atexit and exiting() will take care of everything */ +} + +/* + * Kill all clients except safe. + * Used to start a non-shared client and at shutdown. + */ +static void +killclients(Vncs *safe) +{ + Vncs *v; + + qlock(&clients); + for(v=clients.head; v; v=v->next){ + if(v == safe) + continue; + if(v->ctlfd >= 0){ + hangup(v->ctlfd); + close(v->ctlfd); + v->ctlfd = -1; + close(v->datafd); + v->datafd = -1; + } + } + qunlock(&clients); +} + +/* + * Kill the executing command and then kill everyone else. + * Called to close up shop at the end of the day + * and also if we get an unexpected note. + */ +static char killkin[] = "die vnc kin"; +static void +killall(void) +{ + postnote(PNGROUP, cmdpid, "hangup"); + close(srvfd); + srvfd = -1; + close(exportfd); + exportfd = -1; + postnote(PNGROUP, getpid(), killkin); +} + +void +shutdown(void) +{ + if(verbose) + fprint(2, "vnc server shutdown\n"); + killall(); +} + +static void +noteshutdown(void*, char *msg) +{ + if(strcmp(msg, killkin) == 0) /* already shutting down */ + noted(NDFLT); + killall(); + noted(NDFLT); +} + +/* + * Kill a specific instance of a server. + */ +static void +vnckill(char *net, int display, int baseport) +{ + int fd, i, n, port; + char buf[NETPATHLEN], *p; + + for(i=0;; i++){ + snprint(buf, sizeof buf, "%s/tcp/%d/local", net, i); + if((fd = open(buf, OREAD)) < 0) + sysfatal("did not find display"); + n = read(fd, buf, sizeof buf-1); + close(fd); + if(n <= 0) + continue; + buf[n] = 0; + p = strchr(buf, '!'); + if(p == 0) + continue; + port = atoi(p+1); + if(port != display+baseport) + continue; + snprint(buf, sizeof buf, "%s/tcp/%d/ctl", net, i); + fd = open(buf, OWRITE); + if(fd < 0) + sysfatal("cannot open %s: %r", buf); + if(write(fd, "hangup", 6) != 6) + sysfatal("cannot hangup %s: %r", buf); + close(fd); + break; + } +} + +/* + * Look for a port on which to announce. + * If display != -1, we only try that one. + * Otherwise we hunt. + * + * Returns the announce fd. + */ +static int +vncannounce(char *net, int display, char *adir, int base) +{ + int port, eport, fd; + char addr[NETPATHLEN]; + + if(display == -1){ + port = base; + eport = base+50; + }else{ + port = base+display; + eport = port; + } + + for(; port<=eport; port++){ + snprint(addr, sizeof addr, "%s/tcp!*!%d", net, port); + if((fd = announce(addr, adir)) >= 0){ + fprint(2, "server started on display :%d\n", port-base); + return fd; + } + } + if(display == -1) + fprint(2, "could not find any ports to announce\n"); + else + fprint(2, "announce: %r\n"); + return -1; +} + +/* + * Handle a new connection. + */ +static void clientreadproc(Vncs*); +static void clientwriteproc(Vncs*); +static void chan2fmt(Pixfmt*, ulong); +static ulong fmt2chan(Pixfmt*); + +static void +vncaccept(Vncs *v) +{ + char buf[32]; + int fd; + TLSconn conn; + + /* caller returns to listen */ + switch(rfork(RFPROC|RFMEM|RFNAMEG)){ + case -1: + fprint(2, "%V: fork failed: %r\n", v); + vncclose(v); + return; + default: + return; + case 0: + break; + } + *vncpriv = v; + + if(!atexit(exiting)){ + fprint(2, "%V: could not register atexit handler: %r; hanging up\n", v); + exiting(); + exits(nil); + } + + if(cert != nil){ + memset(&conn, 0, sizeof conn); + conn.cert = readcert(cert, &conn.certlen); + if(conn.cert == nil){ + fprint(2, "%V: could not read cert %s: %r; hanging up\n", v, cert); + exits(nil); + } + fd = tlsServer(v->datafd, &conn); + if(fd < 0){ + fprint(2, "%V: tlsServer: %r; hanging up\n", v); + free(conn.cert); + if(conn.sessionID) + free(conn.sessionID); + exits(nil); + } + close(v->datafd); + v->datafd = fd; + free(conn.cert); + free(conn.sessionID); + } + vncinit(v->datafd, v->ctlfd, v); + + if(verbose) + fprint(2, "%V: handshake\n", v); + if(vncsrvhandshake(v) < 0){ + fprint(2, "%V: handshake failed; hanging up\n", v); + exits(0); + } + if(verbose) + fprint(2, "%V: auth\n", v); + if(vncsrvauth(v) < 0){ + fprint(2, "%V: auth failed; hanging up\n", v); + exits(0); + } + + shared = vncrdchar(v); + + if(verbose) + fprint(2, "%V: %sshared\n", v, shared ? "" : "not "); + if(!shared) + killclients(v); + + v->dim = (Point){Dx(gscreen->r), Dy(gscreen->r)}; + vncwrpoint(v, v->dim); + if(verbose) + fprint(2, "%V: send screen size %P (rect %R)\n", v, v->dim, gscreen->r); + + v->bpp = gscreen->depth; + v->depth = gscreen->depth; + v->truecolor = 1; + v->bigendian = 0; + chan2fmt(v, gscreen->chan); + if(verbose) + fprint(2, "%V: bpp=%d, depth=%d, chan=%s\n", v, + v->bpp, v->depth, chantostr(buf, gscreen->chan)); + vncwrpixfmt(v, v); + vncwrlong(v, 14); + vncwrbytes(v, "Plan9 Desktop", 14); + vncflush(v); + + if(verbose) + fprint(2, "%V: handshaking done\n", v); + + switch(rfork(RFPROC|RFMEM)){ + case -1: + fprint(2, "%V: cannot fork: %r; hanging up\n", v); + vnchungup(v); + default: + clientreadproc(v); + exits(nil); + case 0: + *vncpriv = v; + v->nproc++; + if(atexit(exiting) == 0){ + exiting(); + fprint(2, "%V: could not register atexit handler: %r; hanging up\n", v); + exits(nil); + } + clientwriteproc(v); + exits(nil); + } +} + +static void +vncname(char *fmt, ...) +{ + int fd; + char name[64], buf[32]; + va_list arg; + + va_start(arg, fmt); + vsnprint(name, sizeof name, fmt, arg); + va_end(arg); + + sprint(buf, "/proc/%d/args", getpid()); + if((fd = open(buf, OWRITE)) >= 0){ + write(fd, name, strlen(name)); + close(fd); + } +} + +/* + * Set the pixel format being sent. Can only happen once. + * (Maybe a client would send this again if the screen changed + * underneath it? If we want to support this we need a way to + * make sure the current image is no longer in use, so we can free it. + */ +static void +setpixelfmt(Vncs *v) +{ + ulong chan; + + vncgobble(v, 3); + v->Pixfmt = vncrdpixfmt(v); + chan = fmt2chan(v); + if(chan == 0){ + fprint(2, "%V: bad pixel format; hanging up\n", v); + vnchungup(v); + } + v->imagechan = chan; +} + +/* + * Set the preferred encoding list. Can only happen once. + * If we want to support changing this more than once then + * we need to put a lock around the encoding functions + * so as not to conflict with updateimage. + */ +static void +setencoding(Vncs *v) +{ + int n, x; + + vncrdchar(v); + n = vncrdshort(v); + while(n-- > 0){ + x = vncrdlong(v); + switch(x){ + case EncCopyRect: + v->copyrect = 1; + continue; + case EncMouseWarp: + v->canwarp = 1; + continue; + } + if(v->countrect != nil) + continue; + switch(x){ + case EncRaw: + v->encname = "raw"; + v->countrect = countraw; + v->sendrect = sendraw; + break; + case EncRre: + v->encname = "rre"; + v->countrect = countrre; + v->sendrect = sendrre; + break; + case EncCorre: + v->encname = "corre"; + v->countrect = countcorre; + v->sendrect = sendcorre; + break; + case EncHextile: + v->encname = "hextile"; + v->countrect = counthextile; + v->sendrect = sendhextile; + break; + } + } + + if(v->countrect == nil){ + v->encname = "raw"; + v->countrect = countraw; + v->sendrect = sendraw; + } + + if(verbose) + fprint(2, "Encoding with %s%s%s\n", v->encname, + v->copyrect ? ", copyrect" : "", + v->canwarp ? ", canwarp" : ""); +} + +/* + * Continually read updates from one client. + */ +static void +clientreadproc(Vncs *v) +{ + int incremental, key, keydown, buttons, type, x, y, n; + char *buf; + Rectangle r; + + vncname("read %V", v); + + for(;;){ + type = vncrdchar(v); + switch(type){ + default: + fprint(2, "%V: unknown vnc message type %d; hanging up\n", v, type); + vnchungup(v); + + /* set pixel format */ + case MPixFmt: + setpixelfmt(v); + break; + + /* ignore color map changes */ + case MFixCmap: + vncgobble(v, 3); + n = vncrdshort(v); + vncgobble(v, n*6); + break; + + /* set encoding list */ + case MSetEnc: + setencoding(v); + break; + + /* request image update in rectangle */ + case MFrameReq: + incremental = vncrdchar(v); + r = vncrdrect(v); + if(incremental){ + vnclock(v); + v->updaterequest = 1; + vncunlock(v); + }else{ + drawlock(); /* protects rlist */ + vnclock(v); /* protects updaterequest */ + v->updaterequest = 1; + addtorlist(&v->rlist, r); + vncunlock(v); + drawunlock(); + } + break; + + /* send keystroke */ + case MKey: + keydown = vncrdchar(v); + vncgobble(v, 2); + key = vncrdlong(v); + vncputc(!keydown, key); + break; + + /* send mouse event */ + case MMouse: + buttons = vncrdchar(v); + x = vncrdshort(v); + y = vncrdshort(v); + mousetrack(x, y, buttons, nsec()/(1000*1000LL)); + break; + + /* send cut text */ + case MCCut: + vncgobble(v, 3); + n = vncrdlong(v); + buf = malloc(n+1); + if(buf){ + vncrdbytes(v, buf, n); + buf[n] = 0; + vnclock(v); /* for snarfvers */ + setsnarf(buf, n, &v->snarfvers); + vncunlock(v); + }else + vncgobble(v, n); + break; + } + } +} + +static int +nbits(ulong mask) +{ + int n; + + n = 0; + for(; mask; mask>>=1) + n += mask&1; + return n; +} + +typedef struct Col Col; +struct Col { + int type; + int nbits; + int shift; +}; + +static ulong +fmt2chan(Pixfmt *fmt) +{ + Col c[4], t; + int i, j, depth, n, nc; + ulong mask, u; + + /* unpack the Pixfmt channels */ + c[0] = (Col){CRed, nbits(fmt->red.max), fmt->red.shift}; + c[1] = (Col){CGreen, nbits(fmt->green.max), fmt->green.shift}; + c[2] = (Col){CBlue, nbits(fmt->blue.max), fmt->blue.shift}; + nc = 3; + + /* add an ignore channel if necessary */ + depth = c[0].nbits+c[1].nbits+c[2].nbits; + if(fmt->bpp != depth){ + /* BUG: assumes only one run of ignored bits */ + if(fmt->bpp == 32) + mask = ~0; + else + mask = (1<<fmt->bpp)-1; + mask ^= fmt->red.max << fmt->red.shift; + mask ^= fmt->green.max << fmt->green.shift; + mask ^= fmt->blue.max << fmt->blue.shift; + if(mask == 0) + abort(); + n = 0; + for(; !(mask&1); mask>>=1) + n++; + c[3] = (Col){CIgnore, nbits(mask), n}; + nc++; + } + + /* sort the channels, largest shift (leftmost bits) first */ + for(i=1; i<nc; i++) + for(j=i; j>0; j--) + if(c[j].shift > c[j-1].shift){ + t = c[j]; + c[j] = c[j-1]; + c[j-1] = t; + } + + /* build the channel descriptor */ + u = 0; + for(i=0; i<nc; i++){ + u <<= 8; + u |= CHAN1(c[i].type, c[i].nbits); + } + + return u; +} + +static void +chan2fmt(Pixfmt *fmt, ulong chan) +{ + ulong c, rc, shift; + + shift = 0; + for(rc = chan; rc; rc >>=8){ + c = rc & 0xFF; + switch(TYPE(c)){ + case CRed: + fmt->red = (Colorfmt){(1<<NBITS(c))-1, shift}; + break; + case CBlue: + fmt->blue = (Colorfmt){(1<<NBITS(c))-1, shift}; + break; + case CGreen: + fmt->green = (Colorfmt){(1<<NBITS(c))-1, shift}; + break; + } + shift += NBITS(c); + } +} + +/* + * Note that r has changed on the screen. + * Updating the rlists is okay because they are protected by drawlock. + */ +void +flushmemscreen(Rectangle r) +{ + Vncs *v; + + if(!rectclip(&r, gscreen->r)) + return; + qlock(&clients); + for(v=clients.head; v; v=v->next) + addtorlist(&v->rlist, r); + qunlock(&clients); +} + +/* + * Queue a mouse warp note for the next update to each client. + */ +void +mousewarpnote(Point p) +{ + Vncs *v; + + qlock(&clients); + for(v=clients.head; v; v=v->next){ + if(v->canwarp){ + vnclock(v); + v->needwarp = 1; + v->warppt = p; + vncunlock(v); + } + } + qunlock(&clients); +} + +/* + * Send a client his changed screen image. + * v is locked on entrance, locked on exit, but released during. + */ +static int +updateimage(Vncs *v) +{ + int i, ncount, nsend, docursor, needwarp; + vlong ooffset; + Point warppt; + Rectangle cr; + Rlist rlist; + vlong t1; + int (*count)(Vncs*, Rectangle); + int (*send)(Vncs*, Rectangle); + + if(v->image == nil) + return 0; + + /* warping info and unlock v so that updates can proceed */ + needwarp = v->canwarp && v->needwarp; + warppt = v->warppt; + v->needwarp = 0; + vncunlock(v); + + /* copy the screen bits and then unlock the screen so updates can proceed */ + drawlock(); + rlist = v->rlist; + memset(&v->rlist, 0, sizeof v->rlist); + + /* if the cursor has moved or changed shape, we need to redraw its square */ + lock(&cursor); + if(v->cursorver != cursorver || !eqpt(v->cursorpos, cursorpos)){ + docursor = 1; + v->cursorver = cursorver; + v->cursorpos = cursorpos; + cr = cursorrect(); + }else{ + docursor = 0; + cr = v->cursorr; + } + unlock(&cursor); + + if(docursor){ + addtorlist(&rlist, v->cursorr); + if(!rectclip(&cr, gscreen->r)) + cr.max = cr.min; + addtorlist(&rlist, cr); + } + + /* copy changed screen parts, also check for parts overlapping cursor location */ + for(i=0; i<rlist.nrect; i++){ + if(!docursor) + docursor = rectXrect(v->cursorr, rlist.rect[i]); + memimagedraw(v->image, rlist.rect[i], gscreen, rlist.rect[i].min, memopaque, ZP, S); + } + + if(docursor){ + cursordraw(v->image, cr); + addtorlist(&rlist, v->cursorr); + v->cursorr = cr; + } + + drawunlock(); + + ooffset = Boffset(&v->out); + /* no more locks are held; talk to the client */ + + if(rlist.nrect == 0 && needwarp == 0){ + vnclock(v); + return 0; + } + + count = v->countrect; + send = v->sendrect; + if(count == nil || send == nil){ + count = countraw; + send = sendraw; + } + + ncount = 0; + for(i=0; i<rlist.nrect; i++) + ncount += (*count)(v, rlist.rect[i]); + + if(verbose > 1) + fprint(2, "sendupdate: rlist.nrect=%d, ncount=%d", rlist.nrect, ncount); + + t1 = nsec(); + vncwrchar(v, MFrameUpdate); + vncwrchar(v, 0); + vncwrshort(v, ncount+needwarp); + + nsend = 0; + for(i=0; i<rlist.nrect; i++) + nsend += (*send)(v, rlist.rect[i]); + + if(ncount != nsend){ + fprint(2, "%V: ncount=%d, nsend=%d; hanging up\n", v, ncount, nsend); + vnchungup(v); + } + + if(needwarp){ + vncwrrect(v, Rect(warppt.x, warppt.y, warppt.x+1, warppt.y+1)); + vncwrlong(v, EncMouseWarp); + } + + t1 = nsec() - t1; + if(verbose > 1) + fprint(2, " in %lldms, %lld bytes\n", t1/1000000, Boffset(&v->out) - ooffset); + + freerlist(&rlist); + vnclock(v); + return 1; +} + +/* + * Update the snarf buffer if it has changed. + */ +static void +updatesnarf(Vncs *v) +{ + char *buf; + int len; + + if(v->snarfvers == snarf.vers) + return; + vncunlock(v); + qlock(&snarf); + len = snarf.n; + buf = malloc(len); + if(buf == nil){ + qunlock(&snarf); + vnclock(v); + return; + } + memmove(buf, snarf.buf, len); + v->snarfvers = snarf.vers; + qunlock(&snarf); + + vncwrchar(v, MSCut); + vncwrbytes(v, "pad", 3); + vncwrlong(v, len); + vncwrbytes(v, buf, len); + free(buf); + vnclock(v); +} + +/* + * Continually update one client. + */ +static void +clientwriteproc(Vncs *v) +{ + char buf[32], buf2[32]; + int sent; + + vncname("write %V", v); + for(;;){ + vnclock(v); + if(v->ndead) + break; + if((v->image == nil && v->imagechan!=0) + || (v->image && v->image->chan != v->imagechan)){ + if(v->image) + freememimage(v->image); + v->image = allocmemimage(Rpt(ZP, v->dim), v->imagechan); + if(v->image == nil){ + fprint(2, "%V: allocmemimage: %r; hanging up\n", v); + vnchungup(v); + } + if(verbose) + fprint(2, "%V: translating image from chan=%s to chan=%s\n", + v, chantostr(buf, gscreen->chan), chantostr(buf2, v->imagechan)); + } + sent = 0; + if(v->updaterequest){ + v->updaterequest = 0; + updatesnarf(v); + sent = updateimage(v); + if(!sent) + v->updaterequest = 1; + } + vncunlock(v); + vncflush(v); + if(!sent) + sleep(sleeptime); + } + vncunlock(v); + vnchungup(v); +} + |