xref: /plan9/sys/src/9/pc/mouse.c (revision 8cde348a8db0bd4f8cfcb77e5206af1e21fb4b80)
17dd7cddfSDavid du Colombier #include "u.h"
27dd7cddfSDavid du Colombier #include "../port/lib.h"
37dd7cddfSDavid du Colombier #include "mem.h"
47dd7cddfSDavid du Colombier #include "dat.h"
57dd7cddfSDavid du Colombier #include "fns.h"
67dd7cddfSDavid du Colombier #include "../port/error.h"
77dd7cddfSDavid du Colombier #include "io.h"
87dd7cddfSDavid du Colombier 
97dd7cddfSDavid du Colombier #define	Image	IMAGE
107dd7cddfSDavid du Colombier #include <draw.h>
117dd7cddfSDavid du Colombier #include <memdraw.h>
127dd7cddfSDavid du Colombier #include <cursor.h>
137dd7cddfSDavid du Colombier #include "screen.h"
147dd7cddfSDavid du Colombier 
157dd7cddfSDavid du Colombier /*
167dd7cddfSDavid du Colombier  *  mouse types
177dd7cddfSDavid du Colombier  */
187dd7cddfSDavid du Colombier enum
197dd7cddfSDavid du Colombier {
207dd7cddfSDavid du Colombier 	Mouseother=	0,
217dd7cddfSDavid du Colombier 	Mouseserial=	1,
227dd7cddfSDavid du Colombier 	MousePS2=	2,
237dd7cddfSDavid du Colombier };
24b7b24591SDavid du Colombier 
25*8cde348aSDavid du Colombier extern int mouseshifted;
26*8cde348aSDavid du Colombier 
27b7b24591SDavid du Colombier static QLock mousectlqlock;
287dd7cddfSDavid du Colombier static int mousetype;
2959cc4ca5SDavid du Colombier static int intellimouse;
3059cc4ca5SDavid du Colombier static int packetsize;
3159cc4ca5SDavid du Colombier static int resolution;
3259cc4ca5SDavid du Colombier static int accelerated;
339a747e4fSDavid du Colombier static int mousehwaccel;
34aa46331bSDavid du Colombier static char mouseport[5];
359a747e4fSDavid du Colombier 
369a747e4fSDavid du Colombier enum
379a747e4fSDavid du Colombier {
389a747e4fSDavid du Colombier 	CMaccelerated,
399a747e4fSDavid du Colombier 	CMhwaccel,
409a747e4fSDavid du Colombier 	CMintellimouse,
419a747e4fSDavid du Colombier 	CMlinear,
429a747e4fSDavid du Colombier 	CMps2,
439a747e4fSDavid du Colombier 	CMps2intellimouse,
449a747e4fSDavid du Colombier 	CMres,
459a747e4fSDavid du Colombier 	CMreset,
469a747e4fSDavid du Colombier 	CMserial,
479a747e4fSDavid du Colombier };
489a747e4fSDavid du Colombier 
499a747e4fSDavid du Colombier static Cmdtab mousectlmsg[] =
509a747e4fSDavid du Colombier {
519a747e4fSDavid du Colombier 	CMaccelerated,		"accelerated",		0,
529a747e4fSDavid du Colombier 	CMhwaccel,		"hwaccel",		2,
539a747e4fSDavid du Colombier 	CMintellimouse,		"intellimouse",		1,
549a747e4fSDavid du Colombier 	CMlinear,		"linear",		1,
559a747e4fSDavid du Colombier 	CMps2,			"ps2",			1,
569a747e4fSDavid du Colombier 	CMps2intellimouse,	"ps2intellimouse",	1,
579a747e4fSDavid du Colombier 	CMres,			"res",			0,
589a747e4fSDavid du Colombier 	CMreset,		"reset",		1,
599a747e4fSDavid du Colombier 	CMserial,		"serial",		0,
609a747e4fSDavid du Colombier };
617dd7cddfSDavid du Colombier 
627dd7cddfSDavid du Colombier /*
637dd7cddfSDavid du Colombier  *  ps/2 mouse message is three bytes
647dd7cddfSDavid du Colombier  *
657dd7cddfSDavid du Colombier  *	byte 0 -	0 0 SDY SDX 1 M R L
667dd7cddfSDavid du Colombier  *	byte 1 -	DX
677dd7cddfSDavid du Colombier  *	byte 2 -	DY
687dd7cddfSDavid du Colombier  *
6959cc4ca5SDavid du Colombier  *  shift & right button is the same as middle button
7059cc4ca5SDavid du Colombier  *
7159cc4ca5SDavid du Colombier  * Intellimouse and AccuPoint with extra buttons deliver
7259cc4ca5SDavid du Colombier  *	byte 3 -	00 or 01 or FF according to extra button state.
7359cc4ca5SDavid du Colombier  * extra buttons are mapped in this code to buttons 4 and 5.
7459cc4ca5SDavid du Colombier  * AccuPoint generates repeated events for these buttons;
7559cc4ca5SDavid du Colombier *  it and Intellimouse generate 'down' events only, so
7659cc4ca5SDavid du Colombier  * user-level code is required to generate button 'up' events
7759cc4ca5SDavid du Colombier  * if they are needed by the application.
7859cc4ca5SDavid du Colombier  * Also on laptops with AccuPoint AND external mouse, the
7959cc4ca5SDavid du Colombier  * controller may deliver 3 or 4 bytes according to the type
8059cc4ca5SDavid du Colombier  * of the external mouse; code must adapt.
819a747e4fSDavid du Colombier  *
829a747e4fSDavid du Colombier  * On the NEC Versa series (and perhaps others?) we seem to
839a747e4fSDavid du Colombier  * lose a byte from the packet every once in a while, which
849a747e4fSDavid du Colombier  * means we lose where we are in the instruction stream.
859a747e4fSDavid du Colombier  * To resynchronize, if we get a byte more than two seconds
869a747e4fSDavid du Colombier  * after the previous byte, we assume it's the first in a packet.
877dd7cddfSDavid du Colombier  */
887dd7cddfSDavid du Colombier static void
ps2mouseputc(int c,int shift)897dd7cddfSDavid du Colombier ps2mouseputc(int c, int shift)
907dd7cddfSDavid du Colombier {
9159cc4ca5SDavid du Colombier 	static short msg[4];
927dd7cddfSDavid du Colombier 	static int nb;
9359cc4ca5SDavid du Colombier 	static uchar b[] = {0, 1, 4, 5, 2, 3, 6, 7, 0, 1, 2, 3, 2, 3, 6, 7 };
949a747e4fSDavid du Colombier 	static ulong lasttick;
959a747e4fSDavid du Colombier 	ulong m;
967dd7cddfSDavid du Colombier 	int buttons, dx, dy;
977dd7cddfSDavid du Colombier 
987dd7cddfSDavid du Colombier 	/*
99*8cde348aSDavid du Colombier 	 * non-ps2 keyboards might not set shift
100*8cde348aSDavid du Colombier 	 * but still set mouseshifted.
101*8cde348aSDavid du Colombier 	 */
102*8cde348aSDavid du Colombier 	shift |= mouseshifted;
103*8cde348aSDavid du Colombier 	/*
1049a747e4fSDavid du Colombier 	 * Resynchronize in stream with timing; see comment above.
1059a747e4fSDavid du Colombier 	 */
1069a747e4fSDavid du Colombier 	m = MACHP(0)->ticks;
1079a747e4fSDavid du Colombier 	if(TK2SEC(m - lasttick) > 2)
1089a747e4fSDavid du Colombier 		nb = 0;
1099a747e4fSDavid du Colombier 	lasttick = m;
1109a747e4fSDavid du Colombier 
1119a747e4fSDavid du Colombier 	/*
1127dd7cddfSDavid du Colombier 	 *  check byte 0 for consistency
1137dd7cddfSDavid du Colombier 	 */
1147dd7cddfSDavid du Colombier 	if(nb==0 && (c&0xc8)!=0x08)
11559cc4ca5SDavid du Colombier 		if(intellimouse && (c==0x00 || c==0x01 || c==0xFF)){
11659cc4ca5SDavid du Colombier 			/* last byte of 4-byte packet */
11759cc4ca5SDavid du Colombier 			packetsize = 4;
1187dd7cddfSDavid du Colombier 			return;
11959cc4ca5SDavid du Colombier 		}
1207dd7cddfSDavid du Colombier 
1217dd7cddfSDavid du Colombier 	msg[nb] = c;
12259cc4ca5SDavid du Colombier 	if(++nb == packetsize){
1237dd7cddfSDavid du Colombier 		nb = 0;
1247dd7cddfSDavid du Colombier 		if(msg[0] & 0x10)
1257dd7cddfSDavid du Colombier 			msg[1] |= 0xFF00;
1267dd7cddfSDavid du Colombier 		if(msg[0] & 0x20)
1277dd7cddfSDavid du Colombier 			msg[2] |= 0xFF00;
1287dd7cddfSDavid du Colombier 
1297dd7cddfSDavid du Colombier 		buttons = b[(msg[0]&7) | (shift ? 8 : 0)];
13059cc4ca5SDavid du Colombier 		if(intellimouse && packetsize==4){
13159cc4ca5SDavid du Colombier 			if((msg[3]&0xc8) == 0x08){
13259cc4ca5SDavid du Colombier 				/* first byte of 3-byte packet */
13359cc4ca5SDavid du Colombier 				packetsize = 3;
13459cc4ca5SDavid du Colombier 				msg[0] = msg[3];
13559cc4ca5SDavid du Colombier 				nb = 1;
13659cc4ca5SDavid du Colombier 				/* fall through to emit previous packet */
13759cc4ca5SDavid du Colombier 			}else{
1389847521cSDavid du Colombier 				/* The AccuPoint on the Toshiba 34[48]0CT
1399847521cSDavid du Colombier 				 * encodes extra buttons as 4 and 5. They repeat
1409847521cSDavid du Colombier 				 * and don't release, however, so user-level
1419847521cSDavid du Colombier 				 * timing code is required. Furthermore,
1429847521cSDavid du Colombier 				 * intellimice with 3buttons + scroll give a
1439847521cSDavid du Colombier 				 * two's complement number in the lower 4 bits
1449847521cSDavid du Colombier 				 * (bit 4 is sign extension) that describes
1459847521cSDavid du Colombier 				 * the amount the scroll wheel has moved during
1469847521cSDavid du Colombier 				 * the last sample. Here we use only the sign to
1479847521cSDavid du Colombier 				 * decide whether the wheel is moving up or down
1489847521cSDavid du Colombier 				 * and generate a single button 4 or 5 click
1499847521cSDavid du Colombier 				 * accordingly.
1509847521cSDavid du Colombier 				 */
1519847521cSDavid du Colombier 				if((msg[3] >> 3) & 1)
15259cc4ca5SDavid du Colombier 					buttons |= 1<<3;
1539847521cSDavid du Colombier 				else if(msg[3] & 0x7)
15459cc4ca5SDavid du Colombier 					buttons |= 1<<4;
15559cc4ca5SDavid du Colombier 			}
15659cc4ca5SDavid du Colombier 		}
1577dd7cddfSDavid du Colombier 		dx = msg[1];
1587dd7cddfSDavid du Colombier 		dy = -msg[2];
1599a747e4fSDavid du Colombier 		mousetrack(dx, dy, buttons, TK2MS(MACHP(0)->ticks));
1607dd7cddfSDavid du Colombier 	}
1617dd7cddfSDavid du Colombier 	return;
1627dd7cddfSDavid du Colombier }
1637dd7cddfSDavid du Colombier 
1647dd7cddfSDavid du Colombier /*
1657dd7cddfSDavid du Colombier  *  set up a ps2 mouse
1667dd7cddfSDavid du Colombier  */
1677dd7cddfSDavid du Colombier static void
ps2mouse(void)1687dd7cddfSDavid du Colombier ps2mouse(void)
1697dd7cddfSDavid du Colombier {
1707dd7cddfSDavid du Colombier 	if(mousetype == MousePS2)
1717dd7cddfSDavid du Colombier 		return;
1727dd7cddfSDavid du Colombier 
1737dd7cddfSDavid du Colombier 	i8042auxenable(ps2mouseputc);
1747dd7cddfSDavid du Colombier 	/* make mouse streaming, enabled */
1757dd7cddfSDavid du Colombier 	i8042auxcmd(0xEA);
1767dd7cddfSDavid du Colombier 	i8042auxcmd(0xF4);
1777dd7cddfSDavid du Colombier 
1787dd7cddfSDavid du Colombier 	mousetype = MousePS2;
17959cc4ca5SDavid du Colombier 	packetsize = 3;
1809a747e4fSDavid du Colombier 	mousehwaccel = 1;
1817dd7cddfSDavid du Colombier }
1827dd7cddfSDavid du Colombier 
1839a747e4fSDavid du Colombier /*
1849a747e4fSDavid du Colombier  * The PS/2 Trackpoint multiplexor on the IBM Thinkpad T23 ignores
1859a747e4fSDavid du Colombier  * acceleration commands.  It is supposed to pass them on
1869a747e4fSDavid du Colombier  * to the attached device, but my Logitech mouse is simply
1879a747e4fSDavid du Colombier  * not behaving any differently.  For such devices, we allow
1889a747e4fSDavid du Colombier  * the user to use "hwaccel off" to tell us to back off to
1899a747e4fSDavid du Colombier  * software acceleration even if we're using the PS/2 port.
1909a747e4fSDavid du Colombier  * (Serial mice are always software accelerated.)
1919a747e4fSDavid du Colombier  * For more information on the Thinkpad multiplexor, see
1929a747e4fSDavid du Colombier  * http://wwwcssrv.almaden.ibm.com/trackpoint/
1939a747e4fSDavid du Colombier  */
1947dd7cddfSDavid du Colombier static void
setaccelerated(int x)1957dd7cddfSDavid du Colombier setaccelerated(int x)
1967dd7cddfSDavid du Colombier {
1977dd7cddfSDavid du Colombier 	accelerated = x;
1989a747e4fSDavid du Colombier 	if(mousehwaccel){
1997dd7cddfSDavid du Colombier 		switch(mousetype){
2007dd7cddfSDavid du Colombier 		case MousePS2:
2017dd7cddfSDavid du Colombier 			i8042auxcmd(0xE7);
2029a747e4fSDavid du Colombier 			return;
2037dd7cddfSDavid du Colombier 		}
2047dd7cddfSDavid du Colombier 	}
2059a747e4fSDavid du Colombier 	mouseaccelerate(x);
2069a747e4fSDavid du Colombier }
2077dd7cddfSDavid du Colombier 
2087dd7cddfSDavid du Colombier static void
setlinear(void)2097dd7cddfSDavid du Colombier setlinear(void)
2107dd7cddfSDavid du Colombier {
2117dd7cddfSDavid du Colombier 	accelerated = 0;
2129a747e4fSDavid du Colombier 	if(mousehwaccel){
2137dd7cddfSDavid du Colombier 		switch(mousetype){
2147dd7cddfSDavid du Colombier 		case MousePS2:
2157dd7cddfSDavid du Colombier 			i8042auxcmd(0xE6);
2169a747e4fSDavid du Colombier 			return;
2177dd7cddfSDavid du Colombier 		}
2187dd7cddfSDavid du Colombier 	}
2199a747e4fSDavid du Colombier 	mouseaccelerate(0);
2209a747e4fSDavid du Colombier }
2217dd7cddfSDavid du Colombier 
2227dd7cddfSDavid du Colombier static void
setres(int n)2237dd7cddfSDavid du Colombier setres(int n)
2247dd7cddfSDavid du Colombier {
2257dd7cddfSDavid du Colombier 	resolution = n;
2267dd7cddfSDavid du Colombier 	switch(mousetype){
2277dd7cddfSDavid du Colombier 	case MousePS2:
2287dd7cddfSDavid du Colombier 		i8042auxcmd(0xE8);
2297dd7cddfSDavid du Colombier 		i8042auxcmd(n);
2307dd7cddfSDavid du Colombier 		break;
2317dd7cddfSDavid du Colombier 	}
2327dd7cddfSDavid du Colombier }
2337dd7cddfSDavid du Colombier 
2347dd7cddfSDavid du Colombier static void
setintellimouse(void)2357dd7cddfSDavid du Colombier setintellimouse(void)
2367dd7cddfSDavid du Colombier {
2377dd7cddfSDavid du Colombier 	intellimouse = 1;
23859cc4ca5SDavid du Colombier 	packetsize = 4;
2397dd7cddfSDavid du Colombier 	switch(mousetype){
2407dd7cddfSDavid du Colombier 	case MousePS2:
2417dd7cddfSDavid du Colombier 		i8042auxcmd(0xF3);	/* set sample */
2427dd7cddfSDavid du Colombier 		i8042auxcmd(0xC8);
2437dd7cddfSDavid du Colombier 		i8042auxcmd(0xF3);	/* set sample */
2447dd7cddfSDavid du Colombier 		i8042auxcmd(0x64);
2457dd7cddfSDavid du Colombier 		i8042auxcmd(0xF3);	/* set sample */
2467dd7cddfSDavid du Colombier 		i8042auxcmd(0x50);
2477dd7cddfSDavid du Colombier 		break;
248aa46331bSDavid du Colombier 	case Mouseserial:
249aa46331bSDavid du Colombier 		i8250setmouseputc(mouseport, m5mouseputc);
250aa46331bSDavid du Colombier 		break;
2517dd7cddfSDavid du Colombier 	}
2527dd7cddfSDavid du Colombier }
2537dd7cddfSDavid du Colombier 
2547dd7cddfSDavid du Colombier static void
resetmouse(void)2557dd7cddfSDavid du Colombier resetmouse(void)
2567dd7cddfSDavid du Colombier {
25759cc4ca5SDavid du Colombier 	packetsize = 3;
2587dd7cddfSDavid du Colombier 	switch(mousetype){
2597dd7cddfSDavid du Colombier 	case MousePS2:
2607dd7cddfSDavid du Colombier 		i8042auxcmd(0xF6);
2617dd7cddfSDavid du Colombier 		i8042auxcmd(0xEA);	/* streaming */
2627dd7cddfSDavid du Colombier 		i8042auxcmd(0xE8);	/* set resolution */
2637dd7cddfSDavid du Colombier 		i8042auxcmd(3);
2647dd7cddfSDavid du Colombier 		i8042auxcmd(0xF4);	/* enabled */
2657dd7cddfSDavid du Colombier 		break;
2667dd7cddfSDavid du Colombier 	}
2677dd7cddfSDavid du Colombier }
2687dd7cddfSDavid du Colombier 
2697dd7cddfSDavid du Colombier void
mousectl(Cmdbuf * cb)2709a747e4fSDavid du Colombier mousectl(Cmdbuf *cb)
2717dd7cddfSDavid du Colombier {
2729a747e4fSDavid du Colombier 	Cmdtab *ct;
2739a747e4fSDavid du Colombier 
274b7b24591SDavid du Colombier 	qlock(&mousectlqlock);
275b7b24591SDavid du Colombier 	if(waserror()){
276b7b24591SDavid du Colombier 		qunlock(&mousectlqlock);
277b7b24591SDavid du Colombier 		nexterror();
278b7b24591SDavid du Colombier 	}
279b7b24591SDavid du Colombier 
2809a747e4fSDavid du Colombier 	ct = lookupcmd(cb, mousectlmsg, nelem(mousectlmsg));
2819a747e4fSDavid du Colombier 	switch(ct->index){
2829a747e4fSDavid du Colombier 	case CMaccelerated:
2839a747e4fSDavid du Colombier 		setaccelerated(cb->nf == 1 ? 1 : atoi(cb->f[1]));
2847dd7cddfSDavid du Colombier 		break;
2859a747e4fSDavid du Colombier 	case CMintellimouse:
2869a747e4fSDavid du Colombier 		setintellimouse();
2877dd7cddfSDavid du Colombier 		break;
2889a747e4fSDavid du Colombier 	case CMlinear:
2899a747e4fSDavid du Colombier 		setlinear();
2907dd7cddfSDavid du Colombier 		break;
2919a747e4fSDavid du Colombier 	case CMps2:
2929847521cSDavid du Colombier 		intellimouse = 0;
2937dd7cddfSDavid du Colombier 		ps2mouse();
2949a747e4fSDavid du Colombier 		break;
2959a747e4fSDavid du Colombier 	case CMps2intellimouse:
2967dd7cddfSDavid du Colombier 		ps2mouse();
2977dd7cddfSDavid du Colombier 		setintellimouse();
2989a747e4fSDavid du Colombier 		break;
2999a747e4fSDavid du Colombier 	case CMres:
3009a747e4fSDavid du Colombier 		if(cb->nf >= 2)
3019a747e4fSDavid du Colombier 			setres(atoi(cb->f[1]));
3029a747e4fSDavid du Colombier 		else
3039a747e4fSDavid du Colombier 			setres(1);
3049a747e4fSDavid du Colombier 		break;
3059a747e4fSDavid du Colombier 	case CMreset:
3067dd7cddfSDavid du Colombier 		resetmouse();
3077dd7cddfSDavid du Colombier 		if(accelerated)
3087dd7cddfSDavid du Colombier 			setaccelerated(accelerated);
3097dd7cddfSDavid du Colombier 		if(resolution)
3107dd7cddfSDavid du Colombier 			setres(resolution);
3117dd7cddfSDavid du Colombier 		if(intellimouse)
3127dd7cddfSDavid du Colombier 			setintellimouse();
3139a747e4fSDavid du Colombier 		break;
3149a747e4fSDavid du Colombier 	case CMserial:
315b7b24591SDavid du Colombier 		if(mousetype == Mouseserial)
316b7b24591SDavid du Colombier 			error(Emouseset);
317b7b24591SDavid du Colombier 
318aa46331bSDavid du Colombier 		if(cb->nf > 2){
319aa46331bSDavid du Colombier 			if(strcmp(cb->f[2], "M") == 0)
320b7b24591SDavid du Colombier 				i8250mouse(cb->f[1], m3mouseputc, 0);
321aa46331bSDavid du Colombier 			else if(strcmp(cb->f[2], "MI") == 0)
322aa46331bSDavid du Colombier 				i8250mouse(cb->f[1], m5mouseputc, 0);
323b7b24591SDavid du Colombier 			else
324b7b24591SDavid du Colombier 				i8250mouse(cb->f[1], mouseputc, cb->nf == 1);
325aa46331bSDavid du Colombier 		} else
326aa46331bSDavid du Colombier 			i8250mouse(cb->f[1], mouseputc, cb->nf == 1);
327b7b24591SDavid du Colombier 
328b7b24591SDavid du Colombier 		mousetype = Mouseserial;
32968060204SDavid du Colombier 		strncpy(mouseport, cb->f[1], sizeof(mouseport)-1);
330b7b24591SDavid du Colombier 		packetsize = 3;
3319a747e4fSDavid du Colombier 		break;
3329a747e4fSDavid du Colombier 	case CMhwaccel:
3339a747e4fSDavid du Colombier 		if(strcmp(cb->f[1], "on")==0)
3349a747e4fSDavid du Colombier 			mousehwaccel = 1;
3359a747e4fSDavid du Colombier 		else if(strcmp(cb->f[1], "off")==0)
3369a747e4fSDavid du Colombier 			mousehwaccel = 0;
3377dd7cddfSDavid du Colombier 		else
3389a747e4fSDavid du Colombier 			cmderror(cb, "bad mouse control message");
3399a747e4fSDavid du Colombier 	}
340b7b24591SDavid du Colombier 
341b7b24591SDavid du Colombier 	qunlock(&mousectlqlock);
342b7b24591SDavid du Colombier 	poperror();
3437dd7cddfSDavid du Colombier }
344