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 #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 time25 #define MouseMask (\26 ButtonPressMask|\27 ButtonReleaseMask|\28 PointerMotionMask|\29 Button1MotionMask|\30 Button2MotionMask|\31 Button3MotionMask)33 #define Mask MouseMask|ExposureMask|StructureNotifyMask|KeyPressMask|EnterWindowMask|LeaveWindowMask35 typedef struct Kbdbuf Kbdbuf;36 typedef struct Mousebuf Mousebuf;37 typedef struct Fdbuf Fdbuf;38 typedef struct Tagbuf Tagbuf;40 struct Kbdbuf41 {42 Rune r[32];43 int ri;44 int wi;45 int stall;46 };48 struct Mousebuf49 {50 Mouse m[32];51 int ri;52 int wi;53 int stall;54 int resized;55 };57 struct Tagbuf58 {59 int t[32];60 int ri;61 int wi;62 };64 struct Fdbuf65 {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 void91 usage(void)92 {93 fprint(2, "usage: devdraw (don't run directly)\n");94 exits("usage");95 }97 void98 bell(void *v, char *msg)99 {100 if(strcmp(msg, "alarm") == 0)101 drawsleep = drawsleep ? 0 : 1000;102 noted(NCONT);103 }105 void106 main(int argc, char **argv)107 {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 that115 * 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 }ARGEND134 /*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't158 * 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);171 }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;177 }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);189 }190 if(chatty)191 fprint(2, "got select...\n");193 {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);202 }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;211 }213 /* slide data to beginning of buf */214 fdslide(&fdin);215 }216 {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);228 }229 {230 /*231 * Read an X message if we can.232 * (XPending actually calls select to make sure233 * the display's fd is readable and then reads234 * in any waiting data before declaring whether235 * there are events on the queue.)236 */237 while(XPending(_x.display)){238 XNextEvent(_x.display, &event);239 runxevent(&event);240 }241 }242 }243 }245 int246 fdnoblock(int fd)247 {248 return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL)|O_NONBLOCK);249 }251 void252 fdslide(Fdbuf *fb)253 {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;261 }263 void264 replyerror(Wsysmsg *m)265 {266 char err[256];268 rerrstr(err, sizeof err);269 m->type = Rerror;270 m->error = err;271 replymsg(m);272 }274 /*275 * Handle a single wsysmsg.276 * Might queue for later (kbd, mouse read)277 */278 void279 runmsg(Wsysmsg *m)280 {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 else323 _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);359 }360 break;362 case Twrdraw:363 if(_drawmsgwrite(m->data, m->count) < 0)364 replyerror(m);365 else366 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;378 }379 }381 /*382 * Reply to m.383 */384 void385 replymsg(Wsysmsg *m)386 {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;400 }402 /*403 * Match queued kbd reads with queued kbd characters.404 */405 void406 matchkbd(void)407 {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);421 }422 }424 /*425 * Match queued mouse reads with queued mouse events.426 */427 void428 matchmouse(void)429 {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);448 }449 }451 /*452 * Handle an incoming X event.453 */454 void455 runxevent(XEvent *xev)456 {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;475 }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"); */496 }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;516 }517 }