Blob


1 /*
2 * Window system protocol server.
3 * Use select and a single proc and single stack
4 * to avoid aggravating the X11 library, which is
5 * subtle and quick to anger.
6 */
8 // #define SHOWEVENT
10 #include <u.h>
11 #include <sys/select.h>
12 #include <errno.h>
13 #ifdef SHOWEVENT
14 #include <stdio.h>
15 #endif
16 #include "x11-inc.h"
18 #include <libc.h>
19 #include <draw.h>
20 #include <memdraw.h>
21 #include <memlayer.h>
22 #include <keyboard.h>
23 #include <mouse.h>
24 #include <cursor.h>
25 #include <drawfcall.h>
26 #include "x11-memdraw.h"
27 #include "devdraw.h"
29 #undef time
31 #define MouseMask (\
32 ButtonPressMask|\
33 ButtonReleaseMask|\
34 PointerMotionMask|\
35 Button1MotionMask|\
36 Button2MotionMask|\
37 Button3MotionMask)
39 #define Mask MouseMask|ExposureMask|StructureNotifyMask|KeyPressMask|KeyReleaseMask|EnterWindowMask|LeaveWindowMask|FocusChangeMask
41 typedef struct Kbdbuf Kbdbuf;
42 typedef struct Mousebuf Mousebuf;
43 typedef struct Fdbuf Fdbuf;
44 typedef struct Tagbuf Tagbuf;
46 struct Kbdbuf
47 {
48 Rune r[32];
49 int ri;
50 int wi;
51 int stall;
52 };
54 struct Mousebuf
55 {
56 Mouse m[32];
57 int ri;
58 int wi;
59 int stall;
60 int resized;
61 };
63 struct Tagbuf
64 {
65 int t[32];
66 int ri;
67 int wi;
68 };
70 struct Fdbuf
71 {
72 uchar buf[2*MAXWMSG];
73 uchar *rp;
74 uchar *wp;
75 uchar *ep;
76 };
78 Kbdbuf kbd;
79 Mousebuf mouse;
80 Fdbuf fdin;
81 Fdbuf fdout;
82 Tagbuf kbdtags;
83 Tagbuf mousetags;
85 void fdslide(Fdbuf*);
86 void runmsg(Wsysmsg*);
87 void replymsg(Wsysmsg*);
88 void runxevent(XEvent*);
89 void matchkbd(void);
90 void matchmouse(void);
91 int fdnoblock(int);
93 int chatty;
94 int drawsleep;
95 int fullscreen;
97 Rectangle windowrect;
98 Rectangle screenrect;
100 void
101 usage(void)
103 fprint(2, "usage: devdraw (don't run directly)\n");
104 exits("usage");
107 void
108 bell(void *v, char *msg)
110 if(strcmp(msg, "alarm") == 0)
111 drawsleep = drawsleep ? 0 : 1000;
112 noted(NCONT);
115 void
116 main(int argc, char **argv)
118 int n, top, firstx;
119 fd_set rd, wr, xx;
120 Wsysmsg m;
121 XEvent event;
123 /*
124 * Move the protocol off stdin/stdout so that
125 * any inadvertent prints don't screw things up.
126 */
127 dup(0, 3);
128 dup(1, 4);
129 close(0);
130 close(1);
131 open("/dev/null", OREAD);
132 open("/dev/null", OWRITE);
134 /* reopens stdout if debugging */
135 runxevent(0);
137 fmtinstall('W', drawfcallfmt);
139 ARGBEGIN{
140 case 'D':
141 chatty++;
142 break;
143 default:
144 usage();
145 }ARGEND
147 /*
148 * Ignore arguments. They're only for good ps -a listings.
149 */
151 notify(bell);
153 fdin.rp = fdin.wp = fdin.buf;
154 fdin.ep = fdin.buf+sizeof fdin.buf;
156 fdout.rp = fdout.wp = fdout.buf;
157 fdout.ep = fdout.buf+sizeof fdout.buf;
159 fdnoblock(3);
160 fdnoblock(4);
162 firstx = 1;
163 _x.fd = -1;
164 for(;;){
165 /* set up file descriptors */
166 FD_ZERO(&rd);
167 FD_ZERO(&wr);
168 FD_ZERO(&xx);
169 /*
170 * Don't read unless there's room *and* we haven't
171 * already filled the output buffer too much.
172 */
173 if(fdout.wp < fdout.buf+MAXWMSG && fdin.wp < fdin.ep)
174 FD_SET(3, &rd);
175 if(fdout.wp > fdout.rp)
176 FD_SET(4, &wr);
177 FD_SET(3, &xx);
178 FD_SET(4, &xx);
179 top = 4;
180 if(_x.fd >= 0){
181 if(firstx){
182 firstx = 0;
183 XSelectInput(_x.display, _x.drawable, Mask);
185 FD_SET(_x.fd, &rd);
186 FD_SET(_x.fd, &xx);
187 XFlush(_x.display);
188 if(_x.fd > top)
189 top = _x.fd;
192 if(chatty)
193 fprint(2, "select %d...\n", top+1);
194 /* wait for something to happen */
195 again:
196 if(select(top+1, &rd, &wr, &xx, NULL) < 0){
197 if(errno == EINTR)
198 goto again;
199 if(chatty)
200 fprint(2, "select failure\n");
201 exits(0);
203 if(chatty)
204 fprint(2, "got select...\n");
207 /* read what we can */
208 n = 1;
209 while(fdin.wp < fdin.ep && (n = read(3, fdin.wp, fdin.ep-fdin.wp)) > 0)
210 fdin.wp += n;
211 if(n == 0){
212 if(chatty)
213 fprint(2, "eof\n");
214 exits(0);
216 if(n < 0 && errno != EAGAIN)
217 sysfatal("reading wsys msg: %r");
219 /* pick off messages one by one */
220 while((n = convM2W(fdin.rp, fdin.wp-fdin.rp, &m)) > 0){
221 /* fprint(2, "<- %W\n", &m); */
222 runmsg(&m);
223 fdin.rp += n;
226 /* slide data to beginning of buf */
227 fdslide(&fdin);
230 /* write what we can */
231 n = 1;
232 while(fdout.rp < fdout.wp && (n = write(4, fdout.rp, fdout.wp-fdout.rp)) > 0)
233 fdout.rp += n;
234 if(n == 0)
235 sysfatal("short write writing wsys");
236 if(n < 0 && errno != EAGAIN)
237 sysfatal("writing wsys msg: %r");
239 /* slide data to beginning of buf */
240 fdslide(&fdout);
243 /*
244 * Read an X message if we can.
245 * (XPending actually calls select to make sure
246 * the display's fd is readable and then reads
247 * in any waiting data before declaring whether
248 * there are events on the queue.)
249 */
250 while(XPending(_x.display)){
251 XNextEvent(_x.display, &event);
252 runxevent(&event);
258 int
259 fdnoblock(int fd)
261 return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL)|O_NONBLOCK);
264 void
265 fdslide(Fdbuf *fb)
267 int n;
269 n = fb->wp - fb->rp;
270 if(n > 0)
271 memmove(fb->buf, fb->rp, n);
272 fb->rp = fb->buf;
273 fb->wp = fb->rp+n;
276 void
277 replyerror(Wsysmsg *m)
279 char err[256];
281 rerrstr(err, sizeof err);
282 m->type = Rerror;
283 m->error = err;
284 replymsg(m);
289 /*
290 * Handle a single wsysmsg.
291 * Might queue for later (kbd, mouse read)
292 */
293 void
294 runmsg(Wsysmsg *m)
296 uchar buf[65536];
297 int n;
298 Memimage *i;
300 switch(m->type){
301 case Tinit:
302 memimageinit();
303 i = _xattach(m->label, m->winsize);
304 _initdisplaymemimage(i);
305 replymsg(m);
306 break;
308 case Trdmouse:
309 mousetags.t[mousetags.wi++] = m->tag;
310 if(mousetags.wi == nelem(mousetags.t))
311 mousetags.wi = 0;
312 if(mousetags.wi == mousetags.ri)
313 sysfatal("too many queued mouse reads");
314 /* fprint(2, "mouse unstall\n"); */
315 mouse.stall = 0;
316 matchmouse();
317 break;
319 case Trdkbd:
320 kbdtags.t[kbdtags.wi++] = m->tag;
321 if(kbdtags.wi == nelem(kbdtags.t))
322 kbdtags.wi = 0;
323 if(kbdtags.wi == kbdtags.ri)
324 sysfatal("too many queued keyboard reads");
325 kbd.stall = 0;
326 matchkbd();
327 break;
329 case Tmoveto:
330 _xmoveto(m->mouse.xy);
331 replymsg(m);
332 break;
334 case Tcursor:
335 if(m->arrowcursor)
336 _xsetcursor(nil);
337 else
338 _xsetcursor(&m->cursor);
339 replymsg(m);
340 break;
342 case Tbouncemouse:
343 _xbouncemouse(&m->mouse);
344 replymsg(m);
345 break;
347 case Tlabel:
348 _xsetlabel(m->label);
349 replymsg(m);
350 break;
352 case Trdsnarf:
353 m->snarf = _xgetsnarf();
354 replymsg(m);
355 free(m->snarf);
356 break;
358 case Twrsnarf:
359 _xputsnarf(m->snarf);
360 replymsg(m);
361 break;
363 case Trddraw:
364 n = m->count;
365 if(n > sizeof buf)
366 n = sizeof buf;
367 n = _drawmsgread(buf, n);
368 if(n < 0)
369 replyerror(m);
370 else{
371 m->count = n;
372 m->data = buf;
373 replymsg(m);
375 break;
377 case Twrdraw:
378 if(_drawmsgwrite(m->data, m->count) < 0)
379 replyerror(m);
380 else
381 replymsg(m);
382 break;
384 case Ttop:
385 _xtopwindow();
386 replymsg(m);
387 break;
389 case Tresize:
390 _xresizewindow(m->rect);
391 replymsg(m);
392 break;
396 /*
397 * Reply to m.
398 */
399 void
400 replymsg(Wsysmsg *m)
402 int n;
404 /* T -> R msg */
405 if(m->type%2 == 0)
406 m->type++;
408 /* fprint(2, "-> %W\n", m); */
409 /* copy to output buffer */
410 n = sizeW2M(m);
411 if(fdout.wp+n > fdout.ep)
412 sysfatal("out of space for reply message");
413 convW2M(m, fdout.wp, n);
414 fdout.wp += n;
417 /*
418 * Match queued kbd reads with queued kbd characters.
419 */
420 void
421 matchkbd(void)
423 Wsysmsg m;
425 if(kbd.stall)
426 return;
427 while(kbd.ri != kbd.wi && kbdtags.ri != kbdtags.wi){
428 m.type = Rrdkbd;
429 m.tag = kbdtags.t[kbdtags.ri++];
430 if(kbdtags.ri == nelem(kbdtags.t))
431 kbdtags.ri = 0;
432 m.rune = kbd.r[kbd.ri++];
433 if(kbd.ri == nelem(kbd.r))
434 kbd.ri = 0;
435 replymsg(&m);
439 /*
440 * Match queued mouse reads with queued mouse events.
441 */
442 void
443 matchmouse(void)
445 Wsysmsg m;
447 while(mouse.ri != mouse.wi && mousetags.ri != mousetags.wi){
448 m.type = Rrdmouse;
449 m.tag = mousetags.t[mousetags.ri++];
450 if(mousetags.ri == nelem(mousetags.t))
451 mousetags.ri = 0;
452 m.mouse = mouse.m[mouse.ri];
453 m.resized = mouse.resized;
454 /*
455 if(m.resized)
456 fprint(2, "sending resize\n");
457 */
458 mouse.resized = 0;
459 mouse.ri++;
460 if(mouse.ri == nelem(mouse.m))
461 mouse.ri = 0;
462 replymsg(&m);
466 static int kbuttons;
467 static int altdown;
468 static int kstate;
470 static void
471 sendmouse(Mouse m)
473 m.buttons |= kbuttons;
474 mouse.m[mouse.wi] = m;
475 mouse.wi++;
476 if(mouse.wi == nelem(mouse.m))
477 mouse.wi = 0;
478 if(mouse.wi == mouse.ri){
479 mouse.stall = 1;
480 mouse.ri = 0;
481 mouse.wi = 1;
482 mouse.m[0] = m;
483 /* fprint(2, "mouse stall\n"); */
485 matchmouse();
488 /*
489 * Handle an incoming X event.
490 */
491 void
492 runxevent(XEvent *xev)
494 int c;
495 KeySym k;
496 static Mouse m;
497 XButtonEvent *be;
498 XKeyEvent *ke;
500 #ifdef SHOWEVENT
501 static int first = 1;
502 if(first){
503 dup(create("/tmp/devdraw.out", OWRITE, 0666), 1);
504 setbuf(stdout, 0);
505 first = 0;
507 #endif
509 if(xev == 0)
510 return;
512 #ifdef SHOWEVENT
513 print("\n");
514 ShowEvent(xev);
515 #endif
517 switch(xev->type){
518 case Expose:
519 _xexpose(xev);
520 break;
522 case DestroyNotify:
523 if(_xdestroy(xev))
524 exits(0);
525 break;
527 case ConfigureNotify:
528 if(_xconfigure(xev)){
529 mouse.resized = 1;
530 _xreplacescreenimage();
531 sendmouse(m);
533 break;
535 case ButtonPress:
536 be = (XButtonEvent*)xev;
537 if(be->button == 1) {
538 if(kstate & ControlMask)
539 be->button = 2;
540 else if(kstate & Mod1Mask)
541 be->button = 3;
543 // fall through
544 case ButtonRelease:
545 altdown = 0;
546 // fall through
547 case MotionNotify:
548 if(mouse.stall)
549 return;
550 if(_xtoplan9mouse(xev, &m) < 0)
551 return;
552 sendmouse(m);
553 break;
555 case KeyRelease:
556 case KeyPress:
557 ke = (XKeyEvent*)xev;
558 XLookupString(ke, NULL, 0, &k, NULL);
559 c = ke->state;
560 switch(k) {
561 case XK_Alt_L:
562 case XK_Meta_L: /* Shift Alt on PCs */
563 case XK_Alt_R:
564 case XK_Meta_R: /* Shift Alt on PCs */
565 case XK_Multi_key:
566 if(xev->type == KeyPress)
567 altdown = 1;
568 else if(altdown) {
569 altdown = 0;
570 sendalt();
572 break;
575 switch(k) {
576 case XK_Control_L:
577 if(xev->type == KeyPress)
578 c |= ControlMask;
579 else
580 c &= ~ControlMask;
581 goto kbutton;
582 case XK_Alt_L:
583 case XK_Shift_L:
584 if(xev->type == KeyPress)
585 c |= Mod1Mask;
586 else
587 c &= ~Mod1Mask;
588 kbutton:
589 kstate = c;
590 if(m.buttons || kbuttons) {
591 altdown = 0; // used alt
592 kbuttons = 0;
593 if(c & ControlMask)
594 kbuttons |= 2;
595 if(c & Mod1Mask)
596 kbuttons |= 4;
597 sendmouse(m);
598 break;
602 if(xev->type != KeyPress)
603 break;
604 if(k == XK_F11){
605 fullscreen = !fullscreen;
606 _xmovewindow(fullscreen ? screenrect : windowrect);
607 return;
609 if(kbd.stall)
610 return;
611 if((c = _xtoplan9kbd(xev)) < 0)
612 return;
613 kbd.r[kbd.wi++] = c;
614 if(kbd.wi == nelem(kbd.r))
615 kbd.wi = 0;
616 if(kbd.ri == kbd.wi)
617 kbd.stall = 1;
618 matchkbd();
619 break;
621 case FocusOut:
622 /*
623 * Some key combinations (e.g. Alt-Tab) can cause us
624 * to see the key down event without the key up event,
625 * so clear out the keyboard state when we lose the focus.
626 */
627 kstate = 0;
628 altdown = 0;
629 abortcompose();
630 break;
632 case SelectionRequest:
633 _xselect(xev);
634 break;