]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/vnc/vncs.c
cb4bbd66f43a55a0b7c0a2307fb3a10e9a8a615b
[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 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
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
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
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
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
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
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
337 exiting(void)
338 {
339         vncclose(*vncpriv);
340 }
341
342 void
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
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
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
392 shutdown(void)
393 {
394         if(verbose)
395                 fprint(2, "vnc server shutdown\n");
396         killall();
397 }
398
399 static void
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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