forsyth20121205-174063a80cc13 years agoHistory
394 lines · 8.2 KB · Brainfuck
RawDownload
implement mouse;# ported from plan 9's aux/mouse include "sys.m";	sys: Sys;	sprint, fprint, sleep: import sys;include "draw.m"; stderr: ref Sys->FD; mouse: module {	init: fn(ctxt: ref Draw->Context, argv: list of string);}; Sleep500: 	con 500;Sleep1000:	con 1000;Sleep2000:	con 2000;TIMEOUT: 	con 5000;fail := "fail:";usage(){	fprint(stderr, "usage: mouse [type]\n");	raise fail+"usage";} write(fd: ref Sys->FD, buf: array of byte, n: int): int{	if (debug) {		sys->fprint(stderr, "write(%d) ", fd.fd);		for (i := 0; i < len buf; i++) {			sys->fprint(stderr, "'%c' ", int buf[i]);		}		sys->fprint(stderr, "\n");	}	return sys->write(fd, buf, n);} speeds := array[] of {"b1200", "b2400", "b4800", "b9600"};debug := 0;can9600 := 0; init(nil: ref Draw->Context, argv: list of string){	sys = load Sys Sys->PATH;	stderr = sys->fildes(2); {	if (argv == nil)		usage(); 	argv = tl argv;  	while (argv != nil && len (arg := hd argv) > 1 && arg[0] == '-') {		case arg[1] {		'D' =>			debug = 1;		* =>			usage();		}		argv = tl argv;	}	if (len argv > 1)		usage(); 	p: string;	if (argv == nil)		p = mouseprobe();	else		p = hd argv;	if (p != nil && !isnum(p)) {		mouseconfig(p);		return;	}	if (p == nil) {		serial("0");		serial("1");		fprint(stderr, "mouse: no mouse detected\n");	} else {		err := serial(p);		fprint(stderr, "mouse: %s\n", err);	}}exception{	# this could be taken out so the shell could	# get an indication that the command has failed.	"fail:*" =>		;}} # probe for a serial mouse on port p;# return some an error string if not found. serial(p: string): string{	baud := 0;	f := sys->sprint("/dev/eia%sctl", p);	if ((ctl := sys->open(f, Sys->ORDWR)) == nil)		return sprint("can't open %s - %r\n", f); 	f = sys->sprint("/dev/eia%s", p);	if ((data := sys->open(f, Sys->ORDWR)) == nil)		return sprint("can't open %s - %r\n", f); 	if(debug) fprint(stderr, "ctl=%d, data=%d\n", ctl.fd, data.fd); 	if(debug) fprint(stderr, "MorW()\n");	mtype := MorW(ctl, data);	if (mtype == 0) {		if(debug) return "no mouse detected"; 		if(debug) fprint(stderr, "C()\n");		mtype = C(ctl, data);	}	if (mtype == 0)		return "no mouse detected on port "+p; 	if(debug)fprint(stderr, "done eia setup\n");	mt := "serial " + p;	case mtype {	* =>		return "unknown mouse type";	'C' =>		if(debug) fprint(stderr, "Logitech 5 byte mouse\n");		Cbaud(ctl, data, baud);	'W' =>		if(debug) fprint(stderr, "Type W mouse\n");		Wbaud(ctl, data, baud);	'M' =>		if(debug) fprint(stderr, "Microsoft compatible mouse\n");		mt += " M";	}	mouseconfig(mt);	return nil;} mouseconfig(mt: string){	if ((conf := sys->open("/dev/mousectl", Sys->OWRITE)) == nil) {		fprint(stderr, "mouse: can't open mousectl - %r\n");		raise fail+"open mousectl";	}	if(debug) fprint(stderr, "opened mousectl\n");	if (write(conf, array of byte mt, len array of byte mt) < 0) {		fprint(stderr, "mouse: error setting mouse type - %r\n");		raise fail+"write conf";	}	fprint(stderr, "mouse: configured as '%s'\n", mt);} isnum(s: string): int{	for (i := 0; i < len s; i++)		if (s[i] < '0' || s[i] > '9')			return 0;	return 1;} mouseprobe(): string{	if ((probe := sys->open("/dev/mouseprobe", Sys->OREAD)) == nil) {		fprint(stderr, "mouse: can't open mouseprobe - %r\n");		return nil;	}	buf := array[64] of byte;	n := sys->read(probe, buf, len buf);	if (n <= 0)		return nil;	if (buf[n - 1] == byte '\n')		n--;	if(debug) fprint(stderr, "mouse probe detected mouse of type '%s'\n", string buf[0:n]);	return string buf[0:n];} readbyte(fd: ref Sys->FD): int{	buf := array[1] of byte;	(n, err) := timedread(fd, buf, 1, 200);	if (n < 0) {		if (err == nil)			return -1;		fprint(stderr, "mouse: readbyte failed - %s\n", err);		raise fail+"read failed";	}	return int buf[0];} slowread(fd: ref Sys->FD, buf: array of byte, nbytes: int, msg: string): int{	for (i := 0; i < nbytes; i++) {		if ((c := readbyte(fd)) == -1)			break;		buf[i] = byte c;	}	if(debug) dumpbuf(buf[0:i], msg);	return i;} dumpbuf(buf: array of byte, msg: string){	sys->fprint(stderr, "%s", msg);	for (i := 0; i < len buf; i++)		sys->fprint(stderr, "#%ux ", int buf[i]);	sys->fprint(stderr, "\n");} toggleRTS(fd: ref Sys->FD){	# reset the mouse (toggle RTS)	# must be >100mS	writes(fd, "d0");	sleep(10);	writes(fd, "r0");	sleep(Sleep500);	writes(fd, "d1");	sleep(10);	writes(fd, "r1");	sleep(Sleep500);} setupeia(fd: ref Sys->FD, baud, bits: string){	# set the speed to 1200/2400/4800/9600 baud,	# 7/8-bit data, one stop bit and no parity 	(abaud, abits) := (array of byte baud, array of byte bits);	if(debug)sys->fprint(stderr, "setupeia(%s,%s)\n", baud, bits);	write(fd, abaud, len abaud);	write(fd, abits, len abits);	writes(fd, "s1");	writes(fd, "pn");} # check for types M, M3 & W## we talk to all these mice using 1200 baud MorW(ctl, data: ref Sys->FD): int{	# set up for type M, V or W	# flush any pending data 	setupeia(ctl, "b1200", "l7");	toggleRTS(ctl);	if(debug)sys->fprint(stderr, "toggled RTS\n"); 	buf := array[256] of byte;	while (slowread(data, buf, len buf, "flush: ") > 0)		;	if(debug) sys->fprint(stderr, "done slowread\n");	toggleRTS(ctl); 	# see if there's any data from the mouse	# (type M, V and W mice)	c := slowread(data, buf, len buf, "check M: ");		# type M, V and W mice return "M" or "M3" after reset.	# check for type W by sending a 'Send Standard Configuration'	# command, "*?".	if (c > 0 && int buf[0] == 'M') {		writes(data, "*?");		c = slowread(data, buf, len buf, "check W: ");		# 4 bytes back indicates a type W mouse		if (c == 4) {			if (int buf[1] & (1<<4))				can9600 = 1;			setupeia(ctl, "b1200", "l8");			writes(data, "*U");			slowread(data, buf, len buf, "check W: ");			return 'W';		}		return 'M';	}	return 0;} # check for type C by seeing if it responds to the status# command "s".  the mouse is at an unknown speed so we# have to check all possible speeds.C(ctl, data: ref Sys->FD): int{	buf := array[256] of byte;	for (s := speeds; len s > 0; s = s[1:]) {		if (debug) sys->print("%s\n", s[0]);		setupeia(ctl, s[0], "l8");		writes(data, "s");		c := slowread(data, buf, len buf, "check C: ");		if (c >= 1 && (int buf[0] & 16rbf) == 16r0f) {			sleep(100);			writes(data, "*n");			sleep(100);			setupeia(ctl, "b1200", "l8");			writes(data, "s");			c = slowread(data, buf, len buf, "recheck C: ");			if (c >= 1 && (int buf[0] & 16rbf) == 16r0f) {				writes(data, "U");				return 'C';			}		}		sleep(100);	}	return 0;} Cbaud(ctl, data: ref Sys->FD, baud: int){	buf := array[2] of byte;	case baud {	0 or 1200 =>		return;	2400 =>		buf[1] = byte 'o';	4800 =>		buf[1] = byte 'p';	9600 =>		buf[1] = byte 'q';	* =>		fprint(stderr, "mouse: can't set baud rate, mouse at 1200\n");		return;	}	buf[0] = byte '*';	sleep(100);	write(data, buf, 2);	sleep(100);	write(data, buf, 2);	setupeia(ctl, sys->sprint("b%d", baud), "l8");} Wbaud(ctl, data: ref Sys->FD, baud: int){	case baud {	0 or 1200 =>		return;	* =>		if (baud == 9600 && can9600)			break;		fprint(stderr, "mouse: can't set baud rate, mouse at 1200\n");		return;	}	writes(data, "*q");	setupeia(ctl, "b9600", "l8");	slowread(data, array[32] of byte, 32, "setbaud: ");}		readproc(fd: ref Sys->FD, buf: array of byte, n: int,				pidch: chan of int, ch: chan of (int, string)){	s: string;	pidch <-= sys->pctl(0, nil);	n = sys->read(fd, buf, n);	if (n < 0)		s = sys->sprint("read: %r");	ch <-= (n, s);} sleepproc(t: int, pidch: chan of int, ch: chan of (int, string)){	pidch <-= sys->pctl(0, nil);	sys->sleep(t);	ch <-= (-1, nil);} timedread(fd: ref Sys->FD, buf: array of byte, n: int, t: int): (int, string){	pidch := chan of int;	retch := chan of (int, string);	spawn readproc(fd, buf, n, pidch, retch);	wpid := <-pidch;	spawn sleepproc(t, pidch, retch);	spid := <-pidch; 	(nr, err) := <-retch;	if (nr == -1 && err == nil)		kill(wpid);	else		kill(spid);	return (nr, err);} kill(pid: int){	if ((fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE)) == nil) {		fprint(stderr, "couldn't kill %d: %r\n", pid);		return;	}	sys->write(fd, array of byte "kill", 4);} writes(fd: ref Sys->FD, s: string): int{	a := array of byte s;	return write(fd, a, len a);}