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 resized[32];52 int ri;53 int wi;54 int stall;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;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 void93 usage(void)94 {95 fprint(2, "usage: devdraw (don't run directly)\n");96 exits("usage");97 }99 void100 bell(void *v, char *msg)101 {102 if(strcmp(msg, "alarm") == 0)103 drawsleep = drawsleep ? 0 : 1000;104 noted(NCONT);105 }107 void108 main(int argc, char **argv)109 {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 }ARGEND123 /*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't147 * 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);160 }161 FD_SET(_x.fd, &rd);162 FD_SET(_x.fd, &xx);163 XFlush(_x.display);164 top = _x.fd;165 }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);177 }178 if(chatty)179 fprint(2, "got select...\n");181 {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);190 }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;198 }200 /* slide data to beginning of buf */201 fdslide(&fdin);202 }203 {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);215 }216 {217 /*218 * Read an X message if we can.219 * (XPending actually calls select to make sure220 * the display's fd is readable and then reads221 * in any waiting data before declaring whether222 * there are events on the queue.)223 */224 while(XPending(_x.display)){225 XNextEvent(_x.display, &event);226 runxevent(&event);227 }228 }229 }230 }232 int233 fdnoblock(int fd)234 {235 return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL)|O_NONBLOCK);236 }238 void239 fdslide(Fdbuf *fb)240 {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;248 }250 void251 replyerror(Wsysmsg *m)252 {253 char err[256];255 rerrstr(err, sizeof err);256 m->type = Rerror;257 m->error = err;258 replymsg(m);259 }261 /*262 * Handle a single wsysmsg.263 * Might queue for later (kbd, mouse read)264 */265 void266 runmsg(Wsysmsg *m)267 {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 else309 _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);345 }346 break;348 case Twrdraw:349 if(_drawmsgwrite(m->data, m->count) < 0)350 replyerror(m);351 else352 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;364 }365 }367 /*368 * Reply to m.369 */370 void371 replymsg(Wsysmsg *m)372 {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;385 }387 /*388 * Match queued kbd reads with queued kbd characters.389 */390 void391 matchkbd(void)392 {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);406 }407 }409 /*410 * Match queued mouse reads with queued mouse events.411 */412 void413 matchmouse(void)414 {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);430 }431 }433 /*434 * Handle an incoming X event.435 */436 void437 runxevent(XEvent *xev)438 {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;457 }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;494 }495 }