@@ 0,0 1,520 @@
+#include "u.h"
+#include "../port/lib.h"
+#include <libg.h>
+#include <gnot.h>
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#include "devtab.h"
+
+/*
+ * Some monochrome screens are reversed from what we like:
+ * We want 0's bright and 1's dark.
+ * Indexed by an Fcode, these compensate for the source bitmap being wrong
+ * (exchange S rows) and destination (exchange D columns and invert result)
+ */
+int flipS[] = {
+ 0x0, 0x4, 0x8, 0xC, 0x1, 0x5, 0x9, 0xD,
+ 0x2, 0x6, 0xA, 0xE, 0x3, 0x7, 0xB, 0xF
+};
+
+int flipD[] = {
+ 0xF, 0xD, 0xE, 0xC, 0x7, 0x5, 0x6, 0x4,
+ 0xB, 0x9, 0xA, 0x8, 0x3, 0x1, 0x2, 0x0,
+};
+
+int flipping; /* are flip tables being used to transform Fcodes? */
+
+typedef struct Mouseinfo Mouseinfo;
+typedef struct Cursorinfo Cursorinfo;
+
+struct Mouseinfo
+{
+ /*
+ * First three fields are known in some l.s's
+ */
+ int dx;
+ int dy;
+ int track; /* l.s has updated dx & dy */
+ Mouse;
+ int redraw; /* update cursor on screen */
+ ulong counter; /* increments every update */
+ ulong lastcounter; /* value when /dev/mouse read */
+ Rendez r;
+ Ref;
+ QLock;
+ int open;
+};
+
+struct Cursorinfo
+{
+ Cursor;
+ Lock;
+ int visible; /* on screen */
+ int disable; /* from being used */
+ Rectangle r; /* location */
+};
+
+Mouseinfo mouse;
+Cursorinfo cursor;
+int mouseshifted;
+int mousetype;
+int hwcurs;
+
+Cursor arrow =
+{
+ {-1, -1},
+ {0xFF, 0xE0, 0xFF, 0xE0, 0xFF, 0xC0, 0xFF, 0x00,
+ 0xFF, 0x00, 0xFF, 0x80, 0xFF, 0xC0, 0xFF, 0xE0,
+ 0xE7, 0xF0, 0xE3, 0xF8, 0xC1, 0xFC, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x3E, 0x00, 0x1C, 0x00, 0x08,
+ },
+ {0x00, 0x00, 0x7F, 0xC0, 0x7F, 0x00, 0x7C, 0x00,
+ 0x7E, 0x00, 0x7F, 0x00, 0x6F, 0x80, 0x67, 0xC0,
+ 0x43, 0xE0, 0x41, 0xF0, 0x00, 0xF8, 0x00, 0x7C,
+ 0x00, 0x3E, 0x00, 0x1C, 0x00, 0x08, 0x00, 0x00,
+ }
+};
+
+ulong setbits[16];
+GBitmap set =
+{
+ setbits,
+ 0,
+ 1,
+ 0,
+ {0, 0, 16, 16},
+ {0, 0, 16, 16}
+};
+
+ulong clrbits[16];
+GBitmap clr =
+{
+ clrbits,
+ 0,
+ 1,
+ 0,
+ {0, 0, 16, 16},
+ {0, 0, 16, 16}
+};
+
+ulong cursorbackbits[16*4];
+GBitmap cursorback =
+{
+ cursorbackbits,
+ 0,
+ 1,
+ 0,
+ {0, 0, 16, 16},
+ {0, 0, 16, 16}
+};
+
+void Cursortocursor(Cursor*);
+int mousechanged(void*);
+
+enum{
+ Qdir,
+ Qmouse,
+ Qmousectl,
+};
+
+Dirtab mousedir[]={
+ "mouse", {Qmouse}, 0, 0666,
+ "mousectl", {Qmousectl}, 0, 0220,
+};
+
+#define NMOUSE (sizeof(mousedir)/sizeof(Dirtab))
+
+extern GBitmap gscreen;
+
+void
+mousereset(void)
+{
+ ulong r;
+
+ if(!conf.monitor)
+ return;
+
+ getcolor(0, &r, &r, &r);
+ if(r == 0)
+ flipping = 1;
+ flipping = 0; /* howard, why is this necessary to get a black arrow on carrera? */
+ Cursortocursor(&arrow);
+}
+
+void
+mouseinit(void)
+{
+ if(!conf.monitor)
+ return;
+ if(gscreen.ldepth > 3)
+ cursorback.ldepth = 0;
+ else{
+ cursorback.ldepth = gscreen.ldepth;
+ cursorback.width = ((16 << gscreen.ldepth) + 31) >> 5;
+ }
+ cursoron(1);
+}
+
+Chan*
+mouseattach(char *spec)
+{
+ if(!conf.monitor)
+ error(Egreg);
+ return devattach('m', spec);
+}
+
+Chan*
+mouseclone(Chan *c, Chan *nc)
+{
+ nc = devclone(c, nc);
+ if(c->qid.path != CHDIR)
+ incref(&mouse);
+ return nc;
+}
+
+int
+mousewalk(Chan *c, char *name)
+{
+ return devwalk(c, name, mousedir, NMOUSE, devgen);
+}
+
+void
+mousestat(Chan *c, char *db)
+{
+ devstat(c, db, mousedir, NMOUSE, devgen);
+}
+
+Chan*
+mouseopen(Chan *c, int omode)
+{
+ switch(c->qid.path){
+ case CHDIR:
+ 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;
+ default:
+ incref(&mouse);
+ }
+ c->mode = openmode(omode);
+ c->flag |= COPEN;
+ c->offset = 0;
+ return c;
+}
+
+void
+mousecreate(Chan *c, char *name, int omode, ulong perm)
+{
+ if(!conf.monitor)
+ error(Egreg);
+ USED(c, name, omode, perm);
+ error(Eperm);
+}
+
+void
+mouseremove(Chan *c)
+{
+ USED(c);
+ error(Eperm);
+}
+
+void
+mousewstat(Chan *c, char *db)
+{
+ USED(c, db);
+ error(Eperm);
+}
+
+void
+mouseclose(Chan *c)
+{
+ if(c->qid.path!=CHDIR && (c->flag&COPEN)){
+ lock(&mouse);
+ if(c->qid.path == Qmouse)
+ mouse.open = 0;
+ unlock(&mouse);
+ }
+}
+
+long
+mouseread(Chan *c, void *va, long n, ulong offset)
+{
+ uchar *p;
+
+ USED(offset);
+ if(c->qid.path & CHDIR)
+ return devdirread(c, va, n, mousedir, NMOUSE, devgen);
+
+ if(c->qid.path == Qmouse){
+ /*
+ * mouse:
+ * 'm' 1
+ * buttons 1
+ * point 8
+ * msec 4
+ */
+ if(n < 14)
+ error(Eshort);
+ while(mousechanged(0) == 0)
+ sleep(&mouse.r, mousechanged, 0);
+ lock(&cursor);
+ p = va;
+ p[0] = 'm';
+ p[1] = mouse.buttons;
+ BPLONG(p+2, mouse.xy.x);
+ BPLONG(p+6, mouse.xy.y);
+ BPLONG(p+10, TK2MS(MACHP(0)->ticks));
+ mouse.lastcounter = mouse.counter;
+ unlock(&cursor);
+ return 14;
+ }
+ return 0;
+}
+
+long
+mousewrite(Chan *c, void *va, long n, ulong offset)
+{
+ uchar *p;
+ Point pt;
+ char buf[64];
+
+ USED(offset);
+
+ switch(c->qid.path){
+ case CHDIR:
+ error(Eisdir);
+
+ case Qmousectl:
+ if(n >= sizeof(buf))
+ n = sizeof(buf)-1;
+ strncpy(buf, va, n);
+ buf[n] = 0;
+ mousectl(buf);
+ return n;
+
+ case Qmouse:
+ if(n != 9)
+ error(Eshort);
+ qlock(&mouse);
+ p = va;
+ pt.x = BGLONG(p+1);
+ pt.y = BGLONG(p+5);
+ if(ptinrect(pt, gscreen.r)){
+ mouse.xy = pt;
+ mouse.redraw = 1;
+ mouse.track = 1;
+ mouseclock();
+ }
+ qunlock(&mouse);
+ return n;
+ }
+
+ error(Egreg);
+ return -1;
+}
+
+void
+Cursortocursor(Cursor *c)
+{
+ int i;
+ uchar *p;
+
+ lock(&cursor);
+ memmove(&cursor, c, sizeof(Cursor));
+ for(i=0; i<16; i++){
+ p = (uchar*)&setbits[i];
+ *p = c->set[2*i];
+ *(p+1) = c->set[2*i+1];
+ p = (uchar*)&clrbits[i];
+ *p = c->clr[2*i];
+ *(p+1) = c->clr[2*i+1];
+ }
+ if(hwcurs)
+ hwcursset(set.base, clr.base, cursor.offset.x, cursor.offset.y);
+ unlock(&cursor);
+}
+
+void
+cursoron(int dolock)
+{
+ if(cursor.disable)
+ return;
+ if(dolock)
+ lock(&cursor);
+ if(cursor.visible++ == 0){
+ if(hwcurs)
+ hwcursmove(mouse.xy.x, mouse.xy.y);
+ else {
+ cursor.r.min = mouse.xy;
+ cursor.r.max = add(mouse.xy, Pt(16, 16));
+ cursor.r = raddp(cursor.r, cursor.offset);
+ gbitblt(&cursorback, Pt(0, 0), &gscreen, cursor.r, S);
+ gbitblt(&gscreen, cursor.r.min,
+ &clr, Rect(0, 0, 16, 16), flipping? flipD[D&~S] : D&~S);
+ gbitblt(&gscreen, cursor.r.min,
+ &set, Rect(0, 0, 16, 16), flipping? flipD[S|D] : S|D);
+ mbbrect(cursor.r);
+ }
+ }
+ if(dolock)
+ unlock(&cursor);
+}
+
+void
+cursoroff(int dolock)
+{
+ if(cursor.disable)
+ return;
+ if(dolock)
+ lock(&cursor);
+ if(--cursor.visible == 0) {
+ if(!hwcurs) {
+ gbitblt(&gscreen, cursor.r.min, &cursorback, Rect(0, 0, 16, 16), S);
+ mbbrect(cursor.r);
+ mousescreenupdate();
+ }
+ }
+ if(dolock)
+ unlock(&cursor);
+}
+
+/*
+ * called by the clock routine to redraw the cursor
+ */
+void
+mouseclock(void)
+{
+ if(mouse.track){
+ mousetrack(mouse.buttons, mouse.dx, mouse.dy);
+ mouse.track = 0;
+ mouse.dx = 0;
+ mouse.dy = 0;
+ }
+ if(mouse.redraw && canlock(&cursor)){
+ mouse.redraw = 0;
+ cursoroff(0);
+ cursoron(0);
+ mousescreenupdate();
+ unlock(&cursor);
+ }
+}
+
+/*
+ * called at interrupt level to update the structure and
+ * awaken any waiting procs.
+ */
+void
+mousetrack(int b, int dx, int dy)
+{
+ int x, y;
+
+ x = mouse.xy.x + dx;
+ if(x < gscreen.r.min.x)
+ x = gscreen.r.min.x;
+ if(x >= gscreen.r.max.x)
+ x = gscreen.r.max.x;
+ y = mouse.xy.y + dy;
+ if(y < gscreen.r.min.y)
+ y = gscreen.r.min.y;
+ if(y >= gscreen.r.max.y)
+ y = gscreen.r.max.y;
+ mouse.counter++;
+ mouse.xy = Pt(x, y);
+ mouse.buttons = b;
+ mouse.redraw = 1;
+ wakeup(&mouse.r);
+}
+
+/*
+ * 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(IOQ *q, int c)
+{
+ static uchar msg[3];
+ static int nb;
+ static int middle;
+ static uchar b[] = { 0, 4, 1, 5, 0, 2, 1, 5 };
+ short x;
+ int dx, dy, newbuttons;
+
+ USED(q);
+ /*
+ * check bit 6 for consistency
+ */
+ if(nb==0){
+ if((c&0x40) == 0){
+ /* an extra byte gets sent for the middle button */
+ middle = (c&0x20) ? 2 : 0;
+ newbuttons = (mouse.buttons & ~2) | middle;
+ mousetrack(newbuttons, 0, 0);
+ 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(newbuttons, dx, dy);
+ }
+ 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(IOQ *q, 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, 5, 3, 7};
+ int dx, dy, newbuttons;
+
+ USED(q);
+ 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(newbuttons, dx, dy);
+ nb = 0;
+ }
+ return 0;
+}
+
+int
+mousechanged(void *m)
+{
+ USED(m);
+ return mouse.lastcounter - mouse.counter;
+}