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