]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/vnc/vncs.c
vnc: don't prompt for password on auth_respond() failure
[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     noauth = 0;
59 int     kbdin = -1;
60
61 char *cert;
62 char *pixchan = "r5g6b5";
63 static int      cmdpid;
64 static int      srvfd;
65 static int      exportfd;
66 static Vncs     **vncpriv;
67
68 static int parsedisplay(char*);
69 static void vnckill(char*, int, int);
70 static int vncannounce(char *net, int display, char *adir, int base);
71 static void noteshutdown(void*, char*);
72 static void vncaccept(Vncs*);
73 static int vncsfmt(Fmt*);
74 static void getremote(char*, char*);
75 static void vncname(char*, ...);
76 #pragma varargck argpos vncname 1
77
78 #pragma varargck type "V" Vncs*
79
80 void
81 usage(void)
82 {
83         fprint(2, "usage: vncs [-v] [-c cert] [-d :display] [-g widthXheight] [-p pixelfmt] [-A] [cmd [args]...]\n");
84         fprint(2, "\tto kill a server: vncs [-v] -k :display\n");
85         exits("usage");
86 }
87
88 void
89 main(int argc, char **argv)
90 {
91         int baseport, cfd, display, exnum, fd, pid, h, killing, w;
92         char adir[NETPATHLEN], ldir[NETPATHLEN];
93         char net[NETPATHLEN], *p;
94         char *kbdfs[] = { "/bin/aux/kbdfs", "-dq", nil };
95         char *rc[] = { "/bin/rc", "-i", nil };
96         Vncs *v;
97
98         fmtinstall('V', vncsfmt);
99         display = -1;
100         killing = 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                 break;
147         case 'A':
148                 noauth = 1;
149                 break;
150         }ARGEND
151
152         if(killing){
153                 vnckill(net, display, baseport);
154                 exits(nil);
155         }
156
157         if(argc == 0)
158                 argv = rc;
159
160         /* easy exit */
161         if(access(argv[0], AEXEC) < 0)
162                 sysfatal("access %s for exec: %r", argv[0]);
163
164         /* background ourselves */
165         switch(rfork(RFPROC|RFNAMEG|RFFDG|RFNOTEG)){
166         case -1:
167                 sysfatal("rfork: %r");
168         default:
169                 exits(nil);
170         case 0:
171                 break;
172         }
173
174         vncpriv = privalloc();
175         if(vncpriv == nil)
176                 sysfatal("privalloc: %r");
177
178         /* start screen */
179         initcompat();
180         if(waserror())
181                 sysfatal("screeninit %dx%d %s: %s", w, h, pixchan, up->error);
182         if(verbose)
183                 fprint(2, "geometry is %dx%d\n", w, h);
184         screeninit(w, h, pixchan);
185         poperror();
186
187         /* start file system device slaves */
188         exnum = exporter(devtab, &fd, &exportfd);
189
190         /* rebuild /dev because the underlying connection might go away (ick) */
191         unmount(nil, "/dev");
192         bind("#c", "/dev", MREPL);
193
194         /* mount exporter */
195         if(mounter("/dev", MBEFORE, fd, exnum) < 0)
196                 sysfatal("mounter: %r");
197         close(fd);
198
199         pid = rfork(RFPROC|RFMEM|RFFDG|RFNOTEG);
200         switch(pid){
201         case -1:
202                 sysfatal("rfork: %r");
203                 break;
204         case 0:
205                 close(exportfd);
206
207                 close(1);
208                 open("/dev/cons", OWRITE);
209                 close(2);
210                 open("/dev/cons", OWRITE);
211
212                 /* start and mount kbdfs */
213                 pid = rfork(RFPROC|RFMEM|RFFDG|RFREND);
214                 switch(pid){
215                 case -1:
216                         sysfatal("rfork: %r");
217                         break;
218                 case 0:
219                         exec(kbdfs[0], kbdfs);
220                         fprint(2, "exec %s: %r\n", kbdfs[0]);
221                         _exits("kbdfs");
222                 }
223                 if(waitpid() != pid){
224                         rendezvous(&kbdin, nil);
225                         sysfatal("waitpid: %s: %r", kbdfs[0]);
226                 }
227                 rendezvous(&kbdin, nil);
228
229                 rfork(RFNAMEG|RFREND);
230
231                 close(0);
232                 open("/dev/cons", OREAD);
233                 close(1);
234                 open("/dev/cons", OWRITE);
235                 close(2);
236                 open("/dev/cons", OWRITE);
237
238                 exec(argv[0], argv);
239                 fprint(2, "exec %s: %r\n", argv[0]);
240                 _exits(nil);
241         }
242         cmdpid = pid;
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
528         /* caller returns to listen */
529         switch(rfork(RFPROC|RFMEM|RFNAMEG)){
530         case -1:
531                 fprint(2, "%V: fork failed: %r\n", v);
532                 vncclose(v);
533                 return;
534         default:
535                 return;
536         case 0:
537                 break;
538         }
539         *vncpriv = v;
540
541         if(!atexit(exiting)){
542                 fprint(2, "%V: could not register atexit handler: %r; hanging up\n", v);
543                 exiting();
544                 exits(nil);
545         }
546
547         if(cert != nil){
548                 TLSconn conn;
549
550                 memset(&conn, 0, sizeof conn);
551                 conn.cert = readcert(cert, &conn.certlen);
552                 if(conn.cert == nil){
553                         fprint(2, "%V: could not read cert %s: %r; hanging up\n", v, cert);
554                         exits(nil);
555                 }
556                 fd = tlsServer(v->datafd, &conn);
557                 if(fd < 0){
558                         fprint(2, "%V: tlsServer: %r; hanging up\n", v);
559                         free(conn.cert);
560                         free(conn.sessionID);
561                         exits(nil);
562                 }
563                 v->datafd = fd;
564                 free(conn.cert);
565                 free(conn.sessionID);
566         }
567         vncinit(v->datafd, v->ctlfd, v);
568
569         if(verbose)
570                 fprint(2, "%V: handshake\n", v);
571         if(vncsrvhandshake(v) < 0){
572                 fprint(2, "%V: handshake failed; hanging up\n", v);
573                 exits(0);
574         }
575
576         if(noauth){
577                 if(verbose)
578                         fprint(2, "%V: noauth\n", v);
579                 vncwrlong(v, ANoAuth);
580                 vncflush(v);
581         } else {
582                 if(verbose)
583                         fprint(2, "%V: auth\n", v);
584                 if(vncsrvauth(v) < 0){
585                         fprint(2, "%V: auth failed; hanging up\n", v);
586                         exits(0);
587                 }
588         }
589
590         shared = vncrdchar(v);
591
592         if(verbose)
593                 fprint(2, "%V: %sshared\n", v, shared ? "" : "not ");
594         if(!shared)
595                 killclients(v);
596
597         v->dim = (Point){Dx(gscreen->r), Dy(gscreen->r)};
598         vncwrpoint(v, v->dim);
599         if(verbose)
600                 fprint(2, "%V: send screen size %P (rect %R)\n", v, v->dim, gscreen->r);
601
602         v->bpp = gscreen->depth;
603         v->depth = gscreen->depth;
604         v->truecolor = 1;
605         v->bigendian = 0;
606         chan2fmt(v, gscreen->chan);
607         if(verbose)
608                 fprint(2, "%V: bpp=%d, depth=%d, chan=%s\n", v,
609                         v->bpp, v->depth, chantostr(buf, gscreen->chan));
610         vncwrpixfmt(v, v);
611         vncwrlong(v, 14);
612         vncwrbytes(v, "Plan9 Desktop", 14);
613         vncflush(v);
614
615         if(verbose)
616                 fprint(2, "%V: handshaking done\n", v);
617
618         switch(rfork(RFPROC|RFMEM)){
619         case -1:
620                 fprint(2, "%V: cannot fork: %r; hanging up\n", v);
621                 vnchungup(v);
622         default:
623                 clientreadproc(v);
624                 exits(nil);
625         case 0:
626                 *vncpriv = v;
627                 v->nproc++;
628                 if(atexit(exiting) == 0){
629                         exiting();
630                         fprint(2, "%V: could not register atexit handler: %r; hanging up\n", v);
631                         exits(nil);
632                 }
633                 clientwriteproc(v);
634                 exits(nil);
635         }
636 }
637
638 static void
639 vncname(char *fmt, ...)
640 {
641         int fd;
642         char name[64], buf[32];
643         va_list arg;
644
645         va_start(arg, fmt);
646         vsnprint(name, sizeof name, fmt, arg);
647         va_end(arg);
648
649         sprint(buf, "/proc/%d/args", getpid());
650         if((fd = open(buf, OWRITE)) >= 0){
651                 write(fd, name, strlen(name));
652                 close(fd);
653         }
654 }
655
656 /*
657  * Set the pixel format being sent.  Can only happen once.
658  * (Maybe a client would send this again if the screen changed
659  * underneath it?  If we want to support this we need a way to
660  * make sure the current image is no longer in use, so we can free it. 
661  */
662 static void
663 setpixelfmt(Vncs *v)
664 {
665         ulong chan;
666
667         vncgobble(v, 3);
668         v->Pixfmt = vncrdpixfmt(v);
669         chan = fmt2chan(v);
670         if(chan == 0){
671                 fprint(2, "%V: bad pixel format; hanging up\n", v);
672                 vnchungup(v);
673         }
674         v->imagechan = chan;
675 }
676
677 /*
678  * Set the preferred encoding list.  Can only happen once.
679  * If we want to support changing this more than once then
680  * we need to put a lock around the encoding functions
681  * so as not to conflict with updateimage.
682  */
683 static void
684 setencoding(Vncs *v)
685 {
686         int n, x;
687
688         vncrdchar(v);
689         n = vncrdshort(v);
690         while(n-- > 0){
691                 x = vncrdlong(v);
692                 switch(x){
693                 case EncCopyRect:
694                         v->copyrect = 1;
695                         continue;
696                 case EncMouseWarp:
697                         v->canwarp = 1;
698                         continue;
699                 }
700                 if(v->countrect != nil)
701                         continue;
702                 switch(x){
703                 case EncRaw:
704                         v->encname = "raw";
705                         v->countrect = countraw;
706                         v->sendrect = sendraw;
707                         break;
708                 case EncRre:
709                         v->encname = "rre";
710                         v->countrect = countrre;
711                         v->sendrect = sendrre;
712                         break;
713                 case EncCorre:
714                         v->encname = "corre";
715                         v->countrect = countcorre;
716                         v->sendrect = sendcorre;
717                         break;
718                 case EncHextile:
719                         v->encname = "hextile";
720                         v->countrect = counthextile;
721                         v->sendrect = sendhextile;
722                         break;
723                 }
724         }
725
726         if(v->countrect == nil){
727                 v->encname = "raw";
728                 v->countrect = countraw;
729                 v->sendrect = sendraw;
730         }
731
732         if(verbose)
733                 fprint(2, "Encoding with %s%s%s\n", v->encname,
734                         v->copyrect ? ", copyrect" : "",
735                         v->canwarp ? ", canwarp" : "");
736 }
737
738 /*
739  * Continually read updates from one client.
740  */
741 static void
742 clientreadproc(Vncs *v)
743 {
744         int incremental, key, keydown, buttons, type, x, y, n;
745         char *buf;
746         Rectangle r;
747
748         vncname("read %V", v);
749
750         for(;;){
751                 type = vncrdchar(v);
752                 switch(type){
753                 default:
754                         fprint(2, "%V: unknown vnc message type %d; hanging up\n", v, type);
755                         vnchungup(v);
756
757                 /* set pixel format */
758                 case MPixFmt:
759                         setpixelfmt(v);
760                         break;
761
762                 /* ignore color map changes */
763                 case MFixCmap:
764                         vncgobble(v, 3);
765                         n = vncrdshort(v);
766                         vncgobble(v, n*6);
767                         break;
768
769                 /* set encoding list */
770                 case MSetEnc:
771                         setencoding(v);
772                         break;
773
774                 /* request image update in rectangle */
775                 case MFrameReq:
776                         incremental = vncrdchar(v);
777                         r = vncrdrect(v);
778                         if(incremental){
779                                 vnclock(v);
780                                 v->updaterequest = 1;
781                                 vncunlock(v);
782                         }else{
783                                 drawlock();     /* protects rlist */
784                                 vnclock(v);     /* protects updaterequest */
785                                 v->updaterequest = 1;
786                                 addtorlist(&v->rlist, r);
787                                 vncunlock(v);
788                                 drawunlock();
789                         }
790                         break;
791
792                 /* send keystroke */
793                 case MKey:
794                         keydown = vncrdchar(v);
795                         vncgobble(v, 2);
796                         key = vncrdlong(v);
797                         vncputc(!keydown, key);
798                         break;
799
800                 /* send mouse event */
801                 case MMouse:
802                         buttons = vncrdchar(v);
803                         x = vncrdshort(v);
804                         y = vncrdshort(v);
805                         mousetrack(x, y, buttons, nsec()/(1000*1000LL));
806                         break;
807
808                 /* send cut text */
809                 case MCCut:
810                         vncgobble(v, 3);
811                         n = vncrdlong(v);
812                         buf = malloc(n+1);
813                         if(buf){
814                                 vncrdbytes(v, buf, n);
815                                 buf[n] = 0;
816                                 vnclock(v);     /* for snarfvers */
817                                 setsnarf(buf, n, &v->snarfvers);
818                                 vncunlock(v);
819                         }else
820                                 vncgobble(v, n);
821                         break;
822                 }
823         }
824 }
825
826 static int
827 nbits(ulong mask)
828 {
829         int n;
830
831         n = 0;
832         for(; mask; mask>>=1)
833                 n += mask&1;
834         return n;
835 }
836
837 typedef struct Col Col;
838 struct Col {
839         int type;
840         int nbits;
841         int shift;
842 };
843
844 static ulong
845 fmt2chan(Pixfmt *fmt)
846 {
847         Col c[4], t;
848         int i, j, depth, n, nc;
849         ulong mask, u;
850
851         /* unpack the Pixfmt channels */
852         c[0] = (Col){CRed, nbits(fmt->red.max), fmt->red.shift};
853         c[1] = (Col){CGreen, nbits(fmt->green.max), fmt->green.shift};
854         c[2] = (Col){CBlue, nbits(fmt->blue.max), fmt->blue.shift};
855         nc = 3;
856
857         /* add an ignore channel if necessary */
858         depth = c[0].nbits+c[1].nbits+c[2].nbits;
859         if(fmt->bpp != depth){
860                 /* BUG: assumes only one run of ignored bits */
861                 if(fmt->bpp == 32)
862                         mask = ~0;
863                 else
864                         mask = (1<<fmt->bpp)-1;
865                 mask ^= fmt->red.max << fmt->red.shift;
866                 mask ^= fmt->green.max << fmt->green.shift;
867                 mask ^= fmt->blue.max << fmt->blue.shift;
868                 if(mask == 0)
869                         abort();
870                 n = 0;
871                 for(; !(mask&1); mask>>=1)
872                         n++;
873                 c[3] = (Col){CIgnore, nbits(mask), n};
874                 nc++;
875         }
876
877         /* sort the channels, largest shift (leftmost bits) first */
878         for(i=1; i<nc; i++)
879                 for(j=i; j>0; j--)
880                         if(c[j].shift > c[j-1].shift){
881                                 t = c[j];
882                                 c[j] = c[j-1];
883                                 c[j-1] = t;
884                         }
885
886         /* build the channel descriptor */
887         u = 0;
888         for(i=0; i<nc; i++){
889                 u <<= 8;
890                 u |= CHAN1(c[i].type, c[i].nbits);
891         }
892
893         return u;
894 }
895
896 static void
897 chan2fmt(Pixfmt *fmt, ulong chan)
898 {
899         ulong c, rc, shift;
900
901         shift = 0;
902         for(rc = chan; rc; rc >>=8){
903                 c = rc & 0xFF;
904                 switch(TYPE(c)){
905                 case CRed:
906                         fmt->red = (Colorfmt){(1<<NBITS(c))-1, shift};
907                         break;
908                 case CBlue:
909                         fmt->blue = (Colorfmt){(1<<NBITS(c))-1, shift};
910                         break;
911                 case CGreen:
912                         fmt->green = (Colorfmt){(1<<NBITS(c))-1, shift};
913                         break;
914                 }
915                 shift += NBITS(c);
916         }
917 }
918
919 /*
920  * Note that r has changed on the screen.
921  * Updating the rlists is okay because they are protected by drawlock.
922  */
923 void
924 flushmemscreen(Rectangle r)
925 {
926         Vncs *v;
927
928         if(!rectclip(&r, gscreen->r))
929                 return;
930         qlock(&clients);
931         for(v=clients.head; v; v=v->next)
932                 addtorlist(&v->rlist, r);
933         qunlock(&clients);
934 }
935
936 /*
937  * Queue a mouse warp note for the next update to each client.
938  */
939 void
940 mousewarpnote(Point p)
941 {
942         Vncs *v;
943
944         qlock(&clients);
945         for(v=clients.head; v; v=v->next){
946                 if(v->canwarp){
947                         vnclock(v);
948                         v->needwarp = 1;
949                         v->warppt = p;
950                         vncunlock(v);
951                 }
952         }
953         qunlock(&clients);
954 }
955
956 /*
957  * Send a client his changed screen image.
958  * v is locked on entrance, locked on exit, but released during.
959  */
960 static int
961 updateimage(Vncs *v)
962 {
963         int i, ncount, nsend, docursor, needwarp;
964         vlong ooffset;
965         Point warppt;
966         Rectangle cr;
967         Rlist rlist;
968         vlong t1;
969         int (*count)(Vncs*, Rectangle);
970         int (*send)(Vncs*, Rectangle);
971
972         if(v->image == nil)
973                 return 0;
974
975         /* warping info and unlock v so that updates can proceed */
976         needwarp = v->canwarp && v->needwarp;
977         warppt = v->warppt;
978         v->needwarp = 0;
979         vncunlock(v);
980
981         /* copy the screen bits and then unlock the screen so updates can proceed */
982         drawlock();
983         rlist = v->rlist;
984         memset(&v->rlist, 0, sizeof v->rlist);
985
986         /* if the cursor has moved or changed shape, we need to redraw its square */
987         lock(&cursor);
988         if(v->cursorver != cursorver || !eqpt(v->cursorpos, cursorpos)){
989                 docursor = 1;
990                 v->cursorver = cursorver;
991                 v->cursorpos = cursorpos;
992                 cr = cursorrect();
993         }else{
994                 docursor = 0;
995                 cr = v->cursorr;
996         }
997         unlock(&cursor);
998
999         if(docursor){
1000                 addtorlist(&rlist, v->cursorr);
1001                 if(!rectclip(&cr, gscreen->r))
1002                         cr.max = cr.min;
1003                 addtorlist(&rlist, cr);
1004         }
1005
1006         /* copy changed screen parts, also check for parts overlapping cursor location */
1007         for(i=0; i<rlist.nrect; i++){
1008                 if(!docursor)
1009                         docursor = rectXrect(v->cursorr, rlist.rect[i]);
1010                 memimagedraw(v->image, rlist.rect[i], gscreen, rlist.rect[i].min, memopaque, ZP, S);
1011         }
1012
1013         if(docursor){
1014                 cursordraw(v->image, cr);
1015                 addtorlist(&rlist, v->cursorr);
1016                 v->cursorr = cr;
1017         }
1018
1019         drawunlock();
1020
1021         ooffset = Boffset(&v->out);
1022         /* no more locks are held; talk to the client */
1023
1024         if(rlist.nrect == 0 && needwarp == 0){
1025                 vnclock(v);
1026                 return 0;
1027         }
1028
1029         count = v->countrect;
1030         send = v->sendrect;
1031         if(count == nil || send == nil){
1032                 count = countraw;
1033                 send = sendraw;
1034         }
1035
1036         ncount = 0;
1037         for(i=0; i<rlist.nrect; i++)
1038                 ncount += (*count)(v, rlist.rect[i]);
1039
1040         if(verbose > 1)
1041                 fprint(2, "sendupdate: rlist.nrect=%d, ncount=%d", rlist.nrect, ncount);
1042
1043         t1 = nsec();
1044         vncwrchar(v, MFrameUpdate);
1045         vncwrchar(v, 0);
1046         vncwrshort(v, ncount+needwarp);
1047
1048         nsend = 0;
1049         for(i=0; i<rlist.nrect; i++)
1050                 nsend += (*send)(v, rlist.rect[i]);
1051
1052         if(ncount != nsend){
1053                 fprint(2, "%V: ncount=%d, nsend=%d; hanging up\n", v, ncount, nsend);
1054                 vnchungup(v);
1055         }
1056
1057         if(needwarp){
1058                 vncwrrect(v, Rect(warppt.x, warppt.y, warppt.x+1, warppt.y+1));
1059                 vncwrlong(v, EncMouseWarp);
1060         }
1061
1062         t1 = nsec() - t1;
1063         if(verbose > 1)
1064                 fprint(2, " in %lldms, %lld bytes\n", t1/1000000, Boffset(&v->out) - ooffset);
1065
1066         freerlist(&rlist);
1067         vnclock(v);
1068         return 1;
1069 }
1070
1071 /*
1072  * Update the snarf buffer if it has changed.
1073  */
1074 static void
1075 updatesnarf(Vncs *v)
1076 {
1077         char *buf;
1078         int len;
1079
1080         if(v->snarfvers == snarf.vers)
1081                 return;
1082         vncunlock(v);
1083         qlock(&snarf);
1084         len = snarf.n;
1085         buf = malloc(len);
1086         if(buf == nil){
1087                 qunlock(&snarf);
1088                 vnclock(v);
1089                 return;
1090         }
1091         memmove(buf, snarf.buf, len);
1092         v->snarfvers = snarf.vers;
1093         qunlock(&snarf);
1094
1095         vncwrchar(v, MSCut);
1096         vncwrbytes(v, "pad", 3);
1097         vncwrlong(v, len);
1098         vncwrbytes(v, buf, len);
1099         free(buf);
1100         vnclock(v);
1101 }
1102
1103 /*
1104  * Continually update one client.
1105  */
1106 static void
1107 clientwriteproc(Vncs *v)
1108 {
1109         char buf[32], buf2[32];
1110         int sent;
1111
1112         vncname("write %V", v);
1113         for(;;){
1114                 vnclock(v);
1115                 if(v->ndead)
1116                         break;
1117                 if((v->image == nil && v->imagechan!=0)
1118                 || (v->image && v->image->chan != v->imagechan)){
1119                         if(v->image)
1120                                 freememimage(v->image);
1121                         v->image = allocmemimage(Rpt(ZP, v->dim), v->imagechan);
1122                         if(v->image == nil){
1123                                 fprint(2, "%V: allocmemimage: %r; hanging up\n", v);
1124                                 vnchungup(v);
1125                         }
1126                         if(verbose)
1127                                 fprint(2, "%V: translating image from chan=%s to chan=%s\n",
1128                                         v, chantostr(buf, gscreen->chan), chantostr(buf2, v->imagechan));
1129                 }
1130                 sent = 0;
1131                 if(v->updaterequest){
1132                         v->updaterequest = 0;
1133                         updatesnarf(v);
1134                         sent = updateimage(v);
1135                         if(!sent)
1136                                 v->updaterequest = 1;
1137                 }
1138                 vncunlock(v);
1139                 vncflush(v);
1140                 if(!sent)
1141                         sleep(sleeptime);
1142         }
1143         vncunlock(v);
1144         vnchungup(v);
1145 }
1146