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