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