Blob
1 /*2 * Window system protocol server.3 * Use select and a single proc and single stack4 * to avoid aggravating the X11 library, which is5 * subtle and quick to anger.6 */8 // #define SHOWEVENT10 #include <u.h>11 #include <sys/select.h>12 #include <errno.h>13 #ifdef SHOWEVENT14 #include <stdio.h>15 #endif16 #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 time31 #define MouseMask (\32 ButtonPressMask|\33 ButtonReleaseMask|\34 PointerMotionMask|\35 Button1MotionMask|\36 Button2MotionMask|\37 Button3MotionMask)39 #define Mask MouseMask|ExposureMask|StructureNotifyMask|KeyPressMask|EnterWindowMask|LeaveWindowMask41 typedef struct Kbdbuf Kbdbuf;42 typedef struct Mousebuf Mousebuf;43 typedef struct Fdbuf Fdbuf;44 typedef struct Tagbuf Tagbuf;46 struct Kbdbuf47 {48 Rune r[32];49 int ri;50 int wi;51 int stall;52 };54 struct Mousebuf55 {56 Mouse m[32];57 int ri;58 int wi;59 int stall;60 int resized;61 };63 struct Tagbuf64 {65 int t[32];66 int ri;67 int wi;68 };70 struct Fdbuf71 {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 void101 usage(void)102 {103 fprint(2, "usage: devdraw (don't run directly)\n");104 exits("usage");105 }107 void108 bell(void *v, char *msg)109 {110 if(strcmp(msg, "alarm") == 0)111 drawsleep = drawsleep ? 0 : 1000;112 noted(NCONT);113 }115 void116 main(int argc, char **argv)117 {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 that125 * 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 }ARGEND147 /*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't171 * 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);184 }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;190 }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);202 }203 if(chatty)204 fprint(2, "got select...\n");206 {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);215 }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;224 }226 /* slide data to beginning of buf */227 fdslide(&fdin);228 }229 {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);241 }242 {243 /*244 * Read an X message if we can.245 * (XPending actually calls select to make sure246 * the display's fd is readable and then reads247 * in any waiting data before declaring whether248 * there are events on the queue.)249 */250 while(XPending(_x.display)){251 XNextEvent(_x.display, &event);252 runxevent(&event);253 }254 }255 }256 }258 int259 fdnoblock(int fd)260 {261 return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL)|O_NONBLOCK);262 }264 void265 fdslide(Fdbuf *fb)266 {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;274 }276 void277 replyerror(Wsysmsg *m)278 {279 char err[256];281 rerrstr(err, sizeof err);282 m->type = Rerror;283 m->error = err;284 replymsg(m);285 }289 /*290 * Handle a single wsysmsg.291 * Might queue for later (kbd, mouse read)292 */293 void294 runmsg(Wsysmsg *m)295 {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 else338 _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);374 }375 break;377 case Twrdraw:378 if(_drawmsgwrite(m->data, m->count) < 0)379 replyerror(m);380 else381 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;393 }394 }396 /*397 * Reply to m.398 */399 void400 replymsg(Wsysmsg *m)401 {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;415 }417 /*418 * Match queued kbd reads with queued kbd characters.419 */420 void421 matchkbd(void)422 {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);436 }437 }439 /*440 * Match queued mouse reads with queued mouse events.441 */442 void443 matchmouse(void)444 {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);463 }464 }466 /*467 * Handle an incoming X event.468 */469 void470 runxevent(XEvent *xev)471 {472 int c;473 KeySym k;474 static Mouse m;476 #ifdef SHOWEVENT477 static int first = 1;478 if(first){479 dup(create("/tmp/devdraw.out", OWRITE, 0666), 1);480 setbuf(stdout, 0);481 first = 0;482 }483 #endif485 if(xev == 0)486 return;488 #ifdef SHOWEVENT489 print("\n");490 ShowEvent(xev);491 #endif493 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;508 }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"); */529 }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;541 }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;555 }556 }