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