shithub: drawfs

ref: 798897c28e191cd4c2513afb658fa196b3fbb95c
dir: /mousefs/mousefs.c/

View raw version
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <cursor.h>
#include <mouse.h>
#include <thread.h>
#include <fcall.h>
#include <9p.h>
#include "fns.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;
Rendez readrend;

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;
	rwakeup(&readrend);
	qunlock(&mouse);
}

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);
		rsleep(&readrend);
		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);
	readrend.l = &mouse;
}

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();
	
	initcmdfs(name);
	
	postsrv(&fs, srvname);
}