Blob


1 /*
2 * Window system protocol server.
3 */
5 #include <u.h>
6 #include <libc.h>
7 #include <thread.h>
8 #include <draw.h>
9 #include <memdraw.h>
10 #include <memlayer.h>
11 #include <keyboard.h>
12 #include <mouse.h>
13 #include <cursor.h>
14 #include <drawfcall.h>
15 #include "devdraw.h"
17 static void runmsg(Client*, Wsysmsg*);
18 static void replymsg(Client*, Wsysmsg*);
19 static void matchkbd(Client*);
20 static void matchmouse(Client*);
21 static void serveproc(void*);
22 static void listenproc(void*);
23 Client *client0;
25 int trace = 0;
26 static char *srvname;
27 static int afd;
28 static char adir[40];
30 static void
31 usage(void)
32 {
33 fprint(2, "usage: devdraw (don't run directly)\n");
34 threadexitsall("usage");
35 }
37 void
38 threadmain(int argc, char **argv)
39 {
40 char *p;
42 ARGBEGIN{
43 case 'D': /* for good ps -a listings */
44 break;
45 case 'f': /* fall through for backward compatibility */
46 case 'g':
47 case 'b':
48 break;
49 case 's':
50 // TODO: Update usage, man page.
51 srvname = EARGF(usage());
52 break;
53 default:
54 usage();
55 }ARGEND
57 memimageinit();
58 fmtinstall('H', encodefmt);
59 if((p = getenv("DEVDRAWTRACE")) != nil)
60 trace = atoi(p);
62 if(srvname == nil) {
63 client0 = mallocz(sizeof(Client), 1);
64 if(client0 == nil){
65 fprint(2, "initdraw: allocating client0: out of memory");
66 abort();
67 }
68 client0->displaydpi = 100;
69 client0->rfd = 3;
70 client0->wfd = 4;
72 /*
73 * Move the protocol off stdin/stdout so that
74 * any inadvertent prints don't screw things up.
75 */
76 dup(0,3);
77 dup(1,4);
78 close(0);
79 close(1);
80 open("/dev/null", OREAD);
81 open("/dev/null", OWRITE);
82 }
84 fmtinstall('W', drawfcallfmt);
85 gfx_main();
86 }
88 void
89 gfx_started(void)
90 {
91 char *ns, *addr;
93 if(srvname == nil) {
94 // Legacy mode: serving single client on pipes.
95 proccreate(serveproc, client0, 0);
96 return;
97 }
99 // Server mode.
100 if((ns = getns()) == nil)
101 sysfatal("out of memory");
103 addr = smprint("unix!%s/%s", ns, srvname);
104 free(ns);
105 if(addr == nil)
106 sysfatal("out of memory");
108 if((afd = announce(addr, adir)) < 0)
109 sysfatal("announce %s: %r", addr);
111 proccreate(listenproc, nil, 0);
114 static void
115 listenproc(void *v)
117 Client *c;
118 int fd;
119 char dir[40];
121 USED(v);
123 for(;;) {
124 fd = listen(adir, dir);
125 if(fd < 0)
126 sysfatal("listen: %r");
127 c = mallocz(sizeof(Client), 1);
128 if(c == nil){
129 fprint(2, "initdraw: allocating client0: out of memory");
130 abort();
132 c->displaydpi = 100;
133 c->rfd = fd;
134 c->wfd = fd;
135 proccreate(serveproc, c, 0);
139 static void
140 serveproc(void *v)
142 Client *c;
143 uchar buf[4], *mbuf;
144 int nmbuf, n, nn;
145 Wsysmsg m;
147 c = v;
148 mbuf = nil;
149 nmbuf = 0;
150 while((n = read(c->rfd, buf, 4)) == 4){
151 GET(buf, n);
152 if(n > nmbuf){
153 free(mbuf);
154 mbuf = malloc(4+n);
155 if(mbuf == nil)
156 sysfatal("out of memory");
157 nmbuf = n;
159 memmove(mbuf, buf, 4);
160 nn = readn(c->rfd, mbuf+4, n-4);
161 if(nn != n-4) {
162 fprint(2, "serveproc: eof during message\n");
163 break;
166 /* pick off messages one by one */
167 if(convM2W(mbuf, nn+4, &m) <= 0) {
168 fprint(2, "serveproc: cannot convert message\n");
169 break;
171 if(trace) fprint(2, "%ud [%d] <- %W\n", nsec()/1000000, threadid(), &m);
172 runmsg(c, &m);
175 if(c == client0) {
176 rpc_shutdown();
177 threadexitsall(nil);
181 static void
182 replyerror(Client *c, Wsysmsg *m)
184 char err[256];
186 rerrstr(err, sizeof err);
187 m->type = Rerror;
188 m->error = err;
189 replymsg(c, m);
192 /*
193 * Handle a single wsysmsg.
194 * Might queue for later (kbd, mouse read)
195 */
196 static void
197 runmsg(Client *c, Wsysmsg *m)
199 static uchar buf[65536];
200 int n;
201 Memimage *i;
203 switch(m->type){
204 case Tctxt:
205 c->wsysid = strdup(m->id);
206 replymsg(c, m);
207 break;
209 case Tinit:
210 i = rpc_attach(c, m->label, m->winsize);
211 if(i == nil) {
212 replyerror(c, m);
213 break;
215 draw_initdisplaymemimage(c, i);
216 replymsg(c, m);
217 break;
219 case Trdmouse:
220 qlock(&c->eventlk);
221 if((c->mousetags.wi+1)%nelem(c->mousetags.t) == c->mousetags.ri) {
222 qunlock(&c->eventlk);
223 werrstr("too many queued mouse reads");
224 replyerror(c, m);
225 break;
227 c->mousetags.t[c->mousetags.wi++] = m->tag;
228 if(c->mousetags.wi == nelem(c->mousetags.t))
229 c->mousetags.wi = 0;
230 c->mouse.stall = 0;
231 matchmouse(c);
232 qunlock(&c->eventlk);
233 break;
235 case Trdkbd:
236 case Trdkbd4:
237 qlock(&c->eventlk);
238 if((c->kbdtags.wi+1)%nelem(c->kbdtags.t) == c->kbdtags.ri) {
239 qunlock(&c->eventlk);
240 werrstr("too many queued keyboard reads");
241 replyerror(c, m);
242 break;
244 c->kbdtags.t[c->kbdtags.wi++] = (m->tag<<1) | (m->type==Trdkbd4);
245 if(c->kbdtags.wi == nelem(c->kbdtags.t))
246 c->kbdtags.wi = 0;
247 c->kbd.stall = 0;
248 matchkbd(c);
249 qunlock(&c->eventlk);
250 break;
252 case Tmoveto:
253 c->impl->rpc_setmouse(c, m->mouse.xy);
254 replymsg(c, m);
255 break;
257 case Tcursor:
258 if(m->arrowcursor)
259 c->impl->rpc_setcursor(c, nil, nil);
260 else {
261 scalecursor(&m->cursor2, &m->cursor);
262 c->impl->rpc_setcursor(c, &m->cursor, &m->cursor2);
264 replymsg(c, m);
265 break;
267 case Tcursor2:
268 if(m->arrowcursor)
269 c->impl->rpc_setcursor(c, nil, nil);
270 else
271 c->impl->rpc_setcursor(c, &m->cursor, &m->cursor2);
272 replymsg(c, m);
273 break;
275 case Tbouncemouse:
276 c->impl->rpc_bouncemouse(c, m->mouse);
277 replymsg(c, m);
278 break;
280 case Tlabel:
281 c->impl->rpc_setlabel(c, m->label);
282 replymsg(c, m);
283 break;
285 case Trdsnarf:
286 m->snarf = rpc_getsnarf();
287 replymsg(c, m);
288 free(m->snarf);
289 break;
291 case Twrsnarf:
292 rpc_putsnarf(m->snarf);
293 replymsg(c, m);
294 break;
296 case Trddraw:
297 n = m->count;
298 if(n > sizeof buf)
299 n = sizeof buf;
300 n = draw_dataread(c, buf, n);
301 if(n < 0)
302 replyerror(c, m);
303 else{
304 m->count = n;
305 m->data = buf;
306 replymsg(c, m);
308 break;
310 case Twrdraw:
311 if(draw_datawrite(c, m->data, m->count) < 0)
312 replyerror(c, m);
313 else
314 replymsg(c, m);
315 break;
317 case Ttop:
318 c->impl->rpc_topwin(c);
319 replymsg(c, m);
320 break;
322 case Tresize:
323 c->impl->rpc_resizewindow(c, m->rect);
324 replymsg(c, m);
325 break;
329 /*
330 * Reply to m.
331 */
332 static void
333 replymsg(Client *c, Wsysmsg *m)
335 int n;
337 /* T -> R msg */
338 if(m->type%2 == 0)
339 m->type++;
341 if(trace) fprint(2, "%ud [%d] -> %W\n", nsec()/1000000, threadid(), m);
342 /* copy to output buffer */
343 n = sizeW2M(m);
345 qlock(&c->wfdlk);
346 if(n > c->nmbuf){
347 free(c->mbuf);
348 c->mbuf = malloc(n);
349 if(c->mbuf == nil)
350 sysfatal("out of memory");
351 c->nmbuf = n;
353 convW2M(m, c->mbuf, n);
354 if(write(c->wfd, c->mbuf, n) != n)
355 fprint(2, "client write: %r\n");
356 qunlock(&c->wfdlk);
359 /*
360 * Match queued kbd reads with queued kbd characters.
361 */
362 static void
363 matchkbd(Client *c)
365 int tag;
366 Wsysmsg m;
368 if(c->kbd.stall)
369 return;
370 while(c->kbd.ri != c->kbd.wi && c->kbdtags.ri != c->kbdtags.wi){
371 tag = c->kbdtags.t[c->kbdtags.ri++];
372 m.type = Rrdkbd;
373 if(tag&1)
374 m.type = Rrdkbd4;
375 m.tag = tag>>1;
376 if(c->kbdtags.ri == nelem(c->kbdtags.t))
377 c->kbdtags.ri = 0;
378 m.rune = c->kbd.r[c->kbd.ri++];
379 if(c->kbd.ri == nelem(c->kbd.r))
380 c->kbd.ri = 0;
381 replymsg(c, &m);
385 // matchmouse matches queued mouse reads with queued mouse events.
386 // It must be called with c->eventlk held.
387 static void
388 matchmouse(Client *c)
390 Wsysmsg m;
392 if(canqlock(&c->eventlk)) {
393 fprint(2, "misuse of matchmouse\n");
394 abort();
397 while(c->mouse.ri != c->mouse.wi && c->mousetags.ri != c->mousetags.wi){
398 m.type = Rrdmouse;
399 m.tag = c->mousetags.t[c->mousetags.ri++];
400 if(c->mousetags.ri == nelem(c->mousetags.t))
401 c->mousetags.ri = 0;
402 m.mouse = c->mouse.m[c->mouse.ri];
403 m.resized = c->mouse.resized;
404 c->mouse.resized = 0;
405 /*
406 if(m.resized)
407 fprint(2, "sending resize\n");
408 */
409 c->mouse.ri++;
410 if(c->mouse.ri == nelem(c->mouse.m))
411 c->mouse.ri = 0;
412 replymsg(c, &m);
416 void
417 gfx_mouseresized(Client *c)
419 gfx_mousetrack(c, -1, -1, -1, -1);
422 void
423 gfx_mousetrack(Client *c, int x, int y, int b, uint ms)
425 Mouse *m;
427 qlock(&c->eventlk);
428 if(x == -1 && y == -1 && b == -1 && ms == -1) {
429 Mouse *copy;
430 // repeat last mouse event for resize
431 if(c->mouse.ri == 0)
432 copy = &c->mouse.m[nelem(c->mouse.m)-1];
433 else
434 copy = &c->mouse.m[c->mouse.ri-1];
435 x = copy->xy.x;
436 y = copy->xy.y;
437 b = copy->buttons;
438 ms = copy->msec;
439 c->mouse.resized = 1;
441 if(x < c->mouserect.min.x)
442 x = c->mouserect.min.x;
443 if(x > c->mouserect.max.x)
444 x = c->mouserect.max.x;
445 if(y < c->mouserect.min.y)
446 y = c->mouserect.min.y;
447 if(y > c->mouserect.max.y)
448 y = c->mouserect.max.y;
450 // If reader has stopped reading, don't bother.
451 // If reader is completely caught up, definitely queue.
452 // Otherwise, queue only button change events.
453 if(!c->mouse.stall)
454 if(c->mouse.wi == c->mouse.ri || c->mouse.last.buttons != b){
455 m = &c->mouse.last;
456 m->xy.x = x;
457 m->xy.y = y;
458 m->buttons = b;
459 m->msec = ms;
461 c->mouse.m[c->mouse.wi] = *m;
462 if(++c->mouse.wi == nelem(c->mouse.m))
463 c->mouse.wi = 0;
464 if(c->mouse.wi == c->mouse.ri){
465 c->mouse.stall = 1;
466 c->mouse.ri = 0;
467 c->mouse.wi = 1;
468 c->mouse.m[0] = *m;
470 matchmouse(c);
472 qunlock(&c->eventlk);
475 // kputc adds ch to the keyboard buffer.
476 // It must be called with c->eventlk held.
477 static void
478 kputc(Client *c, int ch)
480 if(canqlock(&c->eventlk)) {
481 fprint(2, "misuse of kputc\n");
482 abort();
485 c->kbd.r[c->kbd.wi++] = ch;
486 if(c->kbd.wi == nelem(c->kbd.r))
487 c->kbd.wi = 0;
488 if(c->kbd.ri == c->kbd.wi)
489 c->kbd.stall = 1;
490 matchkbd(c);
493 // gfx_abortcompose stops any pending compose sequence,
494 // because a mouse button has been clicked.
495 // It is called from the graphics thread with no locks held.
496 void
497 gfx_abortcompose(Client *c)
499 qlock(&c->eventlk);
500 if(c->kbd.alting) {
501 c->kbd.alting = 0;
502 c->kbd.nk = 0;
504 qunlock(&c->eventlk);
507 // gfx_keystroke records a single-rune keystroke.
508 // It is called from the graphics thread with no locks held.
509 void
510 gfx_keystroke(Client *c, int ch)
512 int i;
514 qlock(&c->eventlk);
515 if(ch == Kalt){
516 c->kbd.alting = !c->kbd.alting;
517 c->kbd.nk = 0;
518 qunlock(&c->eventlk);
519 return;
521 if(ch == Kcmd+'r') {
522 if(c->forcedpi)
523 c->forcedpi = 0;
524 else if(c->displaydpi >= 200)
525 c->forcedpi = 100;
526 else
527 c->forcedpi = 225;
528 qunlock(&c->eventlk);
529 c->impl->rpc_resizeimg(c);
530 return;
532 if(!c->kbd.alting){
533 kputc(c, ch);
534 qunlock(&c->eventlk);
535 return;
537 if(c->kbd.nk >= nelem(c->kbd.k)) // should not happen
538 c->kbd.nk = 0;
539 c->kbd.k[c->kbd.nk++] = ch;
540 ch = latin1(c->kbd.k, c->kbd.nk);
541 if(ch > 0){
542 c->kbd.alting = 0;
543 kputc(c, ch);
544 c->kbd.nk = 0;
545 qunlock(&c->eventlk);
546 return;
548 if(ch == -1){
549 c->kbd.alting = 0;
550 for(i=0; i<c->kbd.nk; i++)
551 kputc(c, c->kbd.k[i]);
552 c->kbd.nk = 0;
553 qunlock(&c->eventlk);
554 return;
556 // need more input
557 qunlock(&c->eventlk);
558 return;