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 #include <u.h>
9 #include <sys/select.h>
10 #include <errno.h>
11 #include "x11-inc.h"
12 #include <libc.h>
13 #include <draw.h>
14 #include <memdraw.h>
15 #include <memlayer.h>
16 #include <keyboard.h>
17 #include <mouse.h>
18 #include <cursor.h>
19 #include <drawfcall.h>
20 #include "x11-memdraw.h"
21 #include "devdraw.h"
23 #undef time
25 #define MouseMask (\
26 ButtonPressMask|\
27 ButtonReleaseMask|\
28 PointerMotionMask|\
29 Button1MotionMask|\
30 Button2MotionMask|\
31 Button3MotionMask)
33 #define Mask MouseMask|ExposureMask|StructureNotifyMask|KeyPressMask|EnterWindowMask|LeaveWindowMask
35 typedef struct Kbdbuf Kbdbuf;
36 typedef struct Mousebuf Mousebuf;
37 typedef struct Fdbuf Fdbuf;
38 typedef struct Tagbuf Tagbuf;
40 struct Kbdbuf
41 {
42 Rune r[32];
43 int ri;
44 int wi;
45 int stall;
46 };
48 struct Mousebuf
49 {
50 Mouse m[32];
51 int ri;
52 int wi;
53 int stall;
54 int resized;
55 };
57 struct Tagbuf
58 {
59 int t[32];
60 int ri;
61 int wi;
62 };
64 struct Fdbuf
65 {
66 uchar buf[2*MAXWMSG];
67 uchar *rp;
68 uchar *wp;
69 uchar *ep;
70 };
72 Kbdbuf kbd;
73 Mousebuf mouse;
74 Fdbuf fdin;
75 Fdbuf fdout;
76 Tagbuf kbdtags;
77 Tagbuf mousetags;
79 void fdslide(Fdbuf*);
80 void runmsg(Wsysmsg*);
81 void replymsg(Wsysmsg*);
82 void runxevent(XEvent*);
83 void matchkbd(void);
84 void matchmouse(void);
85 int fdnoblock(int);
87 int chatty;
88 int drawsleep;
90 void
91 usage(void)
92 {
93 fprint(2, "usage: devdraw (don't run directly)\n");
94 exits("usage");
95 }
97 void
98 bell(void *v, char *msg)
99 {
100 if(strcmp(msg, "alarm") == 0)
101 drawsleep = drawsleep ? 0 : 1000;
102 noted(NCONT);
105 void
106 main(int argc, char **argv)
108 int n, top, firstx;
109 fd_set rd, wr, xx;
110 Wsysmsg m;
111 XEvent event;
113 /*
114 * Move the protocol off stdin/stdout so that
115 * any inadvertent prints don't screw things up.
116 */
117 dup(0, 3);
118 dup(1, 4);
119 close(0);
120 close(1);
121 open("/dev/null", OREAD);
122 open("/dev/null", OWRITE);
124 fmtinstall('W', drawfcallfmt);
126 ARGBEGIN{
127 case 'D':
128 chatty++;
129 break;
130 default:
131 usage();
132 }ARGEND
134 /*
135 * Ignore arguments. They're only for good ps -a listings.
136 */
138 notify(bell);
140 fdin.rp = fdin.wp = fdin.buf;
141 fdin.ep = fdin.buf+sizeof fdin.buf;
143 fdout.rp = fdout.wp = fdout.buf;
144 fdout.ep = fdout.buf+sizeof fdout.buf;
146 fdnoblock(3);
147 fdnoblock(4);
149 firstx = 1;
150 _x.fd = -1;
151 for(;;){
152 /* set up file descriptors */
153 FD_ZERO(&rd);
154 FD_ZERO(&wr);
155 FD_ZERO(&xx);
156 /*
157 * Don't read unless there's room *and* we haven't
158 * already filled the output buffer too much.
159 */
160 if(fdout.wp < fdout.buf+MAXWMSG && fdin.wp < fdin.ep)
161 FD_SET(3, &rd);
162 if(fdout.wp > fdout.rp)
163 FD_SET(4, &wr);
164 FD_SET(3, &xx);
165 FD_SET(4, &xx);
166 top = 4;
167 if(_x.fd >= 0){
168 if(firstx){
169 firstx = 0;
170 XSelectInput(_x.display, _x.drawable, Mask);
172 FD_SET(_x.fd, &rd);
173 FD_SET(_x.fd, &xx);
174 XFlush(_x.display);
175 if(_x.fd > top)
176 top = _x.fd;
179 if(chatty)
180 fprint(2, "select %d...\n", top+1);
181 /* wait for something to happen */
182 again:
183 if(select(top+1, &rd, &wr, &xx, NULL) < 0){
184 if(errno == EINTR)
185 goto again;
186 if(chatty)
187 fprint(2, "select failure\n");
188 exits(0);
190 if(chatty)
191 fprint(2, "got select...\n");
194 /* read what we can */
195 n = 1;
196 while(fdin.wp < fdin.ep && (n = read(3, fdin.wp, fdin.ep-fdin.wp)) > 0)
197 fdin.wp += n;
198 if(n == 0){
199 if(chatty)
200 fprint(2, "eof\n");
201 exits(0);
203 if(n < 0 && errno != EAGAIN)
204 sysfatal("reading wsys msg: %r");
206 /* pick off messages one by one */
207 while((n = convM2W(fdin.rp, fdin.wp-fdin.rp, &m)) > 0){
208 /* fprint(2, "<- %W\n", &m); */
209 runmsg(&m);
210 fdin.rp += n;
213 /* slide data to beginning of buf */
214 fdslide(&fdin);
217 /* write what we can */
218 n = 1;
219 while(fdout.rp < fdout.wp && (n = write(4, fdout.rp, fdout.wp-fdout.rp)) > 0)
220 fdout.rp += n;
221 if(n == 0)
222 sysfatal("short write writing wsys");
223 if(n < 0 && errno != EAGAIN)
224 sysfatal("writing wsys msg: %r");
226 /* slide data to beginning of buf */
227 fdslide(&fdout);
230 /*
231 * Read an X message if we can.
232 * (XPending actually calls select to make sure
233 * the display's fd is readable and then reads
234 * in any waiting data before declaring whether
235 * there are events on the queue.)
236 */
237 while(XPending(_x.display)){
238 XNextEvent(_x.display, &event);
239 runxevent(&event);
245 int
246 fdnoblock(int fd)
248 return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL)|O_NONBLOCK);
251 void
252 fdslide(Fdbuf *fb)
254 int n;
256 n = fb->wp - fb->rp;
257 if(n > 0)
258 memmove(fb->buf, fb->rp, n);
259 fb->rp = fb->buf;
260 fb->wp = fb->rp+n;
263 void
264 replyerror(Wsysmsg *m)
266 char err[256];
268 rerrstr(err, sizeof err);
269 m->type = Rerror;
270 m->error = err;
271 replymsg(m);
274 /*
275 * Handle a single wsysmsg.
276 * Might queue for later (kbd, mouse read)
277 */
278 void
279 runmsg(Wsysmsg *m)
281 uchar buf[65536];
282 int n;
283 Memimage *i;
285 switch(m->type){
286 case Tinit:
287 memimageinit();
288 i = _xattach(m->label, m->winsize);
289 _initdisplaymemimage(i);
290 replymsg(m);
291 break;
293 case Trdmouse:
294 mousetags.t[mousetags.wi++] = m->tag;
295 if(mousetags.wi == nelem(mousetags.t))
296 mousetags.wi = 0;
297 if(mousetags.wi == mousetags.ri)
298 sysfatal("too many queued mouse reads");
299 /* fprint(2, "mouse unstall\n"); */
300 mouse.stall = 0;
301 matchmouse();
302 break;
304 case Trdkbd:
305 kbdtags.t[kbdtags.wi++] = m->tag;
306 if(kbdtags.wi == nelem(kbdtags.t))
307 kbdtags.wi = 0;
308 if(kbdtags.wi == kbdtags.ri)
309 sysfatal("too many queued keyboard reads");
310 kbd.stall = 0;
311 matchkbd();
312 break;
314 case Tmoveto:
315 _xmoveto(m->mouse.xy);
316 replymsg(m);
317 break;
319 case Tcursor:
320 if(m->arrowcursor)
321 _xsetcursor(nil);
322 else
323 _xsetcursor(&m->cursor);
324 replymsg(m);
325 break;
327 case Tbouncemouse:
328 _xbouncemouse(&m->mouse);
329 replymsg(m);
330 break;
332 case Tlabel:
333 _xsetlabel(m->label);
334 replymsg(m);
335 break;
337 case Trdsnarf:
338 m->snarf = _xgetsnarf();
339 replymsg(m);
340 free(m->snarf);
341 break;
343 case Twrsnarf:
344 _xputsnarf(m->snarf);
345 replymsg(m);
346 break;
348 case Trddraw:
349 n = m->count;
350 if(n > sizeof buf)
351 n = sizeof buf;
352 n = _drawmsgread(buf, n);
353 if(n < 0)
354 replyerror(m);
355 else{
356 m->count = n;
357 m->data = buf;
358 replymsg(m);
360 break;
362 case Twrdraw:
363 if(_drawmsgwrite(m->data, m->count) < 0)
364 replyerror(m);
365 else
366 replymsg(m);
367 break;
369 case Ttop:
370 _xtopwindow();
371 replymsg(m);
372 break;
374 case Tresize:
375 _xresizewindow(m->rect);
376 replymsg(m);
377 break;
381 /*
382 * Reply to m.
383 */
384 void
385 replymsg(Wsysmsg *m)
387 int n;
389 /* T -> R msg */
390 if(m->type%2 == 0)
391 m->type++;
393 /* fprint(2, "-> %W\n", m); */
394 /* copy to output buffer */
395 n = sizeW2M(m);
396 if(fdout.wp+n > fdout.ep)
397 sysfatal("out of space for reply message");
398 convW2M(m, fdout.wp, n);
399 fdout.wp += n;
402 /*
403 * Match queued kbd reads with queued kbd characters.
404 */
405 void
406 matchkbd(void)
408 Wsysmsg m;
410 if(kbd.stall)
411 return;
412 while(kbd.ri != kbd.wi && kbdtags.ri != kbdtags.wi){
413 m.type = Rrdkbd;
414 m.tag = kbdtags.t[kbdtags.ri++];
415 if(kbdtags.ri == nelem(kbdtags.t))
416 kbdtags.ri = 0;
417 m.rune = kbd.r[kbd.ri++];
418 if(kbd.ri == nelem(kbd.r))
419 kbd.ri = 0;
420 replymsg(&m);
424 /*
425 * Match queued mouse reads with queued mouse events.
426 */
427 void
428 matchmouse(void)
430 Wsysmsg m;
432 while(mouse.ri != mouse.wi && mousetags.ri != mousetags.wi){
433 m.type = Rrdmouse;
434 m.tag = mousetags.t[mousetags.ri++];
435 if(mousetags.ri == nelem(mousetags.t))
436 mousetags.ri = 0;
437 m.mouse = mouse.m[mouse.ri];
438 m.resized = mouse.resized;
439 /*
440 if(m.resized)
441 fprint(2, "sending resize\n");
442 */
443 mouse.resized = 0;
444 mouse.ri++;
445 if(mouse.ri == nelem(mouse.m))
446 mouse.ri = 0;
447 replymsg(&m);
451 /*
452 * Handle an incoming X event.
453 */
454 void
455 runxevent(XEvent *xev)
457 int c;
458 static Mouse m;
460 switch(xev->type){
461 case Expose:
462 _xexpose(xev);
463 break;
465 case DestroyNotify:
466 if(_xdestroy(xev))
467 exits(0);
468 break;
470 case ConfigureNotify:
471 if(_xconfigure(xev)){
472 mouse.resized = 1;
473 _xreplacescreenimage();
474 goto addmouse;
476 break;
478 case ButtonPress:
479 case ButtonRelease:
480 case MotionNotify:
481 if(mouse.stall)
482 return;
483 if(_xtoplan9mouse(xev, &m) < 0)
484 return;
485 addmouse:
486 mouse.m[mouse.wi] = m;
487 mouse.wi++;
488 if(mouse.wi == nelem(mouse.m))
489 mouse.wi = 0;
490 if(mouse.wi == mouse.ri){
491 mouse.stall = 1;
492 mouse.ri = 0;
493 mouse.wi = 1;
494 mouse.m[0] = m;
495 /* fprint(2, "mouse stall\n"); */
497 matchmouse();
498 break;
500 case KeyPress:
501 if(kbd.stall)
502 return;
503 if((c = _xtoplan9kbd(xev)) < 0)
504 return;
505 kbd.r[kbd.wi++] = c;
506 if(kbd.wi == nelem(kbd.r))
507 kbd.wi = 0;
508 if(kbd.ri == kbd.wi)
509 kbd.stall = 1;
510 matchkbd();
511 break;
513 case SelectionRequest:
514 _xselect(xev);
515 break;