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|EnterWindowMask|LeaveWindowMask
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 /*
467 * Handle an incoming X event.
468 */
469 void
470 runxevent(XEvent *xev)
472 int c;
473 KeySym k;
474 static Mouse m;
476 #ifdef SHOWEVENT
477 static int first = 1;
478 if(first){
479 dup(create("/tmp/devdraw.out", OWRITE, 0666), 1);
480 setbuf(stdout, 0);
481 first = 0;
483 #endif
485 if(xev == 0)
486 return;
488 #ifdef SHOWEVENT
489 print("\n");
490 ShowEvent(xev);
491 #endif
493 switch(xev->type){
494 case Expose:
495 _xexpose(xev);
496 break;
498 case DestroyNotify:
499 if(_xdestroy(xev))
500 exits(0);
501 break;
503 case ConfigureNotify:
504 if(_xconfigure(xev)){
505 mouse.resized = 1;
506 _xreplacescreenimage();
507 goto addmouse;
509 break;
511 case ButtonPress:
512 case ButtonRelease:
513 case MotionNotify:
514 if(mouse.stall)
515 return;
516 if(_xtoplan9mouse(xev, &m) < 0)
517 return;
518 addmouse:
519 mouse.m[mouse.wi] = m;
520 mouse.wi++;
521 if(mouse.wi == nelem(mouse.m))
522 mouse.wi = 0;
523 if(mouse.wi == mouse.ri){
524 mouse.stall = 1;
525 mouse.ri = 0;
526 mouse.wi = 1;
527 mouse.m[0] = m;
528 /* fprint(2, "mouse stall\n"); */
530 matchmouse();
531 break;
533 case KeyPress:
534 if(kbd.stall)
535 return;
536 XLookupString((XKeyEvent*)xev, NULL, 0, &k, NULL);
537 if(k == XK_F11){
538 fullscreen = !fullscreen;
539 _xmovewindow(fullscreen ? screenrect : windowrect);
540 return;
542 if((c = _xtoplan9kbd(xev)) < 0)
543 return;
544 kbd.r[kbd.wi++] = c;
545 if(kbd.wi == nelem(kbd.r))
546 kbd.wi = 0;
547 if(kbd.ri == kbd.wi)
548 kbd.stall = 1;
549 matchkbd();
550 break;
552 case SelectionRequest:
553 _xselect(xev);
554 break;