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