diff options
Diffstat (limited to 'appl/cmd/mouse.b')
| -rw-r--r-- | appl/cmd/mouse.b | 394 |
1 files changed, 394 insertions, 0 deletions
diff --git a/appl/cmd/mouse.b b/appl/cmd/mouse.b new file mode 100644 index 00000000..9e21d61a --- /dev/null +++ b/appl/cmd/mouse.b @@ -0,0 +1,394 @@ +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; + def := 0; + baud := 0; + 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); +} + |
