ref: 1787584ad839002584e0752e4df41b6f3b70064b
dir: /sys/src/9/bitsy/devpenmouse.c/
#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #define Image IMAGE #include <draw.h> #include <memdraw.h> #include <cursor.h> #include "screen.h" #include <ctype.h> typedef struct Mouseinfo Mouseinfo; typedef struct Mousestate Mousestate; typedef struct Calibration Calibration; struct Calibration { long scalex; long scaley; long transx; long transy; } calibration = { -16435, 23275, 253, -23 }; /* The pen goes down, tracks some and goes up again. The pen alone can * only simulate a one-button mouse. * To simulate a more-button (five, in this case) mouse, we use the four * keys along the the bottom of the iPaq as modifiers. * When one (or more) of the modifier keys is (are) down, a pen-down event * causes the corresponding bottons to go down. If no modifier key is * depressed, a pen-down event is translated into a button-one down event. * Releasing the modifier keys has no direct effect. The pen-up event is * the one that triggers mouse-up events. */ struct Mousestate { Point xy; /* mouse.xy */ int buttons; /* mouse.buttons */ int modifiers; /* state of physical buttons 2, 3, 4, 5 */ ulong counter; /* increments every update */ ulong msec; /* time of last event */ }; struct Mouseinfo { Lock; Mousestate; ulong lastcounter; /* value when /dev/mouse read */ ulong resize; ulong lastresize; Rendez r; Ref; QLock; int open; int inopen; Mousestate queue[16]; /* circular buffer of click events */ int ri; /* read index into queue */ int wi; /* write index into queue */ uchar qfull; /* queue is full */ }; Mouseinfo mouse; int mouseshifted; int penmousechanged(void*); static void penmousetrack(int b, int x, int y); enum{ Qdir, Qmouse, Qmousein, Qmousectl, }; static Dirtab mousedir[]={ ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, "mouse", {Qmouse}, 0, 0666, "mousein", {Qmousein}, 0, 0220, "mousectl", {Qmousectl}, 0, 0660, }; enum { CMcalibrate, CMswap, }; static Cmdtab penmousecmd[] = { CMcalibrate, "calibrate", 0, CMswap, "swap", 1, }; static uchar buttonmap[8] = { 0, 1, 2, 3, 4, 5, 6, 7, }; static int mouseswap; extern Memimage* gscreen; void penbutton(int up, int b) { // button 5 (side button) immediately causes an event // when the pen is down (button 1), other buttons also // cause events, allowing chording with button 1 if ((b & 0x20) || (mouse.buttons & 0x1)) { if (up) mouse.buttons &= ~b; else mouse.buttons |= b; penmousetrack(mouse.buttons, -1, -1); } else { if (up) mouse.modifiers &= ~b; else mouse.modifiers |= b; } } void pentrackxy(int x, int y) { if (x == -1) { /* pen up. associate with button 1 through 5 up */ mouse.buttons &= ~0x1f; } else { x = ((x*calibration.scalex)>>16) + calibration.transx; y = ((y*calibration.scaley)>>16) + calibration.transy; if ((mouse.buttons & 0x1f) == 0) { if (mouse.modifiers) mouse.buttons |= mouse.modifiers; else mouse.buttons |= 0x1; } } penmousetrack(mouse.buttons, x, y); } static void penmousereset(void) { if(!conf.monitor) return; } static void penmouseinit(void) { if(!conf.monitor) return; } static Chan* penmouseattach(char *spec) { if(!conf.monitor) error(Egreg); return devattach('m', spec); } static Walkqid* penmousewalk(Chan *c, Chan *nc, char **name, int nname) { return devwalk(c, nc, name, nname, mousedir, nelem(mousedir), devgen); } static int penmousestat(Chan *c, uchar *db, int n) { return devstat(c, db, n, mousedir, nelem(mousedir), devgen); } static Chan* penmouseopen(Chan *c, int omode) { switch((ulong)c->qid.path){ case Qdir: if(omode != OREAD) error(Eperm); break; case Qmouse: lock(&mouse); if(mouse.open){ unlock(&mouse); error(Einuse); } mouse.open = 1; mouse.ref++; unlock(&mouse); break; case Qmousein: /* error("disabled"); */ lock(&mouse); if(mouse.inopen){ unlock(&mouse); error(Einuse); } mouse.inopen = 1; unlock(&mouse); break; default: incref(&mouse); } c->mode = openmode(omode); c->flag |= COPEN; c->offset = 0; return c; } static Chan* penmousecreate(Chan*, char*, int, ulong) { if(!conf.monitor) error(Egreg); error(Eperm); return 0; } static void penmouseclose(Chan *c) { if(c->qid.path != Qdir && (c->flag&COPEN)){ lock(&mouse); if(c->qid.path == Qmouse) mouse.open = 0; else if(c->qid.path == Qmousein){ mouse.inopen = 0; unlock(&mouse); return; } --mouse.ref; unlock(&mouse); } } static long penmouseread(Chan *c, void *va, long n, vlong) { char buf[4*12+1]; static int map[8] = {0, 4, 2, 6, 1, 5, 3, 7 }; Mousestate m; switch((ulong)c->qid.path){ case Qdir: return devdirread(c, va, n, mousedir, nelem(mousedir), devgen); case Qmousectl: sprint(buf, "c%11ld %11ld %11ld %11ld", calibration.scalex, calibration.scaley, calibration.transx, calibration.transy); if(n > 1+4*12) n = 1+4*12; memmove(va, buf, n); return n; case Qmouse: while(penmousechanged(0) == 0) sleep(&mouse.r, penmousechanged, 0); mouse.qfull = 0; /* * 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 { m = mouse.Mousestate; } sprint(buf, "m%11d %11d %11d %11lud", m.xy.x, m.xy.y, m.buttons, 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; } 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<<i; } else if(map[i] == '2'){ if(two) error(Ebadarg); two = 1<<i; } else if(map[i] == '3'){ if(three) error(Ebadarg); three = 1<<i; } else error(Ebadarg); } if(map[i]) error(Ebadarg); memset(buttonmap, 0, 8); for(i = 0; i < 8; i++){ x = 0; if(i & 1) x |= one; if(i & 2) x |= two; if(i & 4) x |= three; buttonmap[x] = i; } } static long penmousewrite(Chan *c, void *va, long n, vlong) { char *p; Point pt; Cmdbuf *cb; Cmdtab *ct; char buf[64]; int b; p = va; switch((ulong)c->qid.path){ case Qdir: error(Eisdir); case Qmousectl: cb = parsecmd(va, n); if(waserror()){ free(cb); nexterror(); } ct = lookupcmd(cb, penmousecmd, nelem(penmousecmd)); switch(ct->index){ case CMswap: if(mouseswap) setbuttonmap("123"); else setbuttonmap("321"); mouseswap ^= 1; break; case CMcalibrate: if (cb->nf == 1) { calibration.scalex = 1<<16; calibration.scaley = 1<<16; calibration.transx = 0; calibration.transy = 0; } else if (cb->nf == 5) { if ((!isdigit(*cb->f[1]) && *cb->f[1] != '-') || (!isdigit(*cb->f[2]) && *cb->f[2] != '-') || (!isdigit(*cb->f[3]) && *cb->f[3] != '-') || (!isdigit(*cb->f[4]) && *cb->f[4] != '-')) error("bad syntax in control file message"); calibration.scalex = strtol(cb->f[1], nil, 0); calibration.scaley = strtol(cb->f[2], nil, 0); calibration.transx = strtol(cb->f[3], nil, 0); calibration.transy = strtol(cb->f[4], nil, 0); } else cmderror(cb, Ecmdargs); break; } free(cb); poperror(); return n; case Qmousein: if(n > sizeof buf-1) n = sizeof buf -1; memmove(buf, va, n); buf[n] = 0; p = 0; pt.x = strtol(buf+1, &p, 0); if(p == 0) error(Eshort); pt.y = strtol(p, &p, 0); if(p == 0) error(Eshort); b = strtol(p, &p, 0); penmousetrack(b, pt.x, pt.y); 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); if(ptinrect(pt, gscreen->r)) penmousetrack(mouse.buttons, pt.x, pt.y); qunlock(&mouse); return n; } error(Egreg); return -1; } Dev penmousedevtab = { 'm', "penmouse", penmousereset, penmouseinit, devshutdown, penmouseattach, penmousewalk, penmousestat, penmouseopen, penmousecreate, penmouseclose, penmouseread, devbread, penmousewrite, devbwrite, devremove, devwstat, }; /* * called at interrupt level to update the structure and * awaken any waiting procs. */ static void penmousetrack(int b, int x, int y) { int lastb; if (x >= 0) mouse.xy = Pt(x, y); lastb = mouse.buttons; mouse.buttons = b; mouse.counter++; mouse.msec = TK2MS(MACHP(0)->ticks); /* * 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.Mousestate; if(++mouse.wi == nelem(mouse.queue)) mouse.wi = 0; if(mouse.wi == mouse.ri) mouse.qfull = 1; } wakeup(&mouse.r); drawactive(1); resetsuspendtimer(); } int penmousechanged(void*) { return mouse.lastcounter != mouse.counter || mouse.lastresize != mouse.resize; } Point penmousexy(void) { return mouse.xy; } /* * notify reader that screen has been resized (ha!) */ void mouseresize(void) { mouse.resize++; wakeup(&mouse.r); }