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