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