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;
91 void
92 usage(void)
93 {
94 fprint(2, "usage: devdraw (don't run directly)\n");
95 exits("usage");
96 }
98 void
99 main(int argc, char **argv)
101 int n, top, firstx;
102 fd_set rd, wr, xx;
103 Wsysmsg m;
104 XEvent event;
106 ARGBEGIN{
107 case 'D':
108 chatty++;
109 break;
110 default:
111 usage();
112 }ARGEND
114 if(argc != 0)
115 usage();
117 fdin.rp = fdin.wp = fdin.buf;
118 fdin.ep = fdin.buf+sizeof fdin.buf;
120 fdout.rp = fdout.wp = fdout.buf;
121 fdout.ep = fdout.buf+sizeof fdout.buf;
123 fdnoblock(0);
124 fdnoblock(1);
126 firstx = 1;
127 _x.fd = -1;
128 for(;;){
129 /* set up file descriptors */
130 FD_ZERO(&rd);
131 FD_ZERO(&wr);
132 FD_ZERO(&xx);
133 /*
134 * Don't read unless there's room *and* we haven't
135 * already filled the output buffer too much.
136 */
137 if(fdout.wp < fdout.buf+MAXWMSG && fdin.wp < fdin.ep)
138 FD_SET(0, &rd);
139 if(fdout.wp > fdout.rp)
140 FD_SET(1, &wr);
141 FD_SET(0, &xx);
142 FD_SET(1, &xx);
143 top = 1;
144 if(_x.fd >= 0){
145 if(firstx){
146 firstx = 0;
147 XSelectInput(_x.display, _x.drawable, Mask);
149 FD_SET(_x.fd, &rd);
150 FD_SET(_x.fd, &xx);
151 XFlush(_x.display);
152 top = _x.fd;
155 if(chatty)
156 fprint(2, "select %d...\n", top+1);
157 /* wait for something to happen */
158 if(select(top+1, &rd, &wr, &xx, NULL) < 0){
159 if(chatty)
160 fprint(2, "select failure\n");
161 exits(0);
163 if(chatty)
164 fprint(2, "got select...\n");
167 /* read what we can */
168 n = 1;
169 while(fdin.wp < fdin.ep && (n = read(0, fdin.wp, fdin.ep-fdin.wp)) > 0)
170 fdin.wp += n;
171 if(n == 0){
172 if(chatty)
173 fprint(2, "eof\n");
174 exits(0);
176 if(n < 0 && errno != EAGAIN)
177 sysfatal("reading wsys msg: %r");
179 /* pick off messages one by one */
180 while((n = convM2W(fdin.rp, fdin.wp-fdin.rp, &m)) > 0){
181 runmsg(&m);
182 fdin.rp += n;
185 /* slide data to beginning of buf */
186 fdslide(&fdin);
189 /* write what we can */
190 n = 1;
191 while(fdout.rp < fdout.wp && (n = write(1, fdout.rp, fdout.wp-fdout.rp)) > 0)
192 fdout.rp += n;
193 if(n == 0)
194 sysfatal("short write writing wsys");
195 if(n < 0 && errno != EAGAIN)
196 sysfatal("writing wsys msg: %r");
198 /* slide data to beginning of buf */
199 fdslide(&fdout);
202 /*
203 * Read an X message if we can.
204 * (XPending actually calls select to make sure
205 * the display's fd is readable and then reads
206 * in any waiting data before declaring whether
207 * there are events on the queue.)
208 */
209 while(XPending(_x.display)){
210 XNextEvent(_x.display, &event);
211 runxevent(&event);
217 int
218 fdnoblock(int fd)
220 return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL)|O_NONBLOCK);
223 void
224 fdslide(Fdbuf *fb)
226 int n;
228 n = fb->wp - fb->rp;
229 if(n > 0)
230 memmove(fb->buf, fb->rp, n);
231 fb->rp = fb->buf;
232 fb->wp = fb->rp+n;
235 void
236 replyerror(Wsysmsg *m)
238 char err[256];
240 rerrstr(err, sizeof err);
241 m->type = Rerror;
242 m->error = err;
243 replymsg(m);
246 /*
247 * Handle a single wsysmsg.
248 * Might queue for later (kbd, mouse read)
249 */
250 void
251 runmsg(Wsysmsg *m)
253 uchar buf[65536];
254 int n;
255 Memimage *i;
257 switch(m->type){
258 case Tinit:
259 memimageinit();
260 i = _xattach(m->label, m->winsize);
261 _initdisplaymemimage(i);
262 replymsg(m);
263 break;
265 case Trdmouse:
266 mousetags.t[mousetags.wi++] = m->tag;
267 if(mousetags.wi == nelem(mousetags.t))
268 mousetags.wi = 0;
269 if(mousetags.wi == mousetags.ri)
270 sysfatal("too many queued mouse reads");
271 mouse.stall = 0;
272 matchmouse();
273 break;
275 case Trdkbd:
276 kbdtags.t[kbdtags.wi++] = m->tag;
277 if(kbdtags.wi == nelem(kbdtags.t))
278 kbdtags.wi = 0;
279 if(kbdtags.wi == kbdtags.ri)
280 sysfatal("too many queued keyboard reads");
281 kbd.stall = 0;
282 matchkbd();
283 break;
285 case Tmoveto:
286 _xmoveto(m->mouse.xy);
287 replymsg(m);
288 break;
290 case Tcursor:
291 if(m->arrowcursor)
292 _xsetcursor(nil);
293 else
294 _xsetcursor(&m->cursor);
295 replymsg(m);
296 break;
298 case Tbouncemouse:
299 _xbouncemouse(&m->mouse);
300 replymsg(m);
301 break;
303 case Tlabel:
304 _xsetlabel(m->label);
305 replymsg(m);
306 break;
308 case Trdsnarf:
309 m->snarf = _xgetsnarf();
310 replymsg(m);
311 free(m->snarf);
312 break;
314 case Twrsnarf:
315 _xputsnarf(m->snarf);
316 replymsg(m);
317 break;
319 case Trddraw:
320 n = m->count;
321 if(n > sizeof buf)
322 n = sizeof buf;
323 n = _drawmsgread(buf, n);
324 if(n < 0)
325 replyerror(m);
326 else{
327 m->count = n;
328 m->data = buf;
329 replymsg(m);
331 break;
333 case Twrdraw:
334 if(_drawmsgwrite(m->data, m->count) < 0)
335 replyerror(m);
336 else
337 replymsg(m);
338 break;
340 case Ttop:
341 _xtopwindow();
342 replymsg(m);
343 break;
345 case Tresize:
346 _xresizewindow(m->rect);
347 replymsg(m);
348 break;
352 /*
353 * Reply to m.
354 */
355 void
356 replymsg(Wsysmsg *m)
358 int n;
360 /* T -> R msg */
361 if(m->type%2 == 0)
362 m->type++;
364 /* copy to output buffer */
365 n = sizeW2M(m);
366 if(fdout.wp+n > fdout.ep)
367 sysfatal("out of space for reply message");
368 convW2M(m, fdout.wp, n);
369 fdout.wp += n;
372 /*
373 * Match queued kbd reads with queued kbd characters.
374 */
375 void
376 matchkbd(void)
378 Wsysmsg m;
380 if(kbd.stall)
381 return;
382 while(kbd.ri != kbd.wi && kbdtags.ri != kbdtags.wi){
383 m.type = Rrdkbd;
384 m.tag = kbdtags.t[kbdtags.ri++];
385 if(kbdtags.ri == nelem(kbdtags.t))
386 kbdtags.ri = 0;
387 m.rune = kbd.r[kbd.ri++];
388 if(kbd.ri == nelem(kbd.r))
389 kbd.ri = 0;
390 replymsg(&m);
394 /*
395 * Match queued mouse reads with queued mouse events.
396 */
397 void
398 matchmouse(void)
400 Wsysmsg m;
402 if(mouse.stall)
403 return;
404 while(mouse.ri != mouse.wi && mousetags.ri != mousetags.wi){
405 m.type = Rrdmouse;
406 m.tag = mousetags.t[mousetags.ri++];
407 if(mousetags.ri == nelem(mousetags.t))
408 mousetags.ri = 0;
409 m.mouse = mouse.m[mouse.ri];
410 m.resized = mouse.resized[mouse.ri];
411 mouse.ri++;
412 if(mouse.ri == nelem(mouse.m))
413 mouse.ri = 0;
414 replymsg(&m);
418 /*
419 * Handle an incoming X event.
420 */
421 void
422 runxevent(XEvent *xev)
424 int c;
425 static Mouse m;
427 switch(xev->type){
428 case Expose:
429 _xexpose(xev);
430 break;
432 case DestroyNotify:
433 if(_xdestroy(xev))
434 exits(0);
435 break;
437 case ConfigureNotify:
438 if(_xconfigure(xev)){
439 mouse.resized[mouse.wi] = 1;
440 _xreplacescreenimage();
441 goto addmouse;
443 break;
445 case ButtonPress:
446 case ButtonRelease:
447 case MotionNotify:
448 if(mouse.stall)
449 return;
450 if(_xtoplan9mouse(xev, &m) < 0)
451 return;
452 mouse.resized[mouse.wi] = 0;
453 addmouse:
454 mouse.m[mouse.wi] = m;
455 mouse.wi++;
456 if(mouse.wi == nelem(mouse.m))
457 mouse.wi = 0;
458 if(mouse.wi == mouse.ri)
459 mouse.stall = 1;
460 matchmouse();
461 break;
463 case KeyPress:
464 if(kbd.stall)
465 return;
466 if((c = _xtoplan9kbd(xev)) < 0)
467 return;
468 kbd.r[kbd.wi++] = c;
469 if(kbd.wi == nelem(kbd.r))
470 kbd.wi = 0;
471 if(kbd.ri == kbd.wi)
472 kbd.stall = 1;
473 matchkbd();
474 break;
476 case SelectionRequest:
477 _xselect(xev);
478 break;