]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/vnc/devmouse.c
fix utf and rune handling in preparation for 32bit runes
[plan9front.git] / sys / src / cmd / vnc / devmouse.c
1 #include        <u.h>
2 #include        <libc.h>
3 #include        "compat.h"
4 #include        "error.h"
5
6 #define Image   IMAGE
7 #include        <draw.h>
8 #include        <memdraw.h>
9 #include        <cursor.h>
10 #include        "screen.h"
11
12 typedef struct Mouseinfo        Mouseinfo;
13 typedef struct Mousestate       Mousestate;
14
15 struct Mousestate
16 {
17         Point   xy;                     /* mouse.xy */
18         int     buttons;                /* mouse.buttons */
19         ulong   counter;        /* increments every update */
20         ulong   msec;   /* time of last event */
21 };
22
23 struct Mouseinfo
24 {
25         Mousestate;
26         int     dx;
27         int     dy;
28         int     track;          /* dx & dy updated */
29         int     redraw;         /* update cursor on screen */
30         ulong   lastcounter;    /* value when /dev/mouse read */
31         Rendez  r;
32         Ref;
33         QLock;
34         int     open;
35         int     acceleration;
36         int     maxacc;
37         Mousestate      queue[16];      /* circular buffer of click events */
38         int     ri;     /* read index into queue */
39         int     wi;     /* write index into queue */
40         uchar   qfull;  /* queue is full */
41 };
42
43 Mouseinfo       mouse;
44 Cursorinfo      cursor;
45 int             mouseshifted;
46 Cursor          curs;
47
48 void    Cursortocursor(Cursor*);
49 int     mousechanged(void*);
50 static void mouseclock(void);
51
52 enum{
53         Qdir,
54         Qcursor,
55         Qmouse,
56 };
57
58 static Dirtab mousedir[]={
59         ".",    {Qdir, 0, QTDIR},       0,                      DMDIR|0555,
60         "cursor",       {Qcursor},      0,                      0666,
61         "mouse",        {Qmouse},       0,                      0666,
62 };
63
64 static uchar buttonmap[8] = {
65         0, 1, 2, 3, 4, 5, 6, 7,
66 };
67 static int mouseswap;
68
69 extern  Memimage*       gscreen;
70 extern  void mousewarpnote(Point);
71
72 static void
73 mousereset(void)
74 {
75         curs = arrow;
76         Cursortocursor(&arrow);
77 }
78
79 static void
80 mouseinit(void)
81 {
82         cursoron(1);
83 }
84
85 static Chan*
86 mouseattach(char *spec)
87 {
88         return devattach('m', spec);
89 }
90
91 static Walkqid*
92 mousewalk(Chan *c, Chan *nc, char **name, int nname)
93 {
94         Walkqid *wq;
95
96         wq = devwalk(c, nc, name, nname, mousedir, nelem(mousedir), devgen);
97         if(wq != nil && wq->clone != c && (wq->clone->qid.type&QTDIR)==0)
98                 incref(&mouse);
99         return wq;
100 }
101
102 static int
103 mousestat(Chan *c, uchar *db, int n)
104 {
105         return devstat(c, db, n, mousedir, nelem(mousedir), devgen);
106 }
107
108 static Chan*
109 mouseopen(Chan *c, int omode)
110 {
111         switch((ulong)c->qid.path){
112         case Qdir:
113                 if(omode != OREAD)
114                         error(Eperm);
115                 break;
116         case Qmouse:
117                 lock(&mouse);
118                 if(mouse.open){
119                         unlock(&mouse);
120                         error(Einuse);
121                 }
122                 mouse.open = 1;
123                 mouse.ref++;
124                 unlock(&mouse);
125                 break;
126         default:
127                 incref(&mouse);
128         }
129         c->mode = openmode(omode);
130         c->flag |= COPEN;
131         c->offset = 0;
132         return c;
133 }
134
135 static void
136 mousecreate(Chan*, char*, int, ulong)
137 {
138         error(Eperm);
139 }
140
141 static void
142 mouseclose(Chan *c)
143 {
144         if((c->qid.type&QTDIR)==0 && (c->flag&COPEN)){
145                 lock(&mouse);
146                 if(c->qid.path == Qmouse)
147                         mouse.open = 0;
148                 if(--mouse.ref == 0){
149                         cursoroff(1);
150                         curs = arrow;
151                         Cursortocursor(&arrow);
152                         cursoron(1);
153                 }
154                 unlock(&mouse);
155         }
156 }
157
158
159 static long
160 mouseread(Chan *c, void *va, long n, vlong off)
161 {
162         char buf[4*12+1];
163         uchar *p;
164         static int map[8] = {0, 4, 2, 6, 1, 5, 3, 7 };
165         ulong offset = off;
166         Mousestate m;
167         int b;
168
169         p = va;
170         switch((ulong)c->qid.path){
171         case Qdir:
172                 return devdirread(c, va, n, mousedir, nelem(mousedir), devgen);
173
174         case Qcursor:
175                 if(offset != 0)
176                         return 0;
177                 if(n < 2*4+2*2*16)
178                         error(Eshort);
179                 n = 2*4+2*2*16;
180                 lock(&cursor);
181                 BPLONG(p+0, curs.offset.x);
182                 BPLONG(p+4, curs.offset.y);
183                 memmove(p+8, curs.clr, 2*16);
184                 memmove(p+40, curs.set, 2*16);
185                 unlock(&cursor);
186                 return n;
187
188         case Qmouse:
189                 while(mousechanged(0) == 0)
190                         rendsleep(&mouse.r, mousechanged, 0);
191
192                 mouse.qfull = 0;
193
194                 /*
195                  * No lock of the indicies is necessary here, because ri is only
196                  * updated by us, and there is only one mouse reader
197                  * at a time.  I suppose that more than one process
198                  * could try to read the fd at one time, but such behavior
199                  * is degenerate and already violates the calling
200                  * conventions for sleep above.
201                  */
202                 if(mouse.ri != mouse.wi){
203                         m = mouse.queue[mouse.ri];
204                         if(++mouse.ri == nelem(mouse.queue))
205                                 mouse.ri = 0;
206                 } else {
207                         lock(&cursor);
208         
209                         m = mouse.Mousestate;
210                         unlock(&cursor);
211                 }
212
213                 b = buttonmap[m.buttons&7];
214                 /* put buttons 4 and 5 back in */
215                 b |= m.buttons & (3<<3);
216                 sprint(buf, "m%11d %11d %11d %11lud",
217                         m.xy.x, m.xy.y,
218                         b,
219                         m.msec);
220                 mouse.lastcounter = m.counter;
221                 if(n > 1+4*12)
222                         n = 1+4*12;
223                 memmove(va, buf, n);
224                 return n;
225         }
226         return 0;
227 }
228
229 static void
230 setbuttonmap(char* map)
231 {
232         int i, x, one, two, three;
233
234         one = two = three = 0;
235         for(i = 0; i < 3; i++){
236                 if(map[i] == 0)
237                         error(Ebadarg);
238                 if(map[i] == '1'){
239                         if(one)
240                                 error(Ebadarg);
241                         one = 1<<i;
242                 }
243                 else if(map[i] == '2'){
244                         if(two)
245                                 error(Ebadarg);
246                         two = 1<<i;
247                 }
248                 else if(map[i] == '3'){
249                         if(three)
250                                 error(Ebadarg);
251                         three = 1<<i;
252                 }
253                 else
254                         error(Ebadarg);
255         }
256         if(map[i])
257                 error(Ebadarg);
258
259         memset(buttonmap, 0, 8);
260         for(i = 0; i < 8; i++){
261                 x = 0;
262                 if(i & 1)
263                         x |= one;
264                 if(i & 2)
265                         x |= two;
266                 if(i & 4)
267                         x |= three;
268                 buttonmap[x] = i;
269         }
270 }
271
272 static long
273 mousewrite(Chan *c, void *va, long n, vlong)
274 {
275         char *p;
276         Point pt;
277         char buf[64];
278
279         p = va;
280         switch((ulong)c->qid.path){
281         case Qdir:
282                 error(Eisdir);
283
284         case Qcursor:
285                 cursoroff(1);
286                 if(n < 2*4+2*2*16){
287                         curs = arrow;
288                         Cursortocursor(&arrow);
289                 }else{
290                         n = 2*4+2*2*16;
291                         curs.offset.x = BGLONG(p+0);
292                         curs.offset.y = BGLONG(p+4);
293                         memmove(curs.clr, p+8, 2*16);
294                         memmove(curs.set, p+40, 2*16);
295                         Cursortocursor(&curs);
296                 }
297                 qlock(&mouse);
298                 mouse.redraw = 1;
299                 mouseclock();
300                 qunlock(&mouse);
301                 cursoron(1);
302                 return n;
303
304         case Qmouse:
305                 if(n > sizeof buf-1)
306                         n = sizeof buf -1;
307                 memmove(buf, va, n);
308                 buf[n] = 0;
309                 p = 0;
310                 pt.x = strtoul(buf+1, &p, 0);
311                 if(p == 0)
312                         error(Eshort);
313                 pt.y = strtoul(p, 0, 0);
314                 qlock(&mouse);
315                 if(ptinrect(pt, gscreen->r)){
316                         mousetrack(pt.x, pt.y, mouse.buttons, nsec()/(1000*1000LL));
317                         mousewarpnote(pt);
318                 }
319                 qunlock(&mouse);
320                 return n;
321         }
322
323         error(Egreg);
324         return -1;
325 }
326
327 Dev mousedevtab = {
328         'm',
329         "mouse",
330
331         mousereset,
332         mouseinit,
333         mouseattach,
334         mousewalk,
335         mousestat,
336         mouseopen,
337         mousecreate,
338         mouseclose,
339         mouseread,
340         devbread,
341         mousewrite,
342         devbwrite,
343         devremove,
344         devwstat,
345 };
346
347 void
348 Cursortocursor(Cursor *c)
349 {
350         lock(&cursor);
351         memmove(&cursor.Cursor, c, sizeof(Cursor));
352         setcursor(c);
353         unlock(&cursor);
354 }
355
356 static int
357 scale(int x)
358 {
359         int sign = 1;
360
361         if(x < 0){
362                 sign = -1;
363                 x = -x;
364         }
365         switch(x){
366         case 0:
367         case 1:
368         case 2:
369         case 3:
370                 break;
371         case 4:
372                 x = 6 + (mouse.acceleration>>2);
373                 break;
374         case 5:
375                 x = 9 + (mouse.acceleration>>1);
376                 break;
377         default:
378                 x *= mouse.maxacc;
379                 break;
380         }
381         return sign*x;
382 }
383
384 static void
385 mouseclock(void)
386 {
387         lock(&cursor);
388         if(mouse.redraw){
389                 mouse.redraw = 0;
390                 cursoroff(0);
391                 mouse.redraw = cursoron(0);
392         }
393         unlock(&cursor);
394 }
395
396 /*
397  *  called at interrupt level to update the structure and
398  *  awaken any waiting procs.
399  */
400 void
401 mousetrack(int x, int y, int b, int msec)
402 {
403         int lastb;
404
405         lastb = mouse.buttons;
406         mouse.xy = Pt(x, y);
407         mouse.buttons = b;
408         mouse.redraw = 1;
409         mouse.counter++;
410         mouse.msec = msec;
411
412         /*
413          * if the queue fills, we discard the entire queue and don't
414          * queue any more events until a reader polls the mouse.
415          */
416         if(!mouse.qfull && lastb != b){ /* add to ring */
417                 mouse.queue[mouse.wi] = mouse.Mousestate;
418                 if(++mouse.wi == nelem(mouse.queue))
419                         mouse.wi = 0;
420                 if(mouse.wi == mouse.ri)
421                         mouse.qfull = 1;
422         }
423         rendwakeup(&mouse.r);
424         mouseclock();
425 }
426
427 int
428 mousechanged(void*)
429 {
430         return mouse.lastcounter != mouse.counter;
431 }
432
433 Point
434 mousexy(void)
435 {
436         return mouse.xy;
437 }
438
439 void
440 mouseaccelerate(int x)
441 {
442         mouse.acceleration = x;
443         if(mouse.acceleration < 3)
444                 mouse.maxacc = 2;
445         else
446                 mouse.maxacc = mouse.acceleration;
447 }