/* * Mouse device. Also provides /dev/snarf, because it was convenient. * In a perfect world, this would be autogenerated from a * Plan 9 devmouse. */ #include "u.h" #include "lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "error.h" #define Image IMAGE #include #include #include #include "screen.h" typedef struct Cursor Cursor; Cursor arrow = { { -1, -1 }, { 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C, 0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04, 0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04, 0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40, }, { 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0, 0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8, 0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8, 0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00, }, }; enum { ScrollUp = 0x08, ScrollDown = 0x10, ScrollLeft = 0x20, ScrollRight = 0x40, }; enum { CMbuttonmap, CMscrollswap, CMswap, }; static Cmdtab mousectlmsg[] = { CMbuttonmap, "buttonmap", 0, CMscrollswap, "scrollswap", 0, CMswap, "swap", 1, }; Mouseinfo mouse; Cursorinfo cursor; int mouseshifted; Cursor curs; void Cursortocursor(Cursor*); int mousechanged(void*); enum{ Qdir, Qcursor, Qmouse, Qmousectl, Qsnarf, }; static Dirtab mousedir[]={ ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, "cursor", {Qcursor}, 0, 0666, "mouse", {Qmouse}, 0, 0666, "mousectl", {Qmousectl}, 0, 0220, "snarf", {Qsnarf}, 0, 0666, }; static uchar buttonmap[8] = { 0, 1, 2, 3, 4, 5, 6, 7, }; static int mouseswap; static int scrollswap; static ulong mousetime; Rectangle mouserect; /* maintained by x11 for us */ static void mousereset(void) { if(!conf.monitor) return; } static int mousedevgen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp) { int rc; rc = devgen(c, name, tab, ntab, i, dp); if(rc != -1) dp->atime = mousetime; return rc; } static void mouseinit(void) { if(!conf.monitor) return; mousetime = seconds(); } static Chan* mouseattach(char *spec) { if(!conf.monitor) error(Egreg); curs = arrow; Cursortocursor(&arrow); return devattach('m', spec); } static Walkqid* mousewalk(Chan *c, Chan *nc, char **name, int nname) { Walkqid *wq; /* * We use devgen() and not mousedevgen() here * see "Ugly problem" in dev.c/devwalk() */ wq = devwalk(c, nc, name, nname, mousedir, nelem(mousedir), devgen); if(wq != nil && wq->clone != c && wq->clone != nil && (wq->clone->qid.type&QTDIR)==0) incref(&mouse.ref); return wq; } static int mousestat(Chan *c, uchar *db, int n) { return devstat(c, db, n, mousedir, nelem(mousedir), mousedevgen); } static Chan* mouseopen(Chan *c, int omode) { switch((ulong)c->qid.path){ case Qdir: if(omode != OREAD) error(Eperm); break; case Qmouse: lock(&mouse.ref.lk); if(mouse.open){ unlock(&mouse.ref.lk); error(Einuse); } mouse.open = 1; mouse.ref.ref++; mouse.lastresize = mouse.resize; unlock(&mouse.ref.lk); break; case Qsnarf: if(omode == ORDWR) error(Eperm); /* one at a time please */ c->aux = nil; incref(&mouse.ref); break; default: incref(&mouse.ref); } c->mode = openmode(omode); c->flag |= COPEN; c->offset = 0; return c; } static void mousecreate(Chan *c, char *name, int perm, ulong mode) { if(!conf.monitor) error(Egreg); error(Eperm); } static void mouseclose(Chan *c) { if((c->qid.type&QTDIR)==0 && (c->flag&COPEN)){ if(c->qid.path == Qsnarf){ if(c->mode == OWRITE){ if(c->aux) putsnarf(c->aux); else putsnarf(""); } free(c->aux); } lock(&mouse.ref.lk); if(c->qid.path == Qmouse) mouse.open = 0; if(--mouse.ref.ref == 0){ curs = arrow; Cursortocursor(&arrow); } unlock(&mouse.ref.lk); if(c->qid.path == Qmouse) termredraw(); } } static long mouseread(Chan *c, void *va, long n, vlong off) { char buf[1+4*12+1], *s; uchar *p; ulong offset = off; Mousestate m; int b; p = va; switch((ulong)c->qid.path){ case Qdir: return devdirread(c, va, n, mousedir, nelem(mousedir), mousedevgen); case Qcursor: if(offset != 0) return 0; if(n < 2*4+2*2*16) error(Eshort); n = 2*4+2*2*16; lock(&cursor.lk); BPLONG(p+0, curs.offset.x); BPLONG(p+4, curs.offset.y); memmove(p+8, curs.clr, 2*16); memmove(p+40, curs.set, 2*16); unlock(&cursor.lk); return n; case Qmouse: while(mousechanged(0) == 0) sleep(&mouse.r, mousechanged, 0); mouse.qfull = 0; mousetime = seconds(); /* * No lock of the indices is necessary here, because ri is only * updated by us, and there is only one mouse reader * at a time. I suppose that more than one process * could try to read the fd at one time, but such behavior * is degenerate and already violates the calling * conventions for sleep above. */ if(mouse.ri != mouse.wi) { m = mouse.queue[mouse.ri]; if(++mouse.ri == nelem(mouse.queue)) mouse.ri = 0; } else { while(!canlock(&cursor.lk)) tsleep(&up->sleep, return0, 0, TK2MS(1)); m = mouse.mstate; unlock(&cursor.lk); } b = buttonmap[m.buttons&7]; /* put buttons 4 and 5 back in */ b |= m.buttons & (3<<3); if (scrollswap){ if (b == 8) b = 16; else if (b == 16) b = 8; } sprint(buf, "m%11d %11d %11d %11lud ", m.xy.x, m.xy.y, b, m.msec); mouse.lastcounter = m.counter; if(n > 1+4*12) n = 1+4*12; if(mouse.lastresize != mouse.resize){ mouse.lastresize = mouse.resize; buf[0] = 'r'; } memmove(va, buf, n); return n; case Qsnarf: if(offset == 0){ s = getsnarf(); if(c->aux) free(c->aux); c->aux = s; } if(c->aux == nil) return 0; return readstr(offset, va, n, c->aux); } return 0; } static void setbuttonmap(char* map) { int i, x, one, two, three; one = two = three = 0; for(i = 0; i < 3; i++){ if(map[i] == 0) error(Ebadarg); if(map[i] == '1'){ if(one) error(Ebadarg); one = 1<qid.path){ case Qdir: error(Eisdir); case Qcursor: if(n < 2*4+2*2*16){ curs = arrow; Cursortocursor(&arrow); }else{ n = 2*4+2*2*16; curs.offset.x = BGLONG(p+0); curs.offset.y = BGLONG(p+4); memmove(curs.clr, p+8, 2*16); memmove(curs.set, p+40, 2*16); Cursortocursor(&curs); } qlock(&mouse.qlk); mouse.redraw = 1; qunlock(&mouse.qlk); return n; case Qmousectl: cb = parsecmd(va, n); if(waserror()){ free(cb); nexterror(); } ct = lookupcmd(cb, mousectlmsg, nelem(mousectlmsg)); switch(ct->index){ case CMswap: if(mouseswap) setbuttonmap("123"); else setbuttonmap("321"); mouseswap ^= 1; break; case CMscrollswap: scrollswap ^= 1; break; case CMbuttonmap: if(cb->nf == 1) setbuttonmap("123"); else setbuttonmap(cb->f[1]); break; } free(cb); poperror(); return n; case Qmouse: if(n > sizeof buf-1) n = sizeof buf -1; memmove(buf, va, n); buf[n] = 0; p = 0; pt.x = strtoul(buf+1, &p, 0); if(p == 0) error(Eshort); pt.y = strtoul(p, 0, 0); qlock(&mouse.qlk); if(ptinrect(pt, mouserect)){ mouse.mstate.xy = pt; mouse.redraw = 1; mouse.track = 1; } qunlock(&mouse.qlk); setmouse(pt); return n; case Qsnarf: if(offset+n >= SnarfSize) error("too much snarf"); if(n == 0) return 0; assert(mousedir[Qsnarf].qid.path == Qsnarf); mousedir[Qsnarf].qid.vers++; if(c->aux == nil) nn = 0; else nn = strlen(c->aux); if(offset+n > nn){ nn = offset+n; p = smalloc(nn+1); if(c->aux){ strcpy(p, c->aux); free(c->aux); } c->aux = p; } memmove(c->aux+offset, va, n); return n; } error(Egreg); return -1; } Dev mousedevtab = { 'm', "mouse", mousereset, mouseinit, devshutdown, mouseattach, mousewalk, mousestat, mouseopen, mousecreate, mouseclose, mouseread, devbread, mousewrite, devbwrite, devremove, devwstat, }; void Cursortocursor(Cursor *c) { lock(&cursor.lk); cursor.cursor = *c; unlock(&cursor.lk); setcursor(c); } int mousechanged(void *v) { return mouse.lastcounter != mouse.mstate.counter || mouse.lastresize != mouse.resize; } Point mousexy(void) { return mouse.mstate.xy; } /* * notify reader that screen has been resized */ void mouseresize(void) { mouse.resize++; wakeup(&mouse.r); } /* * called at interrupt level to update the structure and * awaken any waiting procs. */ void mousetrack(int x, int y, int b, int msec) { int lastb; if(x < mouserect.min.x) x = mouserect.min.x; if(x >= mouserect.max.x) x = mouserect.max.x; if(y < mouserect.min.y) y = mouserect.min.y; if(y >= mouserect.max.y) y = mouserect.max.y; lastb = mouse.mstate.buttons; mouse.mstate.xy = Pt(x, y); mouse.mstate.buttons = b; mouse.redraw = 1; mouse.mstate.counter++; mouse.mstate.msec = msec; /* * if the queue fills, we discard the entire queue and don't * queue any more events until a reader polls the mouse. */ if(!mouse.qfull && lastb != b) { /* add to ring */ mouse.queue[mouse.wi] = mouse.mstate; if(++mouse.wi == nelem(mouse.queue)) mouse.wi = 0; if(mouse.wi == mouse.ri) mouse.qfull = 1; } wakeup(&mouse.r); drawactive(1); }