1 #define Image IMAGE
2 #include "vnc.h"
3 #include "vncs.h"
4 #include "compat.h"
5 #include <cursor.h>
6 #include "screen.h"
7 #include "kbd.h"
8
9 #include <mp.h>
10 #include <libsec.h>
11
12 extern Dev drawdevtab;
13 extern Dev mousedevtab;
14 extern Dev consdevtab;
15
16 Dev *devtab[] =
17 {
18 &drawdevtab,
19 &mousedevtab,
20 &consdevtab,
21 nil
22 };
23
24 static char *msgname[] = {
25 [MPixFmt] = "MPixFmt",
26 [MFixCmap] = "MFixCmap",
27 [MSetEnc] = "MSetEnc",
28 [MFrameReq] = "MFrameReq",
29 [MKey] = "MKey",
30 [MMouse] = "MMouse",
31 [MCCut] = "MCCut",
32 };
33
34 static char *encname[] = {
35 [EncRaw] = "raw",
36 [EncCopyRect] = "copy rect",
37 [EncRre] = "rre",
38 [EncCorre] = "corre",
39 [EncHextile] = "hextile",
40 [EncZlib] = "zlib",
41 [EncTight] = "zlibtight",
42 [EncZHextile] = "zhextile",
43 [EncMouseWarp] = "mousewarp",
44
45 };
46
47 /*
48 * list head. used to hold the list, the lock, dim, and pixelfmt
49 */
50 struct {
51 QLock;
52 Vncs *head;
53 } clients;
54
55 int shared;
56 int sleeptime = 5;
57 int verbose = 0;
58 char *cert;
59 char *pixchan = "r5g6b5";
60 static int cmdpid;
61 static int srvfd;
62 static int exportfd;
63 static Vncs **vncpriv;
64
65 static int parsedisplay(char*);
66 static void vnckill(char*, int, int);
67 static int vncannounce(char *net, int display, char *adir, int base);
68 static void noteshutdown(void*, char*);
69 static void vncaccept(Vncs*);
70 static int vncsfmt(Fmt*);
71 static void getremote(char*, char*);
72 static void vncname(char*, ...);
73 #pragma varargck argpos vncname 1
74
75 #pragma varargck type "V" Vncs*
76
77 void
usage(void)78 usage(void)
79 {
80 fprint(2, "usage: vncs [-v] [-c cert] [-d :display] [-g widthXheight] [-p pixelfmt] [cmd [args]...]\n");
81 fprint(2, "\tto kill a server: vncs [-v] -k :display\n");
82 exits("usage");
83 }
84
85 void
main(int argc,char ** argv)86 main(int argc, char **argv)
87 {
88 int altnet, baseport, cfd, display, exnum, fd, h, killing, w;
89 char adir[NETPATHLEN], ldir[NETPATHLEN];
90 char net[NETPATHLEN], *p;
91 char *rc[] = { "/bin/rc", "-i", nil };
92 Vncs *v;
93
94 fmtinstall('V', vncsfmt);
95 display = -1;
96 killing = 0;
97 altnet = 0;
98 w = 1024;
99 h = 768;
100 baseport = 5900;
101 setnetmtpt(net, sizeof net, nil);
102 ARGBEGIN{
103 default:
104 usage();
105 case 'c':
106 cert = EARGF(usage());
107 baseport = 35729;
108 break;
109 case 'd':
110 if(display != -1)
111 usage();
112 display = parsedisplay(EARGF(usage()));
113 break;
114 case 'g':
115 p = EARGF(usage());
116 w = strtol(p, &p, 10);
117 if(*p != 'x' && *p != 'X' && *p != ' ' && *p != ' ')
118 usage();
119 h = strtol(p+1, &p, 10);
120 if(*p != 0)
121 usage();
122 break;
123 case 'k':
124 if(display != -1)
125 usage();
126 display = parsedisplay(EARGF(usage()));
127 killing = 1;
128 break;
129 case 'p':
130 pixchan = EARGF(usage());
131 break;
132 /* DEBUGGING
133 case 's':
134 sleeptime = atoi(EARGF(usage()));
135 break;
136 */
137 case 'v':
138 verbose++;
139 break;
140 case 'x':
141 p = EARGF(usage());
142 setnetmtpt(net, sizeof net, p);
143 altnet = 1;
144 break;
145 }ARGEND
146
147 if(killing){
148 vnckill(net, display, baseport);
149 exits(nil);
150 }
151
152 if(altnet && !cert)
153 sysfatal("announcing on alternate network requires TLS (-c)");
154
155 if(argc == 0)
156 argv = rc;
157
158 /* easy exit */
159 if(access(argv[0], AEXEC) < 0)
160 sysfatal("access %s for exec: %r", argv[0]);
161
162 /* background ourselves */
163 switch(rfork(RFPROC|RFNAMEG|RFFDG|RFNOTEG)){
164 case -1:
165 sysfatal("rfork: %r");
166 default:
167 exits(nil);
168 case 0:
169 break;
170 }
171
172 vncpriv = privalloc();
173 if(vncpriv == nil)
174 sysfatal("privalloc: %r");
175
176 /* start screen */
177 initcompat();
178 if(waserror())
179 sysfatal("screeninit %dx%d %s: %s", w, h, pixchan, up->error);
180 if(verbose)
181 fprint(2, "geometry is %dx%d\n", w, h);
182 screeninit(w, h, pixchan);
183 poperror();
184
185 /* start file system device slaves */
186 exnum = exporter(devtab, &fd, &exportfd);
187
188 /* rebuild /dev because the underlying connection might go away (ick) */
189 unmount(nil, "/dev");
190 bind("#c", "/dev", MREPL);
191
192 /* run the command */
193 switch(cmdpid = rfork(RFPROC|RFFDG|RFNOTEG|RFNAMEG|RFREND)){
194 case -1:
195 sysfatal("rfork: %r");
196 break;
197 case 0:
198 if(mounter("/dev", MBEFORE, fd, exnum) < 0)
199 sysfatal("mounter: %r");
200 close(exportfd);
201 close(0);
202 close(1);
203 close(2);
204 open("/dev/cons", OREAD);
205 open("/dev/cons", OWRITE);
206 open("/dev/cons", OWRITE);
207 exec(argv[0], argv);
208 fprint(2, "exec %s: %r\n", argv[0]);
209 _exits(nil);
210 default:
211 close(fd);
212 break;
213 }
214
215 /* run the service */
216 srvfd = vncannounce(net, display, adir, baseport);
217 if(srvfd < 0)
218 sysfatal("announce failed");
219 if(verbose)
220 fprint(2, "announced in %s\n", adir);
221
222 atexit(shutdown);
223 notify(noteshutdown);
224 for(;;){
225 vncname("listener");
226 cfd = listen(adir, ldir);
227 if(cfd < 0)
228 break;
229 if(verbose)
230 fprint(2, "call in %s\n", ldir);
231 fd = accept(cfd, ldir);
232 if(fd < 0){
233 close(cfd);
234 continue;
235 }
236 v = mallocz(sizeof(Vncs), 1);
237 if(v == nil){
238 close(cfd);
239 close(fd);
240 continue;
241 }
242 v->ctlfd = cfd;
243 v->datafd = fd;
244 v->nproc = 1;
245 v->ndead = 0;
246 getremote(ldir, v->remote);
247 strcpy(v->netpath, ldir);
248 qlock(&clients);
249 v->next = clients.head;
250 clients.head = v;
251 qunlock(&clients);
252 vncaccept(v);
253 }
254 exits(0);
255 }
256
257 static int
parsedisplay(char * p)258 parsedisplay(char *p)
259 {
260 int n;
261
262 if(*p != ':')
263 usage();
264 if(*p == 0)
265 usage();
266 n = strtol(p+1, &p, 10);
267 if(*p != 0)
268 usage();
269 return n;
270 }
271
272 static void
getremote(char * ldir,char * remote)273 getremote(char *ldir, char *remote)
274 {
275 char buf[NETPATHLEN];
276 int fd, n;
277
278 snprint(buf, sizeof buf, "%s/remote", ldir);
279 strcpy(remote, "<none>");
280 if((fd = open(buf, OREAD)) < 0)
281 return;
282 n = readn(fd, remote, NETPATHLEN-1);
283 close(fd);
284 if(n < 0)
285 return;
286 remote[n] = 0;
287 if(n>0 && remote[n-1] == '\n')
288 remote[n-1] = 0;
289 }
290
291 static int
vncsfmt(Fmt * fmt)292 vncsfmt(Fmt *fmt)
293 {
294 Vncs *v;
295
296 v = va_arg(fmt->args, Vncs*);
297 return fmtprint(fmt, "[%d] %s %s", getpid(), v->remote, v->netpath);
298 }
299
300 /*
301 * We register exiting as an atexit handler in each proc, so that
302 * client procs need merely exit when something goes wrong.
303 */
304 static void
vncclose(Vncs * v)305 vncclose(Vncs *v)
306 {
307 Vncs **l;
308
309 /* remove self from client list if there */
310 qlock(&clients);
311 for(l=&clients.head; *l; l=&(*l)->next)
312 if(*l == v){
313 *l = v->next;
314 break;
315 }
316 qunlock(&clients);
317
318 /* if last proc, free v */
319 vnclock(v);
320 if(++v->ndead < v->nproc){
321 vncunlock(v);
322 return;
323 }
324
325 freerlist(&v->rlist);
326 vncterm(v);
327 if(v->ctlfd >= 0)
328 close(v->ctlfd);
329 if(v->datafd >= 0)
330 close(v->datafd);
331 if(v->image)
332 freememimage(v->image);
333 free(v);
334 }
335
336 static void
exiting(void)337 exiting(void)
338 {
339 vncclose(*vncpriv);
340 }
341
342 void
vnchungup(Vnc * v)343 vnchungup(Vnc *v)
344 {
345 if(verbose)
346 fprint(2, "%V: hangup\n", (Vncs*)v);
347 exits(0); /* atexit and exiting() will take care of everything */
348 }
349
350 /*
351 * Kill all clients except safe.
352 * Used to start a non-shared client and at shutdown.
353 */
354 static void
killclients(Vncs * safe)355 killclients(Vncs *safe)
356 {
357 Vncs *v;
358
359 qlock(&clients);
360 for(v=clients.head; v; v=v->next){
361 if(v == safe)
362 continue;
363 if(v->ctlfd >= 0){
364 hangup(v->ctlfd);
365 close(v->ctlfd);
366 v->ctlfd = -1;
367 close(v->datafd);
368 v->datafd = -1;
369 }
370 }
371 qunlock(&clients);
372 }
373
374 /*
375 * Kill the executing command and then kill everyone else.
376 * Called to close up shop at the end of the day
377 * and also if we get an unexpected note.
378 */
379 static char killkin[] = "die vnc kin";
380 static void
killall(void)381 killall(void)
382 {
383 postnote(PNGROUP, cmdpid, "hangup");
384 close(srvfd);
385 srvfd = -1;
386 close(exportfd);
387 exportfd = -1;
388 postnote(PNGROUP, getpid(), killkin);
389 }
390
391 void
shutdown(void)392 shutdown(void)
393 {
394 if(verbose)
395 fprint(2, "vnc server shutdown\n");
396 killall();
397 }
398
399 static void
noteshutdown(void *,char * msg)400 noteshutdown(void*, char *msg)
401 {
402 if(strcmp(msg, killkin) == 0) /* already shutting down */
403 noted(NDFLT);
404 killall();
405 noted(NDFLT);
406 }
407
408 /*
409 * Kill a specific instance of a server.
410 */
411 static void
vnckill(char * net,int display,int baseport)412 vnckill(char *net, int display, int baseport)
413 {
414 int fd, i, n, port;
415 char buf[NETPATHLEN], *p;
416
417 for(i=0;; i++){
418 snprint(buf, sizeof buf, "%s/tcp/%d/local", net, i);
419 if((fd = open(buf, OREAD)) < 0)
420 sysfatal("did not find display");
421 n = read(fd, buf, sizeof buf-1);
422 close(fd);
423 if(n <= 0)
424 continue;
425 buf[n] = 0;
426 p = strchr(buf, '!');
427 if(p == 0)
428 continue;
429 port = atoi(p+1);
430 if(port != display+baseport)
431 continue;
432 snprint(buf, sizeof buf, "%s/tcp/%d/ctl", net, i);
433 fd = open(buf, OWRITE);
434 if(fd < 0)
435 sysfatal("cannot open %s: %r", buf);
436 if(write(fd, "hangup", 6) != 6)
437 sysfatal("cannot hangup %s: %r", buf);
438 close(fd);
439 break;
440 }
441 }
442
443 /*
444 * Look for a port on which to announce.
445 * If display != -1, we only try that one.
446 * Otherwise we hunt.
447 *
448 * Returns the announce fd.
449 */
450 static int
vncannounce(char * net,int display,char * adir,int base)451 vncannounce(char *net, int display, char *adir, int base)
452 {
453 int port, eport, fd;
454 char addr[NETPATHLEN];
455
456 if(display == -1){
457 port = base;
458 eport = base+50;
459 }else{
460 port = base+display;
461 eport = port;
462 }
463
464 for(; port<=eport; port++){
465 snprint(addr, sizeof addr, "%s/tcp!*!%d", net, port);
466 if((fd = announce(addr, adir)) >= 0){
467 fprint(2, "server started on display :%d\n", port-base);
468 return fd;
469 }
470 }
471 if(display == -1)
472 fprint(2, "could not find any ports to announce\n");
473 else
474 fprint(2, "announce: %r\n");
475 return -1;
476 }
477
478 /*
479 * Handle a new connection.
480 */
481 static void clientreadproc(Vncs*);
482 static void clientwriteproc(Vncs*);
483 static void chan2fmt(Pixfmt*, ulong);
484 static ulong fmt2chan(Pixfmt*);
485
486 static void
vncaccept(Vncs * v)487 vncaccept(Vncs *v)
488 {
489 char buf[32];
490 int fd;
491 TLSconn conn;
492
493 /* caller returns to listen */
494 switch(rfork(RFPROC|RFMEM|RFNAMEG)){
495 case -1:
496 fprint(2, "%V: fork failed: %r\n", v);
497 vncclose(v);
498 return;
499 default:
500 return;
501 case 0:
502 break;
503 }
504 *vncpriv = v;
505
506 if(!atexit(exiting)){
507 fprint(2, "%V: could not register atexit handler: %r; hanging up\n", v);
508 exiting();
509 exits(nil);
510 }
511
512 if(cert != nil){
513 memset(&conn, 0, sizeof conn);
514 conn.cert = readcert(cert, &conn.certlen);
515 if(conn.cert == nil){
516 fprint(2, "%V: could not read cert %s: %r; hanging up\n", v, cert);
517 exits(nil);
518 }
519 fd = tlsServer(v->datafd, &conn);
520 if(fd < 0){
521 fprint(2, "%V: tlsServer: %r; hanging up\n", v);
522 free(conn.cert);
523 if(conn.sessionID)
524 free(conn.sessionID);
525 exits(nil);
526 }
527 close(v->datafd);
528 v->datafd = fd;
529 free(conn.cert);
530 free(conn.sessionID);
531 }
532 vncinit(v->datafd, v->ctlfd, v);
533
534 if(verbose)
535 fprint(2, "%V: handshake\n", v);
536 if(vncsrvhandshake(v) < 0){
537 fprint(2, "%V: handshake failed; hanging up\n", v);
538 exits(0);
539 }
540 if(verbose)
541 fprint(2, "%V: auth\n", v);
542 if(vncsrvauth(v) < 0){
543 fprint(2, "%V: auth failed; hanging up\n", v);
544 exits(0);
545 }
546
547 shared = vncrdchar(v);
548
549 if(verbose)
550 fprint(2, "%V: %sshared\n", v, shared ? "" : "not ");
551 if(!shared)
552 killclients(v);
553
554 v->dim = (Point){Dx(gscreen->r), Dy(gscreen->r)};
555 vncwrpoint(v, v->dim);
556 if(verbose)
557 fprint(2, "%V: send screen size %P (rect %R)\n", v, v->dim, gscreen->r);
558
559 v->bpp = gscreen->depth;
560 v->depth = gscreen->depth;
561 v->truecolor = 1;
562 v->bigendian = 0;
563 chan2fmt(v, gscreen->chan);
564 if(verbose)
565 fprint(2, "%V: bpp=%d, depth=%d, chan=%s\n", v,
566 v->bpp, v->depth, chantostr(buf, gscreen->chan));
567 vncwrpixfmt(v, v);
568 vncwrlong(v, 14);
569 vncwrbytes(v, "Plan9 Desktop", 14);
570 vncflush(v);
571
572 if(verbose)
573 fprint(2, "%V: handshaking done\n", v);
574
575 switch(rfork(RFPROC|RFMEM)){
576 case -1:
577 fprint(2, "%V: cannot fork: %r; hanging up\n", v);
578 vnchungup(v);
579 default:
580 clientreadproc(v);
581 exits(nil);
582 case 0:
583 *vncpriv = v;
584 v->nproc++;
585 if(atexit(exiting) == 0){
586 exiting();
587 fprint(2, "%V: could not register atexit handler: %r; hanging up\n", v);
588 exits(nil);
589 }
590 clientwriteproc(v);
591 exits(nil);
592 }
593 }
594
595 static void
vncname(char * fmt,...)596 vncname(char *fmt, ...)
597 {
598 int fd;
599 char name[64], buf[32];
600 va_list arg;
601
602 va_start(arg, fmt);
603 vsnprint(name, sizeof name, fmt, arg);
604 va_end(arg);
605
606 sprint(buf, "/proc/%d/args", getpid());
607 if((fd = open(buf, OWRITE)) >= 0){
608 write(fd, name, strlen(name));
609 close(fd);
610 }
611 }
612
613 /*
614 * Set the pixel format being sent. Can only happen once.
615 * (Maybe a client would send this again if the screen changed
616 * underneath it? If we want to support this we need a way to
617 * make sure the current image is no longer in use, so we can free it.
618 */
619 static void
setpixelfmt(Vncs * v)620 setpixelfmt(Vncs *v)
621 {
622 ulong chan;
623
624 vncgobble(v, 3);
625 v->Pixfmt = vncrdpixfmt(v);
626 chan = fmt2chan(v);
627 if(chan == 0){
628 fprint(2, "%V: bad pixel format; hanging up\n", v);
629 vnchungup(v);
630 }
631 v->imagechan = chan;
632 }
633
634 /*
635 * Set the preferred encoding list. Can only happen once.
636 * If we want to support changing this more than once then
637 * we need to put a lock around the encoding functions
638 * so as not to conflict with updateimage.
639 */
640 static void
setencoding(Vncs * v)641 setencoding(Vncs *v)
642 {
643 int n, x;
644
645 vncrdchar(v);
646 n = vncrdshort(v);
647 while(n-- > 0){
648 x = vncrdlong(v);
649 switch(x){
650 case EncCopyRect:
651 v->copyrect = 1;
652 continue;
653 case EncMouseWarp:
654 v->canwarp = 1;
655 continue;
656 }
657 if(v->countrect != nil)
658 continue;
659 switch(x){
660 case EncRaw:
661 v->encname = "raw";
662 v->countrect = countraw;
663 v->sendrect = sendraw;
664 break;
665 case EncRre:
666 v->encname = "rre";
667 v->countrect = countrre;
668 v->sendrect = sendrre;
669 break;
670 case EncCorre:
671 v->encname = "corre";
672 v->countrect = countcorre;
673 v->sendrect = sendcorre;
674 break;
675 case EncHextile:
676 v->encname = "hextile";
677 v->countrect = counthextile;
678 v->sendrect = sendhextile;
679 break;
680 }
681 }
682
683 if(v->countrect == nil){
684 v->encname = "raw";
685 v->countrect = countraw;
686 v->sendrect = sendraw;
687 }
688
689 if(verbose)
690 fprint(2, "Encoding with %s%s%s\n", v->encname,
691 v->copyrect ? ", copyrect" : "",
692 v->canwarp ? ", canwarp" : "");
693 }
694
695 /*
696 * Continually read updates from one client.
697 */
698 static void
clientreadproc(Vncs * v)699 clientreadproc(Vncs *v)
700 {
701 int incremental, key, keydown, buttons, type, x, y, n;
702 char *buf;
703 Rectangle r;
704
705 vncname("read %V", v);
706
707 for(;;){
708 type = vncrdchar(v);
709 switch(type){
710 default:
711 fprint(2, "%V: unknown vnc message type %d; hanging up\n", v, type);
712 vnchungup(v);
713
714 /* set pixel format */
715 case MPixFmt:
716 setpixelfmt(v);
717 break;
718
719 /* ignore color map changes */
720 case MFixCmap:
721 vncgobble(v, 3);
722 n = vncrdshort(v);
723 vncgobble(v, n*6);
724 break;
725
726 /* set encoding list */
727 case MSetEnc:
728 setencoding(v);
729 break;
730
731 /* request image update in rectangle */
732 case MFrameReq:
733 incremental = vncrdchar(v);
734 r = vncrdrect(v);
735 if(incremental){
736 vnclock(v);
737 v->updaterequest = 1;
738 vncunlock(v);
739 }else{
740 drawlock(); /* protects rlist */
741 vnclock(v); /* protects updaterequest */
742 v->updaterequest = 1;
743 addtorlist(&v->rlist, r);
744 vncunlock(v);
745 drawunlock();
746 }
747 break;
748
749 /* send keystroke */
750 case MKey:
751 keydown = vncrdchar(v);
752 vncgobble(v, 2);
753 key = vncrdlong(v);
754 vncputc(!keydown, key);
755 break;
756
757 /* send mouse event */
758 case MMouse:
759 buttons = vncrdchar(v);
760 x = vncrdshort(v);
761 y = vncrdshort(v);
762 mousetrack(x, y, buttons, nsec()/(1000*1000LL));
763 break;
764
765 /* send cut text */
766 case MCCut:
767 vncgobble(v, 3);
768 n = vncrdlong(v);
769 buf = malloc(n+1);
770 if(buf){
771 vncrdbytes(v, buf, n);
772 buf[n] = 0;
773 vnclock(v); /* for snarfvers */
774 setsnarf(buf, n, &v->snarfvers);
775 vncunlock(v);
776 }else
777 vncgobble(v, n);
778 break;
779 }
780 }
781 }
782
783 static int
nbits(ulong mask)784 nbits(ulong mask)
785 {
786 int n;
787
788 n = 0;
789 for(; mask; mask>>=1)
790 n += mask&1;
791 return n;
792 }
793
794 typedef struct Col Col;
795 struct Col {
796 int type;
797 int nbits;
798 int shift;
799 };
800
801 static ulong
fmt2chan(Pixfmt * fmt)802 fmt2chan(Pixfmt *fmt)
803 {
804 Col c[4], t;
805 int i, j, depth, n, nc;
806 ulong mask, u;
807
808 /* unpack the Pixfmt channels */
809 c[0] = (Col){CRed, nbits(fmt->red.max), fmt->red.shift};
810 c[1] = (Col){CGreen, nbits(fmt->green.max), fmt->green.shift};
811 c[2] = (Col){CBlue, nbits(fmt->blue.max), fmt->blue.shift};
812 nc = 3;
813
814 /* add an ignore channel if necessary */
815 depth = c[0].nbits+c[1].nbits+c[2].nbits;
816 if(fmt->bpp != depth){
817 /* BUG: assumes only one run of ignored bits */
818 if(fmt->bpp == 32)
819 mask = ~0;
820 else
821 mask = (1<<fmt->bpp)-1;
822 mask ^= fmt->red.max << fmt->red.shift;
823 mask ^= fmt->green.max << fmt->green.shift;
824 mask ^= fmt->blue.max << fmt->blue.shift;
825 if(mask == 0)
826 abort();
827 n = 0;
828 for(; !(mask&1); mask>>=1)
829 n++;
830 c[3] = (Col){CIgnore, nbits(mask), n};
831 nc++;
832 }
833
834 /* sort the channels, largest shift (leftmost bits) first */
835 for(i=1; i<nc; i++)
836 for(j=i; j>0; j--)
837 if(c[j].shift > c[j-1].shift){
838 t = c[j];
839 c[j] = c[j-1];
840 c[j-1] = t;
841 }
842
843 /* build the channel descriptor */
844 u = 0;
845 for(i=0; i<nc; i++){
846 u <<= 8;
847 u |= CHAN1(c[i].type, c[i].nbits);
848 }
849
850 return u;
851 }
852
853 static void
chan2fmt(Pixfmt * fmt,ulong chan)854 chan2fmt(Pixfmt *fmt, ulong chan)
855 {
856 ulong c, rc, shift;
857
858 shift = 0;
859 for(rc = chan; rc; rc >>=8){
860 c = rc & 0xFF;
861 switch(TYPE(c)){
862 case CRed:
863 fmt->red = (Colorfmt){(1<<NBITS(c))-1, shift};
864 break;
865 case CBlue:
866 fmt->blue = (Colorfmt){(1<<NBITS(c))-1, shift};
867 break;
868 case CGreen:
869 fmt->green = (Colorfmt){(1<<NBITS(c))-1, shift};
870 break;
871 }
872 shift += NBITS(c);
873 }
874 }
875
876 /*
877 * Note that r has changed on the screen.
878 * Updating the rlists is okay because they are protected by drawlock.
879 */
880 void
flushmemscreen(Rectangle r)881 flushmemscreen(Rectangle r)
882 {
883 Vncs *v;
884
885 if(!rectclip(&r, gscreen->r))
886 return;
887 qlock(&clients);
888 for(v=clients.head; v; v=v->next)
889 addtorlist(&v->rlist, r);
890 qunlock(&clients);
891 }
892
893 /*
894 * Queue a mouse warp note for the next update to each client.
895 */
896 void
mousewarpnote(Point p)897 mousewarpnote(Point p)
898 {
899 Vncs *v;
900
901 qlock(&clients);
902 for(v=clients.head; v; v=v->next){
903 if(v->canwarp){
904 vnclock(v);
905 v->needwarp = 1;
906 v->warppt = p;
907 vncunlock(v);
908 }
909 }
910 qunlock(&clients);
911 }
912
913 /*
914 * Send a client his changed screen image.
915 * v is locked on entrance, locked on exit, but released during.
916 */
917 static int
updateimage(Vncs * v)918 updateimage(Vncs *v)
919 {
920 int i, ncount, nsend, docursor, needwarp;
921 vlong ooffset;
922 Point warppt;
923 Rectangle cr;
924 Rlist rlist;
925 vlong t1;
926 int (*count)(Vncs*, Rectangle);
927 int (*send)(Vncs*, Rectangle);
928
929 if(v->image == nil)
930 return 0;
931
932 /* warping info and unlock v so that updates can proceed */
933 needwarp = v->canwarp && v->needwarp;
934 warppt = v->warppt;
935 v->needwarp = 0;
936 vncunlock(v);
937
938 /* copy the screen bits and then unlock the screen so updates can proceed */
939 drawlock();
940 rlist = v->rlist;
941 memset(&v->rlist, 0, sizeof v->rlist);
942
943 /* if the cursor has moved or changed shape, we need to redraw its square */
944 lock(&cursor);
945 if(v->cursorver != cursorver || !eqpt(v->cursorpos, cursorpos)){
946 docursor = 1;
947 v->cursorver = cursorver;
948 v->cursorpos = cursorpos;
949 cr = cursorrect();
950 }else{
951 docursor = 0;
952 cr = v->cursorr;
953 }
954 unlock(&cursor);
955
956 if(docursor){
957 addtorlist(&rlist, v->cursorr);
958 if(!rectclip(&cr, gscreen->r))
959 cr.max = cr.min;
960 addtorlist(&rlist, cr);
961 }
962
963 /* copy changed screen parts, also check for parts overlapping cursor location */
964 for(i=0; i<rlist.nrect; i++){
965 if(!docursor)
966 docursor = rectXrect(v->cursorr, rlist.rect[i]);
967 memimagedraw(v->image, rlist.rect[i], gscreen, rlist.rect[i].min, memopaque, ZP, S);
968 }
969
970 if(docursor){
971 cursordraw(v->image, cr);
972 addtorlist(&rlist, v->cursorr);
973 v->cursorr = cr;
974 }
975
976 drawunlock();
977
978 ooffset = Boffset(&v->out);
979 /* no more locks are held; talk to the client */
980
981 if(rlist.nrect == 0 && needwarp == 0){
982 vnclock(v);
983 return 0;
984 }
985
986 count = v->countrect;
987 send = v->sendrect;
988 if(count == nil || send == nil){
989 count = countraw;
990 send = sendraw;
991 }
992
993 ncount = 0;
994 for(i=0; i<rlist.nrect; i++)
995 ncount += (*count)(v, rlist.rect[i]);
996
997 if(verbose > 1)
998 fprint(2, "sendupdate: rlist.nrect=%d, ncount=%d", rlist.nrect, ncount);
999
1000 t1 = nsec();
1001 vncwrchar(v, MFrameUpdate);
1002 vncwrchar(v, 0);
1003 vncwrshort(v, ncount+needwarp);
1004
1005 nsend = 0;
1006 for(i=0; i<rlist.nrect; i++)
1007 nsend += (*send)(v, rlist.rect[i]);
1008
1009 if(ncount != nsend){
1010 fprint(2, "%V: ncount=%d, nsend=%d; hanging up\n", v, ncount, nsend);
1011 vnchungup(v);
1012 }
1013
1014 if(needwarp){
1015 vncwrrect(v, Rect(warppt.x, warppt.y, warppt.x+1, warppt.y+1));
1016 vncwrlong(v, EncMouseWarp);
1017 }
1018
1019 t1 = nsec() - t1;
1020 if(verbose > 1)
1021 fprint(2, " in %lldms, %lld bytes\n", t1/1000000, Boffset(&v->out) - ooffset);
1022
1023 freerlist(&rlist);
1024 vnclock(v);
1025 return 1;
1026 }
1027
1028 /*
1029 * Update the snarf buffer if it has changed.
1030 */
1031 static void
updatesnarf(Vncs * v)1032 updatesnarf(Vncs *v)
1033 {
1034 char *buf;
1035 int len;
1036
1037 if(v->snarfvers == snarf.vers)
1038 return;
1039 vncunlock(v);
1040 qlock(&snarf);
1041 len = snarf.n;
1042 buf = malloc(len);
1043 if(buf == nil){
1044 qunlock(&snarf);
1045 vnclock(v);
1046 return;
1047 }
1048 memmove(buf, snarf.buf, len);
1049 v->snarfvers = snarf.vers;
1050 qunlock(&snarf);
1051
1052 vncwrchar(v, MSCut);
1053 vncwrbytes(v, "pad", 3);
1054 vncwrlong(v, len);
1055 vncwrbytes(v, buf, len);
1056 free(buf);
1057 vnclock(v);
1058 }
1059
1060 /*
1061 * Continually update one client.
1062 */
1063 static void
clientwriteproc(Vncs * v)1064 clientwriteproc(Vncs *v)
1065 {
1066 char buf[32], buf2[32];
1067 int sent;
1068
1069 vncname("write %V", v);
1070 for(;;){
1071 vnclock(v);
1072 if(v->ndead)
1073 break;
1074 if((v->image == nil && v->imagechan!=0)
1075 || (v->image && v->image->chan != v->imagechan)){
1076 if(v->image)
1077 freememimage(v->image);
1078 v->image = allocmemimage(Rpt(ZP, v->dim), v->imagechan);
1079 if(v->image == nil){
1080 fprint(2, "%V: allocmemimage: %r; hanging up\n", v);
1081 vnchungup(v);
1082 }
1083 if(verbose)
1084 fprint(2, "%V: translating image from chan=%s to chan=%s\n",
1085 v, chantostr(buf, gscreen->chan), chantostr(buf2, v->imagechan));
1086 }
1087 sent = 0;
1088 if(v->updaterequest){
1089 v->updaterequest = 0;
1090 updatesnarf(v);
1091 sent = updateimage(v);
1092 if(!sent)
1093 v->updaterequest = 1;
1094 }
1095 vncunlock(v);
1096 vncflush(v);
1097 if(!sent)
1098 sleep(sleeptime);
1099 }
1100 vncunlock(v);
1101 vnchungup(v);
1102 }
1103
1104