Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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