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