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