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