summaryrefslogtreecommitdiff
path: root/sys/src/9/port/devmouse.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/port/devmouse.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/port/devmouse.c')
-rwxr-xr-xsys/src/9/port/devmouse.c790
1 files changed, 790 insertions, 0 deletions
diff --git a/sys/src/9/port/devmouse.c b/sys/src/9/port/devmouse.c
new file mode 100755
index 000000000..5b620720c
--- /dev/null
+++ b/sys/src/9/port/devmouse.c
@@ -0,0 +1,790 @@
+#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"
+
+enum {
+ ScrollUp = 0x08,
+ ScrollDown = 0x10,
+ ScrollLeft = 0x20,
+ ScrollRight = 0x40,
+};
+
+typedef struct Mouseinfo Mouseinfo;
+typedef struct Mousestate Mousestate;
+
+struct Mousestate
+{
+ Point xy; /* mouse.xy */
+ int buttons; /* mouse.buttons */
+ ulong counter; /* increments every update */
+ ulong msec; /* time of last event */
+};
+
+struct Mouseinfo
+{
+ Lock;
+ Mousestate;
+ int dx;
+ int dy;
+ int track; /* dx & dy updated */
+ int redraw; /* update cursor on screen */
+ ulong lastcounter; /* value when /dev/mouse read */
+ ulong lastresize;
+ ulong resize;
+ Rendez r;
+ Ref;
+ QLock;
+ int open;
+ int inopen;
+ int acceleration;
+ int maxacc;
+ 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 */
+};
+
+enum
+{
+ CMbuttonmap,
+ CMscrollswap,
+ CMswap,
+ CMwildcard,
+};
+
+static Cmdtab mousectlmsg[] =
+{
+ CMbuttonmap, "buttonmap", 0,
+ CMscrollswap, "scrollswap", 0,
+ CMswap, "swap", 1,
+ CMwildcard, "*", 0,
+};
+
+Mouseinfo mouse;
+Cursorinfo cursor;
+int mouseshifted;
+int kbdbuttons;
+void (*kbdmouse)(int);
+Cursor curs;
+
+void Cursortocursor(Cursor*);
+int mousechanged(void*);
+
+static void mouseclock(void);
+static void xkbdmouse(int);
+
+enum{
+ Qdir,
+ Qcursor,
+ Qmouse,
+ Qmousein,
+ Qmousectl,
+};
+
+static Dirtab mousedir[]={
+ ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
+ "cursor", {Qcursor}, 0, 0666,
+ "mouse", {Qmouse}, 0, 0666,
+ "mousein", {Qmousein}, 0, 0220,
+ "mousectl", {Qmousectl}, 0, 0220,
+};
+
+static uchar buttonmap[8] = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+};
+static int mouseswap;
+static int scrollswap;
+static ulong mousetime;
+
+extern Memimage* gscreen;
+extern ulong kerndate;
+
+static void
+mousereset(void)
+{
+ if(!conf.monitor)
+ return;
+
+ curs = arrow;
+ Cursortocursor(&arrow);
+ /* redraw cursor about 30 times per second */
+ addclock0link(mouseclock, 33);
+}
+
+static void
+mousefromkbd(int buttons)
+{
+ kbdbuttons = buttons;
+ mousetrack(0, 0, 0, TK2MS(MACHP(0)->ticks));
+}
+
+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;
+
+ curs = arrow;
+ Cursortocursor(&arrow);
+ cursoron(1);
+ kbdmouse = mousefromkbd;
+ mousetime = seconds();
+}
+
+static Chan*
+mouseattach(char *spec)
+{
+ if(!conf.monitor)
+ error(Egreg);
+ 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);
+ 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);
+ if(mouse.open){
+ unlock(&mouse);
+ error(Einuse);
+ }
+ mouse.open = 1;
+ mouse.ref++;
+ mouse.lastresize = mouse.resize;
+ unlock(&mouse);
+ break;
+ case Qmousein:
+ if(!iseve())
+ error(Eperm);
+ 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 void
+mousecreate(Chan*, char*, int, ulong)
+{
+ if(!conf.monitor)
+ error(Egreg);
+ error(Eperm);
+}
+
+static void
+mouseclose(Chan *c)
+{
+ if((c->qid.type&QTDIR)==0 && (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;
+ }
+ if(--mouse.ref == 0){
+ cursoroff(1);
+ curs = arrow;
+ Cursortocursor(&arrow);
+ cursoron(1);
+ }
+ unlock(&mouse);
+ }
+}
+
+
+static long
+mouseread(Chan *c, void *va, long n, vlong off)
+{
+ char buf[1+4*12+1];
+ uchar *p;
+ static int map[8] = {0, 4, 2, 6, 1, 5, 3, 7 };
+ 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);
+ 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);
+ 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))
+ tsleep(&up->sleep, return0, 0, TK2MS(1));
+
+ m = mouse.Mousestate;
+ unlock(&cursor);
+ }
+
+ 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;
+ }
+ 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
+mousewrite(Chan *c, void *va, long n, vlong)
+{
+ char *p;
+ Point pt;
+ Cmdbuf *cb;
+ Cmdtab *ct;
+ char buf[64];
+ int b, msec;
+
+ p = va;
+ switch((ulong)c->qid.path){
+ case Qdir:
+ error(Eisdir);
+
+ case Qcursor:
+ cursoroff(1);
+ 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);
+ mouse.redraw = 1;
+ mouseclock();
+ qunlock(&mouse);
+ cursoron(1);
+ 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;
+
+ case CMwildcard:
+ mousectl(cb);
+ 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);
+ msec = strtol(p, &p, 0);
+ if(msec == 0)
+ msec = TK2MS(MACHP(0)->ticks);
+ mousetrack(pt.x, pt.y, b, msec);
+ 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)){
+ mouse.xy = pt;
+ mouse.redraw = 1;
+ mouse.track = 1;
+ mouseclock();
+ }
+ qunlock(&mouse);
+ 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);
+ memmove(&cursor.Cursor, c, sizeof(Cursor));
+ setcursor(c);
+ unlock(&cursor);
+}
+
+
+/*
+ * called by the clock routine to redraw the cursor
+ */
+static void
+mouseclock(void)
+{
+ if(mouse.track){
+ mousetrack(mouse.dx, mouse.dy, mouse.buttons, TK2MS(MACHP(0)->ticks));
+ mouse.track = 0;
+ mouse.dx = 0;
+ mouse.dy = 0;
+ }
+ if(mouse.redraw && canlock(&cursor)){
+ mouse.redraw = 0;
+ cursoroff(0);
+ mouse.redraw = cursoron(0);
+ unlock(&cursor);
+ }
+ drawactive(0);
+}
+
+static int
+scale(int x)
+{
+ int sign = 1;
+
+ if(x < 0){
+ sign = -1;
+ x = -x;
+ }
+ switch(x){
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ break;
+ case 4:
+ x = 6 + (mouse.acceleration>>2);
+ break;
+ case 5:
+ x = 9 + (mouse.acceleration>>1);
+ break;
+ default:
+ x *= mouse.maxacc;
+ break;
+ }
+ return sign*x;
+}
+
+/*
+ * called at interrupt level to update the structure and
+ * awaken any waiting procs.
+ */
+void
+mousetrack(int dx, int dy, int b, int msec)
+{
+ int x, y, lastb;
+
+ if(gscreen==nil)
+ return;
+
+ if(mouse.acceleration){
+ dx = scale(dx);
+ dy = scale(dy);
+ }
+ x = mouse.xy.x + dx;
+ if(x < gscreen->clipr.min.x)
+ x = gscreen->clipr.min.x;
+ if(x >= gscreen->clipr.max.x)
+ x = gscreen->clipr.max.x;
+ y = mouse.xy.y + dy;
+ if(y < gscreen->clipr.min.y)
+ y = gscreen->clipr.min.y;
+ if(y >= gscreen->clipr.max.y)
+ y = gscreen->clipr.max.y;
+
+ lastb = mouse.buttons;
+ mouse.xy = Pt(x, y);
+ mouse.buttons = b|kbdbuttons;
+ mouse.redraw = 1;
+ mouse.counter++;
+ mouse.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.Mousestate;
+ if(++mouse.wi == nelem(mouse.queue))
+ mouse.wi = 0;
+ if(mouse.wi == mouse.ri)
+ mouse.qfull = 1;
+ }
+ wakeup(&mouse.r);
+ drawactive(1);
+}
+
+/*
+ * microsoft 3 button, 7 bit bytes
+ *
+ * byte 0 - 1 L R Y7 Y6 X7 X6
+ * byte 1 - 0 X5 X4 X3 X2 X1 X0
+ * byte 2 - 0 Y5 Y4 Y3 Y2 Y1 Y0
+ * byte 3 - 0 M x x x x x (optional)
+ *
+ * shift & right button is the same as middle button (for 2 button mice)
+ */
+int
+m3mouseputc(Queue*, int c)
+{
+ static uchar msg[3];
+ static int nb;
+ static int middle;
+ static uchar b[] = { 0, 4, 1, 5, 0, 2, 1, 3 };
+ short x;
+ int dx, dy, newbuttons;
+ static ulong lasttick;
+ ulong m;
+
+ /* Resynchronize in stream with timing. */
+ m = MACHP(0)->ticks;
+ if(TK2SEC(m - lasttick) > 2)
+ nb = 0;
+ lasttick = m;
+
+ if(nb==0){
+ /*
+ * an extra byte comes for middle button motion.
+ * only two possible values for the extra byte.
+ */
+ if(c == 0x00 || c == 0x20){
+ /* an extra byte gets sent for the middle button */
+ middle = (c&0x20) ? 2 : 0;
+ newbuttons = (mouse.buttons & ~2) | middle;
+ mousetrack(0, 0, newbuttons, TK2MS(MACHP(0)->ticks));
+ return 0;
+ }
+ }
+ msg[nb] = c;
+ if(++nb == 3){
+ nb = 0;
+ newbuttons = middle | b[(msg[0]>>4)&3 | (mouseshifted ? 4 : 0)];
+ x = (msg[0]&0x3)<<14;
+ dx = (x>>8) | msg[1];
+ x = (msg[0]&0xc)<<12;
+ dy = (x>>8) | msg[2];
+ mousetrack(dx, dy, newbuttons, TK2MS(MACHP(0)->ticks));
+ }
+ return 0;
+}
+
+/*
+ * microsoft intellimouse 3 buttons + scroll
+ * byte 0 - 1 L R Y7 Y6 X7 X6
+ * byte 1 - 0 X5 X4 X3 X2 X1 X0
+ * byte 2 - 0 Y5 Y4 Y3 Y2 Y1 Y0
+ * byte 3 - 0 0 M % % % %
+ *
+ * %: 0xf => U , 0x1 => D
+ *
+ * L: left
+ * R: right
+ * U: up
+ * D: down
+ */
+int
+m5mouseputc(Queue*, int c)
+{
+ static uchar msg[3];
+ static int nb;
+ static ulong lasttick;
+ ulong m;
+
+ /* Resynchronize in stream with timing. */
+ m = MACHP(0)->ticks;
+ if(TK2SEC(m - lasttick) > 2)
+ nb = 0;
+ lasttick = m;
+
+ msg[nb++] = c & 0x7f;
+ if (nb == 4) {
+ schar dx,dy,newbuttons;
+ dx = msg[1] | (msg[0] & 0x3) << 6;
+ dy = msg[2] | (msg[0] & 0xc) << 4;
+ newbuttons =
+ (msg[0] & 0x10) >> (mouseshifted ? 3 : 2)
+ | (msg[0] & 0x20) >> 5
+ | ( msg[3] == 0x10 ? 0x02 :
+ msg[3] == 0x0f ? ScrollUp :
+ msg[3] == 0x01 ? ScrollDown : 0 );
+ mousetrack(dx, dy, newbuttons, TK2MS(MACHP(0)->ticks));
+ nb = 0;
+ }
+ return 0;
+}
+
+/*
+ * Logitech 5 byte packed binary mouse format, 8 bit bytes
+ *
+ * shift & right button is the same as middle button (for 2 button mice)
+ */
+int
+mouseputc(Queue*, int c)
+{
+ static short msg[5];
+ static int nb;
+ static uchar b[] = {0, 4, 2, 6, 1, 5, 3, 7, 0, 2, 2, 6, 1, 3, 3, 7};
+ int dx, dy, newbuttons;
+ static ulong lasttick;
+ ulong m;
+
+ /* Resynchronize in stream with timing. */
+ m = MACHP(0)->ticks;
+ if(TK2SEC(m - lasttick) > 2)
+ nb = 0;
+ lasttick = m;
+
+ if((c&0xF0) == 0x80)
+ nb=0;
+ msg[nb] = c;
+ if(c & 0x80)
+ msg[nb] |= ~0xFF; /* sign extend */
+ if(++nb == 5){
+ newbuttons = b[((msg[0]&7)^7) | (mouseshifted ? 8 : 0)];
+ dx = msg[1]+msg[3];
+ dy = -(msg[2]+msg[4]);
+ mousetrack(dx, dy, newbuttons, TK2MS(MACHP(0)->ticks));
+ nb = 0;
+ }
+ return 0;
+}
+
+int
+mousechanged(void*)
+{
+ return mouse.lastcounter != mouse.counter ||
+ mouse.lastresize != mouse.resize;
+}
+
+Point
+mousexy(void)
+{
+ return mouse.xy;
+}
+
+void
+mouseaccelerate(int x)
+{
+ mouse.acceleration = x;
+ if(mouse.acceleration < 3)
+ mouse.maxacc = 2;
+ else
+ mouse.maxacc = mouse.acceleration;
+}
+
+/*
+ * notify reader that screen has been resized
+ */
+void
+mouseresize(void)
+{
+ mouse.resize++;
+ wakeup(&mouse.r);
+}
+