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