ref: 15fb95683835a788826d0826f28e88fcd68ea85d
dir: /mousefs/mousefs.c/
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <cursor.h>
#include <mouse.h>
#include <thread.h>
#include <fcall.h>
#include <9p.h>
void
usage(void)
{
fprint(2, "usage: %s [-n name]\n", argv0);
exits("usage");
}
static char Enofile[] = "file not found";
static char Eshort[] = "short read";
static char Enoread[] = "read not supported";
#define min(A, B) ((A) < (B) ? (A) : (B))
QLock drawlock;
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,
},
};
Cursor cursor;
static void
Cursortocursor(Cursor *c)
{
qlock(&drawlock);
memmove(&cursor, c, sizeof(Cursor));
qunlock(&drawlock);
}
typedef struct Mouseinfo Mouseinfo;
typedef struct Mousestate Mousestate;
struct Mousestate {
Point xy;
int buttons;
ulong counter;
ulong msec;
};
struct Mouseinfo {
QLock;
Mousestate;
int inbuttons;
int redraw;
Rendez redrawr;
ulong lastcounter;
int resize;
Rendez r;
Ref;
int open;
int acceleration;
int maxacc;
Mousestate queue[16];
ulong ri;
ulong wi;
};
Mouseinfo mouse;
static uchar buttonmap[8] = {
0, 1, 2, 3, 4, 5, 6, 7,
};
static int mouseswap;
static int scrollswap;
static ulong mousetime;
Rectangle screenr;
void
absmousetrack(int x, int y, int b, ulong msec)
{
int lastb;
if (x < screenr.min.x)
x = screenr.min.x;
if (x >= screenr.max.x)
x = screenr.max.x - 1;
if (y < screenr.min.y)
y = screenr.min.y;
if (y >= screenr.max.y)
y = screenr.max.y - 1;
qlock(&mouse);
mouse.xy = Pt(x, y);
lastb = mouse.buttons;
b |= mouse.inbuttons;
mouse.buttons = b;
mouse.msec = msec;
mouse.counter++;
if (b != lastb && (mouse.wi - mouse.ri) < nelem(mouse.queue))
mouse.queue[mouse.wi++ % nelem(mouse.queue)] = mouse.Mousestate;
qunlock(&mouse);
// TODO: wakeup?
}
void
scmousetrack(int x, int y, int b, ulong msec)
{
vlong vx, vy;
vx = (vlong)(uint)x * (screenr.max.x - screenr.min.x);
x = (vx + (1<<30) - (~vx>>31&1) >> 31) + screenr.min.x;
vy = (vlong)(uint)y * (screenr.max.y - screenr.min.y);
y = (vy + (1<<30) - (~vy>>31&1) >> 31) + screenr.min.y;
absmousetrack(x, y, b, msec);
}
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 */
void
mousetrack(int dx, int dy, int b, ulong msec)
{
if (mouse.acceleration) {
dx = scale(dx);
dy = scale(dy);
}
absmousetrack(mouse.xy.x + dx, mouse.xy.y + dy, b, msec);
}
enum {
Qroot,
Qmouse,
Qmousein,
Qmousectl,
Qcursor,
Qmax
};
typedef struct Qfile Qfile;
struct Qfile {
char *name;
int mode;
};
Qfile qfiles[] = {
[Qroot] { nil, 0555 | DMDIR },
[Qmouse] { "mouse", 0666 },
[Qmousein] { "mousein", 0666 },
[Qmousectl] { "mousectl", 0222 },
[Qcursor] { "cursor", 0666 },
};
void
mkqid(Qid *qid, int q)
{
qid->vers = 0;
qid->path = q;
qid->type = qfiles[q].mode&DMDIR ? QTDIR : QTFILE;
}
int
mkqdir(Dir *d, int q)
{
if (q < Qroot || q >= Qmax)
return -1;
mkqid(&d->qid, q);
d->name = qfiles[q].name ? estrdup9p(qfiles[q].name) : nil;
d->mode = qfiles[q].mode;
d->uid = estrdup9p(getuser());
d->gid = estrdup9p(d->uid);
d->muid = estrdup9p(d->uid);
d->atime = d->mtime = time(0);
d->length = 0;
return 0;
}
int
genroot(int n, Dir *dir, void*)
{
n++; /* skip Qroot */
return mkqdir(dir, n);
}
void
fsopen(Req *r)
{
switch (r->fid->qid.path) {
default:
respond(r, nil);
return;
case Qmousein:
r->fid->aux = mallocz(sizeof(Mousestate), 1);
respond(r, nil);
return;
case Qmouse:
qlock(&mouse);
mouse.lastcounter = mouse.counter;
mouse.resize = 0;
qunlock(&mouse);
/* wet floor */
case Qcursor:
incref(&mouse);
respond(r, nil);
}
}
void
fsclose(Fid *fid)
{
Mousestate *m;
switch (fid->qid.path) {
case Qmousein:
m = (Mousestate*)fid->aux;
qlock(&mouse);
mouse.inbuttons &= ~m->buttons;
qunlock(&mouse);
free(fid->aux);
fid->aux = nil;
return;
case Qmouse:
qlock(&mouse);
mouse.open = 0;
qunlock(&mouse);
/* wet floor */
case Qcursor:
if (decref(&mouse) != 0)
return;
Cursortocursor(&arrow);
}
}
void
fsread(Req *r)
{
Mousestate m;
int b;
char buf[1+4*12+1];
char t;
switch (r->fid->qid.path) {
case Qmousectl:
respond(r, Enoread);
return;
case Qroot:
dirread9p(r, genroot, nil);
respond(r, nil);
return;
case Qcursor:
if (r->ifcall.offset != 0) {
respond(r, nil);
return;
}
if (r->ifcall.count < 2*4+2*2*16) {
respond(r, Eshort);
return;
}
BPLONG(r->ofcall.data+0, arrow.offset.x);
BPLONG(r->ofcall.data+4, arrow.offset.y);
memmove(r->ofcall.data+8, arrow.clr, 2*16);
memmove(r->ofcall.data+40, arrow.set, 2*16);
r->ofcall.count = 2*4+2*2*16;
respond(r, nil);
return;
case Qmouse:
qlock(&mouse);
if (mouse.ri != mouse.wi)
m = mouse.queue[mouse.ri++ % nelem(mouse.queue)];
else
m = mouse.Mousestate;
qunlock(&mouse);
if(0) {
case Qmousein:
if (r->ifcall.offset != 0) {
respond(r, nil);
return;
}
qlock(&mouse);
m = mouse.Mousestate;
qunlock(&mouse);
t = 'm';
} else {
/* Qmouse */
mouse.lastcounter = m.counter;
if (mouse.resize) {
mouse.resize = 0;
t = 'r';
} else {
t = 'm';
}
}
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;
snprint(buf, sizeof(buf), "%c%11d %11d %11d %11ld ",
t, m.xy.x, m.xy.y, b, m.msec);
r->ofcall.count = 1+4*12;
memmove(r->ofcall.data, buf, r->ofcall.count);
respond(r, nil);
return;
}
respond(r, Enofile);
}
void
fswrite(Req *r)
{
Point pt;
char *p;
Cursor curs;
char buf[64];
int b, z, msec;
Mousestate *m;
switch (r->fid->qid.path) {
case Qcursor:
if (r->ifcall.count < 2*4+2*2*16) {
Cursortocursor(&arrow);
r->ofcall.count = r->ifcall.count;
respond(r, nil);
return;
}
r->ofcall.count = 2*4+2*2*16;
curs.offset.x = BGLONG(r->ifcall.data+0);
curs.offset.y = BGLONG(r->ifcall.data+4);
memmove(curs.clr, r->ifcall.data+8, 2*16);
memmove(curs.set, r->ifcall.data+40, 2*16);
Cursortocursor(&curs);
respond(r, nil);
return;
case Qmousectl:
/* TODO */
break;
case Qmousein:
r->ofcall.count = sizeof(buf) - 1;
memmove(buf, r->ifcall.data, min(r->ifcall.count, sizeof(buf)-1));
buf[r->ifcall.count] = 0;
pt.x = strtol(buf+1, &p, 0);
if (*p == 0) {
respond(r, Eshort);
return;
}
pt.y = strtol(p, &p, 0);
if (*p == 0) {
respond(r, Eshort);
return;
}
b = strtol(p, &p, 0);
msec = (ulong)strtoll(p, 0, 0);
// if (msec == 0)
// msec = TK2MS(MACHP(0)->ticks); // TODO
/* exclude wheel */
z = b & (8|16);
b ^= z;
m = (Mousestate*)r->fid->aux;
m->xy = pt;
m->msec = msec;
b ^= m->buttons;
m->buttons ^= b;
qlock(&mouse);
mouse.inbuttons = (m->buttons & b) | (mouse.inbuttons & ~b);
b = mouse.buttons & ~b;
qunlock(&mouse);
/* include wheel */
b &= ~(8|16);
b ^= z;
if (buf[0] == 'A')
absmousetrack(pt.x, pt.y, b, msec);
else if (buf[0] == 'a')
scmousetrack(pt.x, pt.y, b, msec);
else
mousetrack(pt.x, pt.y, b, msec);
respond(r, nil);
return;
case Qmouse:
r->ofcall.count = r->ifcall.count;
memmove(buf, r->ifcall.data, r->ifcall.count);
buf[r->ifcall.count] = 0;
pt.x = strtol(buf+1, &p, 0);
if (*p == 0) {
respond(r, Eshort);
return;
}
pt.y = strtol(p, 0, 0);
absmousetrack(pt.x, pt.y, mouse.buttons, 0);
respond(r, nil);
return;
}
respond(r, "not implemented");
}
char*
fswalk(Fid *fid, char *name, Qid *qid)
{
switch (fid->qid.path) {
case Qroot:
if (strcmp(name, "..") == 0) {
*qid = fid->qid;
return nil;
}
for (int q = Qmouse; q < Qmax; q++) {
if (strcmp(qfiles[q].name, name) == 0) {
mkqid(&fid->qid, q);
*qid = fid->qid;
return nil;
}
}
break;
}
return Enofile;
}
void
fsstat(Req *r)
{
if (mkqdir(&r->d, r->fid->qid.path) < 0)
respond(r, Enofile);
else
respond(r, nil);
}
void
fsattach(Req *r)
{
mkqid(&r->fid->qid, Qroot);
r->ofcall.qid = r->fid->qid;
respond(r, nil);
}
Srv fs = {
.attach = fsattach,
.walk1 = fswalk,
.stat = fsstat,
.open = fsopen,
.destroyfid = fsclose,
.read = fsread,
.write = fswrite,
};
static void
mouseinit(void)
{
Cursortocursor(&arrow);
screenr = Rect(0, 0, 1024, 768);
}
void
main(int argc, char **argv)
{
char *name = nil;
char *srvname;
char buf[16];
ARGBEGIN{
case 'D':
chatty9p++;
fprint(2, "enable chatty 9p\n");
break;
case 'n':
name = EARGF(usage());
break;
default:
usage();
}ARGEND;
if (!name) {
snprint(buf, sizeof buf, "%d", getpid());
name = buf;
}
srvname = smprint("mousefs.%s", name);
if (!srvname)
sysfatal("%r");
mouseinit();
postsrv(&fs, srvname);
}