xref: /plan9-contrib/sys/src/9/port/devmouse.c (revision 4d44ba9b9ee4246ddbd96c7fcaf0918ab92ab35a)
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 	Rendez	r;
42 	Ref;
43 	QLock;
44 	int	open;
45 	int	inopen;
46 	int	acceleration;
47 	int	maxacc;
48 	Mousestate	queue[16];	/* circular buffer of click events */
49 	int	ri;		/* read index into queue */
50 	int	wi;		/* write index into queue */
51 	uchar	qfull;		/* queue is full */
52 };
53 
54 enum
55 {
56 	CMbuttonmap,
57 	CMscrollswap,
58 	CMswap,
59 	CMwildcard,
60 };
61 
62 static Cmdtab mousectlmsg[] =
63 {
64 	CMbuttonmap,	"buttonmap",	0,
65 	CMscrollswap,	"scrollswap",	0,
66 	CMswap,		"swap",		1,
67 	CMwildcard,	"*",		0,
68 };
69 
70 Mouseinfo	mouse;
71 Cursorinfo	cursor;
72 int		mouseshifted;
73 Cursor		curs;
74 
75 void	Cursortocursor(Cursor*);
76 int	mousechanged(void*);
77 static void mouseclock(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 extern	Memimage*	gscreen;
101 
102 static void
103 mousereset(void)
104 {
105 	if(!conf.monitor)
106 		return;
107 
108 	curs = arrow;
109 	Cursortocursor(&arrow);
110 	/* redraw cursor about 30 times per second */
111 	addclock0link(mouseclock, 33);
112 }
113 
114 static void
115 mouseinit(void)
116 {
117 	if(!conf.monitor)
118 		return;
119 
120 	curs = arrow;
121 	Cursortocursor(&arrow);
122 	cursoron(1);
123 }
124 
125 static Chan*
126 mouseattach(char *spec)
127 {
128 	if(!conf.monitor)
129 		error(Egreg);
130 	return devattach('m', spec);
131 }
132 
133 static Walkqid*
134 mousewalk(Chan *c, Chan *nc, char **name, int nname)
135 {
136 	Walkqid *wq;
137 
138 	wq = devwalk(c, nc, name, nname, mousedir, nelem(mousedir), devgen);
139 	if(wq != nil && wq->clone != c && wq->clone != nil && (wq->clone->qid.type&QTDIR)==0)
140 		incref(&mouse);
141 	return wq;
142 }
143 
144 static int
145 mousestat(Chan *c, uchar *db, int n)
146 {
147 	return devstat(c, db, n, mousedir, nelem(mousedir), devgen);
148 }
149 
150 static Chan*
151 mouseopen(Chan *c, int omode)
152 {
153 	switch((ulong)c->qid.path){
154 	case Qdir:
155 		if(omode != OREAD)
156 			error(Eperm);
157 		break;
158 	case Qmouse:
159 		lock(&mouse);
160 		if(mouse.open){
161 			unlock(&mouse);
162 			error(Einuse);
163 		}
164 		mouse.open = 1;
165 		mouse.ref++;
166 		unlock(&mouse);
167 		break;
168 	case Qmousein:
169 		if(!iseve())
170 			error(Eperm);
171 		lock(&mouse);
172 		if(mouse.inopen){
173 			unlock(&mouse);
174 			error(Einuse);
175 		}
176 		mouse.inopen = 1;
177 		unlock(&mouse);
178 		break;
179 	default:
180 		incref(&mouse);
181 	}
182 	c->mode = openmode(omode);
183 	c->flag |= COPEN;
184 	c->offset = 0;
185 	return c;
186 }
187 
188 static void
189 mousecreate(Chan*, char*, int, ulong)
190 {
191 	if(!conf.monitor)
192 		error(Egreg);
193 	error(Eperm);
194 }
195 
196 static void
197 mouseclose(Chan *c)
198 {
199 	if((c->qid.type&QTDIR)==0 && (c->flag&COPEN)){
200 		lock(&mouse);
201 		if(c->qid.path == Qmouse)
202 			mouse.open = 0;
203 		else if(c->qid.path == Qmousein){
204 			mouse.inopen = 0;
205 			unlock(&mouse);
206 			return;
207 		}
208 		if(--mouse.ref == 0){
209 			cursoroff(1);
210 			curs = arrow;
211 			Cursortocursor(&arrow);
212 			cursoron(1);
213 		}
214 		unlock(&mouse);
215 	}
216 }
217 
218 
219 static long
220 mouseread(Chan *c, void *va, long n, vlong off)
221 {
222 	char buf[1+4*12+1];
223 	uchar *p;
224 	static int map[8] = {0, 4, 2, 6, 1, 5, 3, 7 };
225 	ulong offset = off;
226 	Mousestate m;
227 	int b;
228 
229 	p = va;
230 	switch((ulong)c->qid.path){
231 	case Qdir:
232 		return devdirread(c, va, n, mousedir, nelem(mousedir), devgen);
233 
234 	case Qcursor:
235 		if(offset != 0)
236 			return 0;
237 		if(n < 2*4+2*2*16)
238 			error(Eshort);
239 		n = 2*4+2*2*16;
240 		lock(&cursor);
241 		BPLONG(p+0, curs.offset.x);
242 		BPLONG(p+4, curs.offset.y);
243 		memmove(p+8, curs.clr, 2*16);
244 		memmove(p+40, curs.set, 2*16);
245 		unlock(&cursor);
246 		return n;
247 
248 	case Qmouse:
249 		while(mousechanged(0) == 0)
250 			sleep(&mouse.r, mousechanged, 0);
251 
252 		mouse.qfull = 0;
253 
254 		/*
255 		 * No lock of the indicies is necessary here, because ri is only
256 		 * updated by us, and there is only one mouse reader
257 		 * at a time.  I suppose that more than one process
258 		 * could try to read the fd at one time, but such behavior
259 		 * is degenerate and already violates the calling
260 		 * conventions for sleep above.
261 		 */
262 		if(mouse.ri != mouse.wi) {
263 			m = mouse.queue[mouse.ri];
264 			if(++mouse.ri == nelem(mouse.queue))
265 				mouse.ri = 0;
266 		} else {
267 			while(!canlock(&cursor))
268 				tsleep(&up->sleep, return0, 0, TK2MS(1));
269 
270 			m = mouse.Mousestate;
271 			unlock(&cursor);
272 		}
273 
274 		b = buttonmap[m.buttons&7];
275 		/* put buttons 4 and 5 back in */
276 		b |= m.buttons & (3<<3);
277 		if (scrollswap)
278 			if (b == 8)
279 				b = 16;
280 			else if (b == 16)
281 				b = 8;
282 		sprint(buf, "m%11d %11d %11d %11lud ",
283 			m.xy.x, m.xy.y,
284 			b,
285 			m.msec);
286 		mouse.lastcounter = m.counter;
287 		if(n > 1+4*12)
288 			n = 1+4*12;
289 		memmove(va, buf, n);
290 		return n;
291 	}
292 	return 0;
293 }
294 
295 static void
296 setbuttonmap(char* map)
297 {
298 	int i, x, one, two, three;
299 
300 	one = two = three = 0;
301 	for(i = 0; i < 3; i++){
302 		if(map[i] == 0)
303 			error(Ebadarg);
304 		if(map[i] == '1'){
305 			if(one)
306 				error(Ebadarg);
307 			one = 1<<i;
308 		}
309 		else if(map[i] == '2'){
310 			if(two)
311 				error(Ebadarg);
312 			two = 1<<i;
313 		}
314 		else if(map[i] == '3'){
315 			if(three)
316 				error(Ebadarg);
317 			three = 1<<i;
318 		}
319 		else
320 			error(Ebadarg);
321 	}
322 	if(map[i])
323 		error(Ebadarg);
324 
325 	memset(buttonmap, 0, 8);
326 	for(i = 0; i < 8; i++){
327 		x = 0;
328 		if(i & 1)
329 			x |= one;
330 		if(i & 2)
331 			x |= two;
332 		if(i & 4)
333 			x |= three;
334 		buttonmap[x] = i;
335 	}
336 }
337 
338 static long
339 mousewrite(Chan *c, void *va, long n, vlong)
340 {
341 	char *p;
342 	Point pt;
343 	Cmdbuf *cb;
344 	Cmdtab *ct;
345 	char buf[64];
346 	int b, msec;
347 
348 	p = va;
349 	switch((ulong)c->qid.path){
350 	case Qdir:
351 		error(Eisdir);
352 
353 	case Qcursor:
354 		cursoroff(1);
355 		if(n < 2*4+2*2*16){
356 			curs = arrow;
357 			Cursortocursor(&arrow);
358 		}else{
359 			n = 2*4+2*2*16;
360 			curs.offset.x = BGLONG(p+0);
361 			curs.offset.y = BGLONG(p+4);
362 			memmove(curs.clr, p+8, 2*16);
363 			memmove(curs.set, p+40, 2*16);
364 			Cursortocursor(&curs);
365 		}
366 		qlock(&mouse);
367 		mouse.redraw = 1;
368 		mouseclock();
369 		qunlock(&mouse);
370 		cursoron(1);
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 CMwildcard:
403 			mousectl(cb);
404 			break;
405 		}
406 
407 		free(cb);
408 		poperror();
409 		return n;
410 
411 	case Qmousein:
412 		if(n > sizeof buf-1)
413 			n = sizeof buf -1;
414 		memmove(buf, va, n);
415 		buf[n] = 0;
416 		p = 0;
417 		pt.x = strtol(buf+1, &p, 0);
418 		if(p == 0)
419 			error(Eshort);
420 		pt.y = strtol(p, &p, 0);
421 		if(p == 0)
422 			error(Eshort);
423 		b = strtol(p, &p, 0);
424 		msec = strtol(p, &p, 0);
425 		if(msec == 0)
426 			msec = TK2MS(MACHP(0)->ticks);
427 		mousetrack(pt.x, pt.y, b, msec);
428 		return n;
429 
430 	case Qmouse:
431 		if(n > sizeof buf-1)
432 			n = sizeof buf -1;
433 		memmove(buf, va, n);
434 		buf[n] = 0;
435 		p = 0;
436 		pt.x = strtoul(buf+1, &p, 0);
437 		if(p == 0)
438 			error(Eshort);
439 		pt.y = strtoul(p, 0, 0);
440 		qlock(&mouse);
441 		if(ptinrect(pt, gscreen->r)){
442 			mouse.xy = pt;
443 			mouse.redraw = 1;
444 			mouse.track = 1;
445 			mouseclock();
446 		}
447 		qunlock(&mouse);
448 		return n;
449 	}
450 
451 	error(Egreg);
452 	return -1;
453 }
454 
455 Dev mousedevtab = {
456 	'm',
457 	"mouse",
458 
459 	mousereset,
460 	mouseinit,
461 	devshutdown,
462 	mouseattach,
463 	mousewalk,
464 	mousestat,
465 	mouseopen,
466 	mousecreate,
467 	mouseclose,
468 	mouseread,
469 	devbread,
470 	mousewrite,
471 	devbwrite,
472 	devremove,
473 	devwstat,
474 };
475 
476 void
477 Cursortocursor(Cursor *c)
478 {
479 	lock(&cursor);
480 	memmove(&cursor.Cursor, c, sizeof(Cursor));
481 	setcursor(c);
482 	unlock(&cursor);
483 }
484 
485 
486 /*
487  *  called by the clock routine to redraw the cursor
488  */
489 static void
490 mouseclock(void)
491 {
492 	if(mouse.track){
493 		mousetrack(mouse.dx, mouse.dy, mouse.buttons, TK2MS(MACHP(0)->ticks));
494 		mouse.track = 0;
495 		mouse.dx = 0;
496 		mouse.dy = 0;
497 	}
498 	if(mouse.redraw && canlock(&cursor)){
499 		mouse.redraw = 0;
500 		cursoroff(0);
501 		mouse.redraw = cursoron(0);
502 		unlock(&cursor);
503 	}
504 	drawactive(0);
505 }
506 
507 static int
508 scale(int x)
509 {
510 	int sign = 1;
511 
512 	if(x < 0){
513 		sign = -1;
514 		x = -x;
515 	}
516 	switch(x){
517 	case 0:
518 	case 1:
519 	case 2:
520 	case 3:
521 		break;
522 	case 4:
523 		x = 6 + (mouse.acceleration>>2);
524 		break;
525 	case 5:
526 		x = 9 + (mouse.acceleration>>1);
527 		break;
528 	default:
529 		x *= mouse.maxacc;
530 		break;
531 	}
532 	return sign*x;
533 }
534 
535 /*
536  *  called at interrupt level to update the structure and
537  *  awaken any waiting procs.
538  */
539 void
540 mousetrack(int dx, int dy, int b, int msec)
541 {
542 	int x, y, lastb;
543 
544 	if(gscreen==nil)
545 		return;
546 
547 	if(mouse.acceleration){
548 		dx = scale(dx);
549 		dy = scale(dy);
550 	}
551 	x = mouse.xy.x + dx;
552 	if(x < gscreen->clipr.min.x)
553 		x = gscreen->clipr.min.x;
554 	if(x >= gscreen->clipr.max.x)
555 		x = gscreen->clipr.max.x;
556 	y = mouse.xy.y + dy;
557 	if(y < gscreen->clipr.min.y)
558 		y = gscreen->clipr.min.y;
559 	if(y >= gscreen->clipr.max.y)
560 		y = gscreen->clipr.max.y;
561 
562 	lastb = mouse.buttons;
563 	mouse.xy = Pt(x, y);
564 	mouse.buttons = b;
565 	mouse.redraw = 1;
566 	mouse.counter++;
567 	mouse.msec = msec;
568 
569 	/*
570 	 * if the queue fills, we discard the entire queue and don't
571 	 * queue any more events until a reader polls the mouse.
572 	 */
573 	if(!mouse.qfull && lastb != b) {	/* add to ring */
574 		mouse.queue[mouse.wi] = mouse.Mousestate;
575 		if(++mouse.wi == nelem(mouse.queue))
576 			mouse.wi = 0;
577 		if(mouse.wi == mouse.ri)
578 			mouse.qfull = 1;
579 	}
580 	wakeup(&mouse.r);
581 	drawactive(1);
582 }
583 
584 /*
585  *  microsoft 3 button, 7 bit bytes
586  *
587  *	byte 0 -	1  L  R Y7 Y6 X7 X6
588  *	byte 1 -	0 X5 X4 X3 X2 X1 X0
589  *	byte 2 -	0 Y5 Y4 Y3 Y2 Y1 Y0
590  *	byte 3 -	0  M  x  x  x  x  x	(optional)
591  *
592  *  shift & right button is the same as middle button (for 2 button mice)
593  */
594 int
595 m3mouseputc(Queue*, int c)
596 {
597 	static uchar msg[3];
598 	static int nb;
599 	static int middle;
600 	static uchar b[] = { 0, 4, 1, 5, 0, 2, 1, 3 };
601 	short x;
602 	int dx, dy, newbuttons;
603 
604 	if(nb==0){
605 		/*
606 		 * an extra byte comes for middle button motion.
607 		 * only two possible values for the extra byte.
608 		 */
609 		if(c == 0x00 || c == 0x20){
610 			/* an extra byte gets sent for the middle button */
611 			middle = (c&0x20) ? 2 : 0;
612 			newbuttons = (mouse.buttons & ~2) | middle;
613 			mousetrack(0, 0, newbuttons, TK2MS(MACHP(0)->ticks));
614 			return 0;
615 		}
616 	}
617 	msg[nb] = c;
618 	if(++nb == 3){
619 		nb = 0;
620 		newbuttons = middle | b[(msg[0]>>4)&3 | (mouseshifted ? 4 : 0)];
621 		x = (msg[0]&0x3)<<14;
622 		dx = (x>>8) | msg[1];
623 		x = (msg[0]&0xc)<<12;
624 		dy = (x>>8) | msg[2];
625 		mousetrack(dx, dy, newbuttons, TK2MS(MACHP(0)->ticks));
626 	}
627 	return 0;
628 }
629 
630 /*
631  * microsoft intellimouse 3 buttons + scroll
632  * 	byte 0 -	1  L  R Y7 Y6 X7 X6
633  *	byte 1 -	0 X5 X4 X3 X2 X1 X0
634  *	byte 2 -	0 Y5 Y4 Y3 Y2 Y1 Y0
635  *	byte 3 -	0  0  M  %  %  %  %
636  *
637  *	%: 0xf => U , 0x1 => D
638  *
639  *	L: left
640  *	R: right
641  *	U: up
642  *	D: down
643  */
644 int
645 m5mouseputc(Queue*, int c)
646 {
647 	static uchar msg[3];
648 	static int nb;
649 	msg[nb++] = c & 0x7f;
650 	if (nb == 4) {
651 		schar dx,dy,newbuttons;
652 		dx = msg[1] | (msg[0] & 0x3) << 6;
653 		dy = msg[2] | (msg[0] & 0xc) << 4;
654 		newbuttons =
655 			(msg[0] & 0x10) >> (mouseshifted ? 3 : 2)
656 			| (msg[0] & 0x20) >> 5
657 			| ( msg[3] == 0x10 ? 0x02 :
658 			    msg[3] == 0x0f ? ScrollUp :
659 			    msg[3] == 0x01 ? ScrollDown : 0 );
660 		mousetrack(dx, dy, newbuttons, TK2MS(MACHP(0)->ticks));
661 		nb = 0;
662 	}
663 	return 0;
664 }
665 
666 /*
667  *  Logitech 5 byte packed binary mouse format, 8 bit bytes
668  *
669  *  shift & right button is the same as middle button (for 2 button mice)
670  */
671 int
672 mouseputc(Queue*, int c)
673 {
674 	static short msg[5];
675 	static int nb;
676 	static uchar b[] = {0, 4, 2, 6, 1, 5, 3, 7, 0, 2, 2, 6, 1, 3, 3, 7};
677 	int dx, dy, newbuttons;
678 
679 	if((c&0xF0) == 0x80)
680 		nb=0;
681 	msg[nb] = c;
682 	if(c & 0x80)
683 		msg[nb] |= ~0xFF;	/* sign extend */
684 	if(++nb == 5){
685 		newbuttons = b[((msg[0]&7)^7) | (mouseshifted ? 8 : 0)];
686 		dx = msg[1]+msg[3];
687 		dy = -(msg[2]+msg[4]);
688 		mousetrack(dx, dy, newbuttons, TK2MS(MACHP(0)->ticks));
689 		nb = 0;
690 	}
691 	return 0;
692 }
693 
694 int
695 mousechanged(void*)
696 {
697 	return mouse.lastcounter != mouse.counter;
698 }
699 
700 Point
701 mousexy(void)
702 {
703 	return mouse.xy;
704 }
705 
706 void
707 mouseaccelerate(int x)
708 {
709 	mouse.acceleration = x;
710 	if(mouse.acceleration < 3)
711 		mouse.maxacc = 2;
712 	else
713 		mouse.maxacc = mouse.acceleration;
714 }
715