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