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|KeyReleaseMask|EnterWindowMask|LeaveWindowMask|FocusChangeMask41 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 static int kbuttons;467 static int altdown;468 static int kstate;470 static void471 sendmouse(Mouse m)472 {473 m.buttons |= kbuttons;474 mouse.m[mouse.wi] = m;475 mouse.wi++;476 if(mouse.wi == nelem(mouse.m))477 mouse.wi = 0;478 if(mouse.wi == mouse.ri){479 mouse.stall = 1;480 mouse.ri = 0;481 mouse.wi = 1;482 mouse.m[0] = m;483 /* fprint(2, "mouse stall\n"); */484 }485 matchmouse();486 }488 /*489 * Handle an incoming X event.490 */491 void492 runxevent(XEvent *xev)493 {494 int c;495 KeySym k;496 static Mouse m;497 XButtonEvent *be;498 XKeyEvent *ke;500 #ifdef SHOWEVENT501 static int first = 1;502 if(first){503 dup(create("/tmp/devdraw.out", OWRITE, 0666), 1);504 setbuf(stdout, 0);505 first = 0;506 }507 #endif509 if(xev == 0)510 return;512 #ifdef SHOWEVENT513 print("\n");514 ShowEvent(xev);515 #endif517 switch(xev->type){518 case Expose:519 _xexpose(xev);520 break;522 case DestroyNotify:523 if(_xdestroy(xev))524 exits(0);525 break;527 case ConfigureNotify:528 if(_xconfigure(xev)){529 mouse.resized = 1;530 _xreplacescreenimage();531 sendmouse(m);532 }533 break;535 case ButtonPress:536 be = (XButtonEvent*)xev;537 if(be->button == 1) {538 if(kstate & ControlMask)539 be->button = 2;540 else if(kstate & Mod1Mask)541 be->button = 3;542 }543 // fall through544 case ButtonRelease:545 altdown = 0;546 // fall through547 case MotionNotify:548 if(mouse.stall)549 return;550 if(_xtoplan9mouse(xev, &m) < 0)551 return;552 sendmouse(m);553 break;555 case KeyRelease:556 case KeyPress:557 ke = (XKeyEvent*)xev;558 XLookupString(ke, NULL, 0, &k, NULL);559 c = ke->state;560 switch(k) {561 case XK_Alt_L:562 case XK_Meta_L: /* Shift Alt on PCs */563 case XK_Alt_R:564 case XK_Meta_R: /* Shift Alt on PCs */565 case XK_Multi_key:566 if(xev->type == KeyPress)567 altdown = 1;568 else if(altdown) {569 altdown = 0;570 sendalt();571 }572 break;573 }575 switch(k) {576 case XK_Control_L:577 if(xev->type == KeyPress)578 c |= ControlMask;579 else580 c &= ~ControlMask;581 goto kbutton;582 case XK_Alt_L:583 case XK_Shift_L:584 if(xev->type == KeyPress)585 c |= Mod1Mask;586 else587 c &= ~Mod1Mask;588 kbutton:589 kstate = c;590 if(m.buttons || kbuttons) {591 altdown = 0; // used alt592 kbuttons = 0;593 if(c & ControlMask)594 kbuttons |= 2;595 if(c & Mod1Mask)596 kbuttons |= 4;597 sendmouse(m);598 break;599 }600 }602 if(xev->type != KeyPress)603 break;604 if(k == XK_F11){605 fullscreen = !fullscreen;606 _xmovewindow(fullscreen ? screenrect : windowrect);607 return;608 }609 if(kbd.stall)610 return;611 if((c = _xtoplan9kbd(xev)) < 0)612 return;613 kbd.r[kbd.wi++] = c;614 if(kbd.wi == nelem(kbd.r))615 kbd.wi = 0;616 if(kbd.ri == kbd.wi)617 kbd.stall = 1;618 matchkbd();619 break;621 case FocusOut:622 /*623 * Some key combinations (e.g. Alt-Tab) can cause us624 * to see the key down event without the key up event,625 * so clear out the keyboard state when we lose the focus.626 */627 kstate = 0;628 altdown = 0;629 abortcompose();630 break;632 case SelectionRequest:633 _xselect(xev);634 break;635 }636 }