ref: 0644aa11c120a581eec1d47c66208e08b946ec08
dir: /appl/cmd/mouse.b/
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); }