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 resized[32];
52 int ri;
53 int wi;
54 int stall;
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;
78 Tagbuf resizetags;
80 void fdslide(Fdbuf*);
81 void runmsg(Wsysmsg*);
82 void replymsg(Wsysmsg*);
83 void runxevent(XEvent*);
84 void matchkbd(void);
85 void matchmouse(void);
86 void matchresized(void);
87 int fdnoblock(int);
89 int chatty;
90 int drawsleep;
92 void
93 usage(void)
94 {
95 fprint(2, "usage: devdraw (don't run directly)\n");
96 exits("usage");
97 }
99 void
100 bell(void *v, char *msg)
102 if(strcmp(msg, "alarm") == 0)
103 drawsleep = drawsleep ? 0 : 1000;
104 noted(NCONT);
107 void
108 main(int argc, char **argv)
110 int n, top, firstx;
111 fd_set rd, wr, xx;
112 Wsysmsg m;
113 XEvent event;
115 ARGBEGIN{
116 case 'D':
117 chatty++;
118 break;
119 default:
120 usage();
121 }ARGEND
123 /*
124 * Ignore arguments. They're only for good ps -a listings.
125 */
127 notify(bell);
129 fdin.rp = fdin.wp = fdin.buf;
130 fdin.ep = fdin.buf+sizeof fdin.buf;
132 fdout.rp = fdout.wp = fdout.buf;
133 fdout.ep = fdout.buf+sizeof fdout.buf;
135 fdnoblock(0);
136 fdnoblock(1);
138 firstx = 1;
139 _x.fd = -1;
140 for(;;){
141 /* set up file descriptors */
142 FD_ZERO(&rd);
143 FD_ZERO(&wr);
144 FD_ZERO(&xx);
145 /*
146 * Don't read unless there's room *and* we haven't
147 * already filled the output buffer too much.
148 */
149 if(fdout.wp < fdout.buf+MAXWMSG && fdin.wp < fdin.ep)
150 FD_SET(0, &rd);
151 if(fdout.wp > fdout.rp)
152 FD_SET(1, &wr);
153 FD_SET(0, &xx);
154 FD_SET(1, &xx);
155 top = 1;
156 if(_x.fd >= 0){
157 if(firstx){
158 firstx = 0;
159 XSelectInput(_x.display, _x.drawable, Mask);
161 FD_SET(_x.fd, &rd);
162 FD_SET(_x.fd, &xx);
163 XFlush(_x.display);
164 top = _x.fd;
167 if(chatty)
168 fprint(2, "select %d...\n", top+1);
169 /* wait for something to happen */
170 again:
171 if(select(top+1, &rd, &wr, &xx, NULL) < 0){
172 if(errno == EINTR)
173 goto again;
174 if(chatty)
175 fprint(2, "select failure\n");
176 exits(0);
178 if(chatty)
179 fprint(2, "got select...\n");
182 /* read what we can */
183 n = 1;
184 while(fdin.wp < fdin.ep && (n = read(0, fdin.wp, fdin.ep-fdin.wp)) > 0)
185 fdin.wp += n;
186 if(n == 0){
187 if(chatty)
188 fprint(2, "eof\n");
189 exits(0);
191 if(n < 0 && errno != EAGAIN)
192 sysfatal("reading wsys msg: %r");
194 /* pick off messages one by one */
195 while((n = convM2W(fdin.rp, fdin.wp-fdin.rp, &m)) > 0){
196 runmsg(&m);
197 fdin.rp += n;
200 /* slide data to beginning of buf */
201 fdslide(&fdin);
204 /* write what we can */
205 n = 1;
206 while(fdout.rp < fdout.wp && (n = write(1, fdout.rp, fdout.wp-fdout.rp)) > 0)
207 fdout.rp += n;
208 if(n == 0)
209 sysfatal("short write writing wsys");
210 if(n < 0 && errno != EAGAIN)
211 sysfatal("writing wsys msg: %r");
213 /* slide data to beginning of buf */
214 fdslide(&fdout);
217 /*
218 * Read an X message if we can.
219 * (XPending actually calls select to make sure
220 * the display's fd is readable and then reads
221 * in any waiting data before declaring whether
222 * there are events on the queue.)
223 */
224 while(XPending(_x.display)){
225 XNextEvent(_x.display, &event);
226 runxevent(&event);
232 int
233 fdnoblock(int fd)
235 return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL)|O_NONBLOCK);
238 void
239 fdslide(Fdbuf *fb)
241 int n;
243 n = fb->wp - fb->rp;
244 if(n > 0)
245 memmove(fb->buf, fb->rp, n);
246 fb->rp = fb->buf;
247 fb->wp = fb->rp+n;
250 void
251 replyerror(Wsysmsg *m)
253 char err[256];
255 rerrstr(err, sizeof err);
256 m->type = Rerror;
257 m->error = err;
258 replymsg(m);
261 /*
262 * Handle a single wsysmsg.
263 * Might queue for later (kbd, mouse read)
264 */
265 void
266 runmsg(Wsysmsg *m)
268 uchar buf[65536];
269 int n;
270 Memimage *i;
272 switch(m->type){
273 case Tinit:
274 memimageinit();
275 i = _xattach(m->label, m->winsize);
276 _initdisplaymemimage(i);
277 replymsg(m);
278 break;
280 case Trdmouse:
281 mousetags.t[mousetags.wi++] = m->tag;
282 if(mousetags.wi == nelem(mousetags.t))
283 mousetags.wi = 0;
284 if(mousetags.wi == mousetags.ri)
285 sysfatal("too many queued mouse reads");
286 mouse.stall = 0;
287 matchmouse();
288 break;
290 case Trdkbd:
291 kbdtags.t[kbdtags.wi++] = m->tag;
292 if(kbdtags.wi == nelem(kbdtags.t))
293 kbdtags.wi = 0;
294 if(kbdtags.wi == kbdtags.ri)
295 sysfatal("too many queued keyboard reads");
296 kbd.stall = 0;
297 matchkbd();
298 break;
300 case Tmoveto:
301 _xmoveto(m->mouse.xy);
302 replymsg(m);
303 break;
305 case Tcursor:
306 if(m->arrowcursor)
307 _xsetcursor(nil);
308 else
309 _xsetcursor(&m->cursor);
310 replymsg(m);
311 break;
313 case Tbouncemouse:
314 _xbouncemouse(&m->mouse);
315 replymsg(m);
316 break;
318 case Tlabel:
319 _xsetlabel(m->label);
320 replymsg(m);
321 break;
323 case Trdsnarf:
324 m->snarf = _xgetsnarf();
325 replymsg(m);
326 free(m->snarf);
327 break;
329 case Twrsnarf:
330 _xputsnarf(m->snarf);
331 replymsg(m);
332 break;
334 case Trddraw:
335 n = m->count;
336 if(n > sizeof buf)
337 n = sizeof buf;
338 n = _drawmsgread(buf, n);
339 if(n < 0)
340 replyerror(m);
341 else{
342 m->count = n;
343 m->data = buf;
344 replymsg(m);
346 break;
348 case Twrdraw:
349 if(_drawmsgwrite(m->data, m->count) < 0)
350 replyerror(m);
351 else
352 replymsg(m);
353 break;
355 case Ttop:
356 _xtopwindow();
357 replymsg(m);
358 break;
360 case Tresize:
361 _xresizewindow(m->rect);
362 replymsg(m);
363 break;
367 /*
368 * Reply to m.
369 */
370 void
371 replymsg(Wsysmsg *m)
373 int n;
375 /* T -> R msg */
376 if(m->type%2 == 0)
377 m->type++;
379 /* copy to output buffer */
380 n = sizeW2M(m);
381 if(fdout.wp+n > fdout.ep)
382 sysfatal("out of space for reply message");
383 convW2M(m, fdout.wp, n);
384 fdout.wp += n;
387 /*
388 * Match queued kbd reads with queued kbd characters.
389 */
390 void
391 matchkbd(void)
393 Wsysmsg m;
395 if(kbd.stall)
396 return;
397 while(kbd.ri != kbd.wi && kbdtags.ri != kbdtags.wi){
398 m.type = Rrdkbd;
399 m.tag = kbdtags.t[kbdtags.ri++];
400 if(kbdtags.ri == nelem(kbdtags.t))
401 kbdtags.ri = 0;
402 m.rune = kbd.r[kbd.ri++];
403 if(kbd.ri == nelem(kbd.r))
404 kbd.ri = 0;
405 replymsg(&m);
409 /*
410 * Match queued mouse reads with queued mouse events.
411 */
412 void
413 matchmouse(void)
415 Wsysmsg m;
417 if(mouse.stall)
418 return;
419 while(mouse.ri != mouse.wi && mousetags.ri != mousetags.wi){
420 m.type = Rrdmouse;
421 m.tag = mousetags.t[mousetags.ri++];
422 if(mousetags.ri == nelem(mousetags.t))
423 mousetags.ri = 0;
424 m.mouse = mouse.m[mouse.ri];
425 m.resized = mouse.resized[mouse.ri];
426 mouse.ri++;
427 if(mouse.ri == nelem(mouse.m))
428 mouse.ri = 0;
429 replymsg(&m);
433 /*
434 * Handle an incoming X event.
435 */
436 void
437 runxevent(XEvent *xev)
439 int c;
440 static Mouse m;
442 switch(xev->type){
443 case Expose:
444 _xexpose(xev);
445 break;
447 case DestroyNotify:
448 if(_xdestroy(xev))
449 exits(0);
450 break;
452 case ConfigureNotify:
453 if(_xconfigure(xev)){
454 mouse.resized[mouse.wi] = 1;
455 _xreplacescreenimage();
456 goto addmouse;
458 break;
460 case ButtonPress:
461 case ButtonRelease:
462 case MotionNotify:
463 if(mouse.stall)
464 return;
465 if(_xtoplan9mouse(xev, &m) < 0)
466 return;
467 mouse.resized[mouse.wi] = 0;
468 addmouse:
469 mouse.m[mouse.wi] = m;
470 mouse.wi++;
471 if(mouse.wi == nelem(mouse.m))
472 mouse.wi = 0;
473 if(mouse.wi == mouse.ri)
474 mouse.stall = 1;
475 matchmouse();
476 break;
478 case KeyPress:
479 if(kbd.stall)
480 return;
481 if((c = _xtoplan9kbd(xev)) < 0)
482 return;
483 kbd.r[kbd.wi++] = c;
484 if(kbd.wi == nelem(kbd.r))
485 kbd.wi = 0;
486 if(kbd.ri == kbd.wi)
487 kbd.stall = 1;
488 matchkbd();
489 break;
491 case SelectionRequest:
492 _xselect(xev);
493 break;