xref: /plan9/sys/src/cmd/vnc/devmouse.c (revision 7b6fd1d25ebab24ad9019c6b743fc531d6ced642)
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
mousereset(void)73 mousereset(void)
74 {
75 	curs = arrow;
76 	Cursortocursor(&arrow);
77 }
78 
79 static void
mouseinit(void)80 mouseinit(void)
81 {
82 	cursoron(1);
83 }
84 
85 static Chan*
mouseattach(char * spec)86 mouseattach(char *spec)
87 {
88 	return devattach('m', spec);
89 }
90 
91 static Walkqid*
mousewalk(Chan * c,Chan * nc,char ** name,int nname)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
mousestat(Chan * c,uchar * db,int n)103 mousestat(Chan *c, uchar *db, int n)
104 {
105 	return devstat(c, db, n, mousedir, nelem(mousedir), devgen);
106 }
107 
108 static Chan*
mouseopen(Chan * c,int omode)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
mousecreate(Chan *,char *,int,ulong)136 mousecreate(Chan*, char*, int, ulong)
137 {
138 	error(Eperm);
139 }
140 
141 static void
mouseclose(Chan * c)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
mouseread(Chan * c,void * va,long n,vlong off)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
setbuttonmap(char * map)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
mousewrite(Chan * c,void * va,long n,vlong)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
Cursortocursor(Cursor * c)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
scale(int x)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
mouseclock(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
mousetrack(int x,int y,int b,int msec)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
mousechanged(void *)428 mousechanged(void*)
429 {
430 	return mouse.lastcounter != mouse.counter;
431 }
432 
433 Point
mousexy(void)434 mousexy(void)
435 {
436 	return mouse.xy;
437 }
438 
439 void
mouseaccelerate(int x)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 }
448