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