510 lines
9.4 KiB
C
510 lines
9.4 KiB
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);
|
|
}
|
|
|