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