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