devmouse.c - vx32 - Local 9vx git repository for patches.
 (HTM) git clone git://r-36.net/vx32
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
       devmouse.c (9700B)
       ---
            1 /*
            2  * Mouse device.  Also provides /dev/snarf, because it was convenient.
            3  * In a perfect world, this would be autogenerated from a
            4  * Plan 9 devmouse.
            5  */
            6 #include        "u.h"
            7 #include        "lib.h"
            8 #include        "mem.h"
            9 #include        "dat.h"
           10 #include        "fns.h"
           11 #include        "error.h"
           12 
           13 #define        Image        IMAGE
           14 #include        <draw.h>
           15 #include        <memdraw.h>
           16 #include        <cursor.h>
           17 #include        "screen.h"
           18 
           19 typedef struct Cursor Cursor;
           20 
           21 Cursor        arrow = {
           22         { -1, -1 },
           23         { 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C, 
           24           0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04, 
           25           0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04, 
           26           0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40, 
           27         },
           28         { 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0, 
           29           0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8, 
           30           0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8, 
           31           0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00, 
           32         },
           33 };
           34 
           35 enum {
           36         ScrollUp = 0x08,
           37         ScrollDown = 0x10,
           38         ScrollLeft = 0x20,
           39         ScrollRight = 0x40,
           40 };
           41 
           42 enum
           43 {
           44         CMbuttonmap,
           45         CMscrollswap,
           46         CMswap,
           47 };
           48 
           49 static Cmdtab mousectlmsg[] =
           50 {
           51         CMbuttonmap,        "buttonmap",        0,
           52         CMscrollswap,        "scrollswap",        0,
           53         CMswap,                "swap",                1,
           54 };
           55 
           56 Mouseinfo        mouse;
           57 Cursorinfo        cursor;
           58 int                mouseshifted;
           59 Cursor                curs;
           60 
           61 void        Cursortocursor(Cursor*);
           62 int        mousechanged(void*);
           63 
           64 enum{
           65         Qdir,
           66         Qcursor,
           67         Qmouse,
           68         Qmousectl,
           69         Qsnarf,
           70 };
           71 
           72 static Dirtab mousedir[]={
           73         ".",        {Qdir, 0, QTDIR},        0,                        DMDIR|0555,
           74         "cursor",        {Qcursor},        0,                        0666,
           75         "mouse",        {Qmouse},        0,                        0666,
           76         "mousectl",        {Qmousectl},        0,                        0220,
           77         "snarf",        {Qsnarf},        0,        0666,
           78 };
           79 
           80 static uchar buttonmap[8] = {
           81         0, 1, 2, 3, 4, 5, 6, 7,
           82 };
           83 static int mouseswap;
           84 static int scrollswap;
           85 static ulong mousetime;
           86 
           87 Rectangle mouserect;        /* maintained by x11 for us */
           88 
           89 static void
           90 mousereset(void)
           91 {
           92         if(!conf.monitor)
           93                 return;
           94 }
           95 
           96 static int
           97 mousedevgen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp)
           98 {
           99         int rc;
          100 
          101         rc = devgen(c, name, tab, ntab, i, dp);
          102         if(rc != -1)
          103                 dp->atime = mousetime;
          104         return rc;
          105 }
          106 
          107 static void
          108 mouseinit(void)
          109 {
          110         if(!conf.monitor)
          111                 return;
          112         mousetime = seconds();
          113 }
          114 
          115 static Chan*
          116 mouseattach(char *spec)
          117 {
          118         if(!conf.monitor)
          119                 error(Egreg);
          120         curs = arrow;
          121         Cursortocursor(&arrow);
          122         return devattach('m', spec);
          123 }
          124 
          125 static Walkqid*
          126 mousewalk(Chan *c, Chan *nc, char **name, int nname)
          127 {
          128         Walkqid *wq;
          129 
          130         /*
          131          * We use devgen() and not mousedevgen() here
          132          * see "Ugly problem" in dev.c/devwalk()
          133          */
          134         wq = devwalk(c, nc, name, nname, mousedir, nelem(mousedir), devgen);
          135         if(wq != nil && wq->clone != c && wq->clone != nil && (wq->clone->qid.type&QTDIR)==0)
          136                 incref(&mouse.ref);
          137         return wq;
          138 }
          139 
          140 static int
          141 mousestat(Chan *c, uchar *db, int n)
          142 {
          143         return devstat(c, db, n, mousedir, nelem(mousedir), mousedevgen);
          144 }
          145 
          146 static Chan*
          147 mouseopen(Chan *c, int omode)
          148 {
          149         switch((ulong)c->qid.path){
          150         case Qdir:
          151                 if(omode != OREAD)
          152                         error(Eperm);
          153                 break;
          154         case Qmouse:
          155                 lock(&mouse.ref.lk);
          156                 if(mouse.open){
          157                         unlock(&mouse.ref.lk);
          158                         error(Einuse);
          159                 }
          160                 mouse.open = 1;
          161                 mouse.ref.ref++;
          162                 mouse.lastresize = mouse.resize;
          163                 unlock(&mouse.ref.lk);
          164                 break;
          165         case Qsnarf:
          166                 if(omode == ORDWR)
          167                         error(Eperm);        /* one at a time please */
          168                 c->aux = nil;
          169                 incref(&mouse.ref);
          170                 break;
          171         default:
          172                 incref(&mouse.ref);
          173         }
          174         c->mode = openmode(omode);
          175         c->flag |= COPEN;
          176         c->offset = 0;
          177         return c;
          178 }
          179 
          180 static void
          181 mousecreate(Chan *c, char *name, int perm, ulong mode)
          182 {
          183         if(!conf.monitor)
          184                 error(Egreg);
          185         error(Eperm);
          186 }
          187 
          188 static void
          189 mouseclose(Chan *c)
          190 {
          191         if((c->qid.type&QTDIR)==0 && (c->flag&COPEN)){
          192                 if(c->qid.path == Qsnarf){
          193                         if(c->mode == OWRITE){
          194                                 if(c->aux)
          195                                         putsnarf(c->aux);
          196                                 else
          197                                         putsnarf("");
          198                         }
          199                         free(c->aux);
          200                 }
          201                 lock(&mouse.ref.lk);
          202                 if(c->qid.path == Qmouse)
          203                         mouse.open = 0;
          204                 if(--mouse.ref.ref == 0){
          205                         curs = arrow;
          206                         Cursortocursor(&arrow);
          207                 }
          208                 unlock(&mouse.ref.lk);
          209                 if(c->qid.path == Qmouse)
          210                         termredraw();
          211         }
          212 }
          213 
          214 static long
          215 mouseread(Chan *c, void *va, long n, vlong off)
          216 {
          217         char buf[1+4*12+1], *s;
          218         uchar *p;
          219         ulong offset = off;
          220         Mousestate m;
          221         int b;
          222 
          223         p = va;
          224         switch((ulong)c->qid.path){
          225         case Qdir:
          226                 return devdirread(c, va, n, mousedir, nelem(mousedir), mousedevgen);
          227 
          228         case Qcursor:
          229                 if(offset != 0)
          230                         return 0;
          231                 if(n < 2*4+2*2*16)
          232                         error(Eshort);
          233                 n = 2*4+2*2*16;
          234                 lock(&cursor.lk);
          235                 BPLONG(p+0, curs.offset.x);
          236                 BPLONG(p+4, curs.offset.y);
          237                 memmove(p+8, curs.clr, 2*16);
          238                 memmove(p+40, curs.set, 2*16);
          239                 unlock(&cursor.lk);
          240                 return n;
          241 
          242         case Qmouse:
          243                 while(mousechanged(0) == 0)
          244                         sleep(&mouse.r, mousechanged, 0);
          245 
          246                 mouse.qfull = 0;
          247                 mousetime = seconds();
          248 
          249                 /*
          250                  * No lock of the indices is necessary here, because ri is only
          251                  * updated by us, and there is only one mouse reader
          252                  * at a time.  I suppose that more than one process
          253                  * could try to read the fd at one time, but such behavior
          254                  * is degenerate and already violates the calling
          255                  * conventions for sleep above.
          256                  */
          257                 if(mouse.ri != mouse.wi) {
          258                         m = mouse.queue[mouse.ri];
          259                         if(++mouse.ri == nelem(mouse.queue))
          260                                 mouse.ri = 0;
          261                 } else {
          262                         while(!canlock(&cursor.lk))
          263                                 tsleep(&up->sleep, return0, 0, TK2MS(1));
          264 
          265                         m = mouse.mstate;
          266                         unlock(&cursor.lk);
          267                 }
          268 
          269                 b = buttonmap[m.buttons&7];
          270                 /* put buttons 4 and 5 back in */
          271                 b |= m.buttons & (3<<3);
          272                 if (scrollswap){
          273                         if (b == 8)
          274                                 b = 16;
          275                         else if (b == 16)
          276                                 b = 8;
          277                 }
          278                 sprint(buf, "m%11d %11d %11d %11lud ",
          279                         m.xy.x, m.xy.y,
          280                         b,
          281                         m.msec);
          282                 mouse.lastcounter = m.counter;
          283                 if(n > 1+4*12)
          284                         n = 1+4*12;
          285                 if(mouse.lastresize != mouse.resize){
          286                         mouse.lastresize = mouse.resize;
          287                         buf[0] = 'r';
          288                 }
          289                 memmove(va, buf, n);
          290                 return n;
          291         
          292         case Qsnarf:
          293                 if(offset == 0){
          294                         s = getsnarf();
          295                         if(c->aux)
          296                                 free(c->aux);
          297                         c->aux = s;
          298                 }
          299                 if(c->aux == nil)
          300                         return 0;
          301                 return readstr(offset, va, n, c->aux);
          302         }
          303         return 0;
          304 }
          305 
          306 static void
          307 setbuttonmap(char* map)
          308 {
          309         int i, x, one, two, three;
          310 
          311         one = two = three = 0;
          312         for(i = 0; i < 3; i++){
          313                 if(map[i] == 0)
          314                         error(Ebadarg);
          315                 if(map[i] == '1'){
          316                         if(one)
          317                                 error(Ebadarg);
          318                         one = 1<<i;
          319                 }
          320                 else if(map[i] == '2'){
          321                         if(two)
          322                                 error(Ebadarg);
          323                         two = 1<<i;
          324                 }
          325                 else if(map[i] == '3'){
          326                         if(three)
          327                                 error(Ebadarg);
          328                         three = 1<<i;
          329                 }
          330                 else
          331                         error(Ebadarg);
          332         }
          333         if(map[i])
          334                 error(Ebadarg);
          335 
          336         memset(buttonmap, 0, 8);
          337         for(i = 0; i < 8; i++){
          338                 x = 0;
          339                 if(i & 1)
          340                         x |= one;
          341                 if(i & 2)
          342                         x |= two;
          343                 if(i & 4)
          344                         x |= three;
          345                 buttonmap[x] = i;
          346         }
          347 }
          348 
          349 static long
          350 mousewrite(Chan *c, void *va, long n, vlong offset)
          351 {
          352         char *p;
          353         Point pt;
          354         Cmdbuf *cb;
          355         Cmdtab *ct;
          356         char buf[64];
          357         int nn;
          358 
          359         p = va;
          360         switch((ulong)c->qid.path){
          361         case Qdir:
          362                 error(Eisdir);
          363 
          364         case Qcursor:
          365                 if(n < 2*4+2*2*16){
          366                         curs = arrow;
          367                         Cursortocursor(&arrow);
          368                 }else{
          369                         n = 2*4+2*2*16;
          370                         curs.offset.x = BGLONG(p+0);
          371                         curs.offset.y = BGLONG(p+4);
          372                         memmove(curs.clr, p+8, 2*16);
          373                         memmove(curs.set, p+40, 2*16);
          374                         Cursortocursor(&curs);
          375                 }
          376                 qlock(&mouse.qlk);
          377                 mouse.redraw = 1;
          378                 qunlock(&mouse.qlk);
          379                 return n;
          380 
          381         case Qmousectl:
          382                 cb = parsecmd(va, n);
          383                 if(waserror()){
          384                         free(cb);
          385                         nexterror();
          386                 }
          387 
          388                 ct = lookupcmd(cb, mousectlmsg, nelem(mousectlmsg));
          389 
          390                 switch(ct->index){
          391                 case CMswap:
          392                         if(mouseswap)
          393                                 setbuttonmap("123");
          394                         else
          395                                 setbuttonmap("321");
          396                         mouseswap ^= 1;
          397                         break;
          398 
          399                 case CMscrollswap:
          400                         scrollswap ^= 1;
          401                         break;
          402 
          403                 case CMbuttonmap:
          404                         if(cb->nf == 1)
          405                                 setbuttonmap("123");
          406                         else
          407                                 setbuttonmap(cb->f[1]);
          408                         break;
          409                 }
          410 
          411                 free(cb);
          412                 poperror();
          413                 return n;
          414 
          415         case Qmouse:
          416                 if(n > sizeof buf-1)
          417                         n = sizeof buf -1;
          418                 memmove(buf, va, n);
          419                 buf[n] = 0;
          420                 p = 0;
          421                 pt.x = strtoul(buf+1, &p, 0);
          422                 if(p == 0)
          423                         error(Eshort);
          424                 pt.y = strtoul(p, 0, 0);
          425                 qlock(&mouse.qlk);
          426                 if(ptinrect(pt, mouserect)){
          427                         mouse.mstate.xy = pt;
          428                         mouse.redraw = 1;
          429                         mouse.track = 1;
          430                 }
          431                 qunlock(&mouse.qlk);
          432                 setmouse(pt);
          433                 return n;
          434         
          435         case Qsnarf:
          436                 if(offset+n >= SnarfSize)
          437                         error("too much snarf");
          438                 if(n == 0)
          439                         return 0;
          440                 assert(mousedir[Qsnarf].qid.path == Qsnarf);
          441                 mousedir[Qsnarf].qid.vers++;
          442                 if(c->aux == nil)
          443                         nn = 0;
          444                 else
          445                         nn = strlen(c->aux);
          446                 if(offset+n > nn){
          447                         nn = offset+n;
          448                         p = smalloc(nn+1);
          449                         if(c->aux){
          450                                 strcpy(p, c->aux);
          451                                 free(c->aux);
          452                         }
          453                         c->aux = p;
          454                 }
          455                 memmove(c->aux+offset, va, n);
          456                 return n;
          457         }
          458 
          459         error(Egreg);
          460         return -1;
          461 }
          462 
          463 Dev mousedevtab = {
          464         'm',
          465         "mouse",
          466 
          467         mousereset,
          468         mouseinit,
          469         devshutdown,
          470         mouseattach,
          471         mousewalk,
          472         mousestat,
          473         mouseopen,
          474         mousecreate,
          475         mouseclose,
          476         mouseread,
          477         devbread,
          478         mousewrite,
          479         devbwrite,
          480         devremove,
          481         devwstat,
          482 };
          483 
          484 void
          485 Cursortocursor(Cursor *c)
          486 {
          487         lock(&cursor.lk);
          488         cursor.cursor = *c;
          489         unlock(&cursor.lk);
          490         setcursor(c);
          491 }
          492 
          493 int
          494 mousechanged(void *v)
          495 {
          496         return mouse.lastcounter != mouse.mstate.counter ||
          497                 mouse.lastresize != mouse.resize;
          498 }
          499 
          500 Point
          501 mousexy(void)
          502 {
          503         return mouse.mstate.xy;
          504 }
          505 
          506 /*
          507  * notify reader that screen has been resized
          508  */
          509 void
          510 mouseresize(void)
          511 {
          512         mouse.resize++;
          513         wakeup(&mouse.r);
          514 }
          515 
          516 /*
          517  *  called at interrupt level to update the structure and
          518  *  awaken any waiting procs.
          519  */
          520 void
          521 mousetrack(int x, int y, int b, int msec)
          522 {
          523         int lastb;
          524 
          525         if(x < mouserect.min.x)
          526                 x = mouserect.min.x;
          527         if(x >= mouserect.max.x)
          528                 x = mouserect.max.x;
          529         if(y < mouserect.min.y)
          530                 y = mouserect.min.y;
          531         if(y >= mouserect.max.y)
          532                 y = mouserect.max.y;
          533 
          534         lastb = mouse.mstate.buttons;
          535         mouse.mstate.xy = Pt(x, y);
          536         mouse.mstate.buttons = b;
          537         mouse.redraw = 1;
          538         mouse.mstate.counter++;
          539         mouse.mstate.msec = msec;
          540 
          541         /*
          542          * if the queue fills, we discard the entire queue and don't
          543          * queue any more events until a reader polls the mouse.
          544          */
          545         if(!mouse.qfull && lastb != b) {        /* add to ring */
          546                 mouse.queue[mouse.wi] = mouse.mstate;
          547                 if(++mouse.wi == nelem(mouse.queue))
          548                         mouse.wi = 0;
          549                 if(mouse.wi == mouse.ri)
          550                         mouse.qfull = 1;
          551         }
          552         wakeup(&mouse.r);
          553         drawactive(1);
          554 }
          555