1 #include "u.h" 2 #include "../port/lib.h" 3 #include "mem.h" 4 #include "dat.h" 5 #include "fns.h" 6 #include "../port/error.h" 7 8 #define Image IMAGE 9 #include <draw.h> 10 #include <memdraw.h> 11 #include <cursor.h> 12 #include "screen.h" 13 14 enum { 15 ScrollUp = 0x08, 16 ScrollDown = 0x10, 17 ScrollLeft = 0x20, 18 ScrollRight = 0x40, 19 }; 20 21 typedef struct Mouseinfo Mouseinfo; 22 typedef struct Mousestate Mousestate; 23 24 struct Mousestate 25 { 26 Point xy; /* mouse.xy */ 27 int buttons; /* mouse.buttons */ 28 ulong counter; /* increments every update */ 29 ulong msec; /* time of last event */ 30 }; 31 32 struct Mouseinfo 33 { 34 Lock; 35 Mousestate; 36 int dx; 37 int dy; 38 int track; /* dx & dy updated */ 39 int redraw; /* update cursor on screen */ 40 ulong lastcounter; /* value when /dev/mouse read */ 41 ulong lastresize; 42 ulong resize; 43 Rendez r; 44 Ref; 45 QLock; 46 int open; 47 int inopen; 48 int acceleration; 49 int maxacc; 50 Mousestate queue[16]; /* circular buffer of click events */ 51 int ri; /* read index into queue */ 52 int wi; /* write index into queue */ 53 uchar qfull; /* queue is full */ 54 }; 55 56 enum 57 { 58 CMbuttonmap, 59 CMscrollswap, 60 CMswap, 61 CMwildcard, 62 }; 63 64 static Cmdtab mousectlmsg[] = 65 { 66 CMbuttonmap, "buttonmap", 0, 67 CMscrollswap, "scrollswap", 0, 68 CMswap, "swap", 1, 69 CMwildcard, "*", 0, 70 }; 71 72 Mouseinfo mouse; 73 Cursorinfo cursor; 74 int mouseshifted; 75 int kbdbuttons; 76 void (*kbdmouse)(int); 77 Cursor curs; 78 79 void Cursortocursor(Cursor*); 80 int mousechanged(void*); 81 static void mouseclock(void); 82 static void xkbdmouse(int); 83 84 enum{ 85 Qdir, 86 Qcursor, 87 Qmouse, 88 Qmousein, 89 Qmousectl, 90 }; 91 92 static Dirtab mousedir[]={ 93 ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, 94 "cursor", {Qcursor}, 0, 0666, 95 "mouse", {Qmouse}, 0, 0666, 96 "mousein", {Qmousein}, 0, 0220, 97 "mousectl", {Qmousectl}, 0, 0220, 98 }; 99 100 static uchar buttonmap[8] = { 101 0, 1, 2, 3, 4, 5, 6, 7, 102 }; 103 static int mouseswap; 104 static int scrollswap; 105 extern Memimage* gscreen; 106 107 static void 108 mousereset(void) 109 { 110 if(!conf.monitor) 111 return; 112 113 curs = arrow; 114 Cursortocursor(&arrow); 115 /* redraw cursor about 30 times per second */ 116 addclock0link(mouseclock, 33); 117 } 118 119 static void 120 mousefromkbd(int buttons) 121 { 122 kbdbuttons = buttons; 123 mousetrack(0, 0, 0, TK2MS(MACHP(0)->ticks)); 124 } 125 126 static void 127 mouseinit(void) 128 { 129 if(!conf.monitor) 130 return; 131 132 curs = arrow; 133 Cursortocursor(&arrow); 134 cursoron(1); 135 kbdmouse = mousefromkbd; 136 } 137 138 static Chan* 139 mouseattach(char *spec) 140 { 141 if(!conf.monitor) 142 error(Egreg); 143 return devattach('m', spec); 144 } 145 146 static Walkqid* 147 mousewalk(Chan *c, Chan *nc, char **name, int nname) 148 { 149 Walkqid *wq; 150 151 wq = devwalk(c, nc, name, nname, mousedir, nelem(mousedir), devgen); 152 if(wq != nil && wq->clone != c && wq->clone != nil && (wq->clone->qid.type&QTDIR)==0) 153 incref(&mouse); 154 return wq; 155 } 156 157 static int 158 mousestat(Chan *c, uchar *db, int n) 159 { 160 return devstat(c, db, n, mousedir, nelem(mousedir), devgen); 161 } 162 163 static Chan* 164 mouseopen(Chan *c, int omode) 165 { 166 switch((ulong)c->qid.path){ 167 case Qdir: 168 if(omode != OREAD) 169 error(Eperm); 170 break; 171 case Qmouse: 172 lock(&mouse); 173 if(mouse.open){ 174 unlock(&mouse); 175 error(Einuse); 176 } 177 mouse.open = 1; 178 mouse.ref++; 179 mouse.lastresize = mouse.resize; 180 unlock(&mouse); 181 break; 182 case Qmousein: 183 if(!iseve()) 184 error(Eperm); 185 lock(&mouse); 186 if(mouse.inopen){ 187 unlock(&mouse); 188 error(Einuse); 189 } 190 mouse.inopen = 1; 191 unlock(&mouse); 192 break; 193 default: 194 incref(&mouse); 195 } 196 c->mode = openmode(omode); 197 c->flag |= COPEN; 198 c->offset = 0; 199 return c; 200 } 201 202 static void 203 mousecreate(Chan*, char*, int, ulong) 204 { 205 if(!conf.monitor) 206 error(Egreg); 207 error(Eperm); 208 } 209 210 static void 211 mouseclose(Chan *c) 212 { 213 if((c->qid.type&QTDIR)==0 && (c->flag&COPEN)){ 214 lock(&mouse); 215 if(c->qid.path == Qmouse) 216 mouse.open = 0; 217 else if(c->qid.path == Qmousein){ 218 mouse.inopen = 0; 219 unlock(&mouse); 220 return; 221 } 222 if(--mouse.ref == 0){ 223 cursoroff(1); 224 curs = arrow; 225 Cursortocursor(&arrow); 226 cursoron(1); 227 } 228 unlock(&mouse); 229 } 230 } 231 232 233 static long 234 mouseread(Chan *c, void *va, long n, vlong off) 235 { 236 char buf[1+4*12+1]; 237 uchar *p; 238 static int map[8] = {0, 4, 2, 6, 1, 5, 3, 7 }; 239 ulong offset = off; 240 Mousestate m; 241 int b; 242 243 p = va; 244 switch((ulong)c->qid.path){ 245 case Qdir: 246 return devdirread(c, va, n, mousedir, nelem(mousedir), devgen); 247 248 case Qcursor: 249 if(offset != 0) 250 return 0; 251 if(n < 2*4+2*2*16) 252 error(Eshort); 253 n = 2*4+2*2*16; 254 lock(&cursor); 255 BPLONG(p+0, curs.offset.x); 256 BPLONG(p+4, curs.offset.y); 257 memmove(p+8, curs.clr, 2*16); 258 memmove(p+40, curs.set, 2*16); 259 unlock(&cursor); 260 return n; 261 262 case Qmouse: 263 while(mousechanged(0) == 0) 264 sleep(&mouse.r, mousechanged, 0); 265 266 mouse.qfull = 0; 267 268 /* 269 * No lock of the indicies is necessary here, because ri is only 270 * updated by us, and there is only one mouse reader 271 * at a time. I suppose that more than one process 272 * could try to read the fd at one time, but such behavior 273 * is degenerate and already violates the calling 274 * conventions for sleep above. 275 */ 276 if(mouse.ri != mouse.wi) { 277 m = mouse.queue[mouse.ri]; 278 if(++mouse.ri == nelem(mouse.queue)) 279 mouse.ri = 0; 280 } else { 281 while(!canlock(&cursor)) 282 tsleep(&up->sleep, return0, 0, TK2MS(1)); 283 284 m = mouse.Mousestate; 285 unlock(&cursor); 286 } 287 288 b = buttonmap[m.buttons&7]; 289 /* put buttons 4 and 5 back in */ 290 b |= m.buttons & (3<<3); 291 if (scrollswap) 292 if (b == 8) 293 b = 16; 294 else if (b == 16) 295 b = 8; 296 sprint(buf, "m%11d %11d %11d %11lud ", 297 m.xy.x, m.xy.y, 298 b, 299 m.msec); 300 mouse.lastcounter = m.counter; 301 if(n > 1+4*12) 302 n = 1+4*12; 303 if(mouse.lastresize != mouse.resize){ 304 mouse.lastresize = mouse.resize; 305 buf[0] = 'r'; 306 } 307 memmove(va, buf, n); 308 return n; 309 } 310 return 0; 311 } 312 313 static void 314 setbuttonmap(char* map) 315 { 316 int i, x, one, two, three; 317 318 one = two = three = 0; 319 for(i = 0; i < 3; i++){ 320 if(map[i] == 0) 321 error(Ebadarg); 322 if(map[i] == '1'){ 323 if(one) 324 error(Ebadarg); 325 one = 1<<i; 326 } 327 else if(map[i] == '2'){ 328 if(two) 329 error(Ebadarg); 330 two = 1<<i; 331 } 332 else if(map[i] == '3'){ 333 if(three) 334 error(Ebadarg); 335 three = 1<<i; 336 } 337 else 338 error(Ebadarg); 339 } 340 if(map[i]) 341 error(Ebadarg); 342 343 memset(buttonmap, 0, 8); 344 for(i = 0; i < 8; i++){ 345 x = 0; 346 if(i & 1) 347 x |= one; 348 if(i & 2) 349 x |= two; 350 if(i & 4) 351 x |= three; 352 buttonmap[x] = i; 353 } 354 } 355 356 static long 357 mousewrite(Chan *c, void *va, long n, vlong) 358 { 359 char *p; 360 Point pt; 361 Cmdbuf *cb; 362 Cmdtab *ct; 363 char buf[64]; 364 int b, msec; 365 366 p = va; 367 switch((ulong)c->qid.path){ 368 case Qdir: 369 error(Eisdir); 370 371 case Qcursor: 372 cursoroff(1); 373 if(n < 2*4+2*2*16){ 374 curs = arrow; 375 Cursortocursor(&arrow); 376 }else{ 377 n = 2*4+2*2*16; 378 curs.offset.x = BGLONG(p+0); 379 curs.offset.y = BGLONG(p+4); 380 memmove(curs.clr, p+8, 2*16); 381 memmove(curs.set, p+40, 2*16); 382 Cursortocursor(&curs); 383 } 384 qlock(&mouse); 385 mouse.redraw = 1; 386 mouseclock(); 387 qunlock(&mouse); 388 cursoron(1); 389 return n; 390 391 case Qmousectl: 392 cb = parsecmd(va, n); 393 if(waserror()){ 394 free(cb); 395 nexterror(); 396 } 397 398 ct = lookupcmd(cb, mousectlmsg, nelem(mousectlmsg)); 399 400 switch(ct->index){ 401 case CMswap: 402 if(mouseswap) 403 setbuttonmap("123"); 404 else 405 setbuttonmap("321"); 406 mouseswap ^= 1; 407 break; 408 409 case CMscrollswap: 410 scrollswap ^= 1; 411 break; 412 413 case CMbuttonmap: 414 if(cb->nf == 1) 415 setbuttonmap("123"); 416 else 417 setbuttonmap(cb->f[1]); 418 break; 419 420 case CMwildcard: 421 mousectl(cb); 422 break; 423 } 424 425 free(cb); 426 poperror(); 427 return n; 428 429 case Qmousein: 430 if(n > sizeof buf-1) 431 n = sizeof buf -1; 432 memmove(buf, va, n); 433 buf[n] = 0; 434 p = 0; 435 pt.x = strtol(buf+1, &p, 0); 436 if(p == 0) 437 error(Eshort); 438 pt.y = strtol(p, &p, 0); 439 if(p == 0) 440 error(Eshort); 441 b = strtol(p, &p, 0); 442 msec = strtol(p, &p, 0); 443 if(msec == 0) 444 msec = TK2MS(MACHP(0)->ticks); 445 mousetrack(pt.x, pt.y, b, msec); 446 return n; 447 448 case Qmouse: 449 if(n > sizeof buf-1) 450 n = sizeof buf -1; 451 memmove(buf, va, n); 452 buf[n] = 0; 453 p = 0; 454 pt.x = strtoul(buf+1, &p, 0); 455 if(p == 0) 456 error(Eshort); 457 pt.y = strtoul(p, 0, 0); 458 qlock(&mouse); 459 if(ptinrect(pt, gscreen->r)){ 460 mouse.xy = pt; 461 mouse.redraw = 1; 462 mouse.track = 1; 463 mouseclock(); 464 } 465 qunlock(&mouse); 466 return n; 467 } 468 469 error(Egreg); 470 return -1; 471 } 472 473 Dev mousedevtab = { 474 'm', 475 "mouse", 476 477 mousereset, 478 mouseinit, 479 devshutdown, 480 mouseattach, 481 mousewalk, 482 mousestat, 483 mouseopen, 484 mousecreate, 485 mouseclose, 486 mouseread, 487 devbread, 488 mousewrite, 489 devbwrite, 490 devremove, 491 devwstat, 492 }; 493 494 void 495 Cursortocursor(Cursor *c) 496 { 497 lock(&cursor); 498 memmove(&cursor.Cursor, c, sizeof(Cursor)); 499 setcursor(c); 500 unlock(&cursor); 501 } 502 503 504 /* 505 * called by the clock routine to redraw the cursor 506 */ 507 static void 508 mouseclock(void) 509 { 510 if(mouse.track){ 511 mousetrack(mouse.dx, mouse.dy, mouse.buttons, TK2MS(MACHP(0)->ticks)); 512 mouse.track = 0; 513 mouse.dx = 0; 514 mouse.dy = 0; 515 } 516 if(mouse.redraw && canlock(&cursor)){ 517 mouse.redraw = 0; 518 cursoroff(0); 519 mouse.redraw = cursoron(0); 520 unlock(&cursor); 521 } 522 drawactive(0); 523 } 524 525 static int 526 scale(int x) 527 { 528 int sign = 1; 529 530 if(x < 0){ 531 sign = -1; 532 x = -x; 533 } 534 switch(x){ 535 case 0: 536 case 1: 537 case 2: 538 case 3: 539 break; 540 case 4: 541 x = 6 + (mouse.acceleration>>2); 542 break; 543 case 5: 544 x = 9 + (mouse.acceleration>>1); 545 break; 546 default: 547 x *= mouse.maxacc; 548 break; 549 } 550 return sign*x; 551 } 552 553 /* 554 * called at interrupt level to update the structure and 555 * awaken any waiting procs. 556 */ 557 void 558 mousetrack(int dx, int dy, int b, int msec) 559 { 560 int x, y, lastb; 561 562 if(gscreen==nil) 563 return; 564 565 if(mouse.acceleration){ 566 dx = scale(dx); 567 dy = scale(dy); 568 } 569 x = mouse.xy.x + dx; 570 if(x < gscreen->clipr.min.x) 571 x = gscreen->clipr.min.x; 572 if(x >= gscreen->clipr.max.x) 573 x = gscreen->clipr.max.x; 574 y = mouse.xy.y + dy; 575 if(y < gscreen->clipr.min.y) 576 y = gscreen->clipr.min.y; 577 if(y >= gscreen->clipr.max.y) 578 y = gscreen->clipr.max.y; 579 580 lastb = mouse.buttons; 581 mouse.xy = Pt(x, y); 582 mouse.buttons = b|kbdbuttons; 583 mouse.redraw = 1; 584 mouse.counter++; 585 mouse.msec = msec; 586 587 /* 588 * if the queue fills, we discard the entire queue and don't 589 * queue any more events until a reader polls the mouse. 590 */ 591 if(!mouse.qfull && lastb != b) { /* add to ring */ 592 mouse.queue[mouse.wi] = mouse.Mousestate; 593 if(++mouse.wi == nelem(mouse.queue)) 594 mouse.wi = 0; 595 if(mouse.wi == mouse.ri) 596 mouse.qfull = 1; 597 } 598 wakeup(&mouse.r); 599 drawactive(1); 600 } 601 602 /* 603 * microsoft 3 button, 7 bit bytes 604 * 605 * byte 0 - 1 L R Y7 Y6 X7 X6 606 * byte 1 - 0 X5 X4 X3 X2 X1 X0 607 * byte 2 - 0 Y5 Y4 Y3 Y2 Y1 Y0 608 * byte 3 - 0 M x x x x x (optional) 609 * 610 * shift & right button is the same as middle button (for 2 button mice) 611 */ 612 int 613 m3mouseputc(Queue*, int c) 614 { 615 static uchar msg[3]; 616 static int nb; 617 static int middle; 618 static uchar b[] = { 0, 4, 1, 5, 0, 2, 1, 3 }; 619 short x; 620 int dx, dy, newbuttons; 621 static ulong lasttick; 622 ulong m; 623 624 /* Resynchronize in stream with timing. */ 625 m = MACHP(0)->ticks; 626 if(TK2SEC(m - lasttick) > 2) 627 nb = 0; 628 lasttick = m; 629 630 if(nb==0){ 631 /* 632 * an extra byte comes for middle button motion. 633 * only two possible values for the extra byte. 634 */ 635 if(c == 0x00 || c == 0x20){ 636 /* an extra byte gets sent for the middle button */ 637 middle = (c&0x20) ? 2 : 0; 638 newbuttons = (mouse.buttons & ~2) | middle; 639 mousetrack(0, 0, newbuttons, TK2MS(MACHP(0)->ticks)); 640 return 0; 641 } 642 } 643 msg[nb] = c; 644 if(++nb == 3){ 645 nb = 0; 646 newbuttons = middle | b[(msg[0]>>4)&3 | (mouseshifted ? 4 : 0)]; 647 x = (msg[0]&0x3)<<14; 648 dx = (x>>8) | msg[1]; 649 x = (msg[0]&0xc)<<12; 650 dy = (x>>8) | msg[2]; 651 mousetrack(dx, dy, newbuttons, TK2MS(MACHP(0)->ticks)); 652 } 653 return 0; 654 } 655 656 /* 657 * microsoft intellimouse 3 buttons + scroll 658 * byte 0 - 1 L R Y7 Y6 X7 X6 659 * byte 1 - 0 X5 X4 X3 X2 X1 X0 660 * byte 2 - 0 Y5 Y4 Y3 Y2 Y1 Y0 661 * byte 3 - 0 0 M % % % % 662 * 663 * %: 0xf => U , 0x1 => D 664 * 665 * L: left 666 * R: right 667 * U: up 668 * D: down 669 */ 670 int 671 m5mouseputc(Queue*, int c) 672 { 673 static uchar msg[3]; 674 static int nb; 675 static ulong lasttick; 676 ulong m; 677 678 /* Resynchronize in stream with timing. */ 679 m = MACHP(0)->ticks; 680 if(TK2SEC(m - lasttick) > 2) 681 nb = 0; 682 lasttick = m; 683 684 msg[nb++] = c & 0x7f; 685 if (nb == 4) { 686 schar dx,dy,newbuttons; 687 dx = msg[1] | (msg[0] & 0x3) << 6; 688 dy = msg[2] | (msg[0] & 0xc) << 4; 689 newbuttons = 690 (msg[0] & 0x10) >> (mouseshifted ? 3 : 2) 691 | (msg[0] & 0x20) >> 5 692 | ( msg[3] == 0x10 ? 0x02 : 693 msg[3] == 0x0f ? ScrollUp : 694 msg[3] == 0x01 ? ScrollDown : 0 ); 695 mousetrack(dx, dy, newbuttons, TK2MS(MACHP(0)->ticks)); 696 nb = 0; 697 } 698 return 0; 699 } 700 701 /* 702 * Logitech 5 byte packed binary mouse format, 8 bit bytes 703 * 704 * shift & right button is the same as middle button (for 2 button mice) 705 */ 706 int 707 mouseputc(Queue*, int c) 708 { 709 static short msg[5]; 710 static int nb; 711 static uchar b[] = {0, 4, 2, 6, 1, 5, 3, 7, 0, 2, 2, 6, 1, 3, 3, 7}; 712 int dx, dy, newbuttons; 713 static ulong lasttick; 714 ulong m; 715 716 /* Resynchronize in stream with timing. */ 717 m = MACHP(0)->ticks; 718 if(TK2SEC(m - lasttick) > 2) 719 nb = 0; 720 lasttick = m; 721 722 if((c&0xF0) == 0x80) 723 nb=0; 724 msg[nb] = c; 725 if(c & 0x80) 726 msg[nb] |= ~0xFF; /* sign extend */ 727 if(++nb == 5){ 728 newbuttons = b[((msg[0]&7)^7) | (mouseshifted ? 8 : 0)]; 729 dx = msg[1]+msg[3]; 730 dy = -(msg[2]+msg[4]); 731 mousetrack(dx, dy, newbuttons, TK2MS(MACHP(0)->ticks)); 732 nb = 0; 733 } 734 return 0; 735 } 736 737 int 738 mousechanged(void*) 739 { 740 return mouse.lastcounter != mouse.counter || 741 mouse.lastresize != mouse.resize; 742 } 743 744 Point 745 mousexy(void) 746 { 747 return mouse.xy; 748 } 749 750 void 751 mouseaccelerate(int x) 752 { 753 mouse.acceleration = x; 754 if(mouse.acceleration < 3) 755 mouse.maxacc = 2; 756 else 757 mouse.maxacc = mouse.acceleration; 758 } 759 760 /* 761 * notify reader that screen has been resized 762 */ 763 void 764 mouseresize(void) 765 { 766 mouse.resize++; 767 wakeup(&mouse.r); 768 } 769 770