#include <9pm/u.h> #include <9pm/libc.h> #include <9pm/draw.h> #include <9pm/memdraw.h> #include <9pm/cursor.h> #include <9pm/mouse.h> #include "dat.h" #include "fns.h" #include "screen.h" enum{ Qdir, Qcursor, Qmouse, Qmousectl, }; static Dirtab mousedir[]={ ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, "cursor", {Qcursor}, 0, 0666, "mouse", {Qmouse}, 0, 0666, }; static void mousereset(void) { curs = arrow; Cursortocursor(&arrow); } static Chan* mouseattach(char *spec) { int i; char *p; Chan *c; Drawgrp *dg; i = strtol(spec, &p, 10); if(i == 0 || *p != '\0' || (dg = lookupdrawgrp(i)) == nil) error(Ebadspec); c = devattach('m', spec); c->aux = dg; return c; } static Walkqid* mousewalk(Chan *c, Chan *nc, char **name, int nname) { Drawgrp *dg; Walkqid *wq; wq = devwalk(c, nc, name, nname, mousedir, nelem(mousedir), devgen); if(wq != nil && wq->clone != c && (wq->clone->qid.type&QTDIR)==0){ dg = c->aux; incref(&dg->ref); wq->aux = dg; } return wq; } static int mousestat(Chan *c, uchar *db, int n) { return devstat(c, db, n, mousedir, nelem(mousedir), devgen); } static Chan* mouseopen(Chan *c, int omode) { switch((ulong)c->qid.path){ case Qdir: if(omode != OREAD) error(Eperm); break; case Qmouse: lock(&mouse.lk); if(mouse.open){ unlock(&mouse.lk); error(Einuse); } mouse.open = 1; incref(&mouse.ref); unlock(&mouse.lk); break; default: incref(&mouse.ref); } c->mode = openmode(omode); c->flag |= COPEN; c->offset = 0; return c; } static void mouseclose(Chan *c) { Drawgrp *dg; dg = c->aux; if((c->qid.type&QTDIR)==0 && (c->flag&COPEN)){ lock(&dg->mouse.lk); if(c->qid.path == Qmouse) dg->mouse.open = 0; if(decref(&dg->mouse.ref) == 0){ curs = arrow; Cursortocursor(&arrow); } unlock(&dg->mouse.lk); } c->aux = nil; closedrawgrp(dg); } static long mouseread(Chan *c, void *va, long n, vlong off) { int nresize; char buf[4*12+1]; uchar *p; ulong offset = off; Drawgrp *dg; Kmouse m; p = va; dg = c->aux; switch((ulong)c->qid.path){ default: error(Egreg); case Qdir: return devdirread(c, va, n, mousedir, nelem(mousedir), devgen); case Qcursor: if(offset != 0) return 0; if(n < 2*4+2*2*16) error(Eshort); n = 2*4+2*2*16; lock(&dg->cursor.lk); BPLONG(p+0, dg->cursor.c.offset.x); BPLONG(p+4, dg->cursor.c.offset.y); memmove(p+8, dg->cursor.c.clr, 2*16); memmove(p+40, dg->cursor.c.set, 2*16); unlock(&dg->cursor.lk); return n; case Qmouse: while(mousechanged(dg) == 0) rendsleep(&dg->mouse.r, mousechanged, dg); dg->mouse.qfull = 0; /* * No lock of the indicies is necessary here, because ri is only * updated by us, and there is only one mouse fd * 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(dg->mouse.ri != dg->mouse.wi) { m = dg->mouse.queue[dg->mouse.ri]; if(++dg->mouse.ri == nelem(dg->mouse.queue)) dg->mouse.ri = 0; } else { lock(&dg->mouse.lk); m = dg->mouse.m; unlock(&dg->mouse.lk); } sprint(buf, "m%11d %11d %11d %11lud", m.m.xy.x, m.m.xy.y, m.m.buttons, m.m.msec); nresize = dg->mouse.nresize; if(dg->mouse.mresize < nresize){ dg->mouse.mresize = nresize; buf[0] = 'r'; } dg->mouse.lastcounter = m.counter; if(n > 1+4*12) n = 1+4*12; memmove(va, buf, n); return n; } } static long mousewrite(Chan *c, void *va, long n, vlong offset) { char *p; Point pt; char buf[64]; Drawgrp *dg; USED(offset); p = va; dg = c->aux; switch((ulong)c->qid.path){ default: error(Egreg); case Qdir: error(Eisdir); case Qcursor: lock(&dg->cursor.lk); if(n < 2*4+2*2*16) dg->cursor.c = arrow; else{ n = 2*4+2*2*16; dg->cursor.c.offset.x = BGLONG(p+0); dg->cursor.c.offset.y = BGLONG(p+4); memmove(dg->cursor.c.clr, p+8, 2*16); memmove(dg->cursor.c.set, p+40, 2*16); } unlock(&dg->cursor.lk); Cursortocursor(&curs); 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); if(ptinrect(pt, dg->screenimage->r)) movecursor(dg, pt); return n; } } Dev devmouse = { 'm', "mouse", mousereset, mouseattach, mousewalk, mousestat, mouseopen, devcreate, mouseclose, mouseread, devbread, mousewrite, devbwrite, devremove, devwstat, }; void Cursortocursor(Cursor *c) { lock(&cursor.lk); memmove(&cursor.c, c, sizeof(Cursor)); cursorload(c); unlock(&cursor.lk); } /* * called at interrupt level to update the structure and * awaken any waiting procs. */ void mousetrack(Point xy, int b, int msec) { int lastb; lastb = mouse.m.buttons; mouse.m.xy = xy; mouse.m.buttons = b; mouse.m.counter++; if(msec == 0) msec = nsec()/1000000; mouse.m.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.m; if(++mouse.wi == nelem(mouse.queue)) mouse.wi = 0; if(mouse.wi == mouse.ri) mouse.qfull = 1; } rendwakeup(&mouse.r); } int mousechanged(void *a) { USED(a); return mouse.lastcounter != mouse.m.counter; } Point mousexy(void) { return mouse.m.xy; }