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