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