diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
| commit | 37da2899f40661e3e9631e497da8dc59b971cbd0 (patch) | |
| tree | cbc6d4680e347d906f5fa7fca73214418741df72 /appl/wm | |
| parent | 54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff) | |
20060303a
Diffstat (limited to 'appl/wm')
158 files changed, 65132 insertions, 0 deletions
diff --git a/appl/wm/about.b b/appl/wm/about.b new file mode 100644 index 00000000..615f106b --- /dev/null +++ b/appl/wm/about.b @@ -0,0 +1,72 @@ +implement WmAbout; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + Display, Image: import draw; + +include "tk.m"; + tk: Tk; + +include "tkclient.m"; + tkclient: Tkclient; + +WmAbout: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +tkcfg(version: string): array of string +{ + return array[] of { + "frame .f -bg black -borderwidth 2 -relief ridge", + "label .b -bg black -bitmap @/icons/inferno.bit", + "label .l1 -bg black -fg #ff5500 -text {Inferno "+ version + "}", + "pack .b .l1 -in .f", + "pack .f -ipadx 4 -ipady 2", + "pack propagate . 0", + "update", + }; +} + +init(ctxt: ref Draw->Context, nil: list of string) +{ + sys = load Sys Sys->PATH; + if (ctxt == nil) { + sys->fprint(sys->fildes(2), "about: no window context\n"); + raise "fail:bad context"; + } + + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient= load Tkclient Tkclient->PATH; + + tkclient->init(); + (t, menubut) := tkclient->toplevel(ctxt, "", "About Inferno", 0); + + tkcmds := tkcfg(rf("/dev/sysctl")); + for (i := 0; i < len tkcmds; i++) + tk->cmd(t,tkcmds[i]); + + tkclient->onscreen(t, nil); + tkclient->startinput(t, "ptr"::nil); + stop := chan of int; + spawn tkclient->handler(t, stop); + while((menu := <-menubut) != "exit") + tkclient->wmctl(t, menu); + stop <-= 1; +} + +rf(name: string): string +{ + fd := sys->open(name, Sys->OREAD); + if(fd == nil) + return nil; + buf := array[128] of byte; + n := sys->read(fd, buf, len buf); + if(n < 0) + n = 0; + return string buf[0:n]; +} diff --git a/appl/wm/avi.b b/appl/wm/avi.b new file mode 100644 index 00000000..a1331a1e --- /dev/null +++ b/appl/wm/avi.b @@ -0,0 +1,384 @@ +implement WmAVI; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + Rect, Display, Image: import draw; + +include "tk.m"; + tk: Tk; + Toplevel: import tk; + +include "tkclient.m"; + tkclient: Tkclient; + ctxt: ref Draw->Context; + +include "selectfile.m"; + selectfile: Selectfile; + +include "dialog.m"; + dialog: Dialog; + +include "riff.m"; + avi: Riff; + AVIhdr, AVIstream, RD: import avi; + video: ref AVIstream; + +WmAVI: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +Stopped, Playing, Paused: con iota; +state := Stopped; + + +cmap: array of byte; +codedbuf: array of byte; +pixelbuf: array of byte; +pixelrec: Draw->Rect; + +task_cfg := array[] of { + "canvas .c", + "frame .b", + "button .b.File -text File -command {send cmd file}", + "button .b.Stop -text Stop -command {send cmd stop}", + "button .b.Pause -text Pause -command {send cmd pause}", + "button .b.Play -text Play -command {send cmd play}", + "frame .f", + "label .f.file -text {File:}", + "label .f.name", + "pack .f.file .f.name -side left", + "pack .b.File .b.Stop .b.Pause .b.Play -side left", + "pack .f -fill x", + "pack .b -anchor w", + "pack .c -side bottom -fill both -expand 1", + "pack propagate . 0", +}; + +init(xctxt: ref Draw->Context, nil: list of string) +{ + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient= load Tkclient Tkclient->PATH; + dialog = load Dialog Dialog->PATH; + selectfile = load Selectfile Selectfile->PATH; + + ctxt = xctxt; + + sys->pctl(Sys->NEWPGRP, nil); + + tkclient->init(); + dialog->init(); + selectfile->init(); + + (t, wmctl) := tkclient->toplevel(ctxt, "", "AVI Player", 0); + + cmd := chan of string; + tk->namechan(t, cmd, "cmd"); + + for (c:=0; c<len task_cfg; c++) + tk->cmd(t, task_cfg[c]); + + tk->cmd(t, "bind . <Configure> {send cmd resize}"); + tk->cmd(t, "update"); + tkclient->onscreen(t, nil); + tkclient->startinput(t, "kbd"::"ptr"::nil); + + avi = load Riff Riff->PATH; + if(avi == nil) { + dialog->prompt(ctxt, t.image, "error -fg red", "Loading Interfaces", + "Failed to load the RIFF/AVI\ninterface:"+sys->sprint("%r"), + 0, "Exit"::nil); + return; + } + avi->init(); + + fname := ""; + state = Stopped; + + for(;;) alt { + s := <-t.ctxt.kbd => + tk->keyboard(t, s); + s := <-t.ctxt.ptr => + tk->pointer(t, *s); + s := <-t.ctxt.ctl or + s = <-t.wreq or + s = <-wmctl => + if(s == "exit") { + state = Stopped; + return; + } + tkclient->wmctl(t, s); + press := <-cmd => + case press { + "file" => + state = Stopped; + patterns := list of { + "*.avi (Microsoft movie files)", + "* (All Files)" + }; + fname = selectfile->filename(ctxt, t.image, "Locate AVI files", + patterns, nil); + if(fname != nil) { + tk->cmd(t, ".f.name configure -text {"+fname+"}"); + tk->cmd(t, "update"); + } + "play" => + if (state != Stopped) { + state = Playing; + continue; + } + if(fname != nil) { + state = Playing; + spawn play(t, fname); + } + "pause" => + if(state == Playing) + state = Paused; + "stop" => + state = Stopped; + } + } +} + +play(t: ref Toplevel, file: string) +{ + sp := list of { "Stop Play" }; + + (r, err) := avi->open(file); + if(err != nil) { + dialog->prompt(ctxt, t.image, "error -fg red", "Open AVI file", err, 0, sp); + return; + } + + err = avi->r.check4("AVI "); + if(err != nil) { + dialog->prompt(ctxt, t.image, "error -fg red", "Read AVI format", err, 0, sp); + return; + } + + (code, l) := avi->r.gethdr(); + if(code != "LIST") { + dialog->prompt(ctxt, t.image, "error -fg red", "Parse AVI headers", + "no list under AVI section header", 0, sp); + return; + } + + err = avi->r.check4("hdrl"); + if(err != nil) { + dialog->prompt(ctxt, t.image, "error -fg red", "Read AVI header", err, 0, sp); + return; + } + + avihdr: ref AVIhdr; + (avihdr, err) = avi->r.avihdr(); + if(err != nil) { + dialog->prompt(ctxt, t.image, "error -fg red", "Read AVI header", err, 0, sp); + return; + } + + # + # read the stream info & format structures + # + stream := array[avihdr.streams] of ref AVIstream; + for(i := 0; i < avihdr.streams; i++) { + (stream[i], err) = avi->r.streaminfo(); + if(err != nil) { + dialog->prompt(ctxt, t.image, "error -fg red", "Parse AVI headers", + "Failed to parse stream headers\n"+err, 0, sp); + return; + } + if(stream[i].stype == "vids") { + video = stream[i]; + err = video.fmt2binfo(); + if(err != nil) { + dialog->prompt(ctxt, t.image, "error -fg red", + "Parse AVI Video format", + "Invalid stream headers\n"+err, 0, sp); + return; + } + } + } + + img: ref Draw->Image; + if(video != nil) { + case video.binfo.compression { + * => + dialog->prompt(ctxt, t.image, "error -fg red", + "Parse AVI Compression method", + "unknown compression/encoding method", 0, sp); + return; + avi->BI_RLE8 => + cmap = array[len video.binfo.cmap] of byte; + for(i = 0; i < len video.binfo.cmap; i++) { + e := video.binfo.cmap[i]; + cmap[i] = byte ctxt.display.rgb2cmap(e.r, e.g, e.b); + } + break; + } + chans: draw->Chans; + case video.binfo.bitcount { + * => + dialog->prompt(ctxt, t.image, "error -fg red", + "Check AVI Video format", + string video.binfo.bitcount+ + " bits per pixel not supported", 0, sp); + return; + 8 => + chans = Draw->CMAP8; + mem := video.binfo.width*video.binfo.height; + pixelbuf = array[mem] of byte; + }; + pixelrec.min = (0, 0); + pixelrec.max = (video.binfo.width, video.binfo.height); + img = ctxt.display.newimage(pixelrec, chans, 0, Draw->White); + if (img == nil) { + sys->fprint(sys->fildes(2), "coffee: failed to allocate image\n"); + exit; + } + } + + # + # Parse out the junk headers we don't understand + # + parse: for(;;) { + (code, l) = avi->r.gethdr(); + if(l < 0) + break; + + case code { + * => +# sys->print("%s %d\n", code, l); + avi->r.skip(l); + "LIST" => + err = avi->r.check4("movi"); + if(err != nil) { + dialog->prompt(ctxt, t.image, "error -fg red", + "Strip AVI headers", + "no movi chunk", 0, sp); + return; + } + break parse; + } + } + + canvr := canvsize(t); + p := (Draw->Point)(0, 0); + dx := canvr.dx(); + if(dx > video.binfo.width) + p.x = (dx - video.binfo.width)/2; + + dy := canvr.dy(); + if(dy > video.binfo.height) + p.y = (dy - video.binfo.height)/2; + + canvr = canvr.addpt(p); + + chunk: for(;;) { + while(state == Paused) + sys->sleep(0); + if(state == Stopped) + break chunk; + (code, l) = avi->r.gethdr(); + if(l <= 0) + break; + if(l & 1) + l++; + case code { + * => + avi->r.skip(l); + "00db" => # Stream 0 Video DIB + dib(r, img, l); + "00dc" => # Stream 0 Video DIB compressed + dibc(r, img, l); + t.image.draw(canvr, img, nil, img.r.min); + "idx1" => + break chunk; + } + } + state = Stopped; +} + +dib(r: ref RD, i: ref Draw->Image, l: int): int +{ + if(len codedbuf < l) + codedbuf = array[l] of byte; + + if(r.readn(codedbuf, l) != l) + return -1; + + case video.binfo.bitcount { + 8 => + for(k := 0; k < l; k++) + codedbuf[k] = cmap[int codedbuf[k]]; + + i.writepixels(pixelrec, codedbuf); + } + return 0; +} + +dibc(r: ref RD, i: ref Draw->Image, l: int): int +{ + if(len codedbuf < l) + codedbuf = array[l] of byte; + + if(r.readn(codedbuf, l) != l) + return -1; + + case video.binfo.compression { + avi->BI_RLE8 => + p := 0; + posn := 0; + x := 0; + y := video.binfo.height-1; + w := video.binfo.width; + decomp: while(p < l) { + n := int codedbuf[p++]; + if(n == 0) { + esc := int codedbuf[p++]; + case esc { + 0 => # end of line + x = 0; + y--; + 1 => # end of image + break decomp; + 2 => # Delta dx,dy + x += int codedbuf[p++]; + y -= int codedbuf[p++]; + * => + posn = x+y*w; + for(k := 0; k < esc; k++) + pixelbuf[posn++] = cmap[int codedbuf[p++]]; + x += esc; + if(p & 1) + p++; + }; + } + else { + posn = x+y*w; + v := cmap[int codedbuf[p++]]; + for(k := 0; k < n; k++) + pixelbuf[posn++] = v; + x += n; + } + } + i.writepixels(pixelrec, pixelbuf); + } + return 0; +} + +canvsize(t: ref Toplevel): Rect +{ + r: Rect; + + r.min.x = int tk->cmd(t, ".c cget -actx"); + r.min.y = int tk->cmd(t, ".c cget -acty"); + r.max.x = r.min.x + int tk->cmd(t, ".c cget -width"); + r.max.y = r.min.y + int tk->cmd(t, ".c cget -height"); + + return r; +} diff --git a/appl/wm/bounce.b b/appl/wm/bounce.b new file mode 100644 index 00000000..7d6cdd1b --- /dev/null +++ b/appl/wm/bounce.b @@ -0,0 +1,356 @@ +implement Bounce; + +# bouncing balls demo. it uses tk and multiple processes to animate a +# number of balls bouncing around the screen. each ball has its own +# process; CPU time is doled out fairly to each process by using +# a central monitor loop. + +include "sys.m"; + sys: Sys; +include "draw.m"; + draw: Draw; + Point, Rect: import draw; +include "tk.m"; + tk: Tk; +include "tkclient.m"; + tkclient: Tkclient; +include "math.m"; + math: Math; +include "rand.m"; + +Bounce: module { + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +BALLSIZE: con 4; +ZERO: con 1e-6; +π: con Math->Pi; + +Line: adt { + p1, p2: Point; +}; + +Realpoint: adt { + x, y: real; +}; + +gamecmds := array[] of { +"canvas .c", +"bind .c <ButtonRelease-1> {send cmd 0 %x %y}", +"bind .c <ButtonRelease-2> {send cmd 0 %x %y}", +"bind .c <Button-1> {send cmd 1 %x %y}", +"bind .c <Button-2> {send cmd 2 %x %y}", +"frame .f", +"button .f.left -bitmap small_color_left.bit -bd 0 -command {send cmd k -1}", +"button .f.right -bitmap small_color_right.bit -bd 0 -command {send cmd k 1}", +"label .f.l -text {8 balls}", +"pack .f.left .f.right -side left", +"pack .f.l -side left", +"pack .f -fill x", +"pack .c -fill both -expand 1", +}; + +randch: chan of int; +lines: list of (int, Line); +lineid := 0; +lineversion := 0; + +addline(win: ref Tk->Toplevel, v: Line) +{ + lines = (++lineid, v) :: lines; + cmd(win, ".c create line " + pt2s(v.p1) + " " + pt2s(v.p2) + " -width 3 -fill black" + + " -tags l" + string lineid); + lineversion++; +} + +nomod(s: string) +{ + sys->fprint(sys->fildes(2), "bounce: cannot load %s: %r\n", s); + raise "fail:bad module"; +} + +init(ctxt: ref Draw->Context, argv: list of string) +{ + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + math = load Math Math->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + if (tkclient == nil) + nomod(Tkclient->PATH); + tkclient->init(); + nballs := 8; + if (argv != nil && tl argv != nil) + nballs = int hd tl argv; + if (nballs < 0) { + sys->fprint(sys->fildes(2), "usage: bounce [nballs]\n"); + raise "fail:usage"; + } + sys->pctl(Sys->NEWPGRP, nil); + if(ctxt == nil) + ctxt = tkclient->makedrawcontext(); + (win, wmctl) := tkclient->toplevel(ctxt, nil, "Bounce", 0); + cmdch := chan of string; + tk->namechan(win, cmdch, "cmd"); + for (i := 0; i < len gamecmds; i++) + cmd(win, gamecmds[i]); + cmd(win, ".c configure -width 400 -height 400"); + cmd(win, "pack propagate . 0"); + cmd(win, ".f.l configure -text '" + string nballs + " balls"); + tkclient->onscreen(win, nil); + tkclient->startinput(win, "kbd"::"ptr"::nil); + + mch := chan of (int, Point); + randch = chan of int; + spawn randgenproc(randch); + csz := Point(int cmd(win, ".c cget -actwidth"), int cmd(win, ".c cget -actheight")); + + # add edges of window + addline(win, ((-1, -1), (csz.x, -1))); + addline(win, ((csz.x, -1), csz)); + addline(win, (csz, (-1, csz.y))); + addline(win, ((-1, csz.y), (-1, -1))); + + spawn makelinesproc(win, mch); + mkball := chan of (int, Realpoint, Realpoint); + spawn monitor(win, mkball); + for (i = 0; i < nballs; i++) + mkball <-= (1, randpoint(csz), makeunit(randpoint(csz))); + for (;;) alt { + s := <-win.ctxt.kbd => + tk->keyboard(win, s); + s := <-win.ctxt.ptr => + tk->pointer(win, *s); + s := <-win.ctxt.ctl or + s = <-win.wreq or + s = <-wmctl => + tkclient->wmctl(win, s); + c := <-cmdch => + (nil, toks) := sys->tokenize(c, " "); + if (hd toks != "k") { + mch <-= (int hd toks, Point(int hd tl toks, int hd tl tl toks)); + continue; + } + n := nballs + int hd tl toks; + if (n < 0) + n = 0; + dn := 1; + if (n < nballs) + dn = -1; + for (; nballs != n; nballs += dn) + mkball <-= (dn, randpoint(csz), makeunit(randpoint(csz))); + cmd(win, ".f.l configure -text '" + string nballs + " balls"); + cmd(win, "update"); + } +} + +randpoint(size: Point): Realpoint +{ + return (randreal(size.x), randreal(size.y)); +} + +# return randomish real number between 1 and x-1 +randreal(x: int): real +{ + return real (<-randch % ((x - 1) * 100)) / 100.0 + 1.0; +} + +# make sure cpu time is handed to all ball processes fairly +# by passing a "token" around to each process in turn. +# each process does its work when it *hasn't* got its +# token but it can't go through two iterations without +# waiting its turn. +# +# new processes can be created and destroyed by +# sending on mkball. processes are arranged in a stack-like +# order: new processes are added to the top of the stack, and +# processes are destroyed from the top of the stack downwards. +monitor(win: ref Tk->Toplevel, mkball: chan of (int, Realpoint, Realpoint)) +{ + procl := proc := chan of int :: nil; + spawn nullproc(hd proc); # always there to avoid deadlock when no balls. + hd proc <-= 1; # hand token to dummy proc + for (;;) { + procc := hd proc; + alt { + (n, p, v) := <-mkball => + if (n > 0) { # start new ball proc going. + procl = chan of int :: procl; + spawn animproc(hd procl, win, p, v); + } else if (tl procl != nil) { # stop a ball proc. + <-hd proc; # get token. + hd procl <-= 0; # stop proc. + proc = procl = tl procl; # remove proc. + hd proc <-= 1; # hand out token. + } + <-procc => # got token. + if ((proc = tl proc) == nil) + proc = procl; + hd proc <-= 1; # hand token to next process. + } + } +} + +nullproc(c: chan of int) +{ + for (;;) + c <-= <-c; +} + +# animate one ball. initial position and unit-velocity are +# given by p and v. +animproc(c: chan of int, win: ref Tk->Toplevel, p, v: Realpoint) +{ + speed := 0.1 + real (<-randch % 40) / 100.0; + ballid := cmd(win, sys->sprint(".c create oval 0 0 1 1 -fill #%.6x", <-randch & 16rffffff)); + hitlineid := -1; + smallcount := 0; + version := lineversion; +loop: for (;;) { + hitline: Line; + hitp: Realpoint; + + dist := 1000000.0; + oldid := hitlineid; + for (l := lines; l != nil; l = tl l) { + (id, line) := hd l; + (ok, hp, hdist) := intersect(p, v, line); + if (ok && hdist < dist && id != oldid && (smallcount < 10 || hdist > 1.5)) { + (hitp, hitline, hitlineid, dist) = (hp, line, id, hdist); + } + } + if (dist > 10000.0) { + sys->print("no intersection!\n"); +# sys->print("p: [%f, %f], v: [%f, %f]\n", p.x, p.y, v.x, v.y); +# for (l := lines; l != nil; l = tl l) { +# (id, line) := hd l; +# (ok, hp, hdist) := intersect(p, v, line); +# sys->print("line: [%d %d]->[%d %d] -> %d, [%f, %f], %f\n", line.p1.x, line.p1.y, line.p2.x, line.p2.y, +# ok, hp.x, hp.y, hdist); +# } + cmd(win, ".c delete " + ballid + ";update"); + while (c <-= <-c) + ; + exit; + } + if (dist < 0.0001) + smallcount++; + else + smallcount = 0; + bouncev := boing(v, hitline); + t0 := sys->millisec(); + dt := int (dist / speed); + t := 0; + do { + s := real t * speed; + currp := Realpoint(p.x + s * v.x, p.y + s * v.y); + bp := Point(int currp.x, int currp.y); + cmd(win, ".c coords " + ballid + " " + + string (bp.x-BALLSIZE)+" "+string (bp.y-BALLSIZE)+" "+ + string (bp.x+BALLSIZE)+" "+string (bp.y+BALLSIZE)); + cmd(win, "update"); + if (lineversion > version) { + (p, hitlineid, version) = (currp, oldid, lineversion); + continue loop; + } + # pass the token back to the monitor. + if (<-c == 0) { + cmd(win, ".c delete " + ballid + ";update"); + exit; + } + c <-= 1; + t = sys->millisec() - t0; + } while (t < dt); + p = hitp; + v = bouncev; + } +} + +# thread-safe access to the Rand module +randgenproc(ch: chan of int) +{ + rand := load Rand Rand->PATH; + for (;;) + ch <-= rand->rand(16r7fffffff); +} + +makelinesproc(win: ref Tk->Toplevel, mch: chan of (int, Point)) +{ + for (;;) { + (down, p1) := <-mch; + addline(win, (p1, p1)); + (id, nil) := hd lines; + p2 := p1; + do { + (down, p2) = <-mch; + cmd(win, ".c coords l" + string id + " " + pt2s(p1) + " " + pt2s(p2)); + cmd(win, "update"); + lines = (id, (p1, p2)) :: tl lines; + lineversion++; + if (down > 1) { + dp := p2.sub(p1); + if (dp.x*dp.x + dp.y*dp.y > 5) { + p1 = p2; + addline(win, (p2, p2)); + (id, nil) = hd lines; + } + } + } while (down); + } +} + +# make a vector of unit-length, parallel to v. +makeunit(v: Realpoint): Realpoint +{ + mag := math->sqrt(v.x * v.x + v.y * v.y); + return (v.x / mag, v.y / mag); +} + +# bounce ball travelling in direction av off line b. +# return the new unit vector. +boing(av: Realpoint, b: Line): Realpoint +{ + f := b.p2.sub(b.p1); + d := math->atan2(real f.y, real f.x) * 2.0 - math->atan2(av.y, av.x); + return (math->cos(d), math->sin(d)); +} + +# compute the intersection of lines a and b. +# b is assumed to be fixed, and a is indefinitely long +# but doesn't extend backwards from its starting point. +# a is defined by the starting point p and the unit vector v. +intersect(p, v: Realpoint, b: Line): (int, Realpoint, real) +{ + w := Realpoint(real (b.p2.x - b.p1.x), real (b.p2.y - b.p1.y)); + det := w.x * v.y - v.x * w.y; + if (det > -ZERO && det < ZERO) + return (0, (0.0, 0.0), 0.0); + + y21 := real b.p1.y - p.y; + x21 := real b.p1.x - p.x; + s := (w.x * y21 - w.y * x21) / det; + if (s < 0.0) + return (0, (0.0, 0.0), 0.0); + + hp := Realpoint(p.x+v.x*s, p.y+v.y*s); + if (b.p1.x > b.p2.x) + (b.p1.x, b.p2.x) = (b.p2.x, b.p1.x); + if (b.p1.y > b.p2.y) + (b.p1.y, b.p2.y) = (b.p2.y, b.p1.y); + + return (int hp.x >= b.p1.x && int hp.x <= b.p2.x + && int hp.y >= b.p1.y && int hp.y <= int b.p2.y, hp, s); +} + +cmd(top: ref Tk->Toplevel, s: string): string +{ + e := tk->cmd(top, s); + if (e != nil && e[0] == '!') + sys->print("tk error %s on '%s'\n", e, s); + return e; +} + +pt2s(p: Point): string +{ + return string p.x + " " + string p.y; +} diff --git a/appl/wm/brutus.b b/appl/wm/brutus.b new file mode 100644 index 00000000..b18d6a9d --- /dev/null +++ b/appl/wm/brutus.b @@ -0,0 +1,2013 @@ +implement Brutus; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + Context: import draw; + ctxt: ref Context; + +include "tk.m"; + tk: Tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "dialog.m"; + dialog: Dialog; + +include "selectfile.m"; + selectfile: Selectfile; + +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; + +include "workdir.m"; + +include "plumbmsg.m"; + plumbmsg: Plumbmsg; + Msg: import plumbmsg; + +include "brutus.m"; +include "brutusext.m"; + +EXTDIR: con "/dis/wm/brutus"; +NEXTRA: con NTAG-NFONTTAG; +DEFFONT: con "/fonts/lucidasans/unicode.8.font"; +DEFFONTNAME: con "Roman"; +DEFSIZE: con 10; +DEFTAG: con "Roman.10"; +SETFONT: con " -font "+DEFFONT+" "; +FOCUS: con "focus .ft.t"; +NOSEL: con ".ft.t tag remove sel sel.first sel.last"; +UPDATE: con "update"; + +# +# Foreign keyboards and languages +# +Remaptab: adt +{ + in, out: int; +}; +include "hebrew.m"; + +BS: con 8; # ^h backspace character +BSW: con 23; # ^w bacspace word +BSL: con 21; # ^u backspace line +ESC: con 27; # ^[ cut selection + +Name: con "Brutus"; + +# build menu +menu_cfg := array[] of { + # menu + "menu .m", + ".m add command -text Cut -command {send edit cut}", + ".m add command -text Paste -command {send edit paste}", + ".m add command -text Snarf -command {send edit snarf}", + ".m add command -text Look -command {send edit look}", +}; + +brutus_cfg := array[] of { + # buttons + "button .b.Tag -text Tag -command {send cmd tag} -state disabled", + "menubutton .b.Font -text Roman -menu .b.Font.menu -underline -1 -state disabled", + "menu .b.Font.menu", + ".b.Font.menu add command -label Roman -command {send cmd font Roman}", + ".b.Font.menu add command -label Italic -command {send cmd font Italic}", + ".b.Font.menu add command -label Bold -command {send cmd font Bold}", + ".b.Font.menu add command -label Type -command {send cmd font Type}", + "checkbutton .b.Applyfont -variable Applyfont -command {send cmd applyfont}} -state disabled", + "button .b.Applyfontnow -text Font -command {send cmd applyfontnow} -state disabled", + "button .b.Applysizenow -text Size -command {send cmd applysizenow} -state disabled", + "button .b.Applyfontsizenow -text F&S -command {send cmd applyfontsizenow} -state disabled", + "menubutton .b.Size -text 10pt -menu .b.Size.menu -underline -1 -state disabled", + "menu .b.Size.menu", + ".b.Size.menu add command -label 6pt -command {send cmd size 6}", + ".b.Size.menu add command -label 8pt -command {send cmd size 8}", + ".b.Size.menu add command -label 10pt -command {send cmd size 10}", + ".b.Size.menu add command -label 12pt -command {send cmd size 12}", + ".b.Size.menu add command -label 16pt -command {send cmd size 16}", + "button .b.Put -text Put -command {send cmd put} -state disabled", + + # text + "frame .ft", + "scrollbar .ft.scroll -command {.ft.t yview}", + "text .ft.t -height 7c -tabs {1c} -wrap word -yscrollcommand {.ft.scroll set}", + FOCUS, + + # pack + "pack .b.File .b.Ext .b.Tag .b.Applyfontnow .b.Applysizenow .b.Applyfontsizenow .b.Applyfont .b.Font .b.Size .b.Put -side left", + "pack .b -anchor w", + "pack .ft.scroll -side left -fill y", + "pack .ft.t -fill both -expand 1", + "pack .ft -fill both -expand 1", + "pack propagate . 0", +}; + +control_cfg := array[] of { + # text + "frame .ft", + "scrollbar .ft.scroll -command {.ft.t yview}", + "text .ft.t -height 4c -wrap word -yscrollcommand {.ft.scroll set}", + "pack .b.File", + "pack .b -anchor w", + "pack .ft.scroll -side left -fill y", + "pack .ft.t -fill both -expand 1", + "pack .ft -fill both -expand 1", + "pack propagate . 0", +}; + +# bindings to build nice controls in text widget +input_cfg := array[] of { + # input + "bind .ft.t <Key> {send keys {%A}}", + "bind .ft.t <Control-h> {send keys {%A}}", + "bind .ft.t <Control-w> {send keys {%A}}", + "bind .ft.t <Control-u> {send keys {%A}}", + "bind .ft.t <Button-1> +{grab set .ft.t; send but1 pressed}", + "bind .ft.t <Double-Button-1> +{grab set .ft.t; send but1 pressed}", + "bind .ft.t <ButtonRelease-1> +{grab release .ft.t; send but1 released}", + "bind .ft.t <Button-2> {send but2 %X %Y}", + "bind .ft.t <Motion-Button-2-Button-1> {}", + "bind .ft.t <Motion-Button-2> {}", + "bind .ft.t <ButtonPress-3> {send but3 pressed}", + "bind .ft.t <ButtonRelease-3> {send but3 released %x %y}", + "bind .ft.t <Motion-Button-3> {}", + "bind .ft.t <Motion-Button-3-Button-1> {}", + "bind .ft.t <Double-Button-3> {}", + "bind .ft.t <Double-ButtonRelease-3> {}", + "bind .ft.t <FocusIn> +{send cmd focus}", + UPDATE +}; + +fontbuts := array[] of { + ".b.Ext", + ".b.Tag", + ".b.Applyfontnow", + ".b.Applysizenow", + ".b.Applyfontsizenow", + ".b.Applyfont", + ".b.Font", + ".b.Size", +}; + +fontname = array[NFONT] of { + "Roman", + "Italic", + "Bold", + "Type", +}; + +sizename = array[NSIZE] of { + "6", + "8", + "10", + "12", + "16", +}; + +tagname = array[NTAG] of { + # first NFONT*NSIZE are font/size names + "Roman.6", + "Roman.8", + "Roman.10", + "Roman.12", + "Roman.16", + "Italic.6", + "Italic.8", + "Italic.10", + "Italic.12", + "Italic.16", + "Bold.6", + "Bold.8", + "Bold.10", + "Bold.12", + "Bold.16", + "Type.6", + "Type.8", + "Type.10", + "Type.12", + "Type.16", + "Example", + "Caption", + "List", + "List-elem", + "Label", + "Label-ref", + "Exercise", + "Heading", + "No-fill", + "Author", + "Title", + "Index", + "Index-topic", +}; + +tagconfig = array[NTAG] of { + "-font /fonts/lucidasans/unicode.6.font", + "-font /fonts/lucidasans/unicode.7.font", + "-font /fonts/lucidasans/unicode.8.font", + "-font /fonts/lucidasans/unicode.10.font", + "-font /fonts/lucidasans/unicode.13.font", + "-font /fonts/lucidasans/italiclatin1.6.font", + "-font /fonts/lucidasans/italiclatin1.7.font", + "-font /fonts/lucidasans/italiclatin1.8.font", + "-font /fonts/lucidasans/italiclatin1.10.font", + "-font /fonts/lucidasans/italiclatin1.13.font", + "-font /fonts/lucidasans/boldlatin1.6.font", + "-font /fonts/lucidasans/boldlatin1.7.font", + "-font /fonts/lucidasans/boldlatin1.8.font", + "-font /fonts/lucidasans/boldlatin1.10.font", + "-font /fonts/lucidasans/boldlatin1.13.font", + "-font /fonts/lucidasans/typelatin1.6.font", + "-font /fonts/lucidasans/typelatin1.7.font", + "-font /fonts/pelm/latin1.9.font", + "-font /fonts/pelm/ascii.12.font", + "-font /fonts/pelm/ascii.16.font", + "-foreground #444444 -lmargin1 1c -lmargin2 1c; .ft.t tag lower Example", + "-foreground #444444; .ft.t tag lower Caption", + "-foreground #444444 -lmargin1 1c -lmargin2 1c; .ft.t tag lower List", + "-foreground #0000A0; .ft.t tag lower List-elem", + "-foreground #444444; .ft.t tag lower Label", + "-foreground #444444; .ft.t tag lower Label-ref", + "-foreground #444444; .ft.t tag lower Exercise", + "-foreground #444444; .ft.t tag lower Heading", + "-foreground #444444; .ft.t tag lower No-fill", + "-foreground #444444; .ft.t tag lower Author", + "-foreground #444444; .ft.t tag lower Title", + "-foreground #444444; .ft.t tag lower Index", + "-foreground #444444; .ft.t tag lower Index-topic", +}; + +enabled := array[] of {"disabled", "normal"}; + +File: adt +{ + tk: ref Tk->Toplevel; + isctl: int; + applyfont: int; + fontsused: int; + name: string; + dirty: int; + font: string; # set by the buttons, not nec. by the text + size: int; # set by the buttons, not nec. by the text + fonttag: string; # set by the buttons, not nec. by the text + configed: array of int; + button1: int; + button3: int; + fontsok: int; # fonts and tags can be set + extensions: list of ref Ext; +}; + +Ext: adt +{ + tkname: string; + modname: string; + mod: Brutusext; + args: string; +}; + +menuindex := "0"; +snarftext := ""; +snarfsgml := ""; +central: chan of (ref File, string); +files: array of ref File; # global but modified only by control thread +plumbed := 0; +curdir := ""; +lang := ""; + +init(c: ref Context, argv: list of string) +{ + ctxt = c; + sys = load Sys Sys->PATH; + if (ctxt == nil) { + sys->fprint(sys->fildes(2), "brutus: no window context\n"); + raise "fail:bad context"; + } + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + dialog = load Dialog Dialog->PATH; + selectfile = load Selectfile Selectfile->PATH; + bufio = load Bufio Bufio->PATH; + plumbmsg = load Plumbmsg Plumbmsg->PATH; + + if(plumbmsg->init(1, "edit", 1000) >= 0){ + plumbed = 1; + workdir := load Workdir Workdir->PATH; + curdir = workdir->init(); + workdir = nil; + } + + tkclient->init(); + dialog->init(); + selectfile->init(); + sys->pctl(Sys->NEWPGRP, nil); # so we can pass "exit" command to tkclient + + file := ""; + if(argv != nil) + argv = tl argv; + if(argv != nil) + file = hd argv; + central = chan of (ref File, string); + spawn control(ctxt); + <-central; + spawn brutus(ctxt, file); +} + +# build menu button for dynamically generated menu +buttoncfg(label, enable: string): string +{ + return "label .b."+label+" -text "+label + " " + enable + + ";bind .b."+label+" <Button-1> {send cmd "+label+"}" + + ";bind .b."+label+" <ButtonRelease-1> {}" + + ";bind .b."+label+" <Motion-Button-1> {}" + + ";bind .b."+label+" <Double-Button-1> {}" + + ";bind .b."+label+" <Double-ButtonRelease-1> {}" + + ";bind .b."+label+" <Enter> {.b."+label+" configure -background #EEEEEE}" + + ";bind .b."+label+" <Leave> {.b."+label+" configure -background #DDDDDD}"; +} + +tkchans(t: ref Tk->Toplevel): (chan of string, chan of string, chan of string, chan of string, chan of string, chan of string, chan of string) +{ + keys := chan of string; + tk->namechan(t, keys, "keys"); + edit := chan of string; + tk->namechan(t, edit, "edit"); + cmd := chan of string; + tk->namechan(t, cmd, "cmd"); + but1 := chan of string; + tk->namechan(t, but1, "but1"); + but2 := chan of string; + tk->namechan(t, but2, "but2"); + but3 := chan of string; + tk->namechan(t, but3, "but3"); + drag := chan of string; + tk->namechan(t, drag, "Wm_drag"); + return (keys, edit, cmd, but1, but2, but3, drag); +} + +control(ctxt: ref Context) +{ + (t, titlectl) := tkclient->toplevel(ctxt, SETFONT, Name, Tkclient->Appl); + + # f is not used to store anything, just to simplify interfaces + # shared by control and brutus + f := ref File (t, 1, 0, 0, "", 0, DEFFONTNAME, DEFSIZE, DEFTAG, nil, 0, 0, 0, nil); + + tkcmds(t, menu_cfg); + tkcmd(t, "frame .b"); + tkcmd(t, buttoncfg("File", "")); + tkcmds(t, control_cfg); + tkcmds(t, input_cfg); + files = array[1] of ref File; + files[0] = f; + + (keys, edit, cmd, but1, but2, but3, drag) := tkchans(t); + + tkcmd(t, ".ft.t mark set typingstart 1.0; .ft.t mark gravity typingstart left"); + central <-= (nil, ""); # signal readiness +# spawn tkclient->wmctl(t, "task"); + curfile: ref File; + + plumbc := chan of (string, string); + spawn plumbproc(plumbc); + + tkclient->startinput(t, "kbd"::"ptr"::nil); + tkclient->onscreen(t, nil); + tkclient->wmctl(t, "task"); + for(;;) alt { + s := <-t.ctxt.kbd => + tk->keyboard(t, s); + s := <-t.ctxt.ptr => + tk->pointer(t, *s); + + menu := <-t.ctxt.ctl or + menu = <-t.wreq or + menu = <-titlectl => + if(menu == "exit"){ + if(shutdown(ctxt, t)){ + killplumb(); + tkclient->wmctl(t, menu); + } + break; + } + # spawn tkclient->wmctl(t, menu); + tkclient->wmctl(t, menu); + + ecmd := <-edit => + editor(f, ecmd); + tkcmd(t, FOCUS); + + c := <-cmd => + (nil, s) := sys->tokenize(c, " "); + case hd s { + * => + sys->print("unknown control cmd %s\n",c ); + "File" => + filemenu(t, 0, 0); + "new" => + (name, ok, nil) := getfilename(ctxt, t, "file for new window", f.name, 1, 0, 0); + if(ok) + spawn brutus(ctxt, name); + "select" => + n := int hd tl s; + if(n > len files) + break; + if(n > 0) + curfile = files[n]; + tkcmd(files[n].tk, ". map; raise .; focus .ft.t"); + "focus" => + ; + } + + (file, action) := <-central => + (nil, s) := sys->tokenize(action, " "); + case hd s { + * => + sys->print("control unknown central command %s\n", action); + "new" => + curfile = file; + nfiles := array[len files+1] of ref File; + nfiles[0:] = files; + files = nfiles; + nfiles = nil; # make sure references don't linger + files[len files-1] = file; + "name" => + name := nameof(file); + index := 0; + for(i:=1; i<len files; i++) + if(files[i] == file){ + index = i; + break; + } + if(index == 0) + sys->print("can't find file\n"); + "focus" => + if(file != f) + curfile = file; + "select" => + n := int hd tl s; + if(n >= len files) + break; + if(n > 0) + curfile = files[n]; + tkcmd(files[n].tk, ". map; raise .; focus .ft.t; update"); + "exiting" => + if(file == nil) + break; + if(file == curfile) + curfile = nil; + index := 0; + for(i:=1; i<len files; i++) + if(files[i] == file){ + index = i; + break; + } + if(index == 0) + sys->print("can't find file\n"); + else{ + # make a new one rather than slice, to clean up references + nfiles := array[len files-1] of ref File; + for(i=0; i<index; i++) + nfiles[i] = files[i]; + for(; i<len nfiles; i++) + nfiles[i] = files[i+1]; + files = nfiles; + } + file = nil; + } + c := <-keys => + char := typing(f, c); + if(curfile!=nil && char=='\n' && insat(t, "end")) + execute(t, curfile, tkcmd(t, ".ft.t get insert-1line insert")); + + c := <-but1 => + mousebut1(f, c); + + c := <-but2 => + mousebut2(f, c); + + c := <-but3 => + mousebut3(f, c); + + c := <-drag => + if(len c < 6 || c[0:5] != "path=") + break; + spawn brutus(ctxt, c[5:]); + + (fname, addr) := <-plumbc => + for(i:=1; i<len files; i++) + if(files[i].name == fname){ + tkcmd(files[i].tk, ". map; raise .; focus .ft.t"); + showaddr(files[i], addr); + break; + } + if(i == len files){ + if(addr != "") + spawn brutus(ctxt, fname+":"+addr); + else + spawn brutus(ctxt, fname); + } + } +} + +brutus(ctxt: ref Context, filename: string) +{ + addr := ""; + for(i:=len filename; --i>0; ){ + if(filename[i] == ':'){ + (ok, dir) := sys->stat(filename[0:i]); + if(ok >= 0){ + addr = filename[i+1:]; + filename = filename[0:i]; + break; + } + } + } + + (t, titlectl) := tkclient->toplevel(ctxt, SETFONT, Name, Tkclient->Appl); + + f := ref File (t, 0, 0, 0, filename, 0, DEFFONTNAME, DEFSIZE, DEFTAG, nil, 0, 0, 0, nil); + f.configed = array[NTAG] of {* => 0}; + + tkcmds(t, menu_cfg); + tkcmd(t, "frame .b"); + tkcmd(t, buttoncfg("File", "")); + tkcmd(t, buttoncfg("Ext", "-state disabled")); + + tkcmds(t, brutus_cfg); + tkcmds(t, input_cfg); + + # buttons work better when they grab the mouse + a := array[] of {".b.Tag", ".b.Applyfontnow", ".b.Applysizenow", ".b.Applyfontsizenow"}; + for(i=0; i<len a; i++){ + tkcmd(t, "bind "+a[i]+" <Button-1> +{grab set "+a[i]+"}"); + tkcmd(t, "bind "+a[i]+" <ButtonRelease-1> +{grab release "+a[i]+"}"); + } + + (keys, edit, cmd, but1, but2, but3, drag) := tkchans(t); + + configfont(f, "Heading"); + configfont(f, "Title"); + configfont(f, f.fonttag); + tkcmd(t, ".ft.t mark set typingstart 1.0; .ft.t mark gravity typingstart left"); + tkcmd(t, "image create bitmap waiting -file cursor.wait"); + + central <-= (f, "new"); + setfilename(f, filename); + + if(filename != "") + if(loadfile(f, filename) < 0) + dialog->prompt(ctxt, t.image, "error -fg red", + "Open file", + sys->sprint("Can't read %s:\n%r", filename), + 0, "Continue" :: nil); + else + showaddr(f, addr); + + tkclient->onscreen(t, nil); + tkclient->startinput(t, "kbd"::"ptr"::nil); + for(;;) alt { + s := <-t.ctxt.kbd => + tk->keyboard(t, s); + s := <-t.ctxt.ptr => + tk->pointer(t, *s); + + menu := <-t.ctxt.ctl or + menu = <-t.wreq or + menu = <-titlectl => + case menu { + "exit" => + if(f.dirty){ + action := confirm(ctxt, t, nameof(f)+" is dirty", 1); + case action { + "cancel" => + continue; + "exitclean" => + if(dumpfile(f, f.name, f.fontsused) < 0) + continue; + break; + "exitdirty" => + break; + } + } + central <-= (f, "exiting"); + # this one tears down temporaries holding references to f + central <-= (nil, "exiting"); + return; + "task" => + tkcmd(t, ". unmap"); + * => + tkclient->wmctl(t, menu); + } + + ecmd := <-edit => + editor(f, ecmd); + tkcmd(t, FOCUS); + + command := <-cmd => + (nil, c) := sys->tokenize(command, " "); + case hd c { + * => + sys->print("unknown command %s\n", command); + "File" => + filemenu(t, 1, f.fontsok); + "Ext" => + extmenu(t); + "new" => + (name, ok, nil) := getfilename(ctxt, t, "file for new window", f.name, 1, 0, 0); + if(ok) + spawn brutus(ctxt, name); + "open" => + if(f.dirty){ + action := confirm(ctxt, t, nameof(f)+" is dirty", 1); + case action { + "cancel" => + continue; + "exitclean" => + if(dumpfile(f, f.name, f.fontsused) < 0) + continue; + break; + "exitdirty" => + break; + } + } + (name, ok, nil) := getfilename(ctxt, t, "file for this window", f.name, 1, 0, 0); + if(ok && name!=""){ + setfilename(f, name); + if(loadfile(f, name) < 0){ + tkcmd(t, ".ft.t delete 1.0 end"); + dialog->prompt(ctxt, t.image, "error -fg red", + "Open file", + sys->sprint("Can't open %s:\n%r", name), + 0, "Continue"::nil); + } + } + "name" => + (name, ok, nil) := getfilename(ctxt, t, "remembered file name", f.name, 1, 0, 0); + if(ok){ + if(name != f.name){ + setfilename(f, name); + dirty(f, 1); + } + } + "write" => + (name, ok, sgml) := getfilename(ctxt, t, "file to write", f.name, 1, 1, f.fontsused); + if(ok && name!=""){ + if(f.name == ""){ + setfilename(f, name); + dirty(f, 1); + } + dumpfile(f, name, sgml); + } + "fonts" => + if(f.fontsok==0 && f.fontsused==0){ + action := confirm(ctxt, t, "Converting "+nameof(f)+" to SGML", 0); + case action { + "cancel" => + continue; + "exitdirty" => + usingfonts(f); + dirty(f, 1); + } + } + enablefonts(f, !f.fontsok); + "language" => + if(lang == "") + lang = "Hebrew"; + else + lang = ""; + "addext" => + ext := hd tl c; + (args, ok, nil) := getfilename(ctxt, t, "parameters for "+ext, "", 0, 0, 0); + if(ok){ + tkcmd(t, "cursor -image waiting; update"); + addextension(f, ext+" "+args, nil); + usingfonts(f); + dirty(f, 1); + tkcmd(t, "cursor -default; update"); + } + "select" => + central <-= (f, command); + "tag" => + tageditor(ctxt, f); + tkcmd(t, FOCUS); + "font" => + f.font = hd tl c; + tkcmd(t, ".b.Font configure -text "+f.font+";"+UPDATE); + f.fonttag = f.font+"."+string f.size; + configfont(f, f.fonttag); + if(changefont(f, f.font)) + dirty(f, 1); + "size" => + sz := hd tl c; + tkcmd(t, ".b.Size configure -text "+sz+"pt; update"); + f.size = int sz; + f.fonttag = f.font+"."+string f.size; + configfont(f, f.fonttag); + if(changesize(f, string f.size)) + dirty(f, 1); + "applyfont" => + f.applyfont = int tkcmd(t, "variable Applyfont"); + if(f.applyfont) + configfont(f, f.fonttag); + "applyfontnow" => + if(changefont(f, f.font)) + dirty(f, 1); + "applysizenow" => + if(changesize(f, string f.size)) + dirty(f, 1); + "applyfontsizenow" => + if(changefontsize(f, f.fonttag)) + dirty(f, 1); + "put" => + dumpfile(f, f.name, f.fontsused); + "focus" => + central <-= (f, "focus"); + } + + c := <-keys => + typing(f, c); + + c := <-but1 => + mousebut1(f, c); + + c := <-but2 => + mousebut2(f, c); + + c := <-but3 => + mousebut3(f, c); + + c := <-drag => + if(len c < 6 || c[0:5] != "path=") + break; + spawn brutus(ctxt, c[5:]); + } +} + +kbdremap(c: int) : (int, int) +{ + tab: array of Remaptab; + + dir := 1; + case lang{ + "" => + return (c, dir); + "Hebrew" => + tab = hebrewtab; + dir = -1; + * => + sys->print("unknown language %s\n", lang); + return (c, dir); + } + for(i:=0; i<len tab; i++) + if(c == tab[i].in) + return (tab[i].out, dir); + return (c, 1); +} + +typing(f: ref File, c: string): int +{ + t := f.tk; + char := c[1]; + if(char == '\\') + char = c[2]; + update := ";.ft.t see insert;"+UPDATE; + if(char != ESC) + cut(f, 1); + case char { + * => + dir := 1; + if(c[1] != '\\') # safe character; remap it + (c[1], dir) = kbdremap(char); + s := ".ft.t insert insert "+c; + if(dir < 0) + s += ";.ft.t mark set insert insert-1c"; + if(f.applyfont){ + usingfonts(f); + s += f.fonttag; + } + tkcmd(t, s+update); + if(f.fontsused && f.applyfont==0){ + # nasty goo to make sure we don't insert text without a font tag; + # must ask after the fact if default rules set a tag. + names := tkcmd(t, ".ft.t tag names insert-1chars"); + if(!somefont(names)) + tkcmd(t, ".ft.t tag add "+DEFTAG+" insert-1chars"); + } + dirty(f, 1); + ESC => + if(nullsel(t)) + tkcmd(t, ".ft.t tag add sel typingstart insert;"+ + ".ft.t mark set typingstart insert"); + else + cut(f, 1); + tkcmd(t, UPDATE); + BS => + bs(f, "c"); + BSL => + bs(f, "l"); + BSW => + bs(f, "w"); + } + return char; +} + +bs(f: ref File, c: string) +{ + if(!insat(f.tk, "1.0")){ + tkcmd(f.tk, ".ft.t tkTextDelIns -"+c+";.ft.t see insert;"+UPDATE); + dirty(f, 1); + } +} + +mousebut1(f: ref File, c: string) +{ + f.button1 = (c == "pressed"); + f.button3 = 0; # abort any pending button 3 action + tkcmd(f.tk, ".ft.t mark set typingstart insert"); +} + +mousebut2(f: ref File, c: string) +{ + if(f.button1){ + cut(f, 1); + tk->cmd(f.tk, UPDATE); + }else{ + (nil, l) := sys->tokenize(c, " "); + x := int hd l - 50; + y := int hd tl l - int tk->cmd(f.tk, ".m yposition "+menuindex) - 10; +# tkcmd(f.tk, "focus .ft.t"); + tkcmd(f.tk, ".m activate "+menuindex+"; .m post "+string x+" "+string y+ + "; update"); + } +} + +mousebut3(f: ref File, c: string) +{ + t := f.tk; + if(c == "pressed"){ + f.button3 = 1; + if(f.button1){ + paste(f); + tk->cmd(t, "update"); + } + return; + } + if(!plumbed || f.button3==0 || f.button1!=0) + return; + f.button3 = 0; + # Plumb message triggered by release of button 3 + (nil, l) := sys->tokenize(c, " "); + x := int hd tl l; + y := int hd tl tl l; + index := tk->cmd(t, ".ft.t index @"+string x+","+string y); + selindex := tk->cmd(t, ".ft.t tag ranges sel"); + if(selindex != "") + insel := tk->cmd(t, ".ft.t compare sel.first <= "+index)=="1" && + tk->cmd(t, ".ft.t compare sel.last >= "+index)=="1"; + else + insel = 0; + attr := ""; + if(insel) + text := tk->cmd(t, ".ft.t get sel.first sel.last"); + else{ + # have line with text in it + # now extract whitespace-bounded string around click + (nil, w) := sys->tokenize(index, "."); + charno := int hd tl w; + left := tk->cmd(t, ".ft.t index {"+index+" linestart}"); + right := tk->cmd(t, ".ft.t index {"+index+" lineend}"); + line := tk->cmd(t, ".ft.t get "+left+" "+right); + for(i:=charno; i>0; --i) + if(line[i-1]==' ' || line[i-1]=='\t') + break; + for(j:=charno; j<len line; j++) + if(line[j]==' ' || line[j]=='\t') + break; + text = line[i:j]; + attr = "click="+string (charno-i); + } + msg := ref Msg( + "Brutus", + "", + directory(f), + "text", + attr, + array of byte text); + if(msg.send() < 0) + sys->fprint(sys->fildes(2), "brutus: plumbing write error: %r\n"); +} + +directory(f: ref File): string +{ + for(i:=len f.name; --i>=0;) + if(f.name[i] == '/'){ + if(i == 0) + i++; + return f.name[0:i]; + } + return curdir; +} + +enablefonts(f: ref File, enable: int) +{ + for(i:=0; i<len fontbuts; i++) + tkcmd(f.tk, fontbuts[i] + " configure -state "+enabled[enable]); + tkcmd(f.tk, "update"); + f.fontsok = enable; +} + +filemenu(t: ref tk->Toplevel, buttons, fontsok: int) +{ + tkcmd(t, "menu .b.Filemenu"); + tkcmd(t, ".b.Filemenu add command -label New -command {send cmd new}"); + if(buttons){ + tkcmd(t, ".b.Filemenu add command -label Open -command {send cmd open}"); + tkcmd(t, ".b.Filemenu add command -label Name -command {send cmd name}"); + tkcmd(t, ".b.Filemenu add command -label Write -command {send cmd write}"); + if(fontsok) + pre := "Dis"; + else + pre = "En"; + tkcmd(t, ".b.Filemenu add command -label {" + +pre+"able Fonts} -command {send cmd fonts}"); + if(lang == "") + pre = "En"; + else + pre = "Dis"; + tkcmd(t, ".b.Filemenu add command -label {" + +pre+"able Hebrew} -command {send cmd language}"); + } + tkcmd(t, ".b.Filemenu add command -label {["+Name+"]} -command {send cmd select 0}"); + if(files != nil) + for(i:=1; i<len files; i++){ + name := nameof(files[i]); + if(files[i].dirty) + name = "{' "+name+"}"; + else + name = "{ "+name+"}"; + tkcmd(t, ".b.Filemenu add command -label "+name+ + " -command {send cmd select "+string i+"}"); + } + tkcmd(t, "bind .b.Filemenu <Unmap> {destroy .b.Filemenu}"); + x := tk->cmd(t, ".ft.scroll cget actx"); + y := tk->cmd(t, ".ft.scroll cget acty"); + tkcmd(t, ".b.Filemenu post "+x+" "+y+"; grab set .b.Filemenu; update"); +} + +extmenu(t: ref tk->Toplevel) +{ + fd := sys->open(EXTDIR, Sys->OREAD); + if(fd == nil || ((n,dir):=sys->dirread(fd)).t0<=0){ + sys->print("%s: can't find extension directory %s: %r\n", Name, EXTDIR); + return; + } + + tkcmd(t, "menu .b.Extmenu"); + for(i:=0; i<n; i++){ + name := dir[i].name; + if(len name>4 && name[len name-4:]==".dis"){ + name = name[0:len name-4]; + tkcmd(t, ".b.Extmenu add command -label {Add "+name+ + "} -command {send cmd addext "+name+"}"); + } + } + + tkcmd(t, "bind .b.Extmenu <Unmap> {destroy .b.Extmenu}"); + x := tk->cmd(t, ".ft.scroll cget actx"); + y := tk->cmd(t, ".ft.scroll cget acty"); + tkcmd(t, ".b.Extmenu post "+x+" "+y+"; grab set .b.Extmenu; update"); +} + +basepath(file: string): (string, string) +{ + for(i := len file-1; i >= 0; i--) { + if(file[i] == '/') + return (file[0:i], file[i+1:]); + } + return (".", file); +} + +putbut(f: ref File) +{ + state := enabled[f.dirty]; + if(f.name != "") + tkcmd(f.tk, ".b.Put configure -state "+state+"; update"); +} + +dirty(f: ref File, nowdirty: int) +{ + if(f.isctl) + return; + old := f.dirty; + f.dirty = nowdirty; + if(old != nowdirty){ + setfilename(f, f.name); + putbut(f); + } +} + +setfilename(f: ref File, name: string) +{ + oldname := f.name; + f.name = name; + if(oldname=="" && name!="") + putbut(f); + name = Name + ": \"" +nameof(f)+ "\""; + if(f.dirty) + name += " (dirty)"; + tkclient->settitle(f.tk, name); + tkcmd(f.tk, UPDATE); + central <-= (f, "name"); +} + +configfont(f: ref File, tag: string) +{ + for(i:=0; i<NTAG; i++) + if(tag == tagname[i]){ + if(f.configed[i] == 0){ + tkcmd(f.tk, ".ft.t tag configure "+tag+" "+tagconfig[i]); + f.configed[i] = 1; + } + return; + } + sys->print("Brutus: can't configure font %s\n", tag); +} + +insat(t: ref Tk->Toplevel, mark: string): int +{ + return tkcmd(t, ".ft.t compare insert == "+mark) == "1"; +} + +isalnum(s: string): int +{ + if(s == "") + return 0; + c := s[0]; + if('a' <= c && c <= 'z') + return 1; + if('A' <= c && c <= 'Z') + return 1; + if('0' <= c && c <= '9') + return 1; + if(c == '_') + return 1; + if(c > 16rA0) + return 1; + return 0; +} + +editor(f: ref File, ecmd: string) +{ + + case ecmd { + "cut" => + menuindex = "0"; + cut(f, 1); + + "paste" => + menuindex = "1"; + paste(f); + + "snarf" => + menuindex = "2"; + if(nullsel(f.tk)) + return; + snarf(f); + + "look" => + menuindex = "3"; + look(f); + } + tkcmd(f.tk, UPDATE); +} + +nullsel(t: ref Tk->Toplevel): int +{ + return tkcmd(t, ".ft.t tag ranges sel") == ""; +} + +cut(f: ref File, snarfit: int) +{ + if(nullsel(f.tk)) + return; + dirty(f, 1); + if(snarfit) + snarf(f); + # sometimes when clicking fast, selection and insert point can + # separate. the only time this really matters is when typing into + # a double-clicked selection. it's easy to fix here. + tkcmd(f.tk, ".ft.t mark set insert sel.first;.ft.t delete sel.first sel.last"); +} + +snarf(f: ref File) +{ + # convert sel.first and sel.last to numeric forms because sgml() + # must clear selection to avoid <sel> tags in result. + (nil, sel) := sys->tokenize(tkcmd(f.tk, ".ft.t tag ranges sel"), " "); + snarftext = tkcmd(f.tk, ".ft.t get "+hd sel+" "+hd tl sel); + snarfsgml = sgml(f.tk, "-sgml", hd sel, hd tl sel); + tkclient->snarfput(snarftext); +} + +paste(f: ref File) +{ +# good question + snarftext = tkclient->snarfget(); + if(snarftext == "" && (f.fontsused == 0 || snarfsgml == nil)) + return; + cut(f, 0); + dirty(f, 1); + + t := f.tk; + start := tkcmd(t, ".ft.t index insert"); + if(f.fontsused == 0) + tkcmd(t, ".ft.t insert insert '"+snarftext); + else if(f.applyfont) + tkcmd(t, ".ft.t insert insert "+tk->quote(snarftext)+" "+f.fonttag); + else + insert(f, snarfsgml); + tkcmd(t, ".ft.t tag add sel "+start+" insert"); +} + +look(f: ref File) +{ + t := f.tk; + (sel0, sel1) := word(t); + if(sel0 == nil) + return; + text := tkcmd(t, ".ft.t get "+sel0+" "+sel1); + if(text == nil) + return; + tkcmd(t, "cursor -image waiting; update"); + search(nil, f, text, 0, 0); + tkcmd(t, "cursor -default; update"); +} + +# First time fonts are used explicitly, establish font tags for all extant text. +usingfonts(f: ref File) +{ + if(f.fontsused) + return; + tkcmd(f.tk, ".ft.t tag add "+DEFTAG+" 1.0 end"); + f.fontsused = 1; +} + +word(t: ref Tk->Toplevel): (string, string) +{ + start := "sel.first"; + end := "sel.last"; + if(nullsel(t)){ + insert := tkcmd(t, ".ft.t index insert"); + start = tkcmd(t, ".ft.t index {insert wordstart}"); + if(insert == start){ # tk's definition of 'wordstart' is bogus + # if at beginning, tk->cmd will return !error and a0 will be false. + a0 := isalnum(tk->cmd(t, ".ft.t get insert-1chars")); + a1 := isalnum(tk->cmd(t, ".ft.t get insert")); + if(a0==0 && a1==0) + return (nil, nil); + if(a1 == 0) + start = tkcmd(t, ".ft.t index {insert-1chars wordstart}"); + } + end = tkcmd(t, ".ft.t index {"+start+" wordend}"); + if(start == end) + return (nil, nil); + } + return (start, end); +} + +# Change the font associated with the selection +changefont(f: ref File, font: string): int +{ + t := f.tk; + (sel0, sel1) := word(f.tk); + mod := 0; + if(sel0 == nil) + return mod; + usingfonts(f); + for(i:=0; i<NFONT; i++){ + if(fontname[i] == font) + continue; + for(j:=0; j<NSIZE; j++){ + tag := fontname[i]+"."+sizename[j]; + start := sel0; + for(;;){ + range := tkcmd(t, ".ft.t tag nextrange "+tag+" "+start+" "+sel1); + if(len range > 0 && range[0] == '!') + break; + (nil, tt) := sys->tokenize(range, " "); + if(tt == nil) + break; + tkcmd(t, ".ft.t tag remove "+tag+" "+hd tt+" "+hd tl tt); + fs := font+"."+sizename[j]; + tkcmd(t, ".ft.t tag add "+fs+" "+hd tt+" "+hd tl tt); + configfont(f, fs); + start = hd tl tt; + mod = 1; + } + } + } + tkcmd(t, UPDATE); + return mod; +} + +# See if tag list includes a font name +somefont(tag: string): int +{ + (nil, tt) := sys->tokenize(tag, " "); + for(; tt!=nil; tt=tl tt) + for(i:=0; i<NFONT*NSIZE; i++){ + if(tagname[i] == hd tt) + return 1; + } + return 0; +} + +# Change the size associated with the selection +changesize(f: ref File, size: string): int +{ + t := f.tk; + (sel0, sel1) := word(f.tk); + mod := 0; + if(sel0 == nil) + return mod; + usingfonts(f); + for(i:=0; i<NFONT; i++){ + for(j:=0; j<NSIZE; j++){ + if(sizename[j] == size) + continue; + tag := fontname[i]+"."+sizename[j]; + start := sel0; + for(;;){ + range := tkcmd(t, ".ft.t tag nextrange "+tag+" "+start+" "+sel1); + if(len range > 0 && range[0] == '!') + break; + (nil, tt) := sys->tokenize(range, " "); + if(tt == nil) + break; + tkcmd(t, ".ft.t tag remove "+tag+" "+hd tt+" "+hd tl tt); + fs := fontname[i]+"."+size; + tkcmd(t, ".ft.t tag add "+fs+" "+hd tt+" "+hd tl tt); + configfont(f, fs); + start = hd tl tt; + mod = 1; + } + } + } + tkcmd(t, UPDATE); + return mod; +} + +# Change the font and size associated with the selection +changefontsize(f: ref File, newfontsize: string): int +{ + t := f.tk; + (sel0, sel1) := word(f.tk); + if(sel0 == nil) + return 0; + usingfonts(f); + (nil, names) := sys->tokenize(tkcmd(t, ".ft.t tag names"), " "); + # clear old tags + tags := tagname[0:NFONT*NSIZE]; + for(l:=names; l!=nil; l=tl l) + for(i:=0; i<len tags; i++) + if(tags[i] == hd l) + tkcmd(t, ".ft.t tag remove "+hd l+" "+sel0+" "+sel1); + tkcmd(t, ".ft.t tag add "+newfontsize+" "+sel0+" "+sel1+"; update"); + return 1; +} + +listtostring(l: list of string): string +{ + s := "{"; + while(l != nil){ + if(len s == 1) + s += hd l; + else + s += " " + hd l; + l = tl l; + } + s += "}"; + return s; +} + +# splitl based on indices rather than slices. this version returns char +# position of the matching character. +splitl(str: string, i, j: int, pat: string): int +{ + while(i < j){ + c := str[i]; + for(k:=len pat-1; k>=0; k--) + if(c == pat[k]) + return i; + i++; + } + return i; +} + +# splitstrl based on indices rather than slices. this version returns char +# position of the beginning of the matching string. +splitstrl(str: string, i, j: int, pat: string): int +{ + l := len pat; + if(l == 0) # shouldn't happen, but be safe + return j; + first := pat[0]; + while(i <= j-l){ + # check first char for speed + if(str[i] == first){ + for(k:=1; k<l && str[i+k]==pat[k]; k++) + ; + if(k == l) + return i; + } + i++; + } + return j; +} + +# place the text, as annotated by SGML tags, into document +# where indicated by insert mark +insert(f: ref File, sgml: string) +{ + taglist: list of string; + + t := f.tk; + usingfonts(f); + if(f.applyfont) + taglist = f.fonttag :: taglist; + tag := listtostring(taglist); + end := len sgml; + j: int; + for(i:=0; i<end; i=j){ + j = splitl(sgml, i, end, "<&"); + tt := tag; + if(tt=="" || tt=="{}") + tt = DEFTAG; # can happen e.g. when pasting plain text + if(j > i) + tkcmd(t, ".ft.t insert insert "+tk->quote(sgml[i:j])+" "+tt); + if(j < end) + case sgml[j] { + '&' => + if(j+4<=end && sgml[j:j+4]=="<"){ + tkcmd(t, ".ft.t insert insert "+"{<} "+tt); + j += 4; + }else{ + tkcmd(t, ".ft.t insert insert {&} "+tt); + j += 1; + } + '<' => + (nc, newtag, on) := tagstring(sgml, j, end); + if(nc < 0){ + tkcmd(t, ".ft.t insert insert "+"{<} "+tt); + j += 1; + }else if(len newtag>9 && newtag[0:10]=="Extension "){ + addextension(f, newtag[10:], taglist); + j += nc; + }else if(len newtag>9 && newtag[0:7]=="Window "){ + repostextension(f, newtag[7:], taglist); + j += nc; + }else{ + if(on){ + taglist = newtag :: taglist; + configfont(f, newtag); + }else{ + taglist = drop(taglist, newtag); + if(f.applyfont && hasfonts(taglist)==0) + taglist = f.fonttag :: taglist; + } + j += nc; + tag = listtostring(taglist); + } + } + } +} + +drop(l: list of string, s: string): list of string +{ + n: list of string; + while(l != nil){ + if(s != hd l) + n = hd l :: n; + l = tl l; + } + return n; +} + +extid := 0; +addextension(f: ref File, s: string, taglist: list of string) +{ + for(i:=0; i<len s; i++) + if(s[i] == ' ') + break; + if(i == 0 || i == len s){ + sys->print("Brutus: badly formed extension %s\n", s); + return; + } + modname := s[0:i]; + s = s[i+1:]; + + mod: Brutusext; + for(el:=f.extensions; el!=nil; el=tl el) + if(modname == (hd el).modname){ + mod = (hd el).mod; + break; + } + + if(mod == nil){ + file := modname; + if(i < 4 || file[i-4:i] != ".dis") + file += ".dis"; + if(file[0] != '/') + file = "/dis/wm/brutus/" + file; + mod = load Brutusext file; + if(mod == nil){ + sys->print("%s: can't load module %s: %r\n", Name, file); + return; + } + } + mkextension(f, mod, modname, s, taglist); +} + +repostextension(f: ref File, tkname: string, taglist: list of string) +{ + mod: Brutusext; + for(el:=f.extensions; el!=nil; el=tl el) + if(tkname == (hd el).tkname){ + mod = (hd el).mod; + break; + } + if(mod == nil){ + sys->print("Brutus: can't find extension widget %s: %r\n", tkname); + return; + } + + mkextension(f, mod, (hd el).modname, (hd el).args, taglist); +} + +mkextension(f: ref File, mod: Brutusext, modname, args: string, taglist: list of string) +{ + t := f.tk; + + name := ".ext"+string extid++; + mod->init(sys, draw, bufio, tk, tkclient); + err := mod->create(f.name, t, name, args); + if(err != ""){ + sys->print("%s: can't create extension widget %s: %s\n", Name, modname, err); + return; + } + tkcmd(t, ".ft.t window create insert -window "+name); + while(taglist != nil){ + tkcmd(t, ".ft.t tag add "+hd taglist+" "+name); + taglist = tl taglist; + } + f.extensions = ref Ext(name, modname, mod, args) :: f.extensions; +} + +# rewrite <window .ext1> tags into <Extension module args> +extrewrite(f: ref File, sgml: string): string +{ + if(f.extensions == nil) + return sgml; + + new := ""; + + end := len sgml; + j: int; + for(i:=0; i<end; i=j){ + j = splitstrl(sgml, i, end, "<Window "); + if(j > i) + new += sgml[i:j]; + if(j < end){ + j += 8; + for(k:=j; sgml[k]!='>' && k<end; k++) + ; + tkname := sgml[j:k]; + for(el:=f.extensions; el!=nil; el=tl el) + if((hd el).tkname == tkname) + break; + if(el == nil) + sys->print("%s: unrecognized extension %s\n", Name, tkname); + else{ + e := hd el; + new += "<Extension "+e.modname+" "+e.args+">"; + } + j = k+1; # skip '>' + } + } + return new; +} + +hasfonts(l: list of string): int +{ + for(i:=0; i<NFONT*NSIZE; i++) + for(ll:=l; ll!=nil; ll=tl ll) + if(hd ll == tagname[i]) + return 1; + return 0; +} + +# s[i] is known to be a less-than sign +tagstring(s: string, i, end: int): (int, string, int) +{ + tag: string; + + j := splitl(s, i+1, end, ">"); + if(j==end || s[j]!='>') + return (-1, "", 0); + nc := (j-i)+1; + on := 1; + if(s[i+1] == '/'){ + on = 0; + i++; + } + tag = s[i+1:j]; +# NEED TO CHECK VALIDITY OF TAG + return (nc, tag, on); +} + +sgml(t: ref Tk->Toplevel, flag, start, end: string): string +{ + # turn off selection, to avoid getting that in output + sel := tkcmd(t, ".ft.t tag ranges sel"); + if(sel != "") + tkcmd(t, ".ft.t tag remove sel "+sel); + s := tkcmd(t, ".ft.t dump "+flag+" "+start+" "+end); + if(sel != "") + tkcmd(t, ".ft.t tag add sel "+sel); + return s; +} + +loadfile(f: ref File, file: string): int +{ + f.size = DEFSIZE; + f.font = DEFFONTNAME; + f.fonttag = DEFTAG; + f.fontsused = 0; + enablefonts(f, 0); + t := f.tk; + tkcmd(t, ".b.Font configure -text "+f.font); + tkcmd(t, ".b.Size configure -text "+string f.size+"pt"); + tkcmd(t, "cursor -image waiting; update"); + r := loadfile1(f, file); + tkcmd(t, "cursor -default"); + return r; +} + +loadfile1(f: ref File, file: string): int +{ + fd := bufio->open(file, Sys->OREAD); + if(fd == nil) + return -1; + (ok, dir) := sys->fstat(fd.fd); + if(ok < 0){ + fd.close(); + return -1; + } + l := int dir.length; + a := array[l] of byte; + n := fd.read(a, len a); + fd.close(); + if(n != len a) + return -1; + t := f.tk; + tkcmd(t, ".ft.t delete 1.0 end"); + if(len a>=7 && string a[0:7]=="<SGML>\n") + insert(f, string a[7:n]); + else + tkcmd(t, ".ft.t insert 1.0 '"+string a[0:n]); + dirty(f, 0); + tkcmd(t, ".ft.t mark set insert 1.0; update"); + return 1; +} + +dumpfile(f: ref File, file: string, sgml: int): int +{ + tkcmd(f.tk, "cursor -image waiting"); + r := dumpfile1(f, file, sgml); + tkcmd(f.tk, "cursor -default"); + return r; +} + +dumpfile1(f: ref File, file: string, sgml: int): int +{ + if(writefile(f, file, sgml) < 0){ + dialog->prompt(ctxt, f.tk.image, "error -fg red", + "Write file", + sys->sprint("Can't write %s:\n%r", file), + 0, "Continue"::nil); + tkcmd(f.tk, FOCUS); + return -1; + } + return 1; +} + +writefile(f: ref File, file: string, sgmlfmt: int): int +{ + if(file == "") + return -1; + fd := bufio->create(file, Sys->OWRITE, 8r666); + if(fd == nil) + return -1; + + t := f.tk; + flag := ""; + if(sgmlfmt){ + flag = "-sgml"; + prefix := "<SGML>\n"; + if(f.fontsused == 0) + prefix += "<"+DEFTAG+">"; + x := array of byte prefix; + if(fd.write(x, len x) != len x){ + fd.close(); + return -1; + } + } + sgmltext := sgml(t, flag, "1.0", "end"); + if(sgmlfmt) + sgmltext = extrewrite(f, sgmltext); + a := array of byte sgmltext; + if(fd.write(a, len a) != len a){ + fd.close(); + return -1; + } + if(sgmlfmt && f.fontsused==0){ + suffix := array of byte ("</"+DEFTAG+">"); + if(fd.write(suffix, len suffix) != len suffix){ + fd.close(); + return -1; + } + } + if(fd.flush() < 0){ + fd.close(); + return -1; + } + fd.close(); + if(file == f.name){ + dirty(f, sgmlfmt!=f.fontsused); + tkcmd(t, UPDATE); + } + return 1; +} + +shutdown(s: ref Draw->Context, t: ref Tk->Toplevel): int +{ + for(i:=1; i<len files; i++){ + f := files[i]; + if(f.dirty){ + action := confirm(s, t, "file "+nameof(f)+" is dirty", 1); + case action { + "cancel" => + return 0; + "exitclean" => + if(dumpfile(f, f.name, f.fontsused) < 0) + return 0; + "exitdirty" => + break; + } + } + } + return 1; +} + +nameof(f: ref File): string +{ + s := f.name; + if(s == "") + s = "(unnamed)"; + return s; +} + +tkcmd(t: ref Tk->Toplevel, s: string): string +{ + res := tk->cmd(t, s); + if(len res > 0 && res[0] == '!') + sys->print("%s: tk error executing '%s': %s\n", Name, s, res); + return res; +} + +confirm_cfg := array[] of { + "frame .f -borderwidth 2 -relief groove -padx 3 -pady 3", + "frame .f.f", +# "label .f.f.l -bitmap error -foreground red", + "label .f.f.l -text Warning:", + "label .f.f.m", + "button .f.exitclean -text { Write and Proceed } -width 17w -command {send cmd exitclean}", + "button .f.exitdirty -text { Proceed } -width 17w -command {send cmd exitdirty}", + "button .f.cancel -text { Cancel } -width 17w -command {send cmd cancel}", + "pack .f.f.l .f.f.m -side left", + "pack .f.f .f.exitclean .f.exitdirty .f.cancel -padx 10 -pady 10", + "pack .f", +}; + +widget(parent: ref Tk->Toplevel, ctxt: ref Draw->Context, cfg: array of string): ref Tk->Toplevel +{ + x := int tk->cmd(parent, ". cget -x"); + y := int tk->cmd(parent, ". cget -y"); + where := sys->sprint("-x %d -y %d ", x+45, y+25); + (t,nil) := tkclient->toplevel(ctxt, where+SETFONT+" -borderwidth 2 -relief raised", "", tkclient->Plain); + tkcmds(t, cfg); + return t; +} + +tkcmds(top: ref Tk->Toplevel, a: array of string) +{ + for(i := 0; i < len a; i++) + v := tk->cmd(top, a[i]); +} + +confirm(ctxt: ref Draw->Context, parent: ref Tk->Toplevel, message: string, write: int): string +{ + s := confirm1(ctxt, parent, message, write); + tkcmd(parent, FOCUS); + return s; +} + +confirm1(ctxt: ref Draw->Context, parent: ref Tk->Toplevel, message: string, write: int): string +{ + t := widget(parent, ctxt, confirm_cfg); + tkcmd(t, ".f.f.m configure -text '"+message); + if(write == 0) + tkcmd(t, "destroy .f.exitclean"); + tkcmd(t, UPDATE); + cmd := chan of string; + tk->namechan(t, cmd, "cmd"); + return <-cmd; +} + +getfilename_cfg := array[] of { + "frame .f", + "label .f.Message", + "entry .f.Name -width 25w", + "checkbutton .f.SGML -text { Write SGML } -variable SGML", + "button .f.Ok -text { OK } -width 14w -command {send cmd ok}", + "button .f.Browse -text { Browse } -width 14w -command {send cmd browse}", + "button .f.Cancel -text { Cancel } -width 14w -command {send cmd cancel}", + "bind .f.Name <Control-j> {send cmd ok}", + "pack .f.Message .f.Name .f.SGML .f.Ok .f.Browse .f.Cancel -padx 10 -pady 10", + "pack .f", + "focus .f.Name", +}; + +getfilename(ctxt: ref Draw->Context, parent: ref Tk->Toplevel, message, name: string, browse, sgml, nowsgml: int): (string, int, int) +{ + (s, i, issgml) := getfilename1(ctxt, parent, message, name, browse, sgml, nowsgml); + tkcmd(parent, FOCUS); + return (s, i, issgml); +} + +getfilename1(ctxt: ref Draw->Context, parent: ref Tk->Toplevel, message, name: string, browse, sgml, nowsgml: int): (string, int, int) +{ + t := widget(parent, ctxt, getfilename_cfg); + tkcmds(t, getfilename_cfg); + + tkcmd(t, ".f.Message configure -text '"+message); + tk->cmd(t, ".f.Name insert 0 "+name); + if(browse == 0) + tkcmd(t, "destroy .f.Browse"); + if(sgml == 0) + tkcmd(t, "destroy .f.SGML"); + else if(nowsgml) + tkcmd(t, ".f.SGML select"); + tkcmd(t, UPDATE); + cmd := chan of string; + tk->namechan(t, cmd, "cmd"); + tkclient->onscreen(t, "exact"); + tkclient->startinput(t, "kbd"::"ptr"::nil); + for(;;) alt { + s := <-t.ctxt.kbd => + tk->keyboard(t, s); + s := <-t.ctxt.ptr => + tk->pointer(t, *s); + c := <-cmd => + case c { + "ok" => + return (tkcmd(t, ".f.Name get"), 1, int tkcmd(t, "variable SGML")); + "cancel" => + return ("", 0, 0); + "browse" => + name = tkcmd(t, ".f.Name get"); + (dir, path) := basepath(name); + + pat := list of { + "* (All files)", + "*.sgml (SGML dump files)", + "*.html (Web source files)", + "*.tex (Latex source files)", + "*.[bm] (Limbo source files)" + }; + + path = selectfile->filename(ctxt, parent.image, message, pat, dir); + if(path != "") + name = path; + tk->cmd(t, ".f.Name delete 0 end; .f.Name insert 0 "+name+";focus .f.Name; update"); + if(path != "") + return (name, 1, int tkcmd(t, "variable SGML")); + } + } +} + +tageditor(ctxt: ref Draw->Context, f: ref File) +{ + (start, end) := word(f.tk); + if(start == nil) + return; + cfg := array[100] of string; + i := 0; + cfg[i++] = "frame .f"; + (nil, names) := sys->tokenize(tkcmd(f.tk, ".ft.t tag names "+start), " "); + pack := "pack"; + set := array[NEXTRA] of int; + for(j:=0; j<NEXTRA; j++){ + n := tagname[j+NFONT*NSIZE]; + cfg[i++] = "checkbutton .f.c"+string j+" -variable c"+string j+ + " -text {"+n+"} -command {send cmd "+string j+"} -anchor w"; + pack += " .f.c"+string j; + set[j] = 0; + for(l:=names; l!=nil; l=tl l) + if(hd l == n){ + cfg[i++] = ".f.c"+string j+" select"; + set[j] = 1; + } + } + cfg[i++] = "button .f.Ok -text { OK } -width 6w -command {send cmd ok}"; + cfg[i++] = "button .f.Cancel -text { Cancel } -width 6w -command {send cmd cancel}"; + cfg[i++] = pack + " -padx 3 -pady 0 -fill x"; + cfg[i++] = "pack .f.Ok .f.Cancel -padx 2 -pady 2 -side left"; + cfg[i++] = "pack .f; grab set .f; update"; + t := widget(f.tk, ctxt, cfg[0:i]); + cmd := chan of string; + tk->namechan(t, cmd, "cmd"); + + loop: + for(;;){ + case c := <-cmd { + "ok" => + break loop; + "cancel" => + return; + * => + j = int c; + set[j] = (tkcmd(t, "variable c"+c) == "1"); + } + } + for(j=0; j<NEXTRA; j++){ + s := tagname[j+NFONT*NSIZE]; + if(set[j]){ + configfont(f, s); + tkcmd(f.tk, ".ft.t tag add "+s+" "+start+" "+end); + }else + tkcmd(f.tk, ".ft.t tag remove "+s+" "+start+" "+end); + } + dirty(f, 1); + usingfonts(f); + tkcmd(f.tk, UPDATE); +} + +plumbpid: int; +plumbproc(plumbc: chan of (string, string)) +{ + plumbpid = sys->pctl(0, nil); + + for(;;){ + msg := Msg.recv(); + if(msg == nil){ + sys->print("Brutus: can't read /chan/plumb.edit: %r\n"); + plumbpid = 0; + return; + } + if(msg.kind != "text"){ + sys->print("Brutus: can't interpret '%s' kind of message\n", msg.kind); + continue; + } + text := string msg.data; + n := len text; + addr := ""; + for(j:=0; j<n; j++) + if(text[j] == ':'){ + addr = text[j+1:]; + break; + } + file := text[0:j]; + if(len file>0 && file[0]!='/' && len msg.dir>0){ + if(msg.dir[len msg.dir-1] == '/') + file = msg.dir+file; + else + file = msg.dir+"/"+file; + } + plumbc <-= (file, addr); + } +} + +killplumb() +{ + if(plumbed == 0) + return; + plumbmsg->shutdown(); + if(plumbpid <= 0) + return; + fname := sys->sprint("#p/%d/ctl", plumbpid); + fd := sys->open(fname, sys->OWRITE); + if(fd != nil) + sys->write(fd, array of byte "kill\n", 8); +} + +lastpat: string; + +execute(cmdwin: ref Tk->Toplevel, f: ref File, cmd: string) +{ + if(len cmd>1 && cmd[len cmd-1]=='\n') + cmd = cmd[0:len cmd-1]; + if(cmd == "") + return; + if(cmd[0] == '/' || cmd[0]=='?'){ + search(cmdwin, f, cmd[1:], cmd[0]=='?', 1); + return; + } + for(i:=0; i<len cmd; i++) + if(cmd[i]<'0' || '9'<cmd[i]){ + sys->print("bad command %s\n", cmd); + return; + } + t := f.tk; + line := int cmd; + if(!nullsel(t)) + tkcmd(t, NOSEL); + tkcmd(t, ".ft.t tag add sel "+string line+".0 {"+string line+".0 lineend+1char}"); + tkcmd(t, ".ft.t mark set insert "+string line+".0; .ft.t see insert;update"); +} + +search(cmdwin: ref Tk->Toplevel, f: ref File, pat: string, backwards, uselast: int) +{ + t := f.tk; + if(pat == nil) + pat = lastpat; + else if(uselast) + lastpat = pat; + if(pat == nil){ + error(cmdwin, "no pattern"); + return; + } + cmd := ".ft.t search "; + if(backwards) + cmd += "-backwards "; + p := ""; + for(i:=0; i<len pat; i++){ + if(pat[i]== '\\' || pat[i]=='{') + p[len p] = '\\'; + p[len p] = pat[i]; + } + cmd += "{"+p+"} "; + null := nullsel(t); + if(null) + cmd += "insert"; + else if(backwards) + cmd += "sel.first"; + else + cmd += "sel.last"; + s := tk->cmd(t, cmd); + if(s == "") + error(cmdwin, "not found"); + else{ + if(!null) + tkcmd(t, NOSEL); + tkcmd(t, ".ft.t tag add sel "+s+" "+s+"+"+string len pat+"chars"); + tkcmd(t, ".ft.t mark set insert "+s+";.ft.t see insert; update"); + } +} + +showaddr(f: ref File, addr: string) +{ + if(addr=="") + return; + t := f.tk; + if(addr[0]=='#' || ('0'<=addr[0] && addr[0]<='9')){ + # UGLY! just do line and character numbers until we get a + # decent command/address interface set up. + if(!nullsel(t)) + tkcmd(t, NOSEL); + if(addr[0] == '#'){ + addr = addr[1:]; + tkcmd(t, ".ft.t mark set insert {1.0+"+addr+"char}; .ft.t see insert;update"); + }else{ + tkcmd(t, ".ft.t tag add sel "+addr+".0 {"+addr+".0 lineend+1char}"); + tkcmd(t, ".ft.t mark set insert "+addr+".0; .ft.t see insert;update"); + } + } +} + +error(cmdwin: ref Tk->Toplevel, err: string) +{ + if(cmdwin == nil) + return; + tkcmd(cmdwin, ".ft.t insert end '?"+err+"\n"); + if(!nullsel(cmdwin)) + tkcmd(cmdwin, NOSEL); + tkcmd(cmdwin, ".ft.t mark set insert end"); + tkcmd(cmdwin, ".ft.t mark set typingstart end; update"); +} diff --git a/appl/wm/brutus/excerpt.b b/appl/wm/brutus/excerpt.b new file mode 100644 index 00000000..ccb0e647 --- /dev/null +++ b/appl/wm/brutus/excerpt.b @@ -0,0 +1,264 @@ +implement Brutusext; + +# <Extension excerpt file [start [end]]> + +Name: con "Brutus entry"; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + Context: import draw; + +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; + +include "regex.m"; + regex: Regex; + +include "tk.m"; + tk: Tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "brutus.m"; +include "brutusext.m"; + +init(s: Sys, d: Draw, b: Bufio, t: Tk, w: Tkclient) +{ + sys = s; + draw = d; + bufio = b; + tk = t; + tkclient = w; + regex = load Regex Regex->PATH; +} + +create(parent: string, t: ref Tk->Toplevel, name, args: string): string +{ + (text, err) := gather(parent, args); + if(err != nil) + return err; + err = tk->cmd(t, "text "+name+" -tabs {1c} -wrap none -font /fonts/pelm/latin1.9.font"); + if(len err > 0 && err[0] == '!') + return err; + (n, maxw) := nlines(text); + if(maxw < 40) + maxw = 40; + if(maxw > 70) + maxw = 70; + tk->cmd(t, name+" configure -height "+string n+".01h -width "+string maxw+"w"); + return tk->cmd(t, name+" insert end '"+text); +} + +gather(parent, args: string): (string, string) +{ + argl := tokenize(args); + nargs := len argl; + if(nargs == 0) + return (nil, "usage: excerpt [start] [end] file"); + file := hd argl; + argl = tl argl; + b := bufio->open(fullname(parent, file), Bufio->OREAD); + if(b == nil) + return (nil, sys->sprint("can't open %s: %r", file)); + start := ""; + end := ""; + if(argl != nil){ + start = hd argl; + if(tl argl != nil) + end = hd tl argl; + } + (text, err) := readall(b, start, end); + return (text, err); +} + +tokenize(s: string): list of string +{ + l: list of string; + i := 0; + a := ""; + first := 1; + while(i < len s){ + (a, i) = arg(first, s, i); + if(a != "") + l = a :: l; + first = 0; + } + rl: list of string; + while(l != nil){ + rl = hd l :: rl; + l = tl l; + } + return rl; +} + +arg(first: int, s: string, i: int): (string, int) +{ + while(i<len s && (s[i]==' ' || s[i]=='\t')) + i++; + if(i == len s) + return ("", i); + j := i+1; + if(first || s[i] != '/'){ + while(j<len s && (s[j]!=' ' && s[j]!='\t')) + j++; + return (s[i:j], j); + } + while(j<len s && s[j]!='/') + if(s[j++] == '\\') + j++; + if(j == len s) + return (s[i:j], j); + return (s[i:j+1], j+1); +} + +readall(b: ref Iobuf, start, end: string): (string, string) +{ + revlines : list of string = nil; + appending := 0; + lineno := 0; + for(;;){ + line := b.gets('\n'); + if(line == nil) + break; + lineno++; + if(!appending){ + m := match(start, line, lineno); + if(m < 0) + return (nil, "error in pattern"); + if(m) + appending = 1; + } + if(appending){ + revlines = line :: revlines; + if(start != ""){ + m := match(end, line, lineno); + if(m < 0) + return (nil, "error in pattern"); + if(m) + break; + } + } + } + return (prep(revlines), ""); +} + +prep(revlines: list of string) : string +{ + tabstrip := -1; + for(l:=revlines; l != nil; l = tl l) { + s := hd l; + if(len s > 1) { + n := nleadtab(hd l); + if(tabstrip == -1 || n < tabstrip) + tabstrip = n; + } + } + # remove tabstrip tabs from each line + # and concatenate in reverse order + ans := ""; + for(l=revlines; l != nil; l = tl l) { + s := hd l; + if(tabstrip > 0 && len s > 1) + s = s[tabstrip:]; + ans = s + ans; + } + return ans; +} + +nleadtab(s: string) : int +{ + slen := len s; + for(i:=0; i<slen; i++) + if(s[i] != '\t') + break; + return i; +} + +nlines(s: string): (int, int) +{ + n := 0; + maxw := 0; + w := 0; + for(i:=0; i<len s; i++) { + if(s[i] == '\n') { + n++; + if(w > maxw) + maxw = w; + w = 0; + } + else if(s[i] == '\t') + w += 5; + else + w++; + } + if(len s>0 && s[len s-1]!='\n') { + n++; + if(w > maxw) + maxw = w; + } + return (n, maxw); +} + +match(pat, line: string, lineno: int): int +{ + if(pat == "") + return 1; + case pat[0] { + '0' to '9' => + return int pat <= lineno; + '/' => + if(len pat < 3 || pat[len pat-1]!='/') + return -1; + re := compile(pat[1:len pat-1]); + if(re == nil) + return -1; + match := regex->execute(re, line); + return match != nil; + } + return -1; +} + +pats: list of (string, Regex->Re); + +compile(pat: string): Regex->Re +{ + l := pats; + while(l != nil){ + (p, r) := hd l; + if(p == pat) + return r; + l = tl l; + } + (re, nil) := regex->compile(pat, 0); + pats = (pat, re) :: pats; + return re; +} + +cook(parent: string, nil: int, args: string): (ref Brutusext->Celem, string) +{ + (text, err) := gather(parent, args); + if(err != nil) + return (nil, err); + el1 := ref Brutusext->Celem(Brutusext->Text, text, nil, nil, nil, nil); + el2 := ref Brutusext->Celem(Brutus->Type*Brutus->NSIZE+Brutus->Size10, "", el1, nil, nil, nil); + el1.parent = el2; + ans := ref Brutusext->Celem(Brutus->Example, "", el2, nil, nil, nil); + el2.parent = ans; + return (ans, ""); +} + +fullname(parent, file: string): string +{ + if(len parent==0 || (len file>0 && (file[0]=='/' || file[0]=='#'))) + return file; + + for(i:=len parent-1; i>=0; i--) + if(parent[i] == '/') + return parent[0:i+1] + file; + return file; +} diff --git a/appl/wm/brutus/image.b b/appl/wm/brutus/image.b new file mode 100644 index 00000000..906c668d --- /dev/null +++ b/appl/wm/brutus/image.b @@ -0,0 +1,259 @@ +implement Brutusext; + +# <Extension image imagefile> + +Name: con "Brutus image"; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + Context, Image, Display, Rect: import draw; + +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; + +include "imagefile.m"; + imageremap: Imageremap; + readgif: RImagefile; + readjpg: RImagefile; + +include "tk.m"; + tk: Tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "pslib.m"; + pslib: Pslib; + +include "brutus.m"; +include "brutusext.m"; + +stderr: ref Sys->FD; + +Cache: adt +{ + args: string; + name: string; + r: Rect; +}; + +init(s: Sys, d: Draw, b: Bufio, t: Tk, w: Tkclient) +{ + sys = s; + draw = d; + bufio = b; + tk = t; + tkclient = w; + imageremap = load Imageremap Imageremap->PATH; + stderr = sys->fildes(2); +} + +cache: list of ref Cache; + +create(parent: string, t: ref Tk->Toplevel, name, args: string): string +{ + if(imageremap == nil) + return sys->sprint(Name + ": can't load remap: %r"); + display := t.image.display; + file := args; + + for(cl:=cache; cl!=nil; cl=tl cl) + if((hd cl).args == args) + break; + + c: ref Cache; + if(cl != nil) + c = hd cl; + else{ + (im, mask, err) := loadimage(display, parent, file); + if(err != "") + return err; + imagename := name+file; + err = tk->cmd(t, "image create bitmap "+imagename); + if(len err > 0 && err[0] == '!') + return err; + err = tk->putimage(t, imagename, im, mask); + if(len err > 0 && err[0] == '!') + return err; + c = ref Cache(args, imagename, im.r); + cache = c :: cache; + } + + err := tk->cmd(t, "canvas "+name+" -height "+string c.r.dy()+" -width "+string c.r.dx()); + if(len err > 0 && err[0] == '!') + return err; + err = tk->cmd(t, name+" create image 0 0 -anchor nw -image "+c.name); + + return ""; +} + +loadimage(display: ref Display, parent, file: string) : (ref Image, ref Image, string) +{ + im := display.open(fullname(parent, file)); + mask: ref Image; + + if(im == nil){ + fd := bufio->open(fullname(parent, file), Bufio->OREAD); + if(fd == nil) + return (nil, nil, sys->sprint(Name + ": can't open %s: %r", file)); + + mod := filetype(file, fd); + if(mod == nil) + return (nil, nil, sys->sprint(Name + ": can't find decoder module for %s: %r", file)); + + (ri, err) := mod->read(fd); + if(ri == nil) + return (nil, nil, sys->sprint(Name + ": %s: %s", file, err)); + if(err != "") + sys->fprint(stderr, Name + ": %s: %s", file, err); + mask = transparency(display, ri); + + # if transparency is enabled, errdiff==1 is probably a mistake, + # but there's no easy solution. + (im, err) = imageremap->remap(ri, display, 1); + if(im == nil) + return (nil, nil, sys->sprint(Name+": remap %s: %s\n", file, err)); + if(err != "") + sys->fprint(stderr, Name+": remap %s: %s\n", file, err); + ri = nil; + } + return(im, mask, ""); +} + +cook(parent: string, fmt: int, args: string): (ref Brutusext->Celem, string) +{ + file := args; + ans : ref Brutusext->Celem = nil; + if(fmt == Brutusext->FHtml) { + s := "<IMG SRC=\"" + file + "\">"; + ans = ref Brutusext->Celem(Brutusext->Special, s, nil, nil, nil, nil); + } + else { + (rc, dir) := sys->stat(file); + if(rc < 0) + return (nil, "can't find " + file); + mtime := dir.mtime; + + # psfile name: in dir of file, with .ps suffix + psfile := file; + for(i := (len psfile)-1; i >= 0; i--) { + if(psfile[i] == '.') { + psfile = psfile[0:i]; + break; + } + } + psfile = psfile + ".ps"; + (rc, dir) = sys->stat(psfile); + if(rc < 0 || dir.mtime < mtime) { + iob := bufio->create(psfile, Bufio->OWRITE, 8r664); + if(iob == nil) + return (nil, "can't create " + psfile); + + display := draw->Display.allocate(""); + (im, mask, err) := loadimage(display, parent, file); + if(err != "") + return (nil, err); + pslib = load Pslib Pslib->PATH; + if(pslib == nil) + return (nil, "can't load Pslib"); + pslib->init(bufio); + pslib->writeimage(iob, im, 100); + iob.close(); + } + s := "\\epsfbox{" + psfile + "}\n"; + ans = ref Brutusext->Celem(Brutusext->Special, s, nil, nil, nil, nil); + } + return (ans, ""); +} + +fullname(parent, file: string): string +{ + if(len parent==0 || (len file>0 && (file[0]=='/' || file[0]=='#'))) + return file; + + for(i:=len parent-1; i>=0; i--) + if(parent[i] == '/') + return parent[0:i+1] + file; + return file; +} + +# +# rest of this is all borrowed from wm/view. +# should probably be packaged - perhaps in RImagefile? +# +filetype(file: string, fd: ref Iobuf): RImagefile +{ + if(len file>4 && file[len file-4:]==".gif") + return loadgif(); + if(len file>4 && file[len file-4:]==".jpg") + return loadjpg(); + + # sniff the header looking for a magic number + buf := array[20] of byte; + if(fd.read(buf, len buf) != len buf){ + sys->fprint(stderr, "View: can't read %s: %r\n", file); + return nil; + } + fd.seek(big 0, 0); + if(string buf[0:6]=="GIF87a" || string buf[0:6]=="GIF89a") + return loadgif(); + jpmagic := array[] of {byte 16rFF, byte 16rD8, byte 16rFF, byte 16rE0, + byte 0, byte 0, byte 'J', byte 'F', byte 'I', byte 'F', byte 0}; + for(i:=0; i<len jpmagic; i++) + if(jpmagic[i]>byte 0 && buf[i]!=jpmagic[i]) + break; + if(i == len jpmagic) + return loadjpg(); + return nil; +} + +loadgif(): RImagefile +{ + if(readgif == nil){ + readgif = load RImagefile RImagefile->READGIFPATH; + if(readgif == nil) + sys->fprint(stderr, "Brutus image: can't load readgif: %r\n"); + else + readgif->init(bufio); + } + return readgif; +} + +loadjpg(): RImagefile +{ + if(readjpg == nil){ + readjpg = load RImagefile RImagefile->READJPGPATH; + if(readjpg == nil) + sys->fprint(stderr, "Brutus image: can't load readjpg: %r\n"); + else + readjpg->init(bufio); + } + return readjpg; +} + +transparency(display: ref Display, r: ref RImagefile->Rawimage): ref Image +{ + if(r.transp == 0) + return nil; + if(r.nchans != 1) + return nil; + i := display.newimage(r.r, display.image.chans, 0, 0); + if(i == nil){ + return nil; + } + pic := r.chans[0]; + npic := len pic; + mpic := array[npic] of byte; + index := r.trindex; + for(j:=0; j<npic; j++) + if(pic[j] == index) + mpic[j] = byte 0; + else + mpic[j] = byte 16rFF; + i.writepixels(i.r, mpic); + return i; +} diff --git a/appl/wm/brutus/mkfile b/appl/wm/brutus/mkfile new file mode 100644 index 00000000..50a8734e --- /dev/null +++ b/appl/wm/brutus/mkfile @@ -0,0 +1,24 @@ +<../../../mkconfig + +TARG=\ + excerpt.dis\ + image.dis\ + mod.dis\ + table.dis\ + +MODULES=\ + +SYSMODULES=\ + brutus.m\ + brutusext.m\ + bufio.m\ + draw.m\ + html.m\ + imagefile.m\ + pslib.m\ + regex.m\ + string.m\ + +DISBIN=$ROOT/dis/wm/brutus + +<$ROOT/mkfiles/mkdis diff --git a/appl/wm/brutus/mod.b b/appl/wm/brutus/mod.b new file mode 100644 index 00000000..02f8e20e --- /dev/null +++ b/appl/wm/brutus/mod.b @@ -0,0 +1,335 @@ +implement Brutusext; + +# <Extension mod file> +# For module descriptions (in book) + +Name: con "Brutus mod"; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + Context, Font: import draw; + +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; + +include "tk.m"; + tk: Tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "string.m"; + S : String; + +include "brutus.m"; + Size8, Index, Roman, Italic, Bold, Type, NFONT, NSIZE: import Brutus; + +include "brutusext.m"; + +Mstring: adt +{ + s: string; + style: int; + indexed: int; + width: int; + next: cyclic ref Mstring; +}; + +fontname := array[NFONT] of { + "/fonts/lucidasans/unicode.7.font", + "/fonts/lucidasans/italiclatin1.7.font", + "/fonts/lucidasans/boldlatin1.7.font", + "/fonts/lucidasans/typelatin1.7.font", + }; + +fontswitch := array[NFONT] of { + "\\fontseries{m}\\rmfamily ", + "\\itshape ", + "\\fontseries{b}\\rmfamily ", + "\\fontseries{mc}\\ttfamily ", + }; + +fontref := array[NFONT] of ref Font; + +LEFTCHARS: con 45; +LEFTPIX: con LEFTCHARS*7; # 7 is width of lucidasans/typelatin1.7 chars + +init(s: Sys, d: Draw, b: Bufio, t: Tk, w: Tkclient) +{ + sys = s; + draw = d; + bufio = b; + tk = t; + tkclient = w; + S = load String String->PATH; +} + +create(parent: string, t: ref Tk->Toplevel, name, args: string): string +{ + (spec, err) := getspec(parent, args); + if(err != nil) + return err; + n := len spec; + if(n == 0) + return "empty spec"; + d := t.image.display; + for(i:=0; i < NFONT; i++) { + if(i == Bold || fontref[i] != nil) + continue; + fontref[i] = Font.open(d, fontname[i]); + if(fontref[i] == nil) + return sys->sprint("can't open font %s: %r\n", fontname[i]); + } + (nil, nil, rw, nil) := measure(spec, 1); + lw := LEFTPIX; + wd := lw + rw; + fnt := fontref[Roman]; + ht := n * fnt.height; + err = tk->cmd(t, "canvas " + name + " -width " + string wd + + " -height " + string ht + + " -font " + fontname[Type]); + if(len err > 0 && err[0] == '!') + return "problem creating canvas"; + y := 0; + xl := 0; + xr := lw; + for(l := spec; l != nil; l = tl l) { + (lm, rm) := hd l; + canvmstring(t, name, lm, xl, y); + canvmstring(t, name, rm, xr, y); + y += fnt.height; + } + tk->cmd(t, "update"); + return ""; +} + +canvmstring(t: ref Tk->Toplevel, canv: string, m: ref Mstring, x, y: int) +{ + # assume fonts all have same ascent + while(m != nil) { + pos := string x + " " + string y; + font := ""; + if(m.style != Type) + font = " -font " + fontname[m.style]; + e := tk->cmd(t, canv + " create text " + pos + " -anchor nw " + + font + " -text '" + m.s); + x += m.width; + m = m.next; + } +} + +getspec(parent, args: string) : (list of (ref Mstring, ref Mstring), string) +{ + (n, argl) := sys->tokenize(args, " "); + if(n != 1) + return (nil, "usage: " + Name + " file"); + b := bufio->open(fullname(parent, hd argl), Sys->OREAD); + if(b == nil) + return (nil, sys->sprint("can't open %s, the error was: %r", hd argl)); + mm : list of (ref Mstring, ref Mstring) = nil; + for(;;) { + s := b.gets('\n'); + if(s == "") + break; + (nf, fl) := sys->tokenize(s, " "); + if(nf == 0) + mm = (nil, nil) :: mm; + else { + sleft := ""; + sright := ""; + if(nf == 1) { + f := hd fl; + if(s[0] == '\t') + sright = f; + else + sleft = f; + } + else { + sleft = hd fl; + sright = hd tl fl; + } + mm = (tom(sleft, Type, Roman, 1), tom(sright, Italic, Type, 0)) :: mm; + } + } + ans : list of (ref Mstring, ref Mstring) = nil; + while(mm != nil) { + ans = hd mm :: ans; + mm = tl mm; + } + return (ans, ""); +} + +tom(str: string, defstyle, altstyle, doindex: int) : ref Mstring +{ + if(str == "") + return nil; + if(str[len str - 1] == '\n') + str = str[0: len str - 1]; + if(str == "") + return nil; + style := defstyle; + if(str[0] == '|') + style = altstyle; + (nil, l) := sys->tokenize(str, "|"); + dummy := ref Mstring; + last := dummy; + if(doindex && l != nil && S->prefix(" ", hd l)) + doindex = 0; # continuation line + while(l != nil) { + s := hd l; + m : ref Mstring; + if(doindex && style == defstyle) { + # index 'words' in defstyle, but not past : or ( + (sl,sr) := S->splitl(s, ":("); + while(sl != nil) { + a : string; + (a,sl) = S->splitl(sl, "a-zA-Z"); + if(a != "") { + m = ref Mstring(a, style, 0, 0, nil); + last.next = m; + last = m; + } + if(sl != "") { + b : string; + (b,sl) = S->splitl(sl, "^a-zA-Z0-9_"); + if(b != "") { + m = ref Mstring(b, style, 1, 0, nil); + last.next = m; + last = m; + } + } + } + if(sr != "") { + m = ref Mstring(sr, style, 0, 0, nil); + last.next = m; + last = m; + doindex = 0; + } + } + else { + m = ref Mstring(s, style, 0, 0, nil); + last.next = m; + last = m; + } + l = tl l; + if(style == defstyle) + style = altstyle; + else + style = defstyle; + } + return dummy.next; +} + +measure(spec: list of (ref Mstring, ref Mstring), pixels: int) : (int, ref Mstring, int, ref Mstring) +{ + maxl := 0; + maxr := 0; + maxlm : ref Mstring = nil; + maxrm : ref Mstring = nil; + while(spec != nil) { + (lm, rm) := hd spec; + spec = tl spec; + (maxl, maxlm) = measuremax(lm, maxl, maxlm, pixels); + (maxr, maxrm) = measuremax(rm, maxr, maxrm, pixels); + } + return (maxl, maxlm, maxr, maxrm); +} + +measuremax(m: ref Mstring, maxw: int, maxm: ref Mstring, pixels: int) : (int, ref Mstring) +{ + w := 0; + for(mm := m; mm != nil; mm = mm.next) { + if(pixels) + mm.width = fontref[mm.style].width(mm.s); + else + mm.width = len mm.s; + w += mm.width; + } + if(w > maxw) { + maxw = w; + maxm = m; + } + return (maxw, maxm); +} + +cook(parent: string, nil: int, args: string): (ref Celem, string) +{ + (spec, err) := getspec(parent, args); + if(err != nil) + return (nil, err); + (nil, maxlm, nil, nil) := measure(spec, 0); + ans := fontce(Roman); + tail := specialce("\\begin{tabbing}\\hspace{3in}\\=\\kill\n"); + tail = add(ans, nil, tail); + for(l := spec; l != nil; l = tl l) { + (lm, rm) := hd l; + tail = cookmstring(ans, tail, lm, 1); + tail = add(ans, tail, specialce("\\>")); + tail = cookmstring(ans, tail, rm, 0); + tail = add(ans, tail, specialce("\\\\\n")); + } + add(ans, tail, specialce("\\end{tabbing}")); + return (ans, ""); +} + +cookmstring(par, tail: ref Celem, m: ref Mstring, doindex: int) : ref Celem +{ + s := ""; + if(m == nil) + return tail; + while(m != nil) { + e := fontce(m.style); + te := textce(m.s); + add(e, nil, te); + if(doindex && m.indexed) { + ie := ref Celem(Index, nil, nil, nil, nil, nil); + add(ie, nil, e); + e = ie; + } + tail = add(par, tail, e); + m = m.next; + } + return tail; +} + +specialce(s: string) : ref Celem +{ + return ref Celem(Special, s, nil, nil, nil, nil); +} + +textce(s: string) : ref Celem +{ + return ref Celem(Text, s, nil, nil, nil, nil); +} + +fontce(sty: int) : ref Celem +{ + return ref Celem(sty*NSIZE+Size8, nil, nil, nil, nil, nil); +} + +add(par, tail: ref Celem, e: ref Celem) : ref Celem +{ + if(tail == nil) { + par.contents = e; + e.parent = par; + } + else + tail.next = e; + e.prev = tail; + return e; +} + +fullname(parent, file: string): string +{ + if(len parent==0 || (len file>0 && (file[0]=='/' || file[0]=='#'))) + return file; + + for(i:=len parent-1; i>=0; i--) + if(parent[i] == '/') + return parent[0:i+1] + file; + return file; +} diff --git a/appl/wm/brutus/table.b b/appl/wm/brutus/table.b new file mode 100644 index 00000000..24740a31 --- /dev/null +++ b/appl/wm/brutus/table.b @@ -0,0 +1,1478 @@ +implement Brutusext; + +# <Extension table tablefile> + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + Point, Font, Rect: import draw; + +include "tk.m"; + tk: Tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "bufio.m"; + +include "string.m"; + S: String; + +include "html.m"; + html: HTML; + Lex, Attr, RBRA, Data, Ttable, Tcaption, Tcol, Ttr, Ttd: import html; + +include "brutus.m"; + Size6, Size8, Size10, Size12, Size16, NSIZE, + Roman, Italic, Bold, Type, NFONT, NFONTTAG, + Example, List, Listelem, Heading, Nofill, Author, Title, + DefFont, DefSize, TitleFont, TitleSize, HeadingFont, HeadingSize: import Brutus; + +include "brutusext.m"; + +Name: con "Table"; + +# alignment types +Anone, Aleft, Acenter, Aright, Ajustify, Atop, Amiddle, Abottom, Abaseline: con iota; + +# A cell has a number of Lines, each of which has a number of Items. +# Each Item is a string in one font. +Item: adt +{ + itemid: int; # canvas text item id + s: string; + fontnum: int; # (style*NumSizes + size) + pos: Point; # nw corner of text item, relative to line origin + width: int; # of s, in pixels, when displayed in font + line: cyclic ref Line; # containing line + prev: cyclic ref Item; + next: cyclic ref Item; +}; + +Line: adt +{ + items: cyclic ref Item; + pos: Point; # nw corner of Line relative to containing cell; + height: int; + ascent: int; + width: int; + cell: cyclic ref Tablecell; # containing cell + next: cyclic ref Line; +}; + +Align: adt +{ + halign: int; + valign: int; +}; + +Tablecell: adt +{ + cellid: int; + content: array of ref Lex; + lines: cyclic ref Line; + rowspan: int; + colspan: int; + nowrap: int; + align: Align; + width: int; + height: int; + ascent: int; + row: int; + col: int; + pos: Point; # nw corner of cell, in canvas coords +}; + +Tablegcell: adt +{ + cell: ref Tablecell; + drawnhere: int; +}; + +Tablerow: adt +{ + cells: list of ref Tablecell; + height: int; + ascent: int; + align: Align; + pos: Point; + rule: int; # width of rule below row, if > 0 + ruleids: list of int; # canvas ids of lines used to draw rule +}; + +Tablecol: adt +{ + width: int; + align: Align; + pos: Point; + rule: int; # width of rule to right of col, if > 0 + ruleids: list of int; # canvas ids of lines used to draw rule +}; + +Table: adt +{ + nrow: int; + ncol: int; + ncell: int; + width: int; + height: int; + capcell: ref Tablecell; + border: int; + brectid: int; + cols: array of ref Tablecol; + rows: array of ref Tablerow; + cells: list of ref Tablecell; + grid: array of array of ref Tablegcell; + colw: array of int; + rowh: array of int; +}; + +# Font stuff + +DefaultFnum: con (DefFont*NSIZE + Size10); + +fontnames := array[NFONTTAG] of { + "/fonts/lucidasans/unicode.6.font", + "/fonts/lucidasans/unicode.7.font", + "/fonts/lucidasans/unicode.8.font", + "/fonts/lucidasans/unicode.10.font", + "/fonts/lucidasans/unicode.13.font", + "/fonts/lucidasans/italiclatin1.6.font", + "/fonts/lucidasans/italiclatin1.7.font", + "/fonts/lucidasans/italiclatin1.8.font", + "/fonts/lucidasans/italiclatin1.10.font", + "/fonts/lucidasans/italiclatin1.13.font", + "/fonts/lucidasans/boldlatin1.6.font", + "/fonts/lucidasans/boldlatin1.7.font", + "/fonts/lucidasans/boldlatin1.8.font", + "/fonts/lucidasans/boldlatin1.10.font", + "/fonts/lucidasans/boldlatin1.13.font", + "/fonts/lucidasans/typelatin1.6.font", + "/fonts/lucidasans/typelatin1.7.font", + "/fonts/pelm/latin1.9.font", + "/fonts/pelm/ascii.12.font", + "/fonts/pelm/ascii.16.font" +}; + +fontrefs := array[NFONTTAG] of ref Font; +fontused := array[NFONTTAG] of { DefaultFnum => 1, * => 0}; + +# TABHPAD, TABVPAD are extra space between columns, rows +TABHPAD: con 10; +TABVPAD: con 4; + +tab: ref Table; +top: ref Tk->Toplevel; +display: ref Draw->Display; +canv: string; + +init(asys: Sys, adraw: Draw, nil: Bufio, atk: Tk, aw: Tkclient) +{ + sys = asys; + draw = adraw; + tk = atk; + tkclient = aw; + html = load HTML HTML->PATH; + S = load String String->PATH; +} + +create(parent: string, t: ref Tk->Toplevel, name, args: string): string +{ + if(html == nil) + return "can't load HTML module"; + top = t; + display = t.image.display; + canv = name; + err := tk->cmd(t, "canvas " + canv); + if(len err > 0 && err[0] == '!') + return err_ret(err); + + spec: array of ref Lex; + (spec, err) = getspec(parent, args); + if(err != "") + return err_ret(err); + + err = parsetab(spec); + if(err != "") + return err_ret(err); + + err = build(); + if(err != "") + return err_ret(err); + return ""; +} + +err_ret(s: string) : string +{ + return Name + ": " + s; +} + +getspec(parent, args: string) : (array of ref Lex, string) +{ + (n, argl) := sys->tokenize(args, " "); + if(n != 1) + return (nil, "usage: " + Name + " file"); + (filebytes, err) := readfile(fullname(parent, hd argl)); + if(err != "") + return (nil, err); + return(html->lex(filebytes, HTML->UTF8, 1), ""); +} + +readfile(path: string): (array of byte, string) +{ + fd := sys->open(path, sys->OREAD); + if(fd == nil) + return (nil, sys->sprint("can't open %s, the error was: %r", path)); + (ok, d) := sys->fstat(fd); + if(ok < 0) + return (nil, sys->sprint("can't stat %s, the error was: %r", path)); + if(d.mode & Sys->DMDIR) + return (nil, sys->sprint("%s is a directory", path)); + + l := int d.length; + buf := array[l] of byte; + tot := 0; + while(tot < l) { + need := l - tot; + n := sys->read(fd, buf[tot:], need); + if(n <= 0) + return (nil, sys->sprint("error reading %s, the error was: %r", path)); + tot += n; + } + return (buf, ""); +} + +# Use HTML 3.2 table spec as external representation +# (But no th cells, width specs; and extra "rule" attribute +# for col and tr meaning that a rule of given width is to +# follow the given column or row). +# DTD elements: +# table: - O (caption?, col*, tr*) +# caption: - - (%text+) +# col: - O empty +# tr: - O td* +# td: - O (%body.content) +parsetab(toks: array of ref Lex) : string +{ + tabletlex := toks[0]; + n := len toks; + (tlex, i) := nexttok(toks, n, 0); + + # caption + capcell: ref Tablecell = nil; + if(tlex != nil && tlex.tag == Tcaption) { + for(j := i+1; j < n; j++) { + tlex = toks[j]; + if(tlex.tag == Tcaption + RBRA) + break; + } + if(j >= n) + return syntax_err(tlex, j); + if(j > i+1) { + captoks := toks[i+1:j]; + (caplines, e) := lexes2lines(captoks); + if(e != nil) + return e; + # we ignore caption now +# capcell = ref Tablecell(0, captoks, caplines, 1, 1, 1, Align(Anone, Anone), +# 0, 0, 0, 0, 0, Point(0,0)); + } + (tlex, i) = nexttok(toks, n, j); + } + + # col* + cols: list of ref Tablecol = nil; + while(tlex != nil && tlex.tag == Tcol) { + col := makecol(tlex); + if(col.align.halign == Anone) + col.align.halign = Aleft; + cols = col :: cols; + (tlex, i) = nexttok(toks, n, i); + } + cols = revcols(cols); + + body : list of ref Tablerow = nil; + cells : list of ref Tablecell = nil; + cellid := 0; + rows: list of ref Tablerow = nil; + + # tr* + while(tlex != nil && tlex.tag == Ttr) { + currow := ref Tablerow(nil, 0, 0, makealign(tlex), Point(0,0), makelinew(tlex, "rule"), nil); + rows = currow :: rows; + + # td* + (tlex, i) = nexttok(toks, n, i); + while(tlex != nil && tlex.tag == Ttd) { + rowspan := 1; + (rsfnd, rs) := html->attrvalue(tlex.attr, "rowspan"); + if(rsfnd && rs != "") + rowspan = int rs; + colspan := 1; + (csfnd, cs) := html->attrvalue(tlex.attr, "colspan"); + if(csfnd && cs != "") + colspan = int cs; + nowrap := 0; + (nwfnd, nil) := html->attrvalue(tlex.attr, "nowrap"); + if(nwfnd) + nowrap = 1; + align := makealign(tlex); + for(j := i+1; j < n; j++) { + tlex = toks[j]; + tg := tlex.tag; + if(tg == Ttd + RBRA || tg == Ttd || tg == Ttr + RBRA || tg == Ttr) + break; + } + if(j == n) + tlex = nil; + content: array of ref Lex = nil; + if(j > i+1) + content = toks[i+1:j]; + (lines, err) := lexes2lines(content); + if(err != "") + return err; + curcell := ref Tablecell(cellid, content, lines, rowspan, colspan, nowrap, align, 0, 0, 0, 0, 0, Point(0,0)); + currow.cells = curcell :: currow.cells; + cells = curcell :: cells; + cellid++; + if(tlex != nil && tlex.tag == Ttd + RBRA) + (tlex, i) = nexttok(toks, n, j); + else + i = j; + } + if(tlex != nil && tlex.tag == Ttr + RBRA) + (tlex, i) = nexttok(toks, n, i); + } + if(tlex == nil || tlex.tag != Ttable + RBRA) + return syntax_err(tlex, i); + + # now reverse all the lists that were built in reverse order + # and calculate nrow, ncol + + rows = revrowl(rows); + nrow := len rows; + rowa := array[nrow] of ref Tablerow; + ncol := 0; + r := 0; + for(rl := rows; rl != nil; rl = tl rl) { + row := hd rl; + rowa[r++] = row; + rcols := 0; + cl := row.cells; + row.cells = nil; + while(cl != nil) { + c := hd cl; + row.cells = c :: row.cells; + rcols += c.colspan; + cl = tl cl; + } + if(rcols > ncol) + ncol = rcols; + } + cells = revcelll(cells); + + cola := array[ncol] of ref Tablecol; + for(c := 0; c < ncol; c++) { + if(cols != nil) { + cola[c] = hd cols; + cols = tl cols; + } + else + cola[c] = ref Tablecol(0, Align(Anone, Anone), Point(0,0), 0, nil); + } + + if(tabletlex.tag != Ttable) + return syntax_err(tabletlex, 0); + border := makelinew(tabletlex, "border"); + tab = ref Table(nrow, ncol, cellid, 0, 0, capcell, border, 0, cola, rowa, cells, nil, nil, nil); + + return ""; +} + +syntax_err(tlex: ref Lex, i: int) : string +{ + if(tlex == nil) + return "syntax error in table: premature end"; + else + return "syntax error in table at token " + string i + ": " + html->lex2string(tlex); +} + +# next token after toks[i], skipping whitespace +nexttok(toks: array of ref Lex, ntoks, i: int) : (ref Lex, int) +{ + i++; + if(i >= ntoks) + return (nil, i); + t := toks[i]; + while(t.tag == Data) { + if(S->drop(t.text, " \t\n\r") != "") + break; + i++; + if(i >= ntoks) + return (nil, i); + t = toks[i]; + } +# sys->print("nexttok returning (%s,%d)\n", html->lex2string(t), i); + return(t, i); +} + +makecol(tlex: ref Lex) : ref Tablecol +{ + return ref Tablecol(0, makealign(tlex), Point(0,0), makelinew(tlex, "rule"), nil); +} + +makelinew(tlex: ref Lex, aname: string) : int +{ + ans := 0; + (fnd, val) := html->attrvalue(tlex.attr, aname); + if(fnd) { + if(val == "") + ans = 1; + else + ans = int val; + } + return ans; +} + +makealign(tlex: ref Lex) : Align +{ + (nil,h) := html->attrvalue(tlex.attr, "align"); + (nil,v) := html->attrvalue(tlex.attr, "valign"); + hal := align_val(h, Anone); + val := align_val(v, Anone); + return Align(hal, val); +} + +align_val(sal: string, dflt: int) : int +{ + ans := dflt; + case sal { + "left" => ans = Aleft; + "center" => ans = Acenter; + "right" => ans = Aright; + "justify" => ans = Ajustify; + "top" => ans = Atop; + "middle" => ans = Amiddle; + "bottom" => ans = Abottom; + "baseline" => ans = Abaseline; + } + return ans; +} + +revcols(l : list of ref Tablecol) : list of ref Tablecol +{ + ans : list of ref Tablecol = nil; + while(l != nil) { + ans = hd l :: ans; + l = tl l; + } + return ans; +} + +revrowl(l : list of ref Tablerow) : list of ref Tablerow +{ + ans : list of ref Tablerow = nil; + while(l != nil) { + ans = hd l :: ans; + l = tl l; + } + return ans; +} + +revcelll(l : list of ref Tablecell) : list of ref Tablecell +{ + ans : list of ref Tablecell = nil; + while(l != nil) { + ans = hd l :: ans; + l = tl l; + } + return ans; +} + +revintl(l : list of int) : list of int +{ + ans : list of int = nil; + while(l != nil) { + ans = hd l :: ans; + l = tl l; + } + return ans; +} + +# toks should contain only Font (i.e., size) and style changes, along with text. +lexes2lines(toks: array of ref Lex) : (ref Line, string) +{ + n := len toks; + (tlex, i) := nexttok(toks, n, -1); + ans: ref Line = nil; + if(tlex == nil) + return(ans, ""); + curline : ref Line = nil; + curitem : ref Item = nil; + stylestk := DefFont :: nil; + sizestk := DefSize :: nil; + f := DefaultFnum; + fontstk:= f :: nil; + for(;;) { + if(i >= n) + break; + tlex = toks[i++]; + case tlex.tag { + Data => + text := tlex.text; + while(text != "") { + if(curline == nil) { + curline = ref Line(nil, Point(0,0), 0, 0, 0, nil, nil); + ans = curline; + } + s : string; + (s, text) = S->splitl(text, "\n"); + if(s != "") { + f = hd fontstk; + it := ref Item(0, s, f, Point(0,0), 0, curline, curitem, nil); + if(curitem == nil) + curline.items = it; + else + curitem.next = it; + curitem = it; + } + if(text != "") { + text = text[1:]; + curline.next = ref Line(nil, Point(0,0), 0, 0, 0, nil, nil); + curline = curline.next; + curitem = nil; + } + } + HTML->Tfont => + (fnd, ssize) := html->attrvalue(tlex.attr, "size"); + if(fnd && len ssize > 0) { + # HTML size 3 == our Size10 + sz := (int ssize) + (Size10 - 3); + if(sz < 0 || sz >= NSIZE) + return (nil, "bad font size " + ssize); + sizestk = sz :: sizestk; + fontstk = fnum(hd stylestk, sz) :: fontstk; + } + else + return (nil, "bad font command: no size"); + HTML->Tfont + RBRA => + fontstk = tl fontstk; + sizestk = tl sizestk; + if(sizestk == nil) + return (nil, "unmatched </FONT>"); + HTML->Tb => + stylestk = Bold :: stylestk; + fontstk = fnum(Bold, hd sizestk) :: fontstk; + HTML->Ti => + stylestk = Italic :: stylestk; + fontstk = fnum(Italic, hd sizestk) :: fontstk; + HTML->Ttt => + stylestk = Type :: stylestk; + fontstk = fnum(Type, hd sizestk) :: fontstk; + HTML->Tb + RBRA or HTML->Ti + RBRA or HTML->Ttt + RBRA => + fontstk = tl fontstk; + stylestk = tl stylestk; + if(stylestk == nil) + return (nil, "unmatched </B>, </I>, or </TT>"); + } + } + return (ans, ""); +} + +fnum(fstyle, fsize: int) : int +{ + ans := fstyle*NSIZE + fsize; + fontused[ans] = 1; + return ans; +} + +loadfonts() : string +{ + for(i := 0; i < NFONTTAG; i++) { + if(fontused[i] && fontrefs[i] == nil) { + fname := fontnames[i]; + f := Font.open(display, fname); + if(f == nil) + return sys->sprint("can't open font %s: %r", fname); + fontrefs[i] = f; + } + } + return ""; +} + +# Find where each cell goes in nrow x ncol grid +setgrid() +{ + gcells := array[tab.nrow] of { * => array[tab.ncol] of { * => ref Tablegcell(nil, 1)} }; + + # The following arrays keep track of cells that are spanning + # multiple rows; rowspancnt[i] is the number of rows left + # to be spanned in column i. + # When done, cell's (row,col) is upper left grid point. + rowspancnt := array[tab.ncol] of { * => 0}; + rowspancell := array[tab.ncol] of ref Tablecell; + + ri := 0; + ci := 0; + for(ri = 0; ri < tab.nrow; ri++) { + row := tab.rows[ri]; + cl := row.cells; + for(ci = 0; ci < tab.ncol; ) { + if(rowspancnt[ci] > 0) { + gcells[ri][ci].cell = rowspancell[ci]; + gcells[ri][ci].drawnhere = 0; + rowspancnt[ci]--; + ci++; + } + else { + if(cl == nil) { + ci++; + continue; + } + c := hd cl; + cl = tl cl; + cspan := c.colspan; + if(cspan == 0) { + cspan = tab.ncol - ci; + c.colspan = cspan; + } + rspan := c.rowspan; + if(rspan == 0) { + rspan = tab.nrow - ri; + c.rowspan = rspan; + } + c.row = ri; + c.col = ci; + for(i := 0; i < cspan && ci < tab.ncol; i++) { + gcells[ri][ci].cell = c; + if(i > 0) + gcells[ri][ci].drawnhere = 0; + if(rspan > 1) { + rowspancnt[ci] = rspan-1; + rowspancell[ci] = c; + } + ci++; + } + } + } + } + tab.grid = gcells; +} + +build() : string +{ + ri, ci: int; + +# sys->print("\n\ninitial table\n"); printtable(); + if(tab.ncol == 0 || tab.nrow == 0) + return ""; + + setgrid(); + + err := loadfonts(); + if(err != "") + return err; + + for(cl := tab.cells; cl != nil; cl = tl cl) + cell_geom(hd cl); + + for(ci = 0; ci < tab.ncol; ci++) + col_geom(ci); + + for(ri = 0; ri < tab.nrow; ri++) + row_geom(ri); + + caption_geom(); + + table_geom(); +# sys->print("\n\ntable after geometry set\n"); printtable(); + + h := tab.height; + w := tab.width; + if(tab.capcell != nil) { + h += tab.capcell.height; + if(tab.capcell.width > w) + w = tab.capcell.width; + } + + err = tk->cmd(top, canv + " configure -width " + string w + + " -height " + string h); + if(len err > 0 && err[0] == '!') + return err; + err = create_cells(); + if(err != "") + return err; + err = create_border(); + if(err != "") + return err; + err = create_rules(); + if(err != "") + return err; + err = create_caption(); + if(err != "") + return err; + tk->cmd(top, "update"); + + return ""; +} + +create_cells() : string +{ + for(cl := tab.cells; cl != nil; cl = tl cl) { + c := hd cl; + cpos := c.pos; + for(l := c.lines; l != nil; l = l.next) { + lpos := l.pos; + for(it := l.items; it != nil; it = it.next) { + ipos := it.pos; + pos := ipos.add(lpos.add(cpos)); + fnt := fontrefs[it.fontnum]; + v := tk->cmd(top, canv + " create text " + string pos.x + " " + + string pos.y + " -anchor nw -font " + fnt.name + + " -text '" + it.s); + if(len v > 0 && v[0] == '!') + return v; + it.itemid = int v; + } + } + } + return ""; +} + +create_border() : string +{ + bd := tab.border; + if(bd > 0) { + x1 := string (bd / 2); + y1 := x1; + x2 := string (tab.width - bd/2 -1); + y2 := string (tab.height - bd/2 -1); + v := tk->cmd(top, canv + " create rectangle " + + x1 + " " + y1 + " " + x2 + " " + y2 + " -width " + string bd); + if(len v > 0 && v[0] == '!') + return v; + tab.brectid = int v; + } + return ""; +} + +create_rules() : string +{ + ci, ri, i: int; + err : string; + c : ref Tablecell; + for(ci = 0; ci < tab.ncol; ci++) { + col := tab.cols[ci]; + rw := col.rule; + if(rw > 0) { + x := col.pos.x + col.width + TABHPAD/2 - rw/2; + ids: list of int = nil; + startri := 0; + for(ri = 0; ri < tab.nrow; ri++) { + c = tab.grid[ri][ci].cell; + if(c.col+c.colspan-1 > ci) { + # rule would cross a spanning cell at this column + if(ri > startri) { + (err, i) = create_col_rule(startri, ri-1, x, rw); + if(err != "") + return err; + ids = i :: ids; + } + startri = ri+1; + } + } + if(ri > startri) + (err, i) = create_col_rule(startri, ri-1, x, rw); + ids = i :: ids; + col.ruleids = revintl(ids); + } + } + for(ri = 0; ri < tab.nrow; ri++) { + row := tab.rows[ri]; + rw := row.rule; + if(rw > 0) { + y := row.pos.y + row.height + TABVPAD/2 - rw/2; + ids: list of int = nil; + startci := 0; + for(ci = 0; ci < tab.ncol; ci++) { + c = tab.grid[ri][ci].cell; + if(c.row+c.rowspan-1 > ri) { + # rule would cross a spanning cell at this row + if(ci > startci) { + (err, i) = create_row_rule(startci, ci-1, y, rw); + if(err != "") + return err; + ids = i :: ids; + } + startci = ci+1; + } + } + if(ci > startci) + (err, i) = create_row_rule(startci, ci-1, y, rw); + ids = i :: ids; + row.ruleids = revintl(ids); + } + } + return ""; +} + +create_col_rule(topri, botri, x, rw: int) : (string, int) +{ + y1, y2: int; + if(topri == 0) + y1 = 0; + else + y1 = tab.rows[topri].pos.y - TABVPAD/2; + if(botri == tab.nrow-1) + y2 = tab.height; + else + y2 = tab.rows[botri].pos.y + tab.rows[botri].height + TABVPAD/2; + sx := string x; + v := tk->cmd(top, canv + " create line " + sx + " " + + string y1 + " " + sx + " " + string y2 + " -width " + string rw); + if(len v > 0 && v[0] == '!') + return (v, 0); + return ("", int v); +} + +create_row_rule(leftci, rightci, y, rw: int) : (string, int) +{ + x1, x2: int; + if(leftci == 0) + x1 = 0; + else + x1 = tab.cols[leftci].pos.x - TABHPAD/2; + if(rightci == tab.ncol-1) + x2 = tab.width; + else + x2 = tab.cols[rightci].pos.x + tab.cols[rightci].width + TABHPAD/2; + sy := string y; + v := tk->cmd(top, canv + " create line " + string x1 + " " + + sy + " " + string x2 + " " + sy + " -width " + string rw); + if(len v > 0 && v[0] == '!') + return (v, 0); + return ("", int v); +} + +create_caption() : string +{ + if(tab.capcell == nil) + return ""; + cpos := Point(0, tab.height + 2*TABVPAD); + for(l := tab.capcell.lines; l != nil; l = l.next) { + lpos := l.pos; + for(it := l.items; it != nil; it = it.next) { + ipos := it.pos; + pos := ipos.add(lpos.add(cpos)); + fnt := fontrefs[it.fontnum]; + v := tk->cmd(top, canv + " create text " + string pos.x + " " + + string pos.y + " -anchor nw -font " + fnt.name + + " -text '" + it.s); + if(len v > 0 && v[0] == '!') + return v; + it.itemid = int v; + } + } + return ""; +} + +# Assuming row and col geoms correct, set row, col, and cell origins +table_geom() +{ + row: ref Tablerow; + col: ref Tablecol; + orig := Point(0,0); + bd := tab.border; + if(bd > 0) + orig = orig.add(Point(TABHPAD+bd, TABVPAD+bd)); + o := orig; + for(ci := 0; ci < tab.ncol; ci++) { + col = tab.cols[ci]; + col.pos = o; + o.x += col.width + col.rule; + if(ci < tab.ncol-1) + o.x += TABHPAD; + } + if(bd > 0) + o.x += TABHPAD + bd; + tab.width = o.x; + + o = orig; + for(ri := 0; ri < tab.nrow; ri++) { + row = tab.rows[ri]; + row.pos = o; + o.y += row.height + row.rule; + if(ri < tab.nrow-1) + o.y += TABVPAD; + } + if(bd > 0) + o.y += TABVPAD + bd; + tab.height = o.y; + + if(tab.capcell != nil) { + tabw := tab.width; + if(tab.capcell.width > tabw) + tabw = tab.capcell.width; + for(l := tab.capcell.lines; l != nil; l = l.next) + l.pos.x += (tabw - l.width)/2; + } + + for(cl := tab.cells; cl != nil; cl = tl cl) { + c := hd cl; + row = tab.rows[c.row]; + col = tab.cols[c.col]; + x := col.pos.x; + y := row.pos.y; + w := spanned_col_width(c.col, c.col+c.colspan-1); + case (cellhalign(c)) { + Aright => + x += w - c.width; + Acenter => + x += (w - c.width) / 2; + } + h := spanned_row_height(c.row, c.row+c.rowspan-1); + case (cellvalign(c)) { + Abottom => + y += h - c.height; + Anone or Amiddle => + y += (h - c.height) / 2; + Abaseline => + y += row.ascent - c.ascent; + } + c.pos = Point(x,y); + } +} + +spanned_col_width(firstci, lastci: int) : int +{ + firstcol := tab.cols[firstci]; + if(firstci == lastci) + return firstcol.width; + lastcol := tab.cols[lastci]; + return (lastcol.pos.x + lastcol.width - firstcol.pos.x); +} + +spanned_row_height(firstri, lastri: int) : int +{ + firstrow := tab.rows[firstri]; + if(firstri == lastri) + return firstrow.height; + lastrow := tab.rows[lastri]; + return (lastrow.pos.y + lastrow.height - firstrow.pos.y); +} + +# Assuming cell geoms are correct, set col widths. +# This code is sloppy for spanned columns; +# it will allocate too much space for them because +# inter-column pad is ignored, and it may make +# narrow columns wider than they have to be. +col_geom(ci: int) +{ + col := tab.cols[ci]; + col.width = 0; + for(ri := 0; ri < tab.nrow; ri++) { + c := tab.grid[ri][ci].cell; + if(c == nil) + continue; + cwd := c.width / c.colspan; + if(cwd > col.width) + col.width = cwd; + } +} + +# Assuming cell geoms are correct, set row heights +row_geom(ri: int) +{ + row := tab.rows[ri]; + # find rows's global height and ascent + h := 0; + a := 0; + n : int; + for(cl := row.cells; cl != nil; cl = tl cl) { + c := hd cl; + al := cellvalign(c); + if(al == Abaseline) { + n = c.ascent; + if(n > a) { + h += (n - a); + a = n; + } + n = c.height - c.ascent; + if(n > h-a) + h = a + n; + } + else { + n = c.height; + if(n > h) + h = n; + } + } + row.height = h; + row.ascent = a; +} + +cell_geom(c: ref Tablecell) +{ + width := 0; + o := Point(0,0); + for(l := c.lines; l != nil; l = l.next) { + line_geom(l, o); + o.y += l.height; + if(l.width > width) + width = l.width; + } + c.width = width; + c.height = o.y; + if(c.lines != nil) + c.ascent = c.lines.ascent; + else + c.ascent = 0; + + al := cellhalign(c); + if(al == Acenter || al == Aright) { + for(l = c.lines; l != nil; l = l.next) { + xdelta := c.width - l.width; + if(al == Acenter) + xdelta /= 2; + l.pos.x += xdelta; + } + } +} + +caption_geom() +{ + if(tab.capcell != nil) { + o := Point(0,TABVPAD); + width := 0; + for(l := tab.capcell.lines; l != nil; l = l.next) { + line_geom(l, o); + o.y += l.height; + if(l.width > width) + width = l.width; + } + tab.capcell.width = width; + tab.capcell.height = o.y + 4*TABVPAD; + } +} + +line_geom(l: ref Line, o: Point) +{ + # find line's global height and ascent + h := 0; + a := 0; + for(it := l.items; it != nil; it = it.next) { + fnt := fontrefs[it.fontnum]; + n := fnt.ascent; + if(n > a) { + h += (n - a); + a = n; + } + n = fnt.height - fnt.ascent; + if(n > h-a) + h = a + n; + } + l.height = h; + l.ascent = a; + # set positions + l.pos = o; + for(it = l.items; it != nil; it = it.next) { + fnt := fontrefs[it.fontnum]; + it.width = fnt.width(it.s); + it.pos.x = o.x; + o.x += it.width; + it.pos.y = a - fnt.ascent; + } + l.width = o.x; +} + +cellhalign(c: ref Tablecell) : int +{ + a := c.align.halign; + if(a == Anone) + a = tab.cols[c.col].align.halign; + return a; +} + +cellvalign(c: ref Tablecell) : int +{ + a := c.align.valign; + if(a == Anone) + a = tab.rows[c.row].align.valign; + return a; +} + +# table debugging +printtable() +{ + if(tab == nil) { + sys->print("no table\n"); + return; + } + sys->print("Table %d rows, %d cols width %d height %d\n", + tab.nrow, tab.ncol, tab.width, tab.height); + if(tab.capcell != nil) + sys->print(" caption: "); printlexes(tab.capcell.content, " "); + sys->print(" cols:\n"); printcols(tab.cols); + sys->print(" rows:\n"); printrows(tab.rows); +} + +align2string(al: int) : string +{ + s := ""; + case al { + Anone => s = "none"; + Aleft => s = "left"; + Acenter => s = "center"; + Aright => s = "right"; + Ajustify => s = "justify"; + Atop => s = "top"; + Amiddle => s = "middle"; + Abottom => s = "bottom"; + Abaseline => s = "baseline"; + } + return s; +} + +printcols(cols: array of ref Tablecol) +{ + n := len cols; + for(i := 0 ; i < n; i++) { + c := cols[i]; + sys->print(" width %d align = %s,%s pos (%d,%d) rule %d\n", c.width, + align2string(c.align.halign), align2string(c.align.valign), c.pos.x, c.pos.y, c.rule); + } +} + +printrows(rows: array of ref Tablerow) +{ + n := len rows; + for(i := 0; i < n; i++) { + tr := rows[i]; + sys->print(" row height %d ascent %d align=%s,%s pos (%d,%d) rule %d\n", tr.height, tr.ascent, + align2string(tr.align.halign), align2string(tr.align.valign), tr.pos.x, tr.pos.y, tr.rule); + for(cl := tr.cells; cl != nil; cl = tl cl) { + c := hd cl; + sys->print(" cell %d width %d height %d ascent %d align=%s,%s\n", + c.cellid, c.width, c.height, c.ascent, + align2string(c.align.halign), align2string(c.align.valign)); + sys->print(" pos (%d,%d) rowspan=%d colspan=%d nowrap=%d\n", + c.pos.x, c.pos.y, c.rowspan, c.colspan, c.nowrap); + printlexes(c.content, " "); + printlines(c.lines); + } + } +} + +printlexes(lexes: array of ref Lex, indent: string) +{ + for(i := 0; i < len lexes; i++) + sys->print("%s%s\n", indent, html->lex2string(lexes[i])); +} + +printlines(l: ref Line) +{ + if(l == nil) + return; + sys->print("lines: \n"); + while(l != nil) { + sys->print(" Line: pos (%d,%d), height %d ascent %d\n", l.pos.x, l.pos.y, l.height, l.ascent); + printitems(l.items); + l = l.next; + } +} + +printitems(i: ref Item) +{ + while(i != nil) { + sys->print(" '%s' id %d fontnum %d w %d, pos (%d,%d)\n", i.s, i.itemid, i.fontnum, + i.width, i.pos.x, i.pos.y); + i = i.next; + } +} + +printgrid(g: array of array of ref Tablegcell) +{ + nr := len g; + nc := len g[0]; + for(r := 0; r < nr; r++) { + for(c := 0; c < nc; c++) { + x := g[r][c]; + cell := x.cell; + suf := " "; + if(x.drawnhere == 0) + suf = "*"; + if(cell == nil) + sys->print(" %s", suf); + else + sys->print("%5d%s", cell.cellid, suf); + } + sys->print("\n"); + } +} + +# Return (table in correct format, error string) +cook(parent: string, fmt: int, args: string) : (ref Celem, string) +{ + (spec, err) := getspec(parent, args); + if(err != "") + return (nil, err); + if(fmt == FHtml) + return cookhtml(spec); + else + return cooklatex(spec); +} + +# Return (table as latex, error string) +# BUG: cells spanning multiple rows not handled correctly +# (all their contents go in the first row of span, though hrules properly broken) +cooklatex(spec: array of ref Lex) : (ref Celem, string) +{ + s : string; + ci, ri: int; + err := parsetab(spec); + if(err != "") + return (nil, err_ret(err)); + + setgrid(); + + ans := ref Celem(SGML, "", nil, nil, nil, nil); + cur : ref Celem = nil; + cur = add(ans, cur, specialce("\\begin{tabular}[t]{" + lcolspec() + "}\n")); + if(tab.border) { + if(tab.border == 1) + s = "\\hline\n"; + else + s = "\\hline\\hline\n"; + cur = add(ans, cur, specialce(s)); + } + for(ri = 0; ri < tab.nrow; ri++) { + row := tab.rows[ri]; + ci = 0; + anyrowspan := 0; + for(cl := row.cells; cl != nil; cl = tl cl) { + c := hd cl; + while(ci < c.col) { + cur = add(ans, cur, specialce("&")); + ci++; + } + mcol := 0; + if(c.colspan > 1) { + cur = add(ans, cur, specialce("\\multicolumn{" + string c.colspan + "}{" + + lnthcolspec(ci, ci+c.colspan-1, c.align.halign) + "}{")); + mcol = 1; + } + else if(c.align.halign != Anone) { + cur = add(ans, cur, specialce("\\multicolumn{1}{" + + lnthcolspec(ci, ci, c.align.halign) + "}{")); + mcol = 1; + } + if(c.rowspan > 1) + anyrowspan = 1; + cur = addlconvlines(ans, cur, c); + if(mcol) { + cur = add(ans, cur, specialce("}")); + ci += c.colspan-1; + } + } + while(ci++ < tab.ncol-1) + cur = add(ans, cur, specialce("&")); + if(ri < tab.nrow-1 || row.rule > 0 || tab.border > 0) + cur = add(ans, cur, specialce("\\\\\n")); + if(row.rule) { + if(anyrowspan) { + startci := 0; + for(ci = 0; ci < tab.ncol; ci++) { + c := tab.grid[ri][ci].cell; + if(c.row+c.rowspan-1 > ri) { + # rule would cross a spanning cell at this row + if(ci > startci) + cur = add(ans, cur, specialce("\\cline{" + + string (startci+1) + "-" + string ci + "}")); + startci = ci+1; + } + } + if(ci > startci) + cur = add(ans, cur, specialce("\\cline{" + + string (startci+1) + "-" + string ci + "}")); + } + else + cur = add(ans, cur, specialce("\\hline\n")); + } + } + if(tab.border) { + if(tab.border == 1) + s = "\\hline\n"; + else + s = "\\hline\\hline\n"; + cur = add(ans, cur, specialce(s)); + } + cur = add(ans, cur, specialce("\\end{tabular}\n")); + + if(ans != nil) + ans = ans.contents; + return (ans, ""); +} + +lcolspec() : string +{ + ans := ""; + for(ci := 0; ci < tab.ncol; ci++) + ans += lnthcolspec(ci, ci, Anone); + return ans; +} + +lnthcolspec(ci, cie, al: int) : string +{ + ans := ""; + if(ci == 0) { + if(tab.border == 1) + ans = "|"; + else if(tab.border > 1) + ans = "||"; + } + col := tab.cols[ci]; + if(al == Anone) + al = col.align.halign; + case al { + Acenter => + ans += "c"; + Aright => + ans += "r"; + * => + ans += "l"; + } + if(ci == cie) { + if(col.rule == 1) + ans += "|"; + else if(col.rule > 1) + ans += "||"; + } + if(cie == tab.ncol - 1) { + if(tab.border == 1) + ans += "|"; + else if(tab.border > 1) + ans += "||"; + } + return ans; +} + +addlconvlines(par, tail: ref Celem, c: ref Tablecell) : ref Celem +{ + line := c.lines; + if(line == nil) + return tail; + multiline := 0; + if(line.next != nil) { + multiline = 1; + val := ""; + case cellvalign(c) { + Abaseline or Atop => val = "[t]"; + Abottom => val = "[b]"; + } + hal := "l"; + case cellhalign(c) { + Aright => hal = "r"; + Acenter => hal = "c"; + } + # The @{}'s in the colspec eliminate extra space before and after result + tail = add(par, tail, specialce("\\begin{tabular}" + val + "{@{}" + hal + "@{}}\n")); + } + while(line != nil) { + for(it := line.items; it != nil; it = it.next) { + fnum := it.fontnum; + f := fnum / NSIZE; + sz := fnum % NSIZE; + grouped := 0; + if((f != DefFont || sz != DefSize) && (it.prev!=nil || it.next!=nil)) { + tail = add(par, tail, specialce("{")); + grouped = 1; + } + if(f != DefFont) { + fcmd := ""; + case f { + Roman => fcmd = "\\rmfamily "; + Italic => fcmd = "\\itshape "; + Bold => fcmd = "\\bfseries "; + Type => fcmd = "\\ttfamily "; + } + tail = add(par, tail, specialce(fcmd)); + } + if(sz != DefSize) { + szcmd := ""; + case sz { + Size6 => szcmd = "\\footnotesize "; + Size8 => szcmd = "\\small "; + Size10 => szcmd = "\\normalsize "; + Size12 => szcmd = "\\large "; + Size16 => szcmd = "\\Large "; + } + tail = add(par, tail, specialce(szcmd)); + } + tail = add(par, tail, textce(it.s)); + if(grouped) + tail = add(par, tail, specialce("}")); + } + ln := line.next; + if(multiline && ln != nil) + tail = add(par, tail, specialce("\\\\\n")); + line = line.next; + } + if(multiline) + tail = add(par, tail, specialce("\\end{tabular}\n")); + return tail; +} + +# Return (table as html, error string) +cookhtml(spec: array of ref Lex) : (ref Celem, string) +{ + n := len spec; + ans := ref Celem(SGML, "", nil, nil, nil, nil); + cur : ref Celem = nil; + for(i := 0; i < n; i++) { + tok := spec[i]; + if(tok.tag == Data) + cur = add(ans, cur, textce(tok.text)); + else { + s := html->lex2string(spec[i]); + cur = add(ans, cur, specialce(s)); + } + } + if(ans != nil) + ans = ans.contents; + return (ans, ""); +} + +textce(s: string) : ref Celem +{ + return ref Celem(Text, s, nil, nil, nil, nil); +} + +specialce(s: string) : ref Celem +{ + return ref Celem(Special, s, nil, nil, nil, nil); +} + +add(par, tail: ref Celem, e: ref Celem) : ref Celem +{ + if(tail == nil) { + par.contents = e; + e.parent = par; + } + else + tail.next = e; + e.prev = tail; + return e; +} + +fullname(parent, file: string): string +{ + if(len parent==0 || (len file>0 && (file[0]=='/' || file[0]=='#'))) + return file; + + for(i:=len parent-1; i>=0; i--) + if(parent[i] == '/') + return parent[0:i+1] + file; + return file; +} diff --git a/appl/wm/c4.b b/appl/wm/c4.b new file mode 100644 index 00000000..185b807b --- /dev/null +++ b/appl/wm/c4.b @@ -0,0 +1,718 @@ +implement Connect; + +# +# Copyright © 2000 Vita Nuova Limited. All rights reserved. +# + +include "sys.m"; + sys: Sys; +include "draw.m"; + draw: Draw; + Point, Rect, Image, Font, Context, Screen, Display: import draw; +include "tk.m"; + tk: Tk; + Toplevel: import tk; +include "tkclient.m"; + tkclient: Tkclient; +include "daytime.m"; + daytime: Daytime; +include "rand.m"; + rand: Rand; + +# adtize and modularize + +stderr: ref Sys->FD; + +Connect: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +nosleep, printout, auto: int; +display: ref Draw->Display; + +init(ctxt: ref Draw->Context, argv: list of string) +{ + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + daytime = load Daytime Daytime->PATH; + rand = load Rand Rand->PATH; + + argv = tl argv; + while(argv != nil){ + s := hd argv; + if(s != nil && s[0] == '-'){ + for(i := 1; i < len s; i++){ + case s[i]{ + 'a' => auto = 1; + 'p' => printout = 1; + 's' => nosleep = 1; + } + } + } + argv = tl argv; + } + stderr = sys->fildes(2); + rand->init(daytime->now()); + daytime = nil; + + if(ctxt == nil) + fatal("wm not running"); + display = ctxt.display; + tkclient->init(); + (win, wmcmd) := tkclient->toplevel(ctxt, "", "Connect", Tkclient->Resize | Tkclient->Hide); + mainwin = win; + sys->pctl(Sys->NEWPGRP, nil); + cmdch := chan of string; + tk->namechan(win, cmdch, "cmd"); + for(i := 0; i < len win_config; i++) + cmd(win, win_config[i]); + pid := -1; + sync := chan of int; + mvch := chan of (int, int); + initboard(); + setimage(); + spawn game(sync, mvch); + pid = <- sync; + tkclient->onscreen(win, nil); + tkclient->startinput(win, "kbd"::"ptr"::nil); + + for(;;){ + alt{ + s := <-win.ctxt.kbd => + tk->keyboard(win, s); + s := <-win.ctxt.ptr => + tk->pointer(win, *s); + c := <-win.ctxt.ctl or + c = <-win.wreq or + c = <-wmcmd => + case c{ + "exit" => + if(pid != -1) + kill(pid); + exit; + * => + e := tkclient->wmctl(win, c); + if(e == nil && c[0] == '!'){ + setimage(); + drawboard(); + } + } + c := <- cmdch => + (nil, toks) := sys->tokenize(c, " "); + case hd toks{ + "b1" or "b2" or "b3" => + alt{ + mvch <-= (int hd tl toks, int hd tl tl toks) => ; + * => ; + } + "bh" or "bm" or "wh" or "wm" => + colour := BLACK; + knd := HUMAN; + if((hd toks)[0] == 'w') + colour = WHITE; + if((hd toks)[1] == 'm') + knd = MACHINE; + kind[colour] = knd; + "blev" or "wlev" => + colour := BLACK; + e := "be"; + if((hd toks)[0] == 'w'){ + colour = WHITE; + e = "we"; + } + sk := int cmd(win, ".f0." + e + " get"); + if(sk > MAXPLIES) + sk = MAXPLIES; + if(sk >= 0) + skill[colour] = sk; + * => + ; + } + <- sync => + pid = -1; + # exit; + spawn game(sync, mvch); + pid = <- sync; + } + } +} + +WIDTH: con 400; +HEIGHT: con 400; + +SZW: con 7; +SZH: con 6; +SZC: con 4; +SZS: con 1024; +PIECES: con SZW*SZH; + +BLACK, WHITE, EMPTY: con iota; +MACHINE, HUMAN: con iota; +SKILLB : con 8; +SKILLW : con 0; +MAXPLIES: con 10; + +board: array of array of int; # for display +brd: array of array of int; # for calculations +col: array of int; +pieces: array of int; +val: array of int; +kind: array of int; +skill: array of int; +name: array of string; +lines: array of array of int; +line: array of array of list of int; + +mainwin: ref Toplevel; +brdimg: ref Image; +brdr: Rect; +brdx, brdy: int; + +black, white, bg: ref Image; + +movech: chan of (int, int); + +setimage() +{ + brdw := int tk->cmd(mainwin, ".p cget -actwidth"); + brdh := int tk->cmd(mainwin, ".p cget -actheight"); + brdr = Rect((0,0), (brdw, brdh)); + brdimg = display.newimage(brdr, display.image.chans, 0, Draw->White); + if(brdimg == nil) + fatal("not enough image memory"); + tk->putimage(mainwin, ".p", brdimg, nil); +} + +game(sync: chan of int, mvch: chan of (int, int)) +{ + sync <-= sys->pctl(0, nil); + movech = mvch; + initbrd(); + play(); + sync <-= 0; +} + +initboard() +{ + i, j, k: int; + + board = array[SZW] of array of int; + brd = array[SZW] of array of int; + line = array[SZW] of array of list of int; + col = array[SZW] of int; + for(i = 0; i < SZW; i++){ + board[i] = array[SZH] of int; + brd[i] = array[SZH] of int; + line[i] = array[SZH] of list of int; + } + pieces = array[2] of int; + val = array[2] of int; + kind = array[2] of int; + kind[BLACK] = MACHINE; + if(auto) + kind[WHITE] = MACHINE; + else + kind[WHITE] = HUMAN; + skill = array[2] of int; + skill[BLACK] = SKILLB; + skill[WHITE] = SKILLW; + name = array[2] of string; + name[BLACK] = "black"; + name[WHITE] = "white"; + black = display.color(Draw->Black); + white = display.color(Draw->White); + bg = display.color(Draw->Yellow); + n := SZW*(SZH-SZC+1)+SZH*(SZW-SZC+1)+2*(SZH-SZC+1)*(SZW-SZC+1); + lines = array[n] of array of int; + for(i = 0; i < n; i++) + lines[i] = array[2] of int; + m := 0; + for(i = 0; i < SZW; i++){ + for(j = 0; j <= SZH-SZC; j++){ + for(k = 0; k < SZC; k++){ + line[i][j+k] = m :: line[i][j+k]; + } + m++; + } + } + for(i = 0; i < SZH; i++){ + for(j = 0; j <= SZW-SZC; j++){ + for(k = 0; k < SZC; k++){ + line[j+k][i] = m :: line[j+k][i]; + } + m++; + } + } + for(i = 0; i <= SZW-SZC; i++){ + for(j = 0; j <= SZH-SZC; j++){ + for(k = 0; k < SZC; k++){ + line[i+k][j+k] = m :: line[i+k][j+k]; + } + m++; + } + } + for(i = 0; i <= SZW-SZC; i++){ + for(j = 0; j <= SZH-SZC; j++){ + for(k = 0; k < SZC; k++){ + line[SZW-1-i-k][j+k] = m :: line[SZW-1-i-k][j+k]; + } + m++; + } + } + if(m != n) + fatal(sys->sprint("%d != %d\n", m, n)); +} + +initbrd() +{ + i, j: int; + + for(i = 0; i < SZW; i++){ + col[i] = 0; + for(j = 0; j < SZH; j++) + board[i][j] = brd[i][j] = EMPTY; + } + pieces[BLACK] = pieces[WHITE] = 0; + val[BLACK] = val[WHITE] = 0; + drawboard(); + n := len lines; + for(i = 0; i < n; i++) + lines[i][0] = lines[i][1] = 0; +} + +plays := 0; +bwins := 0; +wwins := 0; + +play() +{ + if(plays&1) + (first, second) := (WHITE, BLACK); + else + (first, second) = (BLACK, WHITE); + for(;;){ + if(pieces[BLACK]+pieces[WHITE] == PIECES) + break; + m1 := move(first, second); + if(printout) + sys->print("%s: %d %d %d\n", name[first], m1, val[BLACK], val[WHITE]); + if(win(first)) + break; + if(pieces[BLACK]+pieces[WHITE] == PIECES) + break; + m2 := move(second, first); + if(printout) + sys->print("%s: %d %d %d\n", name[second], m2, val[BLACK], val[WHITE]); + if(win(second)) + break; + } + if(win(BLACK)){ + bwins++; + puts("black wins"); + highlight(BLACK); + } + else if(win(WHITE)){ + wwins++; + puts("white wins"); + highlight(WHITE); + } + else + puts("draw"); + sleep(2500); + plays++; + puts(sys->sprint("black %d:%d white", bwins, wwins)); + sleep(2500); + if(printout) + sys->print("\n"); +} + +move(me: int, you: int): int +{ + if(kind[me] == MACHINE){ + puts("machine " + name[me] + " move"); + return genmove(me, you); + } + else{ + m, n: int; + + # mvs := findmoves(); + for(;;){ + puts("human " + name[me] + " move"); + m = getmove(); + if(m < 0 || m >= SZW) + continue; + n = col[m]; + valid := n >= 0 && n < SZH; + if(valid && brd[m][n] != EMPTY) + fatal("! EMPTY"); + if(valid) + break; + puts("illegal move"); + sleep(2500); + } + makemove(m, n, me, you, 0); + return m*SZS+n; + } +} + +genmove(me: int, you: int): int +{ + m, n, v: int; + + mvs := findmoves(); + if(skill[me] == 0){ + l := len mvs; + r := rand->rand(l); + # r = 0; + while(--r >= 0) + mvs = tl mvs; + (m, n) = hd mvs; + } + else{ + plies := skill[me]; + left := PIECES-(pieces[BLACK]+pieces[WHITE]); + if(left < plies) # limit search + plies = left; + else if(left < 2*plies) # expand search to end + plies = left; + else{ # expand search nearer end of game + k := left/plies; + if(k < 3) + plies = ((k+2)*plies)/(k+1); + } + visits = leaves = 0; + (v, (m, n)) = minimax(me, you, plies, ∞); + if(0){ + while(mvs != nil){ + v0: int; + (a, b) := hd mvs; + makemove(a, b, me, you, 1); + (v0, (m, n)) = minimax(you, me, plies-1, ∞); + sys->print(" (%d, %d): %d\n", a, b, -v0); + undomove(a, b, me, you); + mvs = tl mvs; + } + sys->print("best move is %d, %d\n", m, n); + kind[WHITE] = HUMAN; + } + if(auto) + sys->print("eval = %d plies=%d goes=%d visits=%d\n", v, plies, len mvs, leaves); + } + makemove(m, n, me, you, 0); + return m*SZS+n; +} + +findmoves(): list of (int, int) +{ + mvs: list of (int, int); + + for(i := 0; i < SZW; i++){ + if((j := col[i]) < SZH) + mvs = (i, j) :: mvs; + } + return mvs; +} + +makemove(m: int, n: int, me: int, you: int, gen: int) +{ + pieces[me]++; + brd[m][n] = me; + col[m]++; + for(l := line[m][n]; l != nil; l = tl l){ + i := hd l; + a := lines[i][me]; + b := lines[i][you]; + lines[i][me]++; + if(a+b >= SZC) + fatal("makemove a+b"); + if(b == 0){ + val[me] += 2*a+1; + if(a == SZC-1) + val[me] += WIN; + } + else if(a == 0) + val[you] -= b*b; + } + if(!gen){ + board[m][n] = me; + drawpiece(m, n, me); + panelupdate(); + # sleep(1000); + } +} + +undomove(m: int, n: int, me: int, you: int) +{ + brd[m][n] = EMPTY; + pieces[me]--; + col[m]--; + for(l := line[m][n]; l != nil; l = tl l){ + i := hd l; + a := lines[i][me]; + b := lines[i][you]; + lines[i][me]--; + if(a == 0 || a+b > SZC) + fatal("undomove a+b"); + if(b == 0){ + val[me] -= 2*a-1; + if(a == SZC) + val[me] -= WIN; + } + else if(a == 1) + val[you] += b*b; + } +} + +win(me: int): int +{ + return val[me] > WIN/2; +} + +highlight(me: int) +{ + n := len lines; + for(i := 0; i < n; i++){ + if(lines[i][me] == SZC){ + for(j := 0; j < SZW; j++){ + for(k := 0; k < SZH; k++){ + for(l := line[j][k]; l != nil; l = tl l){ + if(i == hd l) + highpiece(j, k, board[j][k]); + } + } + } + } + } +} + +getmove(): int +{ + (x, nil) := <- movech; + return x/brdx; +} + +drawboard() +{ + brdx = brdr.dx()/SZW; + brdy = brdr.dy()/SZH; + brdimg.draw(brdr, bg, nil, (0, 0)); + for(i := 1; i < SZW; i++) + drawline(lmap(i, 0), lmap(i, SZH), nil); + for(j := 1; j < SZH; j++) + drawline(lmap(0, j), lmap(SZW, j), nil); + for(i = 0; i < SZW; i++){ + for(j = 0; j < SZH; j++){ + if (board[i][j] == BLACK || board[i][j] == WHITE) + drawpiece(i, j, board[i][j]); + } + } + panelupdate(); +} + +drawpiece(m, n, p: int) +{ + if(p == BLACK) + src := black; + else if(p == WHITE) + src = white; + else + src = bg; + brdimg.fillellipse(cmap(m, n), 3*brdx/8, 3*brdy/8, src, (0, 0)); +} + +highpiece(m, n, p: int) +{ + if(p == BLACK) + src := white; + else if(p == WHITE) + src = black; + else + src = bg; + pt := cmap(m, n); + rx := (3*brdx/8, 0); + ry := (0, 3*brdy/8); + drawline(pt.add(rx), pt.sub(rx), src); + drawline(pt.add(ry), pt.sub(ry), src); +} + +panelupdate() +{ + tk->cmd(mainwin, sys->sprint(".p dirty %d %d %d %d", brdr.min.x, brdr.min.y, brdr.max.x, brdr.max.y)); + tk->cmd(mainwin, "update"); +} + +drawline(p0, p1: Point, c: ref Image) +{ + if(c == nil) + c = black; + brdimg.line(p0, p1, Draw->Endsquare, Draw->Endsquare, 0, c, (0, 0)); +} + +cmap(m, n: int): Point +{ + return brdr.min.add((m*brdx+brdx/2, (SZH-1-n)*brdy+brdy/2)); +} + +lmap(m, n: int): Point +{ + return brdr.min.add((m*brdx, n*brdy)); +} + +∞: con (1<<30); +WIN: con (1<<20); +MAXVISITS: con 1024; + +visits, leaves : int; + +minimax(me: int, you: int, plies: int, αβ: int): (int, (int, int)) +{ + v: int; + + if(plies == 0){ + visits++; + leaves++; + if(visits == MAXVISITS){ + visits = 0; + sys->sleep(0); + } + return (eval(me, you), (0, 0)); + } + mvs := findmoves(); + if(mvs == nil){ + fatal("mvs==nil"); + # if(mv) + # (v, nil) := minimax(you, me, plies, ∞); + # else + # (v, nil) = minimax(you, me, plies-1, ∞); + # return (-v, (0, 0)); + } + bestv := -∞; + bestm := (0, 0); + e := 0; + for(; mvs != nil; mvs = tl mvs){ + (m, n) := hd mvs; + makemove(m, n, me, you, 1); + if(win(me)) + v = eval(me, you); + else{ + (v, nil) = minimax(you, me, plies-1, -bestv); + v = -v; + } + undomove(m, n, me, you); + if(v > bestv || (v == bestv && rand->rand(++e) == 0)){ + if(v > bestv) + e = 1; + bestv = v; + bestm = (m, n); + if(bestv >= αβ) + return (∞, (0, 0)); + } + } + return (bestv, bestm); +} + +eval(me: int, you: int): int +{ + return val[me]-val[you]; +} + +fatal(s: string) +{ + sys->fprint(stderr, "%s\n", s); + exit; +} + +sleep(t: int) +{ + if(nosleep) + sys->sleep(0); + else + sys->sleep(t); +} + +kill(pid: int): int +{ + fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE); + if(fd == nil) + return -1; + if(sys->write(fd, array of byte "kill", 4) != 4) + return -1; + return 0; +} + +cmd(top: ref Toplevel, s: string): string +{ + e := tk->cmd(top, s); + if (e != nil && e[0] == '!') + sys->fprint(stderr, "connect: tk error on '%s': %s\n", s, e); + return e; +} + +# swidth: int; +# sfont: ref Font; + +# gettxtattrs() +# { +# swidth = int cmd(mainwin, ".f1.txt cget -width"); # always initial value ? +# f := cmd(mainwin, ".f1.txt cget -font"); +# sfont = Font.open(brdimg.display, f); +# } + +puts(s: string) +{ + # while(sfont.width(s) > swidth) + # s = s[0: len s -1]; + cmd(mainwin, ".f1.txt configure -text {" + s + "}"); + cmd(mainwin, "update"); +} + +win_config := array[] of { + "frame .f", + "menubutton .f.bk -text Black -menu .f.bk.bm", + "menubutton .f.wk -text White -menu .f.wk.wm", + "menu .f.bk.bm", + ".f.bk.bm add command -label Human -command { send cmd bh }", + ".f.bk.bm add command -label Machine -command { send cmd bm }", + "menu .f.wk.wm", + ".f.wk.wm add command -label Human -command { send cmd wh }", + ".f.wk.wm add command -label Machine -command { send cmd wm }", + "pack .f.bk -side left", + "pack .f.wk -side right", + + "frame .f0", + "label .f0.bl -text {Black level}", + "label .f0.wl -text {White level}", + "entry .f0.be -width 32", + "entry .f0.we -width 32", + ".f0.be insert 0 {" + string SKILLB+"}", + ".f0.we insert 0 {" + string SKILLW+"}", + "pack .f0.bl -side left", + "pack .f0.be -side left", + "pack .f0.wl -side right", + "pack .f0.we -side right", + + "frame .f1", + "label .f1.txt -text { } -width " + string WIDTH, + "pack .f1.txt -side top -fill x", + + "panel .p -width " + string WIDTH + " -height " + string HEIGHT, + + "pack .f -side top -fill x", + "pack .f0 -side top -fill x", + "pack .f1 -side top -fill x", + "pack .p -side bottom -fill both -expand 1", + "pack propagate . 0", + + "bind .p <Button-1> {send cmd b1 %x %y}", + "bind .p <Button-2> {send cmd b2 %x %y}", + "bind .p <Button-3> {send cmd b3 %x %y}", + # "bind .c <ButtonRelease-1> {send cmd b1r %x %y}", + # "bind .c <ButtonRelease-2> {send cmd b2r %x %y}", + # "bind .c <ButtonRelease-3> {send cmd b3r %x %y}", + "bind .f0.be <Key-\n> {send cmd blev}", + "bind .f0.we <Key-\n> {send cmd wlev}", + "update", +}; diff --git a/appl/wm/calendar.b b/appl/wm/calendar.b new file mode 100644 index 00000000..6e52afd4 --- /dev/null +++ b/appl/wm/calendar.b @@ -0,0 +1,1064 @@ +implement Calendar; + +# +# Copyright © 2000 Vita Nuova Limited. All rights reserved. +# + +include "sys.m"; + sys: Sys; +include "draw.m"; + draw: Draw; + Font, Point, Rect: import draw; +include "daytime.m"; + daytime: Daytime; + Tm: import Daytime; +include "tk.m"; + tk: Tk; +include "tkclient.m"; + tkclient: Tkclient; +include "dialog.m"; + dialog: Dialog; +include "readdir.m"; +include "translate.m"; + translate: Translate; + Dict: import translate; +include "arg.m"; + arg: Arg; +include "sh.m"; + +Calendar: module { + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +Cal: adt { + w: string; + dx, dy: int; + onepos: int; + top: ref Tk->Toplevel; + sched: ref Schedule; + date: int; + marked: array of int; + make: fn(top: ref Tk->Toplevel, sched: ref Schedule, w: string): (ref Cal, chan of string); + show: fn(cal: self ref Cal, date: int); + mark: fn(cal: self ref Cal, ent: Entry); +}; + +Entry: adt { + date: int; # YYYYMMDD + mark: int; +}; + +Sentry: adt { + ent: Entry; + file: int; +}; + +Schedule: adt { + dir: string; + entries: array of Sentry; + new: fn(dir: string): (ref Schedule, string); + getentry: fn(sched: self ref Schedule, date: int): (int, Entry); + readentry: fn(sched: self ref Schedule, date: int): (Entry, string); + setentry: fn(sched: self ref Schedule, ent: Entry, data: string): (int, string); +}; + +Markset: adt { + new: fn(top: ref Tk->Toplevel, cal: ref Cal, w: string): (ref Markset, chan of string); + set: fn(m: self ref Markset, kind: int); + get: fn(m: self ref Markset): int; + ctl: fn(m: self ref Markset, c: string); + + top: ref Tk->Toplevel; + cal: ref Cal; + w: string; + curr: int; +}; + +DBFSPATH: con "/dis/rawdbfs.dis"; +SCHEDDIR: con "/mnt/schedule"; + +stderr: ref Sys->FD; +dict: ref Dict; +font := "/fonts/lucidasans/unicode.7.font"; +days, months: array of string; + +packcmds := array[] of { +"pack .ctf.show .ctf.set .ctf.date -side right", +"pack .ctf -side top -fill x", + +"pack .cf.head.fwd .cf.head.bwd .cf.head.date -side right", +"pack .cf.head -side top -fill x", +"pack .cf.cal -side top", +"pack .cf -side top", + +"pack .schedf.head.fwd .schedf.head.bwd .schedf.head.date .schedf.head.markset" + + " .schedf.head.save .schedf.head.del -side right", +"pack .schedf.head -side top -fill x", +"pack .schedf.tf.scroll -side left -fill y", +"pack .schedf.tf.t -side top -fill both -expand 1", +"pack .schedf.tf -side top -fill both -expand 1", +"pack .schedf -side top -fill both -expand 1", +}; + +Savebut: con ".schedf.head.save"; +Delbut: con ".schedf.head.del"; + +usage() +{ + sys->fprint(stderr, "usage: calendar [-f font] [/mnt/schedule | schedfile]\n"); + raise "fail:usage"; +} + +init(ctxt: ref Draw->Context, argv: list of string) +{ + loadmods(); + if (ctxt == nil) { + sys->fprint(sys->fildes(2), "calendar: no window context\n"); + raise "fail:bad context"; + } + days = Xa(array[] of {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}); + months = Xa(array[] of {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}); + arg->init(argv); + while ((opt := arg->opt()) != 0) { + case opt { + 'f' => + if ((font = arg->arg()) == nil) + usage(); + * => + usage(); + } + } + argv = arg->argv(); + scheddir := SCHEDDIR; + if (argv != nil) + scheddir = hd argv; + (top, wmctl) := tkclient->toplevel(ctxt, "", X("Calendar"), Tkclient->Appl); + if (top == nil) { + sys->fprint(stderr, "cal: cannot make window: %r\n"); + raise "fail:cannot make window"; + } + (sched, err) := Schedule.new(scheddir); + if (sched == nil) + sys->fprint(stderr, "cal: cannot load schedule: %s\n", err); + currtime := daytime->local(daytime->now()); + if (currtime == nil) { + sys->fprint(stderr, "cannot get local time: %r\n"); + raise "fail:failed to get local time"; + } + date := tm2date(currtime); + sys->pctl(Sys->NEWPGRP|Sys->FORKNS, nil); + + cmdch := chan of string; + tk->namechan(top, cmdch, "cmd"); + wincmds := array[] of { + "frame .ctf", + "button .ctf.set -text {"+X("Set")+"} -command {send cmd settime}", + "button .ctf.show -text {"+X("Show")+"} -command {send cmd showtime}", + + "frame .cf -bd 2 -relief raised", + "frame .cf.head", + "button .cf.head.bwd -text {<<} -command {send cmd bwdmonth}", + "button .cf.head.fwd -text {>>} -command {send cmd fwdmonth}", + "label .cf.head.date -text {XXX 0000}", + + "frame .schedf -bd 2 -relief raised", + "frame .schedf.head", + "button .schedf.head.save -text {"+X("Save")+"} -command {send cmd save}", + "button .schedf.head.del -text {"+X("Del")+"} -command {send cmd del}", + "label .schedf.head.date -text {0000/00/00}", + "canvas .schedf.head.markset", + "button .schedf.head.bwd -text {<<} -command {send cmd bwdday}", + "button .schedf.head.fwd -text {>>} -command {send cmd fwdday}", + "frame .schedf.tf", + "scrollbar .schedf.tf.scroll -command {.schedf.tf.t yview}", + "text .schedf.tf.t -wrap word -yscrollcommand {.schedf.tf.scroll set} -height 7h -width 20w", + "bind .schedf.tf.t <Key> +{send cmd dirty}", + }; + tkcmds(top, wincmds); + (cal, calch) := Cal.make(top, sched, ".cf.cal"); + sync := chan of int; + spawn clock(top, ".ctf.date", sync); + clockpid := <-sync; + (ms, msch) := Markset.new(top, cal, ".schedf.head.markset"); + tkcmds(top, packcmds); + if (sched == nil) + cmd(top, "pack forget .schedf"); + + showdate(top, cal, ms, date); + cmd(top, "pack propagate . 0"); + cmd(top, "update"); + if (date < 19700002) + raisesettime(ctxt, top); + + setting := 0; + dirty := 0; + empty := scheduleempty(top); + currsched := 0; + + tkclient->onscreen(top, nil); + tkclient->startinput(top, "kbd"::"ptr"::nil); + + for (;;) { + enable(top, Savebut, dirty); + enable(top, Delbut, !empty); + cmd(top, "update"); + ndate := date; + alt { + c := <-calch => + (y,m,d) := date2ymd(date); + d = int c; + ndate = ymd2date(y,m,d); + c := <-msch => + ms.ctl(c); + cal.mark(Entry(date, ms.get())); + dirty = 1; + c := <-cmdch => + case c { + "dirty" => + dirty = 1; + nowempty := scheduleempty(top); + if (nowempty != empty) { + if (nowempty) { + ms.set(0); + cal.mark(Entry(date, 0)); + } else { + ms.set(1); + cal.mark(Entry(date, ms.get())); + } + empty = nowempty; + } + "bwdmonth" => + ndate = decmonth(date); + "fwdmonth" => + ndate = incmonth(date); + "bwdday" => + ndate = adddays(date, -1); + "fwdday" => + ndate = adddays(date, 1); + "del" => + if (!empty) { + cmd(top, ".schedf.tf.t delete 1.0 end"); + empty = 1; + dirty = 1; + cal.mark(Entry(date, 0)); + } + "save" => + if (dirty && save(ctxt, top, cal, ms, date) != -1) + dirty = 0; + "settime" => + raisesettime(ctxt, top); + "showtime" => + ndate = tm2date(daytime->local(daytime->now())); + * => + sys->fprint(stderr, "cal: unknown command '%s'\n", c); + } + s := <-top.ctxt.kbd => + tk->keyboard(top, s); + s := <-top.ctxt.ptr => + tk->pointer(top, *s); + c := <-top.ctxt.ctl or + c = <-top.wreq or + c = <-wmctl => + if (c == "exit" && dirty) + save(ctxt, top, cal, ms, date); + tkclient->wmctl(top, c); + } + if (ndate != date) { + e := 0; + if (dirty) + e = save(ctxt, top, cal, ms, date); + if (e != -1) { + dirty = 0; + showdate(top, cal, ms, ndate); + empty = scheduleempty(top); + date = ndate; + cmd(top, "update"); + } + } + } +} + +Markset.new(top: ref Tk->Toplevel, cal: ref Cal, w: string): (ref Markset, chan of string) +{ + cmd(top, w+" configure -width "+string (cal.dx * 2 + 6) + + " -height "+string (cal.dy + 4)); + ch := chan of string; + tk->namechan(top, ch, "markcmd"); + return (ref Markset(top, cal, w, 0), ch); +} + +Markset.set(m: self ref Markset, kind: int) +{ + cmd(m.top, m.w + " delete x"); + if (kind > 0) { + (shape, col) := kind2shapecol(kind); + id := cmd(m.top, m.w + " create " + + shapestr(m.cal, (m.cal.dx/2+2, m.cal.dy/2+2), Square) + + " -fill " + colours[col] + " -tags x"); + cmd(m.top, m.w + " bind " + id + " <ButtonRelease-1> {send markcmd col}"); + id = cmd(m.top, m.w + " create " + + shapestr(m.cal, (m.cal.dx * 3 / 2+4, m.cal.dy/2+2), shape) + + " -tags x -width 2"); + cmd(m.top, m.w + " bind " + id + " <ButtonRelease-1> {send markcmd shape}"); + } + m.curr = kind; +} + +Markset.get(m: self ref Markset): int +{ + return m.curr; +} + +Markset.ctl(m: self ref Markset, c: string) +{ + (shape, col) := kind2shapecol(m.curr); + case c { + "col" => col = (col + 1) % len colours; + "shape" => shape = (shape + 1) % Numshapes; + } + m.set(shapecol2kind((shape, col))); +} + +scheduleempty(top: ref Tk->Toplevel): int +{ + return int cmd(top, ".schedf.tf.t compare 1.0 == end"); +} + +enable(top: ref Tk->Toplevel, but: string, enable: int) +{ + cmd(top, but + " configure -state " + + (array[] of {"disabled", "normal"})[!!enable]); +} + +save(ctxt: ref Draw->Context, top: ref Tk->Toplevel, cal: ref Cal, ms: ref Markset, date: int): int +{ + s := cmd(top, ".schedf.tf.t get 1.0 end"); + empty := scheduleempty(top); + mark := ms.get(); + if (empty) + mark = 0; + ent := Entry(date, mark); + cal.mark(ent); + (ok, err) := cal.sched.setentry(ent, s); + if (ok == -1) { + notice(ctxt, top, "Cannot save entry: " + err); + return -1; + } + return 0; +} + +notice(ctxt: ref Draw->Context, top: ref Tk->Toplevel, s: string) +{ + dialog->prompt(ctxt, top.image, nil, "Notice", s, 0, "OK"::nil); +} + +showdate(top: ref Tk->Toplevel, cal: ref Cal, ms: ref Markset, date: int) +{ + (y,m,d) := date2ymd(date); + cal.show(date); + cmd(top, ".cf.head.date configure -text {" + sys->sprint("%.4d/%.2d", y, m+1) + "}"); + cmd(top, ".schedf.head.date configure -text {" + sys->sprint("%.4d/%.2d/%.2d", y, m+1, d) + "}"); + (ent, s) := cal.sched.readentry(date); + ms.set(ent.mark); + cmd(top, ".schedf.tf.t delete 1.0 end; .schedf.tf.t insert 1.0 '" + s); +} + +nomod(s: string) +{ + sys->fprint(stderr, "cal: cannot load %s: %r\n", s); + raise "fail:bad module"; +} + +loadmods() +{ + sys = load Sys Sys->PATH; + stderr = sys->fildes(2); + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + daytime = load Daytime Daytime->PATH; + if (daytime == nil) + nomod(Daytime->PATH); + tkclient = load Tkclient Tkclient->PATH; + if (tkclient == nil) + nomod(Tkclient->PATH); + translate = load Translate Translate->PATH; + if(translate != nil){ + translate->init(); + (dict, nil) = translate->opendict(translate->mkdictname("", "calendar")); + } + tkclient->init(); + arg = load Arg Arg->PATH; + if (arg == nil) + nomod(Arg->PATH); + dialog = load Dialog Dialog->PATH; + if(dialog == nil) + nomod(Dialog->PATH); + dialog->init(); +} + +s2a(s: string, min, max: int, sep: string): array of int +{ + (ntoks, toks) := sys->tokenize(s, sep); + if (ntoks < min || ntoks > max) + return nil; + a := array[max] of int; + for (i := 0; toks != nil; toks = tl toks) { + if (!isnum(hd toks)) + return nil; + a[i++] = int hd toks; + } + return a[0:i]; +} + +validtm(t: ref Daytime->Tm): int +{ + if (t.hour < 0 || t.hour > 23 + || t.min < 0 || t.min > 59 + || t.sec < 0 || t.sec > 59 + || t.mday < 1 || t.mday > 31 + || t.mon < 0 || t.mon > 11 + || t.year < 70 || t.year > 137) + return 0; + if (t.mon == 1 && dysize(t.year+1900) > 365) + return t.mday <= 29; + return t.mday <= dmsize[t.mon]; +} + +clock(top: ref Tk->Toplevel, w: string, sync: chan of int) +{ + cmd(top, "label " + w); + fd := sys->open("/dev/time", Sys->OREAD); + if (fd == nil) { + sync <-= -1; + return; + } + buf := array[128] of byte; + for (;;) { + sys->seek(fd, big 0, Sys->SEEKSTART); + n := sys->read(fd, buf, len buf); + if (n < 0) { + sys->fprint(stderr, "cal: could not read time: %r\n"); + if (sync != nil) + sync <-= -1; + break; + } + ms := big string buf[0:n] / big 1000; + ct := ms / big 1000; + t := daytime->local(int ct); + + s := sys->sprint("%s %s %d %.2d:%.2d.%.2d", + days[t.wday], months[t.mon], t.mday, t.hour, t.min, t.sec); + cmd(top, w + " configure -text {" + s + "}"); + cmd(top, "update"); + if (sync != nil) { + sync <-= sys->pctl(0, nil); + sync = nil; + } + sys->sleep(int ((ct + big 1) * big 1000 - ms)); + } +} + +# "the world is the lord's and all it contains, +# save the highlands and islands, which belong to macbraynes" +Cal.make(top: ref Tk->Toplevel, sched: ref Schedule, w: string): (ref Cal, chan of string) +{ + f := Font.open(top.display, font); + if (f == nil) { + sys->fprint(stderr, "cal: could not open font %s: %r\n", font); + font = cmd(top, ". cget -font"); + f = Font.open(top.display, font); + } + if (f == nil) + return (nil, nil); + maxw := 0; + for (i := 0; i < 7; i++) { + if ((dw := f.width(days[i] + " ")) > maxw) + maxw = dw; + } + for (i = 10; i < 32; i++) { + if ((dw := f.width(string i + " ")) > maxw) + maxw = dw; + } + cal := ref Cal; + cal.w = w; + cal.dx = maxw; + cal.dy = f.height; + cal.onepos = 0; + cal.top = top; + cal.sched = sched; + cal.marked = array[31] of {* => 0}; + cmd(top, "canvas " + w + " -width " + string (cal.dx * 7) + " -height " + string (cal.dy * 7)); + for (i = 0; i < 7; i++) + cmd(top, w + " create text " + posstr(daypos(cal, i, 0)) + + " -text " + days[i] + " -font " + font); + ch := chan of string; + tk->namechan(top, ch, "ch" + w); + return (cal, ch); +} + +Cal.show(cal: self ref Cal, date: int) +{ + if (date == cal.date) + return; + mon := (date / 100) % 100; + year := date / 10000; + cmd(cal.top, cal.w + " delete curr"); + if (cal.date / 100 != date / 100) { + cmd(cal.top, cal.w + " delete date"); + cmd(cal.top, cal.w + " delete mark"); + for (i := 0; i < len cal.marked; i++) + cal.marked[i] = 0; + (md, wd) := monthinfo(mon, year); + base := year * 10000 + mon * 100; + cal.onepos = wd; + for (i = 0; i < 6; i++) { + for (j := 0; j < 7; j++) { + d := i * 7 + j - wd; + if (d >= 0 && d < md) { + id := cmd(cal.top, cal.w + " create text " + posstr(daypos(cal, j, i+1)) + + " -tags date -text " + string (d+1) + + " -font " + font); + cmd(cal.top, cal.w + " bind " + id + + " <ButtonRelease-1> {send ch" + cal.w + " " + string (d+1) + "}"); + (ok, ent) := cal.sched.getentry(base + d + 1); + if (ok != -1) + cal.mark(ent); + } + } + } + } + if (cal.sched != nil) { + e := date % 100 - 1 + cal.onepos; + p := daypos(cal, e % 7, e / 7 + 1); + cmd(cal.top, cal.w + " create " + shapestr(cal, p, Square) + + " -tags curr -width 3"); + } + cal.date = date; +} + +Cal.mark(cal: self ref Cal, ent: Entry) +{ + if (ent.date / 100 != ent.date / 100) + return; + (nil, nil, d) := date2ymd(ent.date); + d--; + cmd(cal.top, cal.w + " delete m" + string d); + if (ent.mark) { + e := d + cal.onepos; + p := daypos(cal, e % 7, e / 7 + 1); + id := cmd(cal.top, cal.w + " create " + itemshape(cal, p, ent.mark) + + " -tags {mark m"+string d + "}"); + cmd(cal.top, cal.w + " bind " + id + + " <ButtonRelease-1> {send ch" + cal.w + " " + string (d+1) + "}"); + cmd(cal.top, cal.w + " lower " + id); + } + cal.marked[d] = ent.mark; +} + +Oval, Diamond, Square, Numshapes: con iota; + +colours := array[] of { + "red", + "yellow", + "#00eeee", + "white" +}; + +kind2shapecol(kind: int): (int, int) +{ + kind = (kind - 1) & 16rffff; + return ((kind & 16rff) % Numshapes, (kind >> 8) % len colours); +} + +shapecol2kind(shapecol: (int, int)): int +{ + (shape, colour) := shapecol; + return (shape + (colour << 8)) + 1; +} + +itemshape(cal: ref Cal, centre: Point, kind: int): string +{ + (shape, colour) := kind2shapecol(kind); + return shapestr(cal, centre, shape) + " -fill " + colours[colour]; +} + +shapestr(cal: ref Cal, p: Point, kind: int): string +{ + (hdx, hdy) := (cal.dx / 2, cal.dy / 2); + case kind { + Oval => + r := Rect((p.x - hdx, p.y - hdy), (p.x + hdx, p.y + hdy)); + return "oval " + rectstr(r); + Diamond => + return "polygon " + string (p.x - hdx) + " " + string p.y + " " + + string p.x + " " + string (p.y - hdy) + " " + + string (p.x + hdx) + " " + string p.y + " " + + string p.x + " " + string (p.y + hdy) + + " -outline black"; + Square => + r := Rect((p.x - hdx, p.y - hdy), (p.x + hdx, p.y + hdy)); + return "rectangle " + rectstr(r); + * => + sys->fprint(stderr, "cal: unknown shape %d\n", kind); + return nil; + } +} + +rectstr(r: Rect): string +{ + return string r.min.x + " " + string r.min.y + " " + + string r.max.x + " " + string r.max.y; +} + +posstr(p: Point): string +{ + return string p.x + " " + string p.y; +} + +# return centre point of position for day. +daypos(cal: ref Cal, d, w: int): Point +{ + return Point(d * cal.dx + cal.dx / 2, w * cal.dy + cal.dy / 2); +} + +body2entry(body: string): (int, Entry, string) +{ + for (i := 0; i < len body; i++) + if (body[i] == '\n') + break; + if (i == len body) + return (-1, (-1, -1), "invalid schedule header (no newline)"); + (n, toks) := sys->tokenize(body[0:i], " \t\n"); + if (n < 2) + return (-1, (-1, -1), "invalid schedule header (too few fields)"); + date := int hd toks; + (y, m, d) := (date / 10000, (date / 100) % 100, date%100); + if (y < 1970 || y > 2037 || m > 12 || m < 1 || d > 31 || d < 1) + return (-1, (-1,-1), sys->sprint("invalid date (%.8d) in schedule header", date)); + e := Entry(ymd2date(y, m-1, d), int hd tl toks); + return (0, e, body[i+1:]); +} + +startdbfs(f: string): (string, string) +{ + dbfs := load Command DBFSPATH; + if (dbfs == nil) + return (nil, sys->sprint("cannot load %s: %r", DBFSPATH)); + sync := chan of string; + spawn rundbfs(sync, dbfs, f, SCHEDDIR); + e := <-sync; + if (e != nil) + return (nil, e); + return (SCHEDDIR, nil); +} + +rundbfs(sync: chan of string, dbfs: Command, f, d: string) +{ + sys->pctl(Sys->FORKFD, nil); + { + dbfs->init(nil, "dbfs" :: "-r" :: f :: d :: nil); + sync <-= nil; + }exception e{ + "fail:*" => + sync <-= "dbfs failed: " + e[5:]; + exit; + } +} + +Schedule.new(d: string): (ref Schedule, string) +{ + (rc, info) := sys->stat(d); + if (rc == -1) + return (nil, sys->sprint("cannot find %s: %r", d)); + if ((info.mode & Sys->DMDIR) == 0) { + err: string; + (d, err) = startdbfs(d); + if (d == nil) + return (nil, err); + } + (rc, nil) = sys->stat(d + "/new"); + if (rc == -1) + return (nil, "no dbfs mounted on " + d); + + readdir := load Readdir Readdir->PATH; + if (readdir == nil) + return (nil, sys->sprint("cannot load %s: %r", Readdir->PATH)); + sched := ref Schedule; + sched.dir = d; + (de, nil) := readdir->init(d, Readdir->NONE); + if (de == nil) + return (nil, "could not read schedule directory"); + buf := array[Sys->ATOMICIO] of byte; + sched.entries = array[len de] of Sentry; + ne := 0; + for (i := 0; i < len de; i++) { + if (!isnum(de[i].name)) + continue; + f := d + "/" + de[i].name; + fd := sys->open(f, Sys->OREAD); + if (fd == nil) { + sys->fprint(stderr, "cal: cannot open %s: %r\n", f); + } else { + n := sys->read(fd, buf, len buf); + if (n == -1) { + sys->fprint(stderr, "cal: error reading %s: %r\n", f); + } else { + (ok, e, err) := body2entry(string buf[0:n]); + if (ok == -1) + sys->fprint(stderr, "cal: error on entry %s: %s\n", f, err); + else + sched.entries[ne++] = (e, int de[i].name); + err = nil; + } + } + } + sched.entries = sched.entries[0:ne]; + sortentries(sched.entries); + return (sched, nil); +} + +Schedule.getentry(sched: self ref Schedule, date: int): (int, Entry) +{ + if (sched == nil) + return (-1, (-1, -1)); + ent := search(sched, date); + if (ent == -1) + return (-1, (-1,-1)); + return (0, sched.entries[ent].ent); +} + +Schedule.readentry(sched: self ref Schedule, date: int): (Entry, string) +{ + if (sched == nil) + return ((-1, -1), nil); + ent := search(sched, date); + if (ent == -1) + return ((-1, -1), nil); + (nil, fno) := sched.entries[ent]; + + f := sched.dir + "/" + string fno; + fd := sys->open(f, Sys->OREAD); + if (fd == nil) { + sys->fprint(stderr, "cal: cannot open %s: %r", f); + return ((-1, -1), nil); + } + buf := array[Sys->ATOMICIO] of byte; + n := sys->read(fd, buf, len buf); + if (n == -1) { + sys->fprint(stderr, "cal: cannot read %s: %r", f); + return ((-1, -1), nil); + } + (ok, e, body) := body2entry(string buf[0:n]); + if (ok == -1) { + sys->fprint(stderr, "cal: couldn't get body in file %s: %s\n", f, body); + return ((-1, -1), nil); + } + return (e, body); +} + +writeentry(fd: ref Sys->FD, ent: Entry, data: string): (int, string) +{ + ent.date += 100; + b := array of byte (sys->sprint("%d %d\n", ent.date, ent.mark) + data); + if (len b > Sys->ATOMICIO) + return (-1, "entry is too long"); + if (sys->write(fd, b, len b) != len b) + return (-1, sys->sprint("cannot write entry: %r")); + return (0, nil); +} + +Schedule.setentry(sched: self ref Schedule, ent: Entry, data: string): (int, string) +{ + if (sched == nil) + return (-1, "no schedule"); + idx := search(sched, ent.date); + if (idx == -1) { + if (data == nil) + return (0, nil); + fd := sys->open(sched.dir + "/new", Sys->OWRITE); + if (fd == nil) + return (-1, sys->sprint("cannot open new: %r")); + (ok, info) := sys->fstat(fd); + if (ok == -1) + return (-1, sys->sprint("cannot stat new: %r")); + if (!isnum(info.name)) + return (-1, "new dbfs entry is not numeric"); + err: string; + (ok, err) = writeentry(fd, ent, data); + if (ok == -1) + return (ok, err); + (fd, data) = (nil, nil); + e := sched.entries; + for (i := 0; i < len e; i++) + if (ent.date < e[i].ent.date) + break; + ne := array[len e + 1] of Sentry; + (ne[0:], ne[i], ne[i+1:]) = (e[0:i], (ent, int info.name), e[i:]); + sched.entries = ne; + return (0, nil); + } else { + fno := sched.entries[idx].file; + f := sched.dir + "/" + string fno; + if (data == nil) { + sys->remove(f); + sched.entries[idx:] = sched.entries[idx+1:]; + sched.entries = sched.entries[0:len sched.entries - 1]; + return (0, nil); + } else { + sched.entries[idx] = (ent, fno); + fd := sys->open(f, Sys->OWRITE); + if (fd == nil) + return (-1, sys->sprint("cannot open %s: %r", sched.dir + "/" + string fno)); + return writeentry(fd, ent, data); + } + } +} + +search(sched: ref Schedule, date: int): int +{ + e := sched.entries; + lo := 0; + hi := len e - 1; + while (lo <= hi) { + mid := (lo + hi) / 2; + if (date < e[mid].ent.date) + hi = mid - 1; + else if (date > e[mid].ent.date) + lo = mid + 1; + else + return mid; + } + return -1; +} + +sortentries(a: array of Sentry) +{ + m: int; + n := len a; + for(m = n; m > 1; ) { + if(m < 5) + m = 1; + else + m = (5*m-1)/11; + for(i := n-m-1; i >= 0; i--) { + tmp := a[i]; + for(j := i+m; j <= n-1 && tmp.ent.date > a[j].ent.date; j += m) + a[j-m] = a[j]; + a[j-m] = tmp; + } + } +} + +raisesettime(ctxt: ref Draw->Context, top: ref Tk->Toplevel) +{ + panelcmds := array[] of { + "frame .d", + "label .d.title -text {"+X("Date (YYYY/MM/DD):")+"}", + "entry .d.de -width 11w}", + "frame .t", + "label .t.title -text {"+X("Time (HH:MM.SS):")+"}", + "entry .t.te -width 11w}", + "frame .b", + "button .b.set -text Set -command {send cmd set}", + "button .b.cancel -text Cancel -command {send cmd cancel}", + "pack .d .t .b -side top -fill x", + "pack .d.de .d.title -side right", + "pack .t.te .t.title -side right", + "pack .b.set .b.cancel -side right", + }; + fd := sys->open("/dev/time", Sys->OWRITE); + if (fd == nil) { + notice(ctxt, top, X("Cannot set time: ") + sys->sprint("%r")); + return; + } + (panel, wmctl) := tkclient->toplevel(ctxt, "", X("Set Time"), 0); + tkcmds(panel, panelcmds); + cmdch := chan of string; + tk->namechan(panel, cmdch, "cmd"); + t := daytime->local(daytime->now()); + if (t.year < 71) + (t.year, t.mon, t.mday) = (100, 0, 1); + cmd(panel, ".d.de insert 0 " + sys->sprint("%.4d/%.2d/%.2d", + t.year+1900, t.mon+1, t.mday)); + cmd(panel, ".t.te insert 0 " + sys->sprint("%.2d:%.2d.%.2d", t.hour, t.min, t.sec)); + #cmd(panel, "grab set ."); XXX should, but not a good idea with global tk. + # wouldn't work with current dialog->prompt() either... + cmd(panel, "update"); + tkclient->onscreen(panel, nil); + tkclient->startinput(panel, "kbd"::"ptr"::nil); + +loop: for (;;) alt { + s := <-panel.ctxt.kbd => + tk->keyboard(panel, s); + s := <-panel.ctxt.ptr => + tk->pointer(panel, *s); + c := <-cmdch => + case c { + "set" => + err := settime(fd, cmd(panel, ".d.de get"), cmd(panel, ".t.te get")); + if (err == nil) + break loop; + notice(ctxt, panel, X("Cannot set time: ") + err); + "cancel" => + break loop; + * =>; + } + c := <-wmctl => + case c { + "exit" => + break loop; + * => + tkclient->wmctl(panel, c); + } + } +} + +settime(tfd: ref Sys->FD, date, time: string): string +{ + da := s2a(date, 3, 3, "/"); + if (da == nil) + return X("Invalid date syntax"); + ta := s2a(time, 2, 3, ":."); + if (ta == nil) + return X("Invalid time syntax"); + t := ref blanktm; + if (da[2] > 1000) + (da[0], da[1], da[2]) = (da[2], da[1], da[0]); + (t.year, t.mon, t.mday) = (da[0]-1900, da[1]-1, da[2]); + if (len ta == 3) + (t.hour, t.min, t.sec) = (ta[0], ta[1], ta[2]); + else + (t.hour, t.min, t.sec) = (ta[0], ta[1], 0); + if (!validtm(t)) + return X("Invalid time or date given"); + s := string daytime->tm2epoch(t) + "000000"; + if (sys->fprint(tfd, "%s", s) == -1) + return X("write failed:") + sys->sprint(" %r"); + return nil; +} + + +cmd(top: ref Tk->Toplevel, cmd: string): string +{ + e := tk->cmd(top, cmd); + if (e != nil && e[0] == '!') + sys->fprint(stderr, "cal: tk error on '%s': %s\n", cmd, e); + return e; +} + +tkcmds(top: ref Tk->Toplevel, a: array of string) +{ + for (i := 0; i < len a; i++) + cmd(top, a[i]); +} + +isnum(s: string): int +{ + for (i := 0; i < len s; i++) + if (s[i] < '0' || s[i] > '9') + return 0; + return 1; +} + +tm2date(t: ref Tm): int +{ + if (t == nil) + return 19700001; + return ymd2date(t.year+1900, t.mon, t.mday); +} + +date2ymd(date: int): (int, int, int) +{ + return (date / 10000, (date / 100) % 100, date%100); +} + +ymd2date(y, m, d: int): int +{ + return d + m* 100 + y * 10000; +} + +adddays(date, delta: int): int +{ + t := ref blanktm; + t.mday = date % 100; + t.mon = (date / 100) % 100; + t.year = (date / 10000) - 1900; + t.hour = 12; + e := daytime->tm2epoch(t); + e += delta * 24 * 60 * 60; + t = daytime->gmt(e); + if (!validtm(t)) + return date; + return tm2date(t); +} + +incmonth(date: int): int +{ + (y,m,d) := date2ymd(date); + if (m < 11) + m++; + else if (y < 2037) + (y, m) = (y+1, 0); + (n, nil) := monthinfo(m, y); + if (d > n) + d = n; + return ymd2date(y,m,d); +} + +decmonth(date: int): int +{ + (y,m,d) := date2ymd(date); + if (m > 0) + m--; + else if (y > 1970) + (y, m) = (y-1, 11); + (n, nil) := monthinfo(m, y); + if (d > n) + d = n; + return ymd2date(y,m,d); +} + +dmsize := array[] of { + 31, 28, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31 +}; + +dysize(y: int): int +{ + if( (y%4) == 0 && (y % 100 != 0 || y % 400 == 0) ) + return 366; + return 365; +} + +blanktm: Tm; + +# return number of days in month and +# starting day of month/year. +monthinfo(mon, year: int): (int, int) +{ + t := ref blanktm; + t.mday = 1; + t.mon = mon; + t.year = year - 1900; + t = daytime->gmt(daytime->tm2epoch(t)); + md := dmsize[mon]; + if (dysize(year) == 366 && t.mon == 1) + md++; + return (md, t.wday); +} + +X(s: string): string +{ + #sys->print("\"%s\"\n", s); + if (dict == nil) + return s; + return dict.xlate(s); +} + +Xa(a: array of string): array of string +{ + for (i := 0; i < len a; i++) + a[i] = X(a[i]); + return a; +} + diff --git a/appl/wm/clock.b b/appl/wm/clock.b new file mode 100644 index 00000000..a8022f09 --- /dev/null +++ b/appl/wm/clock.b @@ -0,0 +1,123 @@ +implement Clock; + +# +# Subject to the Lucent Public License 1.02 +# + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + Display, Image, Point, Rect: import draw; + +include "math.m"; + math: Math; + +include "tk.m"; +include "wmclient.m"; + wmclient: Wmclient; + Window: import wmclient; + +include "daytime.m"; + daytime: Daytime; + Tm: import daytime; + +Clock: module +{ + init: fn(nil: ref Draw->Context, nil: list of string); +}; + +hrhand: ref Image; +minhand: ref Image; +dots: ref Image; +back: ref Image; + +init(ctxt: ref Draw->Context, nil: list of string) +{ + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + math = load Math Math->PATH; + daytime = load Daytime Daytime->PATH; + wmclient = load Wmclient Wmclient->PATH; + + sys->pctl(Sys->NEWPGRP, nil); + wmclient->init(); + + w := wmclient->window(ctxt, "clock", Wmclient->Appl); # Plain? + display := w.display; + back = display.colormix(Draw->Palebluegreen, Draw->White); + + hrhand = display.newimage(Rect((0,0),(1,1)), Draw->CMAP8, 1, Draw->Darkblue); + minhand = display.newimage(Rect((0,0),(1,1)), Draw->CMAP8, 1, Draw->Paleblue); + dots = display.newimage(Rect((0,0),(1,1)), Draw->CMAP8, 1, Draw->Blue); + + w.reshape(Rect((0, 0), (100, 100))); + w.startinput("ptr" :: nil); + + now := daytime->now(); + w.onscreen(nil); + drawclock(w.image, now); + + ticks := chan of int; + spawn timer(ticks, 30*1000); + for(;;) alt{ + ctl := <-w.ctl or + ctl = <-w.ctxt.ctl => + w.wmctl(ctl); + if(ctl != nil && ctl[0] == '!') + drawclock(w.image, now); + p := <-w.ctxt.ptr => + w.pointer(*p); + <-ticks => + t := daytime->now(); + if(t != now){ + now = t; + drawclock(w.image, now); + } + } +} + +ZP := Point(0, 0); + +drawclock(screen: ref Image, t: int) +{ + if(screen == nil) + return; + tms := daytime->local(t); + anghr := 90-(tms.hour*5 + tms.min/10)*6; + angmin := 90-tms.min*6; + r := screen.r; + c := r.min.add(r.max).div(2); + if(r.dx() < r.dy()) + rad := r.dx(); + else + rad = r.dy(); + rad /= 2; + rad -= 8; + + screen.draw(screen.r, back, nil, ZP); + for(i:=0; i<12; i++) + screen.fillellipse(circlept(c, rad, i*(360/12)), 2, 2, dots, ZP); + + screen.line(c, circlept(c, (rad*3)/4, angmin), 0, 0, 1, minhand, ZP); + screen.line(c, circlept(c, rad/2, anghr), 0, 0, 1, hrhand, ZP); + + screen.flush(Draw->Flushnow); +} + +circlept(c: Point, r: int, degrees: int): Point +{ + rad := real degrees * Math->Pi/180.0; + c.x += int (math->cos(rad)*real r); + c.y -= int (math->sin(rad)*real r); + return c; +} + +timer(c: chan of int, ms: int) +{ + for(;;){ + sys->sleep(ms); + c <-= 1; + } +} diff --git a/appl/wm/coffee.b b/appl/wm/coffee.b new file mode 100644 index 00000000..09369bc9 --- /dev/null +++ b/appl/wm/coffee.b @@ -0,0 +1,227 @@ +implement Coffee; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + Context, Display, Point, Rect, Image, Screen: import draw; + +include "tk.m"; + tk: Tk; + Toplevel: import tk; + +include "tkclient.m"; + tkclient: Tkclient; + +Coffee: module +{ + init: fn(ctxt: ref Context, argv: list of string); +}; + +display: ref Display; +t: ref Toplevel; + +NC: con 6; + +task_cfg := array[] of { + "frame .f", + "frame .b", + "button .b.Stop -text Stop -command {send cmd stop}", + "scale .b.Rate -from 1 -to 10 -orient horizontal"+ + " -showvalue 0 -command {send cmd rate}", + "scale .b.Jitter -from 0 -to 5 -orient horizontal"+ + " -showvalue 0 -command {send cmd jitter}", + "scale .b.Skip -from 0 -to 25 -orient horizontal"+ + " -showvalue 0 -command {send cmd skip}", + ".b.Rate set 3", + ".b.Jitter set 2", + ".b.Skip set 5", + "pack .b.Stop .b.Rate .b.Jitter .b.Skip -side left", + "pack .b -anchor w", + "pack .f -side bottom -fill both -expand 1", +}; + +init(ctxt: ref Context, nil: list of string) +{ + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + + sys->pctl(Sys->NEWPGRP, nil); + + tkclient->init(); + if(ctxt == nil) + ctxt = tkclient->makedrawcontext(); + display = ctxt.display; + + menubut: chan of string; + (t, menubut) = tkclient->toplevel(ctxt, "", "Infernal Coffee", 0); + + cmdch := chan of string; + tk->namechan(t, cmdch, "cmd"); + + for (i := 0; i < len task_cfg; i++) + cmd(t, task_cfg[i]); + + tk->cmd(t, "update"); + tkclient->startinput(t, "ptr"::"kbd"::nil); + tkclient->onscreen(t, nil); + + ctl := chan of (string, int, int); + spawn animate(ctl); + + for(;;) alt { + s := <-t.ctxt.kbd => + tk->keyboard(t, s); + s := <-t.ctxt.ptr => + tk->pointer(t, *s); + s := <-t.ctxt.ctl or + s = <-t.wreq or + s = <-menubut => + tkclient->wmctl(t, s); + press := <-cmdch => + (nil, word) := sys->tokenize(press, " "); + case hd word { + "stop" or "go" => + ctl <-= (hd word, 0, 0); + "rate" or "jitter" or "skip" => + ctl <-= (hd word, int hd tl word, 0); + } + } + +} + +animate(ctl: chan of (string, int, int)) +{ + stopped := 0; + + fill := display.open("/icons/bigdelight.bit"); + if (fill == nil) { + sys->fprint(sys->fildes(2), "coffee: failed to allocate image\n"); + exit; + } + + c := array[NC] of ref Image; + m := array[NC] of ref Image; + + for(i:=0; i<NC; i++){ + c[i] = display.open("/icons/coffee"+string i+".bit"); + m[i] = display.open("/icons/coffee"+string i+".mask"); + if (c[i] == nil || m[i] == nil) { + sys->fprint(sys->fildes(2), "coffee: failed to allocate image\n"); + exit; + } + } + + r := Rect((0, 0), (400, 300)); + buffer := display.newimage(r, t.image.chans, 0, Draw->Black); + if (buffer == nil) { + sys->fprint(sys->fildes(2), "coffee: failed to allocate image\n"); + exit; + } + cmd(t, "panel .f.p -bd 3 -relief flat"); + cmd(t, "pack .f.p -fill both -expand 1"); + cmd(t, "update"); + # org := buffer.r.min; + tk->putimage(t, ".f.p", buffer, nil); + + rate := 3; + jitter := 2; + skip := 5; + + i = 0; + for(k:=0; ; k++){ + sys->sleep(1); + if(k%25 > 25-skip) + i -= rate; + else + i += rate; + buffer.draw(buffer.clipr, fill, nil, fill.r.min); + center := buffer.r.max.div(2); + for(j:=0; j<NC; j++){ + (sin, cos) := sincos(i+j*(360/NC)); + x := (sin*150)/1000 + jitter*(k%5); + y := (cos*100)/1000 + jitter*(k%5); + p0 := center.add((x-c[j].r.dx()/2, y-c[j].r.dy()/2)); + buffer.draw(c[j].r.addpt(p0), c[j], m[j], (0,0)); + if(j & 1) # be nice from time to time + sys->sleep(0); + } + tk->cmd(t, ".f.p dirty; update"); + sys->sleep(5); + alt{ + (cmd, i0, i1) := <-ctl => + Pause: + for(;;){ + case cmd{ + "go" => + if(stopped){ + tk->cmd(t, ".b.Stop configure -text Stop -command {send cmd stop}"); + tk->cmd(t, "update"); + stopped = 0; + } + break Pause; + "stop" => + if(!stopped){ + tk->cmd(t, ".b.Stop configure -text { Go } -command {send cmd go}"); + tk->cmd(t, "update"); + stopped = 1; + } + "rate" => + rate = i0; + if(stopped == 0) + break Pause; + "jitter" => + jitter = i0; + if(stopped == 0) + break Pause; + "skip" => + skip = i0; + if(stopped == 0) + break Pause; + } + (cmd, i0, i1) = <-ctl; + } + * => + ; + } + } +} + +sintab := array[] of { + 0000, 0017, 0035, 0052, 0070, 0087, 0105, 0122, 0139, 0156, + 0174, 0191, 0208, 0225, 0242, 0259, 0276, 0292, 0309, 0326, + 0342, 0358, 0375, 0391, 0407, 0423, 0438, 0454, 0469, 0485, + 0500, 0515, 0530, 0545, 0559, 0574, 0588, 0602, 0616, 0629, + 0643, 0656, 0669, 0682, 0695, 0707, 0719, 0731, 0743, 0755, + 0766, 0777, 0788, 0799, 0809, 0819, 0829, 0839, 0848, 0857, + 0866, 0875, 0883, 0891, 0899, 0906, 0914, 0921, 0927, 0934, + 0940, 0946, 0951, 0956, 0961, 0966, 0970, 0974, 0978, 0982, + 0985, 0988, 0990, 0993, 0995, 0996, 0998, 0999, 0999, 1000, + 1000, }; + +sincos(a: int): (int, int) +{ + a %= 360; + if(a < 0) + a += 360; + + if(a <= 90) + return (sintab[a], sintab[90-a]); + if(a <= 180) + return (sintab[180-a], -sintab[a-90]); + if(a <= 270) + return (-sintab[a-180], -sintab[270-a]); + return (-sintab[360-a], sintab[a-270]); +} + +cmd(win: ref Tk->Toplevel, s: string): string +{ + r := tk->cmd(win, s); + if (len r > 0 && r[0] == '!') { + sys->print("error executing '%s': %s\n", s, r[1:]); + } + return r; +} diff --git a/appl/wm/collide.b b/appl/wm/collide.b new file mode 100644 index 00000000..1d8a2527 --- /dev/null +++ b/appl/wm/collide.b @@ -0,0 +1,2180 @@ +# +# initially generated by c2l +# + +implement Collide; + +include "draw.m"; + draw: Draw; + Display, Image: import draw; + +Collide: module +{ + init: fn(nil: ref Draw->Context, argl: list of string); +}; + +include "sys.m"; + sys: Sys; +include "tk.m"; + tk: Tk; + Toplevel: import tk; +include "tkclient.m"; + tkclient: Tkclient; +include "math.m"; + maths: Math; +include "rand.m"; + rand: Rand; +include "daytime.m"; + daytime: Daytime; +include "bufio.m"; +include "arg.m"; + arg: Arg; +include "math/polyhedra.m"; + polyhedra: Polyhedra; + +init(ctxt: ref Draw->Context, argl: list of string) +{ + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + maths = load Math Math->PATH; + rand = load Rand Rand->PATH; + arg = load Arg Arg->PATH; + daytime = load Daytime Daytime->PATH; + main(ctxt, argl); +} + +π: con Math->Pi; +∞: con real (1<<30); +ε: con 0.001; +√2: con 1.4142135623730950488016887242096980785696718753769486732; + +M1: con 1.0; +M2: con 1.0; +E: con 1.0; # coefficient of restitution/elasticity + +COLLIDE, REFLECT: con 1<<iota; + +MAXX, MAXY: con 512; + +RDisp: ref Draw->Image; +black, white, red: ref Draw->Image; +display: ref Draw->Display; +toplev: ref Toplevel; + +Vector: adt{ + x: real; + y: real; + z: real; +}; + +Line: adt{ + a: Vector; + d: Vector; # normalized +}; + +Plane: adt{ + id: int; + n: Vector; # normalized + d: real; + min: Vector; + max: Vector; + far: Vector; + v: array of Vector; +}; + +Object: adt{ + id: int; + poly: ref Polyhedra->Polyhedron; # if any + c: ref Draw->Image; # colour + cb: ref Draw->Image; # border colour + l: ref Line; # initial point and direction + p: Vector; # current position + rp: Vector; # position after reflection + cp: Vector; # any collision point + rt: real; # time to reflection + ct: real; # time to collision + plane: ref Plane; # reflecting off + pmask: int; # plane mask + obj: cyclic ref Object; # colliding with + v: real; # speed + ω: real; # speed of rotation + roll: real; # roll + pitch: real; # pitch + yaw: real; # yaw + todo: int; # work to do +}; + +planes: list of ref Plane; + +V0: con Vector(real 0, real 0, real 0); +VZ: con Vector(0.0, 0.0, 1.0); + +far: Vector; + +DOCIRCLE: con 1; +POLY, FILLPOLY, CIRCLE, FILLCIRCLE, ELLIPSE, FILLELLIPSE: con iota; + +# +# final object is centred on (0, 0, -objd) +# viewer is at origin looking along (0 0 -1) +# +maxx, maxy: int; + +SCRW: con 320; # screen width +SCRH: con 240; # screen height + +frac := 0.5; # % of screen for cube +front := 0.5; # % of cube in front of screen +hpar := 0.0; # horizontal parallax +fov := -1.0; # field of view : 0 for parallel projection, -1 for unspecified +objd := 500.0; # eye to middle of cube +cubd := 100.0; # half side of cube +icubd: real; # half side of inner cube +icubd2: real; # square of above +eyed := 32.0; # half eye to eye +trkd := 5.0; # side/diameter of object +trkd2: real; # square of above +rpy := 0; +roll := 0.0; # z +pitch := 0.0; # y +yaw := 0.0; # x + +scrd, objD, scrD: real; # screen distance +left := 0; # left or right eye +sx, sy, sz: real; # screen scale factors +sf: real; # perspective scale factor +fbpar: real; # -1 for front of cube, 1 for back +vf := 1.0; # current velocity factor + +cmin, cmax: Vector; # cube extents + +# special transformation matrix without roll, pitch, yaw +# this is needed so that spheres can be drawn as circles +mod0 := array[4] of array of real; + +stereo := 0; # stereopsis + +SPHERE, ELLIPSOID, CUBE, POLYHEDRON: con iota; +surr := CUBE; # surround + +poly := 0; # show polyhedra +flat: int; # objects in one plane +projx: int; # strange projection + +# ellipse parameters +ef: Vector = (1.0, 0.8, 1.0); +e2: Vector; + +# objects +nobjs: int; +objs: array of ref Object; +me: ref Object; + +# circle drawing +NC: con 72; +cost, sint: array of real; + +# polyhedra +polys: ref Polyhedra->Polyhedron; +npolys: int; +polyh: ref Polyhedra->Polyhedron; + +rgba(r: int, g: int, b: int, α: int): ref Image +{ + c := draw->setalpha((r<<24)|(g<<16)|(b<<8), α); + return display.newimage(((0, 0), (1, 1)), display.image.chans, 1, c); +} + +random(a: int, b: int): int +{ + return a+rand->rand(b-a+1); +} + +urand(): real +{ + M: con 1000; + return real random(0, M)/real M; +} + +randomr(a: real, b: real): real +{ + return a+urand()*(b-a); +} + +randomc(): ref Image +{ + r, g, b: int; + + do{ + r = random(0, 255); + g = random(0, 255); + b = random(0, 255); + }while(r+g+b < 384); + return rgba(r, g, b, 255); +} + +randomv(a: real, b: real): Vector +{ + x := randomr(a, b); + y := randomr(a, b); + if(flat) + return (x, y, (a+b)/2.0); + return (x, y, randomr(a, b)); +} + +randomd(): Vector +{ + M: con 1000.0; + v := randomv(-M, M); + while(vlen(v) == 0.0) + v = randomv(-M, M); + return vnorm(v); +} + +randomp(min: real, max: real): Vector +{ + case(surr){ + SPHERE => + return vmul(randomd(), max*maths->sqrt(urand())); + ELLIPSOID => + return vmul(randomd(), max*vmin(ef)*maths->sqrt(urand())); + CUBE => + return randomv(min, max); + * => + v := randomv(min, max); + while(outside3(v, cmin, cmax)) + v = randomv(min, max); + return v; + } +} + +det(a: real, b: real, c: real, d: real): real +{ + return a*d-b*c; +} + +simeq(a: real, b: real, c: real, d: real, e: real, f: real): (real, real) +{ + de := det(a, b, c, d); + return (det(e, b, f, d)/de, det(a, e, c, f)/de); +} + +cksimeq(a: real, b: real, c: real, d: real, e: real, f: real): (int, real, real) +{ + ade := de := det(a, b, c, d); + if(ade < 0.0) + ade = -ade; + if(ade < ε) + return (0, 0.0, 0.0); + return (1, det(e, b, f, d)/de, det(a, e, c, f)/de); +} + +ostring(o: ref Object): string +{ + return lstring(o.l) + "+" + vstring(o.p) + "+" + string o.v; +} + +pstring(p: ref Plane): string +{ + return vstring(p.n) + "=" + string p.d; +} + +lstring(l: ref Line): string +{ + return vstring(l.a) + "->" + vstring(l.d); +} + +vstring(v: Vector): string +{ + return "(" + string v.x + " " + string v.y + " " + string v.z + ")"; +} + +vpt(x: real, y: real, z: real): Vector +{ + p: Vector; + + p.x = x; + p.y = y; + p.z = z; + return p; +} + +vdot(v1: Vector, v2: Vector): real +{ + return v1.x*v2.x+v1.y*v2.y+v1.z*v2.z; +} + +vcross(v1: Vector, v2: Vector): Vector +{ + v: Vector; + + v.x = v1.y*v2.z-v1.z*v2.y; + v.y = v1.z*v2.x-v1.x*v2.z; + v.z = v1.x*v2.y-v1.y*v2.x; + return v; +} + +vadd(v1: Vector, v2: Vector): Vector +{ + v: Vector; + + v.x = v1.x+v2.x; + v.y = v1.y+v2.y; + v.z = v1.z+v2.z; + return v; +} + +vsub(v1: Vector, v2: Vector): Vector +{ + v: Vector; + + v.x = v1.x-v2.x; + v.y = v1.y-v2.y; + v.z = v1.z-v2.z; + return v; +} + +vmul(v1: Vector, s: real): Vector +{ + v: Vector; + + v.x = s*v1.x; + v.y = s*v1.y; + v.z = s*v1.z; + return v; +} + +vdiv(v1: Vector, s: real): Vector +{ + v: Vector; + + v.x = v1.x/s; + v.y = v1.y/s; + v.z = v1.z/s; + return v; +} + +vlen(v: Vector): real +{ + return maths->sqrt(vdot(v, v)); +} + +vlen2(v: Vector): (real, real) +{ + d2 := vdot(v, v); + d := maths->sqrt(d2); + return (d, d2); +} + +vnorm(v: Vector): Vector +{ + d := maths->sqrt(vdot(v, v)); + if(d == 0.0) + return v; + return vmul(v, real 1/d); +} + +vnorm2(v: Vector): (real, Vector) +{ + d := maths->sqrt(vdot(v, v)); + if(d == 0.0) + return (0.0, VZ); + return (d, vmul(v, real 1/d)); +} + +clip(x: real, d: real): real +{ + if(x < -d) + x = -d; + if(x > d) + x = d; + return x; +} + +vclip(v: Vector, d: real): Vector +{ + c: Vector; + + c.x = clip(v.x, d); + c.y = clip(v.y, d); + c.z = clip(v.z, d); + return c; +} + +vout(v1: Vector, v2: Vector): int +{ + v := vsub(v2, v1); + return v.x < 0.0 || v.y < 0.0 || v.z < 0.0; +} + +vmin(v: Vector): real +{ + m := v.x; + if(v.y < m) + m = v.y; + if(v.z < m) + m = v.z; + return m; +} + +vvmul(v1: Vector, v2: Vector): Vector +{ + v: Vector; + + v.x = v1.x*v2.x; + v.y = v1.y*v2.y; + v.z = v1.z*v2.z; + return v; +} + +vvdiv(v1: Vector, v2: Vector): Vector +{ + v: Vector; + + v.x = v1.x/v2.x; + v.y = v1.y/v2.y; + v.z = v1.z/v2.z; + return v; +} + +vmuldiv(v1: Vector, v2: Vector, v3: Vector): real +{ + return vdot(vvdiv(v1, v3), v2); +} + +newp(id: int, a: real, b: real, c: real, d: real, m: real, v: array of Vector) +{ + p := ref Plane; + p.id = id; + p.n = (a, b, c); + p.d = d; + m += ε; + p.min = (-m, -m, -m); + p.max = (+m, +m, +m); + p.v = v; + if(v != nil){ + p.min = (∞, ∞, ∞); + p.max = (-∞, -∞, -∞); + for(i := 0; i < len v; i++){ + vtx := v[i]; + if(vtx.x < p.min.x) + p.min.x = vtx.x; + if(vtx.y < p.min.y) + p.min.y = vtx.y; + if(vtx.z < p.min.z) + p.min.z = vtx.z; + if(vtx.x > p.max.x) + p.max.x = vtx.x; + if(vtx.y > p.max.y) + p.max.y = vtx.y; + if(vtx.z > p.max.z) + p.max.z = vtx.z; + } + (x, y, z) := p.far = vmul(p.max, 2.0); + if(a != 0.0) + p.far.x = (d-b*y-c*z)/a; + else if(b != 0.0) + p.far.y = (d-c*z-a*x)/b; + else if(c != 0.0) + p.far.z = (d-a*x-b*y)/c; + else + fatal("null plane"); + } + planes = p :: planes; +} + +pinit() +{ + case(surr){ + SPHERE or + ELLIPSOID => + newp(0, 0.0, 0.0, 1.0, ∞, ∞, nil); + CUBE => + newp(0, 1.0, 0.0, 0.0, -icubd, icubd, nil); + newp(1, 1.0, 0.0, 0.0, +icubd, icubd, nil); + newp(2, 0.0, 1.0, 0.0, -icubd, icubd, nil); + newp(3, 0.0, 1.0, 0.0, +icubd, icubd, nil); + newp(4, 0.0, 0.0, 1.0, -icubd, icubd, nil); + newp(5, 0.0, 0.0, 1.0, +icubd, icubd, nil); + * => + p := polyh; + F := p.F; + v := p.v; + f := p.f; + fv := p.fv; + d := 0.0; + for(i := 0; i < F; i++){ + n := vnorm(f[i]); + dn := vmul(n, cubd-icubd); + fvi := fv[i]; + m := fvi[0]; + av := array[m] of Vector; + for(j := 0; j < m; j++){ + av[j] = vtx := vsub(vmul(v[fvi[j+1]], 2.0*cubd), dn); + d += vdot(n, vtx); + } + d /= real m; + newp(i, n.x, n.y, n.z, d, 0.0, av); + } + } +} + +inside(v: Vector, vmin: Vector, vmax: Vector): int +{ + return !vout(vmin, v) && !vout(v, vmax); +} + +inside2(v: Vector, p: ref Plane): int +{ + h := 0; + pt := p.far; + vs := p.v; + n := len p.v; + j := n-1; + for(i := 0; i < n; i++){ + (ok, λ, μ) := cksimeq(vs[j].x-vs[i].x, v.x-pt.x, vs[j].y-vs[i].y, v.y-pt.y, v.x-vs[i].x, v.y-vs[i].y); + if(!ok) + (ok, λ, μ) = cksimeq(vs[j].y-vs[i].y, v.y-pt.y, vs[j].z-vs[i].z, v.z-pt.z, v.y-vs[i].y, v.z-vs[i].z); + if(!ok) + (ok, λ, μ) = cksimeq(vs[j].z-vs[i].z, v.z-pt.z, vs[j].x-vs[i].x, v.x-pt.x, v.z-vs[i].z, v.x-vs[i].x); + if(ok && μ >= 0.0 && λ >= 0.0 && λ < 1.0) + h++; + j = i; + } + return h&1; +} + +inside3(v: Vector, lp: list of ref Plane): int +{ + h := 0; + l := ref Line; + l.a = v; + l.d = vnorm(vsub(far, v)); + for( ; lp != nil; lp = tl lp){ + (ok, nil, nil) := intersect(l, hd lp); + if(ok) + h++; + } + return h&1; +} + +# outside of a face +outside2(v: Vector, p: ref Plane): int +{ + if(surr == CUBE) + return vout(p.min, v) || vout(v, p.max); + else + return !inside2(v, p); +} + +# outside of a polyhedron +outside3(v: Vector, vmin: Vector, vmax: Vector): int +{ + case(surr){ + SPHERE => + return vout(vmin, v) || vout(v, vmax) || vdot(v, v) > icubd2 ; + ELLIPSOID => + return vout(vmin, v) || vout(v, vmax) || vmuldiv(v, v, e2) > 1.0; + CUBE => + return vout(vmin, v) || vout(v, vmax); + * => + return !inside3(v, planes); + } +} + +intersect(l: ref Line, p: ref Plane): (int, real, Vector) +{ + m := vdot(p.n, l.d); + if(m == real 0) + return (0, real 0, V0); + c := vdot(p.n, l.a); + λ := (p.d-c)/m; + if(λ < real 0) + return (0, λ, V0); + pt := vadd(l.a, vmul(l.d, λ)); + if(outside2(pt, p)) + return (0, λ, pt); + return (1, λ, pt); +} + +reflection(tr: ref Object, lp: list of ref Plane) +{ + ok: int; + λ: real; + + l := tr.l; + if(surr == SPHERE){ + (ok, λ) = quadratic(1.0, 2.0*vdot(l.a, l.d), vdot(l.a, l.a)-icubd2); + if(!ok || λ < 0.0) + fatal("no sphere intersections"); + tr.rp = vadd(l.a, vmul(l.d, λ)); + tr.plane = hd lp; # anything + } + else if(surr == ELLIPSOID){ + (ok, λ) = quadratic(vmuldiv(l.d, l.d, e2), 2.0*vmuldiv(l.a, l.d, e2), vmuldiv(l.a, l.a, e2)-1.0); + if(!ok || λ < 0.0) + fatal("no ellipsoid intersections"); + tr.rp = vadd(l.a, vmul(l.d, λ)); + tr.plane = hd lp; # anything + } + else{ + p: ref Plane; + pt := V0; + λ = ∞; + for( ; lp != nil; lp = tl lp){ + p0 := hd lp; + if((1<<p0.id)&tr.pmask) + continue; + (ok0, λ0, pt0) := intersect(l, p0); + if(ok0 && λ0 < λ){ + λ = λ0; + p = p0; + pt = pt0; + } + } + if(λ == ∞) + fatal("no intersections"); + tr.rp = pt; + tr.plane = p; + } + if(tr.v == 0.0) + tr.rt = ∞; + else + tr.rt = λ/tr.v; +} + +reflect(tr: ref Object) +{ + l := tr.l; + if(surr == SPHERE) + n := vdiv(tr.rp, -icubd); + else if(surr == ELLIPSOID) + n = vnorm(vdiv(vvdiv(tr.rp, e2), -1.0)); + else + n = tr.plane.n; + tr.l.a = tr.rp; + tr.l.d = vnorm(vsub(l.d, vmul(n, 2.0*vdot(n, l.d)))); +} + +impact(u2: real): (real, real) +{ + # u1 == 0 + return simeq(M1, M2, -1.0, 1.0, M2*u2, -E*u2); +} + +collision(t1: ref Object, t2: ref Object): (int, real, Vector, Vector) +{ + # stop t2 + (v3, f) := vnorm2(vsub(vmul(t1.l.d, t1.v), vmul(t2.l.d, t2.v))); + if(v3 == 0.0) + return (0, 0.0, V0, V0); + ab := vsub(t2.p, t1.p); + (d, d2) := vlen2(ab); + cos := vdot(f, ab)/d; + cos2 := cos*cos; + if(cos < 0.0 || (disc := trkd2 - d2*(1.0-cos2)) < 0.0) + return (0, 0.0, V0, V0); + s := d*cos - maths->sqrt(disc); + t := s/v3; + s1 := t1.v*t; + s2 := t2.v*t; + cp1 := vadd(t1.p, vmul(t1.l.d, s1)); + if(outside3(cp1, cmin, cmax)) + return (0, 0.0, V0, V0); + cp2 := vadd(t2.p, vmul(t2.l.d, s2)); + if(outside3(cp2, cmin, cmax)) + return (0, 0.0, V0, V0); + return (1, t, cp1, cp2); +} + +collisions(tr: ref Object) +{ + mincp1, mincp2: Vector; + + n := nobjs; + t := objs; + tr0 := tr.obj; + mint := tr.ct; + tr1: ref Object; + for(i := 0; i < n; i++){ + if((tr3 := t[i]) == tr || tr3 == tr0) + continue; + (c, tm, cp1, cp2) := collision(tr, tr3); + if(c && tm < mint && tm < tr3.ct){ + mint = tm; + tr1 = tr3; + mincp1 = cp1; + mincp2 = cp2; + } + } + if(tr1 != nil){ + tr.ct = mint; + tr1.ct = mint; + tr.obj = tr1; + tr2 := tr1.obj; + tr1.obj = tr; + tr.cp = mincp1; + tr1.cp = mincp2; + zerot(tr0, COLLIDE, 0); + zerot(tr2, COLLIDE, 0); + if(tr0 != nil && tr0 != tr2) + collisions(tr0); + if(tr2 != nil) + collisions(tr2); + } +} + +collide(t1: ref Object, t2: ref Object) +{ + # stop t2 + ov := vmul(t2.l.d, t2.v); + (v3, f) := vnorm2(vsub(vmul(t1.l.d, t1.v), ov)); + ab := vsub(t2.cp, t1.cp); + α := vdot(f, ab)/vdot(ab, ab); + abr := vsub(f, vmul(ab, α)); + (v2, v1) := impact(α*v3); + t1.l.a = t1.cp; + t2.l.a = t2.cp; + dir1 := vadd(vmul(ab, v1), vmul(abr, v3)); + dir2 := vmul(ab, v2); + # start t2 + (t1.v, t1.l.d) = vnorm2(vadd(dir1, ov)); + (t2.v, t2.l.d) = vnorm2(vadd(dir2, ov)); +} + +deg2rad(d: real): real +{ + return π*d/180.0; +} + +rad2deg(r: real): real +{ + return 180.0*r/π; +} + +rp2d(r: real, p: real): Vector +{ + r = deg2rad(r); + cr := maths->cos(r); + sr := maths->sin(r); + p = deg2rad(p); + cp := maths->cos(p); + sp := maths->sin(p); + return (cr*cp, sr*cp, sp); +} + +d2rp(v: Vector): (real, real) +{ + r := maths->atan2(v.y, v.x); + p := maths->asin(v.z); + return (rad2deg(r), rad2deg(p)); +} + +collideω(t1: ref Object, t2: ref Object) +{ + d1 := rp2d(t1.roll, t1.pitch); + d2 := rp2d(t2.roll, t2.pitch); + oω := vmul(d2, t2.ω); + (ω3, f) := vnorm2(vsub(vmul(d1, t1.ω), oω)); + ab := vsub(t2.cp, t1.cp); + α := vdot(f, ab)/vdot(ab, ab); + abr := vsub(f, vmul(ab, α)); + (ω2, ω1) := impact(α*ω3); + dir1 := vadd(vmul(ab, ω1), vmul(abr, ω3)); + dir2 := vmul(ab, ω2); + (t1.ω, d1) = vnorm2(vadd(dir1, oω)); + (t2.ω, d2) = vnorm2(vadd(dir2, oω)); + (t1.roll, t1.pitch) = d2rp(d1); + (t2.roll, t2.pitch) = d2rp(d2); +} + +plane(p1: Vector, p2: Vector, p3: Vector): (Vector, real) +{ + n := vnorm(vcross(vsub(p2, p1), vsub(p3, p1))); + d := vdot(n, p1); + return (n, d); +} + +# angle subtended by the eyes at p in minutes +angle(p: Vector): real +{ + l, r: Vector; + + # left eye at (-eyed, 0, 0) + # right eye at (+eyed, 0, 0) + # + l = p; + l.x += eyed; + r = p; + r.x -= eyed; + return real 60*(real 180*maths->acos(vdot(l, r)/(maths->sqrt(vdot(l, l))*maths->sqrt(vdot(r, r))))/π); +} + +# given coordinates relative to centre of cube +disparity(p: Vector, b: Vector): real +{ + p.z -= objd; + b.z -= objd; + return angle(p)-angle(b); +} + +# rotation about any of the axes +# rotate(theta, &x, &y, &z) for x-axis +# rotate(theta, &y, &z, &x) for y-axis +# rotate(theta, &z, &x, &y) for z-axis +# +rotate(theta: int, x: real, y: real, z: real): (real, real, real) +{ + a := π*real theta/real 180; + c := maths->cos(a); + s := maths->sin(a); + oy := y; + oz := z; + y = c*oy-s*oz; + z = c*oz+s*oy; + return (x, y, z); +} + +# solve the quadratic ax^2 + bx + c = 0, returning the larger root +# * (a > 0) +# +quadratic(a: real, b: real, c: real): (int, real) +{ + d := b*b-real 4*a*c; + if(d < real 0) + return (0, 0.0); # no real roots + x := (maths->sqrt(d)-b)/(real 2*a); + return (1, x); +} + +# calculate the values of objD, scrD given the required parallax +dopar() +{ + a := real 1; + b, c: real; + f := real 2*front-real 1; + x: real; + s: int; + w := sx*real SCRW; + ok: int; + + if(hpar == 0.0){ # natural parallax + objD = objd; + scrD = scrd; + return; + } + if(fbpar < f) + s = -1; + else + s = 1; + if(fbpar == f) + fatal("parallax value is zero at screen distance"); + b = (fbpar+f)*cubd-(fbpar-f)*w*eyed*real s*frac/hpar; + c = fbpar*f*cubd*cubd; + (ok, x) = quadratic(a, b, c); + if(ok){ + objD = x; + scrD = x+f*cubd; + if(objD > real 0 && scrD > real 0) + return; + } + fatal("unachievable parallax value"); +} + +# update graphics parameters +update(init: int) +{ + if(fov != real 0){ + if(objd == real 0) + fov = 180.0; + else + fov = real 2*(real 180*maths->atan(cubd/(frac*objd))/π); + } + scrd = objd+(real 2*front-real 1)*cubd; + if(init){ + if(objd < ε) + objd = ε; + if(fov != real 0) + sf = real (1<<2)*cubd/(objd*frac); + else + sf = cubd/frac; + } + # dopar(); + domats(); +} + +fovtodist() +{ + if(fov != real 0) + objd = cubd/(frac*maths->tan(π*(fov/real 2)/real 180)); +} + +getpolys() +{ + (n, p, b) := polyhedra->scanpolyhedra("/lib/polyhedra"); + polyhedra->getpolyhedra(p, b); + polys = p; + npolys = n; + do{ + for(i := 0; i < p.V; i++) + p.v[i] = vmul(p.v[i], 0.5); + for(i = 0; i < p.F; i++) + p.f[i] = vmul(p.f[i], 0.5); + p = p.nxt; + }while(p != polys); +} + +randpoly(p: ref Polyhedra->Polyhedron, n: int): ref Polyhedra->Polyhedron +{ + r := random(0, n-1); + for( ; --r >= 0; p = p.nxt) + ; + return p; +} + +drawpoly(p: ref Polyhedra->Polyhedron, typex: int) +{ + # V := p.V; + F := p.F; + v := p.v; + # f := p.f; + fv := p.fv; + for(i := 0; i < F; i++){ + fvi := fv[i]; + n := fvi[0]; + m_begin(typex, n); + for(j := 0; j < n; j++){ + vtx := v[fvi[j+1]]; + m_vertex(vtx.x, vtx.y, vtx.z); + } + m_end(); + } +} + +# objects with unit sides/diameter +H: con 0.5; + +square(typex: int) +{ + m_begin(typex, 4); + m_vertex(-H, -H, 0.0); + m_vertex(-H, +H, 0.0); + m_vertex(+H, +H, 0.0); + m_vertex(+H, -H, 0.0); + m_end(); +} + +diamond(typex: int) +{ + m_pushmatrix(); + m_rotatez(45.0); + square(typex); + m_popmatrix(); +} + +circleinit() +{ + i: int; + a := 0.0; + cost = array[NC] of real; + sint = array[NC] of real; + for(i = 0; i < NC; i++){ + cost[i] = H*maths->cos(a); + sint[i] = H*maths->sin(a); + a += (2.0*π)/real NC; + } +} + +circle(typex: int) +{ + i: int; + + if(DOCIRCLE){ + m_begin(typex, 2); + m_circle(0.0, 0.0, 0.0, 0.5); + m_end(); + return; + } + else{ + m_begin(typex, NC); + for(i = 0; i < NC; i++) + m_vertex(cost[i], sint[i], 0.0); + m_end(); + } +} + +ellipse(typex: int) +{ + m_begin(typex, 4); + m_ellipse(0.0, 0.0, 0.0, vmul(ef, 0.5)); + m_end(); +} + +hexahedron(typex: int) +{ + i, j, k: int; + V := array[8] of { + array[3] of { + -H, -H, -H, + }, + array[3] of { + -H, -H, +H, + }, + array[3] of { + -H, +H, -H, + }, + array[3] of { + -H, +H, +H, + }, + array[3] of { + +H, -H, -H, + }, + array[3] of { + +H, -H, +H, + }, + array[3] of { + +H, +H, -H, + }, + array[3] of { + +H, +H, +H, + }, + }; + F := array[6] of { + array[4] of { + 0, 4, 6, 2, + }, + array[4] of { + 0, 4, 5, 1, + }, + array[4] of { + 0, 1, 3, 2, + }, + array[4] of { + 1, 5, 7, 3, + }, + array[4] of { + 2, 6, 7, 3, + }, + array[4] of { + 4, 5, 7, 6, + }, + }; + + for(i = 0; i < 6; i++){ + m_begin(typex, 4); + for(j = 0; j < 4; j++){ + k = F[i][j]; + m_vertex(V[k][0], V[k][1], V[k][2]); + } + m_end(); + } +} + +zerot(tr: ref Object, zero: int, note: int) +{ + if(tr != nil){ + if(zero&REFLECT){ + tr.rt = ∞; + tr.plane = nil; + } + if(zero&COLLIDE){ + tr.ct = ∞; + tr.obj = nil; + } + if(note) + tr.todo = zero; + } +} + +newobj(t: array of ref Object, n: int, vel: int, velf: real): ref Object +{ + tr: ref Object; + p1: Vector; + again: int; + + d := icubd-1.0; + cnt := 1024; + do{ + p1 = randomp(-d, d); + again = 0; + for(i := 0; i < n; i++){ + (nil, d2) := vlen2(vsub(t[i].p, p1)); + if(d2 <= trkd2){ + again = 1; + break; + } + } + cnt--; + }while(again && cnt > 0); + if(again) + return nil; + # p2 := randomp(-d, d); + p21 := randomd(); + tr = ref Object; + tr.id = n; + tr.poly = nil; + if(poly){ + if(n == 0) + tr.poly = randpoly(polys, npolys); + else + tr.poly = t[0].poly; + } + tr.c = randomc(); + tr.cb = tr.c; # randomc(); + if(vel) + tr.v = vf*velf*randomr(0.5, 2.0); + else + tr.v = 0.0; + tr.ω = vf*randomr(1.0, 10.0); + tr.roll = randomr(0.0, 360.0); + tr.pitch = randomr(0.0, 360.0); + tr.yaw = randomr(0.0, 360.0); + tr.l = ref Line(p1, vnorm(p21)); + tr.p = p1; + tr.todo = 0; + zerot(tr, REFLECT|COLLIDE, 0); + tr.pmask = 0; + reflection(tr, planes); + return tr; +} + +objinit(m: int, v: int) +{ + velf := real m/real v; + p := nobjs; + n := p+m; + v += p; + t := array[n] of ref Object; + for(i := 0; i < p; i++) + t[i] = objs[i]; + for(i = p; i < n; i++){ + t[i] = newobj(t, i, i < v, velf); + if(t[i] == nil) + return; + } + sort(t, n); + nobjs = n; + objs = t; + for(i = p; i < n; i++) + collisions(t[i]); +} + +zobj: Object; + +newo(n: int, p: Vector, c: ref Draw->Image): ref Object +{ + o := ref Object; + *o = zobj; + o.id = n; + o.c = o.cb = c; + o.l = ref Line(p, VZ); + o.p = p; + zerot(o, REFLECT|COLLIDE, 0); + reflection(o, planes); + return o; +} + +objinits(nil: int, nil: int) +{ + n := 16; + t := array[n] of ref Object; + r := trkd/2.0; + i := 0; + yc := 0.0; + for(y := 0; y < 5; y++){ + xc := -real y*r; + for(x := 0; x <= y; x++){ + t[i] = newo(i, (xc, yc, 0.0), red); + xc += trkd; + i++; + } + yc += trkd; + } + t[i] = newo(i, (0.0, -50.0, 0.0), white); + t[i].l.d = (0.0, 1.0, 0.0); + t[i].v = 1.0; + sort(t, n); + nobjs = n; + objs = t; + for(i = 0; i < n; i++) + collisions(t[i]); +} + +initme(): ref Object +{ + t := newobj(nil, 0, 1, 1.0); + t.roll = t.pitch = t.yaw = 0.0; + t.v = t.ω = 0.0; + t.l.a = (0.0, 0.0, objd); # origin when translated + t.l.d = (0.0, 0.0, -1.0); + t.p = t.l.a; + zerot(t, REFLECT|COLLIDE, 0); + return t; +} + +retime(s: real) +{ + r := 1.0/s; + n := nobjs; + t := objs; + for(i := 0; i < n; i++){ + tr := t[i]; + tr.v *= s; + tr.ω *= s; + tr.rt *= r; + tr.ct *= r; + } + me.v *= s; + me.ω *= s; + me.rt *= r; + me.ct *= r; +} + +drawobjs() +{ + tr: ref Object; + p: Vector; + + n := nobjs; + t := objs; + + for(i := 0; i < n; i++){ + tr = t[i]; + tr.pmask = 0; + p = tr.p; + m_pushmatrix(); + if(rpy && tr.poly == nil){ + m_loadmatrix(mod0); + (p.x, p.y, p.z) = rotate(int yaw, p.x, p.y, p.z); + (p.y, p.z, p.x) = rotate(int pitch, p.y, p.z, p.x); + (p.z, p.x, p.y) = rotate(int roll, p.z, p.x, p.y); + } + m_translate(p.x, p.y, p.z); + m_scale(trkd, trkd, trkd); + if(tr.poly != nil){ + m_rotatez(tr.roll); + m_rotatey(tr.pitch); + m_rotatex(tr.yaw); + tr.yaw += tr.ω; + } + m_matmul(); + if(tr.cb != tr.c){ + m_colour(tr.cb); + if(tr.poly != nil) + drawpoly(tr.poly, POLY); + else if(DOCIRCLE) + circle(CIRCLE); + else + circle(POLY); + } + m_colour(tr.c); + if(tr.poly != nil) + drawpoly(tr.poly, FILLPOLY); + else if(DOCIRCLE) + circle(FILLCIRCLE); + else + circle(FILLPOLY); + m_popmatrix(); + } + + tm := 1.0; + do{ + δt := ∞; + + for(i = 0; i < n; i++){ + tr = t[i]; + if(tr.rt < δt) + δt = tr.rt; + if(tr.ct < δt) + δt = tr.ct; + } + + if(δt > tm) + δt = tm; + + for(i = 0; i < n; i++){ + tr = t[i]; + if(tr.rt == δt){ + tr1 := tr.obj; + reflect(tr); + tr.p = tr.rp; + if(δt > 0.0) + tr.pmask = (1<<tr.plane.id); + else + tr.pmask |= (1<<tr.plane.id); + zerot(tr, REFLECT|COLLIDE, 1); + zerot(tr1, COLLIDE, 1); + } + else if(tr.ct == δt){ + tr1 := tr.obj ; + collide(tr, tr1); + if(0 && poly) + collideω(tr, tr1); + tr.p = tr.cp; + tr1.p = tr1.cp; + tr.pmask = tr1.pmask = 0; + zerot(tr, REFLECT|COLLIDE, 1); + zerot(tr1, REFLECT|COLLIDE, 1); + } + else if(tr.todo != (REFLECT|COLLIDE)){ + tr.p = vclip(vadd(tr.p, vmul(tr.l.d, tr.v*δt)), icubd); + tr.rt -= δt; + tr.ct -= δt; + } + } + + for(i = 0; i < n; i++){ + tr = t[i]; + if(tr.todo){ + if(tr.todo&REFLECT) + reflection(tr, planes); + if(tr.todo&COLLIDE) + collisions(tr); + tr.todo = 0; + } + } + + tm -= δt; + + }while(tm > 0.0); + + sort(t, n); +} + +drawscene() +{ + m_pushmatrix(); + m_scale(real 2*cubd, real 2*cubd, real 2*cubd); + m_colour(white); + m_matmul(); + case(surr){ + SPHERE => + if(DOCIRCLE) + circle(CIRCLE); + else + circle(POLY); + ELLIPSOID => + ellipse(ELLIPSE); + CUBE => + if(flat) + square(POLY); + else + hexahedron(POLY); + * => + drawpoly(polyh, POLY); + } + m_popmatrix(); + drawobjs(); +} + +# ensure parallax doesn't alter between images +adjpar(x: array of real, y: array of real, z: array of real) +{ + zed, incr: real; + + y = nil; + if(z[0] < real 0) + zed = -z[0]; + else + zed = z[0]; + incr = eyed*zed*(real 1-scrD/(zed+objD-objd))/scrd; + if(!stereo || fov == real 0) + return; + if(left) + x[0] -= incr; + else + x[0] += incr; +} + +view() +{ + m_mode(PROJ); + m_loadidentity(); + m_scale(sx, sy, sz); + if(fov != real 0) + m_frustum(sf, real (1<<2), real (1<<20)); + else + m_ortho(sf, real (1<<2), real (1<<20)); + # m_print(); + m_mode(MODEL); +} + +model(rot: int) +{ + m_loadidentity(); + m_translate(0.0, 0.0, -objd); + if(rpy && rot){ + m_rotatez(roll); + m_rotatey(pitch); + m_rotatex(yaw); + } +} + +# store projection and modelview matrices +domats() +{ + model(0); + m_storematrix(mod0); + model(1); + view(); +} + +scale() +{ + if(maxx > maxy) + sx = real maxy/real maxx; + else + sx = 1.0; + if(maxy > maxx) + sy = real maxx/real maxy; + else + sy = 1.0; + sz = 1.0; +} + +rescale(w: int, h: int) +{ + maxx = w; + maxy = h; + scale(); + m_viewport(0, 0, maxx, maxy); +} + +grinit() +{ + for(i := 0; i < 4; i++) + mod0[i] = array[4] of real; + far = (2.0*cubd, 2.0*cubd, 2.0*cubd); + icubd = cubd-trkd/2.0; + icubd2 = icubd*icubd; + trkd2 = trkd*trkd; + cmin = vpt(-icubd, -icubd, -icubd); + cmax = vpt(+icubd, +icubd, +icubd); + maxx = MAXX; + maxy = MAXY; + e2 = vmul(vvmul(ef, ef), icubd2); + + m_init(); + pinit(); + circleinit(); + + m_viewport(0, 0, maxx, maxy); + + scale(); + if(fov > real 0) + fovtodist(); + update(1); +} + +newimage(win: ref Toplevel, init: int) +{ + maxx = int cmd(win, ".p cget -actwidth"); + maxy = int cmd(win, ".p cget -actheight"); + RDisp = display.newimage(((0, 0), (maxx, maxy)), win.image.chans, 0, Draw->Black); + tk->putimage(win, ".p", RDisp, nil); + RDisp.draw(RDisp.r, black, nil, (0, 0)); + reveal(); + rescale(maxx, maxy); + update(init); +} + +reveal() +{ + cmd(toplev, ".p dirty; update"); +} + +usage() +{ + sys->fprint(sys->fildes(2), "usage: collide [-cse] [-f] [-op] [-b num] [-v num]\n"); + exit; +} + +main(ctxt: ref Draw->Context, args: list of string) +{ + rand->init(daytime->now()); + daytime = nil; + + b := v := random(16, 32); + + arg->init(args); + while((o := arg->opt()) != 0){ + case(o){ + * => + usage(); + 's' => + surr = SPHERE; + 'e' => + surr = ELLIPSOID; + 'c' => + surr = CUBE; + 'o' => + fov = 0.0; + 'p' => + fov = -1.0; + 'f' => + flat = 1; + 'b' => + b = v = int arg->arg(); + 'v' => + v = int arg->arg(); + } + } + if(arg->argv() != nil) + usage(); + + if(b <= 0) + b = 1; + if(b > 100) + b = 100; + if(v <= 0) + v = 1; + if(v > b) + v = b; + + if(poly || surr == POLYHEDRON){ + polyhedra = load Polyhedra Polyhedra->PATH; + getpolys(); + } + if(surr == POLYHEDRON) + polyh = randpoly(polys, npolys); + + grinit(); + + tkclient->init(); + (win, wmch) := tkclient->toplevel(ctxt, "", "Collide", Tkclient->Resize | Tkclient->Hide); + toplev = win; + sys->pctl(Sys->NEWPGRP, nil); + cmdch := chan of string; + tk->namechan(win, cmdch, "cmd"); + for(i := 0; i < len winconfig; i++) + cmd(win, winconfig[i]); + cmd(win, "update"); + + tkclient->onscreen(win, nil); + tkclient->startinput(win, "kbd"::"ptr"::nil); + + display = win.image.display; + newimage(win, 1); + + black = display.color(Draw->Black); + white = display.color(Draw->White); + red = display.color(Draw->Red); + + objinit(b, v); + me = initme(); + + pid := -1; + sync := chan of int; + cmdc := chan of int; + spawn animate(sync, cmdc); + pid = <- sync; + + for(;;){ + alt{ + c := <-win.ctxt.kbd => + tk->keyboard(win, c); + c := <-win.ctxt.ptr => + tk->pointer(win, *c); + c := <-win.ctxt.ctl or + c = <-win.wreq => + tkclient->wmctl(win, c); + c := <- wmch => + case c{ + "exit" => + if(pid != -1) + kill(pid); + exit; + * => + sync <-= 0; + tkclient->wmctl(win, c); + if(c[0] == '!') + newimage(win, 0); + sync <-= 1; + } + c := <- cmdch => + case c{ + "stop" => + cmdc <-= 's'; + "zoomin" => + cmdc <-= 'z'; + "zoomout" => + cmdc <-= 'o'; + "slow" => + cmdc <-= '-'; + "fast" => + cmdc <-= '+'; + "objs" => + sync <-= 0; + b >>= 1; + if(b == 0) + b = 1; + objinit(b, b); + sync <-= 1; + } + } + } +} + +sign(r: real): real +{ + if(r < 0.0) + return -1.0; + return 1.0; +} + +abs(r: real): real +{ + if(r < 0.0) + return -r; + return r; +} + +animate(sync: chan of int, cmd: chan of int) +{ + zd := objd/250.0; + δ := θ := 0.0; + f := 8; + + sync <- = sys->pctl(0, nil); + for(;;){ + σ := 1.0; + alt{ + <- sync => + <- sync; + c := <- cmd => + case(c){ + 's' => + δ = θ = 0.0; + 'z' => + δ = zd; + 'o' => + δ = -zd; + 'r' => + θ = 1.0; + '+' => + σ = 1.25; + f++; + if(f > 16){ + f--; + σ = 1.0; + } + else + vf *= σ; + '-' => + σ = 0.8; + f--; + if(f < 0){ + f++; + σ = 1.0; + } + else + vf *= σ; + } + * => + sys->sleep(0); + } + + RDisp.draw(RDisp.r, black, nil, (0, 0)); + drawscene(); + reveal(); + + if(δ != 0.0 || θ != 0.0){ + objd -= δ; + me.l.a.z -= δ; + if(θ != 0.0){ + roll += θ; + pitch += θ; + yaw += θ; + rpy = 1; + } + update(projx); + } + if(σ != 1.0) + retime(σ); + } +} + +# usually almost sorted +sort(ts: array of ref Object, n: int) +{ + done: int; + t: ref Object; + q, p: int; + + q = n; + do{ + done = 1; + q--; + for(p = 0; p < q; p++){ + if(ts[p].p.z > ts[p+1].p.z){ + t = ts[p]; + ts[p] = ts[p+1]; + ts[p+1] = t; + done = 0; + } + } + }while(!done); +} + +kill(pid: int): int +{ + fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE); + if(fd == nil) + return -1; + if(sys->write(fd, array of byte "kill", 4) != 4) + return -1; + return 0; +} + +fatal(e: string) +{ + sys->fprint(sys->fildes(2), "%s\n", e); + raise "fatal"; +} + +cmd(top: ref Toplevel, s: string): string +{ + e := tk->cmd(top, s); + if (e != nil && e[0] == '!') + fatal(sys->sprint("tk error on '%s': %s", s, e)); + return e; +} + +winconfig := array[] of { + "frame .f", + "button .f.zoomin -text {zoomin} -command {send cmd zoomin}", + "button .f.zoomout -text {zoomout} -command {send cmd zoomout}", + "button .f.stop -text {stop} -command {send cmd stop}", + "pack .f.zoomin -side left", + "pack .f.zoomout -side right", + "pack .f.stop -side top", + + "frame .f2", + "button .f2.slow -text {slow} -command {send cmd slow}", + "button .f2.fast -text {fast} -command {send cmd fast}", + "button .f2.objs -text {objects} -command {send cmd objs}", + "pack .f2.slow -side left", + "pack .f2.fast -side right", + "pack .f2.objs -side top", + + "panel .p -width " + string MAXX + " -height " + string MAXY, + + "pack .f -side top -fill x", + "pack .f2 -side top -fill x", + "pack .p -side bottom -fill both -expand 1", + + "pack propagate . 0", + "update" +}; + +############################################################ + +# gl.b + +# +# initially generated by c2l +# + +MODEL, PROJ: con iota; + +Matrix: type array of array of real; + +Mstate: adt{ + matl: list of Matrix; + modl: list of Matrix; + prjl: list of Matrix; + mull: Matrix; + freel: list of Matrix; + vk: int; + vr: int; + vrr: int; + vc: ref Draw->Image; + ap: array of Draw->Point; + apn: int; + mx, cx, my, cy: real; + ignore: int; +}; + +ms: Mstate; + +m_new(): Matrix +{ + if(ms.freel != nil){ + m := hd ms.freel; + ms.freel = tl ms.freel; + return m; + } + m := array[4] of array of real; + for(i := 0; i < 4; i++) + m[i] = array[4] of real; + return m; +} + +m_free(m: Matrix) +{ + ms.freel = m :: ms.freel; +} + +m_init() +{ + ms.modl = m_new() :: nil; + ms.prjl = m_new() :: nil; + ms.matl = ms.modl; + ms.mull = m_new(); + ms.vk = 0; + ms.apn = 0; + ms.mx = ms.cx = ms.my = ms.cy = 0.0; + ms.ignore = 0; +} + +m_print() +{ + m := hd ms.matl; + + for(i := 0; i < 4; i++){ + for(j := 0; j < 4; j++) + sys->print("%f ", m[i][j]); + sys->print("\n"); + } + sys->print("\n"); +} + +m_mode(m: int) +{ + if(m == PROJ) + ms.matl = ms.prjl; + else + ms.matl = ms.modl; +} + +m_pushmatrix() +{ + if(ms.matl == ms.modl){ + ms.modl = m_new() :: ms.modl; + ms.matl = ms.modl; + } + else{ + ms.prjl = m_new() :: ms.prjl; + ms.matl = ms.prjl; + } + s := hd tl ms.matl; + d := hd ms.matl; + for(i := 0; i < 4; i++) + for(j := 0; j < 4; j++) + d[i][j] = s[i][j]; +} + +m_popmatrix() +{ + if(ms.matl == ms.modl){ + m_free(hd ms.modl); + ms.modl = tl ms.modl; + ms.matl = ms.modl; + } + else{ + m_free(hd ms.prjl); + ms.prjl = tl ms.prjl; + ms.matl = ms.prjl; + } +} + +m_loadidentity() +{ + i, j: int; + m := hd ms.matl; + + for(i = 0; i < 4; i++){ + for(j = 0; j < 4; j++) + m[i][j] = real 0; + m[i][i] = real 1; + } +} + +m_translate(x: real, y: real, z: real) +{ + i: int; + m := hd ms.matl; + + for(i = 0; i < 4; i++) + m[i][3] = x*m[i][0]+y*m[i][1]+z*m[i][2]+m[i][3]; +} + +m_scale(x: real, y: real, z: real) +{ + i: int; + m := hd ms.matl; + + for(i = 0; i < 4; i++){ + m[i][0] *= x; + m[i][1] *= y; + m[i][2] *= z; + } +} + +# rotate about x, y or z axes +rot(deg: real, j: int, k: int) +{ + i: int; + m := hd ms.matl; + rad := Math->Pi*deg/real 180; + s := maths->sin(rad); + c := maths->cos(rad); + a, b: real; + + for(i = 0; i < 4; i++){ + a = m[i][j]; + b = m[i][k]; + m[i][j] = c*a+s*b; + m[i][k] = c*b-s*a; + } +} + +m_rotatex(a: real) +{ + rot(a, 1, 2); +} + +m_rotatey(a: real) +{ + rot(a, 2, 0); +} + +m_rotatez(a: real) +{ + rot(a, 0, 1); +} + +# (l m n) normalized +m_rotate(deg: real, l: real, m: real, n:real) +{ + i: int; + mx := hd ms.matl; + rad := Math->Pi*deg/real 180; + s := maths->sin(rad); + c := maths->cos(rad); + f := 1.0-c; + m0, m1, m2: real; + + for(i = 0; i < 4; i++){ + m0 = mx[i][0]; + m1 = mx[i][1]; + m2 = mx[i][2]; + mx[i][0] = m0*(l*l*f+c)+m1*(l*m*f+n*s)+m2*(l*n*f-m*s); + mx[i][1] = m0*(l*m*f-n*s)+m1*(m*m*f+c)+m2*(m*n*f+l*s); + mx[i][2] = m0*(l*n*f+m*s)+m1*(m*n*f-l*s)+m2*(n*n*f+c); + } +} + +# Frustum(-l, l, -l, l, n, f) +m_frustum(l: real, n: real, f: real) +{ + i: int; + m := hd ms.matl; + r := n/l; + a, b: real; + + f = ∞; + for(i = 0; i < 4; i++){ + a = m[i][2]; + b = m[i][3]; + m[i][0] *= r; + m[i][1] *= r; + m[i][2] = a+b; + m[i][3] = 0.0; + # m[i][2] = -(a*(f+n)/(f-n)+b); + # m[i][3] = real -2*f*n*a/(f-n); + } +} + +# Ortho(-l, l, -l, l, n, f) +m_ortho(l: real, n: real, f: real) +{ + i: int; + m := hd ms.matl; + r := real 1/l; + # a: real; + + n = 0.0; + f = ∞; + for(i = 0; i < 4; i++){ + # a = m[i][2]; + m[i][0] *= r; + m[i][1] *= r; + # m[i][2] *= real -2/(f-n); + # m[i][3] -= a*(f+n)/(f-n); + } +} + +m_loadmatrix(u: array of array of real) +{ + m := hd ms.matl; + + for(i := 0; i < 4; i++) + for(j := 0; j < 4; j++) + m[i][j] = u[i][j]; +} + +m_storematrix(u: array of array of real) +{ + m := hd ms.matl; + + for(i := 0; i < 4; i++) + for(j := 0; j < 4; j++) + u[i][j] = m[i][j]; +} + +m_matmul() +{ + m, p, r: Matrix; + + m = hd ms.modl; + p = hd ms.prjl; + r = ms.mull; + for(i := 0; i < 4; i++){ + pr := p[i]; + rr := r[i]; + for(j := 0; j < 4; j++) + rr[j] = pr[0]*m[0][j]+pr[1]*m[1][j]+pr[2]*m[2][j]+pr[3]*m[3][j]; + } +} + +m_vertexo(x: real, y: real, z: real) +{ + m: Matrix; + mr: array of real; + w, x1, y1, z1: real; + + m = hd ms.modl; + mr = m[0]; x1 = x*mr[0]+y*mr[1]+z*mr[2]+mr[3]; + mr = m[1]; y1 = x*mr[0]+y*mr[1]+z*mr[2]+mr[3]; + mr = m[2]; z1 = x*mr[0]+y*mr[1]+z*mr[2]+mr[3]; + mr = m[3]; w = x*mr[0]+y*mr[1]+z*mr[2]+mr[3]; + if(w != real 1){ + x1 /= w; + y1 /= w; + z1 /= w; + } + if(z1 >= 0.0){ + ms.ignore = 1; + return; + } + m = hd ms.prjl; + mr = m[0]; x = x1*mr[0]+y1*mr[1]+z1*mr[2]+mr[3]; + mr = m[1]; y = x1*mr[0]+y1*mr[1]+z1*mr[2]+mr[3]; + mr = m[2]; z = x1*mr[0]+y1*mr[1]+z1*mr[2]+mr[3]; + mr = m[3]; w = x1*mr[0]+y1*mr[1]+z1*mr[2]+mr[3]; + if(w == real 0){ + ms.ignore = 1; + return; + } + if(w != real 1){ + x /= w; + y /= w; + # z /= w; + } + ms.ap[ms.apn++] = (int (ms.mx*x+ms.cx), int (ms.my*y+ms.cy)); +} + +m_vertex(x: real, y: real, z: real): (real, real) +{ + m: Matrix; + mr: array of real; + w, x1, y1, z1: real; + + m = ms.mull; + mr = m[0]; x1 = x*mr[0]+y*mr[1]+z*mr[2]+mr[3]; + mr = m[1]; y1 = x*mr[0]+y*mr[1]+z*mr[2]+mr[3]; + mr = m[2]; z1 = x*mr[0]+y*mr[1]+z*mr[2]+mr[3]; + mr = m[3]; w = x*mr[0]+y*mr[1]+z*mr[2]+mr[3]; + if(w == real 0){ + ms.ignore = 1; + return (x1, y1); + } + if(w != real 1){ + x1 /= w; + y1 /= w; + # z1 /= w; + } + if(z1 >= 0.0){ + ms.ignore = 1; + return (x1, y1); + } + ms.ap[ms.apn++] = (int (ms.mx*x1+ms.cx), int (ms.my*y1+ms.cy)); + return (x1, y1); +} + +m_circle(x: real, y: real, z: real, r: real) +{ + (d, nil) := m_vertex(x, y, z); + (e, nil) := m_vertex(x+r, y, z); + d -= e; + if(d < 0.0) + d = -d; + ms.vr = int (ms.mx*d); +} + +m_ellipse(x: real, y: real, z: real, v: Vector) +{ + m_circle(x, y, z, v.x); + (nil, d) := m_vertex(x, y, z); + (nil, e) := m_vertex(x, y+v.y, z); + d -= e; + if(d < 0.0) + d = -d; + ms.vrr = int (ms.my*d); +} + +m_begin(k: int, n: int) +{ + ms.ignore = 0; + ms.vk = k; + ms.ap = array[n+1] of Draw->Point; + ms.apn = 0; +} + +m_end() +{ + if(ms.ignore) + return; + case(ms.vk){ + CIRCLE => + RDisp.ellipse(ms.ap[0], ms.vr, ms.vr, 0, ms.vc, (0, 0)); + FILLCIRCLE => + RDisp.fillellipse(ms.ap[0], ms.vr, ms.vr, ms.vc, (0, 0)); + ELLIPSE => + RDisp.ellipse(ms.ap[0], ms.vr, ms.vrr, 0, ms.vc, (0, 0)); + FILLELLIPSE => + RDisp.fillellipse(ms.ap[0], ms.vr, ms.vrr, ms.vc, (0, 0)); + POLY => + ms.ap[len ms.ap-1] = ms.ap[0]; + RDisp.poly(ms.ap, Draw->Endsquare, Draw->Endsquare, 0, ms.vc, (0, 0)); + FILLPOLY => + ms.ap[len ms.ap-1] = ms.ap[0]; + RDisp.fillpoly(ms.ap, ~0, ms.vc, (0, 0)); + } +} + +m_colour(i: ref Draw->Image) +{ + ms.vc = i; +} + +m_viewport(x1: int, y1: int, x2: int, y2: int) +{ + ms.mx = real (x2-x1)/2.0; + ms.cx = real (x2+x1)/2.0; + ms.my = real (y2-y1)/2.0; + ms.cy = real (y2+y1)/2.0; +} + +# sys->print("%d %f (%f %f %f) %s\n", ok, λ, 1.0, 2.0*vdot(l.a, l.d), vdot(l.a, l.a)-icubd2, lstring(l)); + +# sys->print("%d %f (%f %f %f) %s\n", ok, λ, vmuldiv(l.d, l.d, e2), 2.0*vmuldiv(l.a, l.d, e2), vmuldiv(l.a, l.a, e2)-1.0, lstring(l)); + +# for(lp = lp0 ; lp != nil; lp = tl lp){ +# p := hd lp; +# (ok, λ, pt) := intersect(l, p); +# sys->print("%d %x %d %f %s %s %s\n", p.id, tr.pmask, ok, λ, vstring(pt), pstring(p), lstring(l)); +# }
\ No newline at end of file diff --git a/appl/wm/colors.b b/appl/wm/colors.b new file mode 100644 index 00000000..619cffe0 --- /dev/null +++ b/appl/wm/colors.b @@ -0,0 +1,153 @@ +implement Colors; + +include "sys.m"; + sys: Sys; +include "draw.m"; + draw: Draw; + Display, Point, Rect, Image: import draw; +include "tk.m"; + tk: Tk; +include "tkclient.m"; + tkclient: Tkclient; + +Colors: module { + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +display: ref Display; +top: ref Tk->Toplevel; +tmpi: ref Image; + +task_cfg := array[] of { + "panel .c", + "label .l -anchor w -text {col:}", + "pack .l -fill x", + "pack .c -fill both -expand 1", + "bind .c <Button-1> {grab set .c; send cmd %X %Y}", + "bind .c <ButtonRelease-1> {grab release .c}", +}; + +init(ctxt: ref Draw->Context, nil: list of string) +{ + spawn init1(ctxt); +} + +init1(ctxt: ref Draw->Context) +{ + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + + tkclient->init(); + display = ctxt.display; + tmpi = display.newimage(((0,0), (1, 1)), Draw->RGB24, 0, 0); + + titlectl: chan of string; + (top, titlectl) = tkclient->toplevel(ctxt, "", "Colors", Tkclient->Appl); + + cmdch := chan of string; + tk->namechan(top, cmdch, "cmd"); + + for (i := 0; i < len task_cfg; i++) + cmd(top, task_cfg[i]); + tk->putimage(top, ".c", cmap((256, 256)), nil); + cmd(top, "pack propagate . 0"); + cmd(top, "update"); + tkclient->onscreen(top, nil); + tkclient->startinput(top, "kbd"::"ptr"::nil); + + for(;;) alt { + s := <-top.ctxt.kbd => + tk->keyboard(top, s); + s := <-top.ctxt.ptr => + tk->pointer(top, *s); + c := <-top.ctxt.ctl or + c = <-top.wreq or + c = <-titlectl => + if(c == "exit") + return; + e := tkclient->wmctl(top, c); + if(e == nil && c[0] == '!'){ + tk->putimage(top, ".c", cmap(actr(".c").size()), nil); + cmd(top, "update"); + } + + press := <-cmdch => + (nil, toks) := sys->tokenize(press, " "); + color((int hd toks, int hd tl toks)); + } +} + +color(p: Point) +{ + r, g, b: int; + col: string; + + cr := actr(".c"); + if(p.in(cr)){ + p = p.sub(cr.min); + p.x = (16*p.x)/cr.dx(); + p.y = (16*p.y)/cr.dy(); + (r, g, b) = display.cmap2rgb(16*p.y+p.x); + col = string (16*p.y+p.x); + }else{ + tmpi.draw(tmpi.r, display.image, nil, p); + data := array[3] of byte; + ok := tmpi.readpixels(tmpi.r, data); + if(ok != len data) + return; + (r, g, b) = (int data[2], int data[1], int data[0]); + c := display.rgb2cmap(r, g, b); + (r1, g1, b1) := display.cmap2rgb(c); + if (r == r1 && g == g1 && b == b1) + col = string c; + else + col = "~" + string c; + } + + cmd(top, ".l configure -text " + + sys->sprint("{col:%s #%.6X r%d g%d b%d}", col, (r<<16)|(g<<8)|b, r, g, b)); + cmd(top, "update"); +} + +cmap(size: Point): ref Image +{ + # use writepixels because it's much faster than allocating all those colors. + img := display.newimage(((0, 0), size), Draw->CMAP8, 0, 0); + if (img == nil){ + sys->print("colors: cannot make new image: %r\n"); + return nil; + } + + dy := (size.y / 16 + 1); + buf := array[size.x * dy] of byte; + + for(y:=0; y<16; y++){ + for (i := 0; i < size.x; i++) + buf[i] = byte (16*y + (16*i)/size.x); + for (i = 1; i < dy; i++) + buf[size.x*i:] = buf[0:size.x]; + img.writepixels(((0, (y*size.y)/16), (size.x, ((y+1)*size.y) / 16)), buf); + } + return img; +} + +actr(w: string): Rect +{ + r: Rect; + bd := int cmd(top, w + " cget -bd"); + r.min.x = int cmd(top, w + " cget -actx") + bd; + r.min.y = int cmd(top, w + " cget -acty") + bd; + r.max.x = r.min.x + int cmd(top, w + " cget -actwidth"); + r.max.y = r.min.y + int cmd(top, w + " cget -actheight"); + return r; +} + +cmd(top: ref Tk->Toplevel, cmd: string): string +{ + e := tk->cmd(top, cmd); + if (e != nil && e[0] == '!') + sys->print("colors: tk error on '%s': %s\n", cmd, e); + return e; +} diff --git a/appl/wm/cprof.b b/appl/wm/cprof.b new file mode 100644 index 00000000..1d998236 --- /dev/null +++ b/appl/wm/cprof.b @@ -0,0 +1,360 @@ +implement Wmcprof; + +include "sys.m"; + sys: Sys; +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; +include "draw.m"; + draw: Draw; +include "tk.m"; + tk: Tk; +include "tkclient.m"; + tkclient: Tkclient; +include "arg.m"; + arg: Arg; +include "profile.m"; + +Prof: module{ + init0: fn(ctxt: ref Draw->Context, argv: list of string): Profile->Coverage; +}; + +prof: Prof; + +Wmcprof: module{ + init: fn(ctxt: ref Draw->Context, argl: list of string); +}; + +usage(s: string) +{ + sys->fprint(sys->fildes(2), "wm/cprof: %s\n", s); + sys->fprint(sys->fildes(2), "usage: wm/cprof [-efr] [-m modname]... cmd [arg ... ]"); + exit; +} + +TXTBEGIN: con 3; + +freq: int; + +init(ctxt: ref Draw->Context, argl: list of string) +{ + sys = load Sys Sys->PATH; + bufio = load Bufio Bufio->PATH; + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + arg = load Arg Arg->PATH; + + if(ctxt == nil) + fatal("wm not running"); + sys->pctl(Sys->NEWPGRP, nil); + + arg->init(argl); + while((o := arg->opt()) != 0){ + case(o){ + 'e' or 'r' => + ; + 'f' => + freq = 1; + 'm' => + if(arg->arg() == nil) + usage("missing module/file"); + * => + usage(sys->sprint("unknown option -%c", o)); + } + } + + cover := execprof(ctxt, argl); + + tkclient->init(); + (win, wmc) := tkclient->toplevel(ctxt, nil, hd argl, Tkclient->Resize|Tkclient->Hide); + tkc := chan of string; + tk->namechan(win, tkc, "tkc"); + for(i := 0; i < len wincfg; i++) + cmd(win, wincfg[i]); + tkclient->onscreen(win, nil); + tkclient->startinput(win, "kbd"::"ptr"::nil); + createmenu(win, cover); + curc := 0; + curm := newprint(win, cover, curc); + + for(;;){ + alt{ + c := <-win.ctxt.kbd => + tk->keyboard(win, c); + c := <-win.ctxt.ptr => + tk->pointer(win, *c); + c := <-win.ctxt.ctl or + c = <-win.wreq or + c = <-wmc => + tkclient->wmctl(win, c); + c := <- tkc => + (nil, toks) := sys->tokenize(c, " "); + case(hd toks){ + "b" => + if(curc > 0) + curm = newprint(win, cover, --curc); + "f" => + if(curc < len cover - 1) + curm = newprint(win, cover, ++curc); + "s" => + if(curm != nil) + scroll(win, curm); + "m" => + x := cmd(win, ".f cget actx"); + y := cmd(win, ".f cget acty"); + cmd(win, ".f.menu post " + x + " " + y); + * => + curc = int hd toks; + curm = newprint(win, cover, curc); + } + } + } +} + +execprof(ctxt: ref Draw->Context, argl: list of string): Profile->Coverage +{ + { + prof = load Prof "/dis/cprof.dis"; + if(prof == nil) + fatal("cannot load profiler"); + return prof->init0(ctxt, hd argl :: "-g" :: tl argl); + } + exception{ + "fail:*" => + return nil; + } + return nil; +} + +maxf(rs: list of (int, int, int)): int +{ + fmax := 0; + for(r := rs; r != nil; r = tl r){ + (nil, nil, f) := hd r; + if(f > fmax) + fmax = f; + } + return fmax; +} + +print(win: ref Tk->Toplevel, cvr: Profile->Coverage, i: int, c: chan of Profile->Coverage) +{ + cmd(win, ".f.t delete 1.0 end"); + cmd(win, "update"); + m0, m1: Profile->Coverage; + for(m := cvr; m != nil && --i >= 0; m = tl m) + m0 = m; + if(m == nil){ + c <- = nil; + return; + } + m1 = tl m; + (name, cvd, ls) := hd m; + name0 := name1 := "nil"; + if(m0 != nil) + (name0, nil, nil) = hd m0; + if(m1 != nil) + (name1, nil, nil) = hd m1; + if(freq){ + cvd = 0; + for(l := ls; l != nil; l = tl l){ + (rs, nil) := hd l; + cvd += maxf(rs); + } + } + else + name += sys->sprint(" (%d%% coverage) ", cvd); + cmd(win, ".f.t insert end {" + name + " <- " + name0 + " -> " + name1 + "}"); + cmd(win, ".f.t insert end \n\n"); + cmd(win, "update"); + line := TXTBEGIN; + for(l := ls; l != nil; l = tl l){ + tab := 0; + (rs, s) := hd l; + if(freq){ + fmax := maxf(rs); + s = string fmax + "\t" + s; + tab = len string fmax + 1; + } + cmd(win, ".f.t insert end " + tk->quote(s)); + for(r := rs; r != nil; r = tl r){ + tag: string; + (a, b, e) := hd r; + if(freq){ + tag = gettag(win, e, cvd); + a += tab; + b += tab; + } + else{ + if(int e) # partly executed + tag = "halfexec"; + else + tag = "notexec"; + } + cmd(win, ".f.t tag add " + tag + " " + string line + "." + string a + " " + string line + "." + string b); + } + cmd(win, "update"); + line++; + } + c <- = m; +} + +newprint(win: ref Tk->Toplevel, cvr: Profile->Coverage, i: int): Profile->Coverage +{ + c := chan of Profile->Coverage; + spawn print(win, cvr, i, c); + return <- c; +} + +index(win: ref Tk->Toplevel, x: int, y: int): int +{ + t := cmd(win, ".f.t index @" + string x + "," + string y); + (nil, l) := sys->tokenize(t, "."); +# sys->print("%d,%d -> %s\n", x, y, t); + return int hd l; +} + +winextent(win: ref Tk->Toplevel): (int, int) +{ + w := int cmd(win, ".f.t cget -actwidth"); + h := int cmd(win, ".f.t cget -actheight"); + lw := index(win, 0, 0); + uw := index(win, w-1, h-1); + return (lw, uw); +} + +see(win: ref Tk->Toplevel, line: int) +{ + cmd(win, ".f.t see " + string line + ".0"); + cmd(win, "update"); +} + +scroll(win: ref Tk->Toplevel, m: Profile->Coverage) +{ + (nil, cvd, ls) := hd m; + if(freq) + cvd = 0; + (nil, uw) := winextent(win); + line := TXTBEGIN; + for(l := ls; l != nil; l = tl l){ + (rs, nil) := hd l; + if(rs != nil && line > uw){ + see(win, line); + return; + } + line++; + } + if(cvd < 100){ + line = TXTBEGIN; + for(l = ls; l != nil; l = tl l){ + (rs, nil) := hd l; + if(rs != nil){ + see(win, line); + return; + } + line++; + } + } + return; +} + +cmd(top: ref Tk->Toplevel, s: string): string +{ + # sys->print("%s\n", s); + e := tk->cmd(top, s); + if (e != nil && e[0] == '!') + sys->fprint(sys->fildes(2), "tk error on '%s': %s\n", s, e); + return e; +} + +fatal(s: string) +{ + sys->fprint(sys->fildes(2), "%s\n", s); + exit; +} + +MENUMAX: con 20; + +createmenu(top: ref Tk->Toplevel, cvr: Profile->Coverage ) +{ + mn := ".f.menu"; + cmd(top, "menu " + mn); + i := j := 0; + for(m := cvr; m != nil; m = tl m){ + (name, nil, nil) := hd m; + cmd(top, mn + " add command -label " + name + " -command {send tkc " + string i + "}"); + i++; + j++; + if(j == MENUMAX && tl m != nil){ + cmd(top, mn + " add cascade -label MORE -menu " + mn + ".menu"); + mn += ".menu"; + cmd(top, "menu " + mn); + j = 0; + } + } +} + +SNT: con 16; +NT: con SNT*SNT; +NTF: con 256/SNT; + +tags := array[NT] of { * => byte 0 }; + +gettag(win: ref Tk->Toplevel, n: int, d: int): string +{ + i := int ((real n/real d) * real (NT-1)); + if(i < 0 || i > NT-1) + i = 0; + s := "tag" + string i; + if(tags[i] == byte 0){ + rgb := "#" + hex2(255-NTF*0)+hex2(255-NTF*(i/SNT))+hex2(255-NTF*(i%SNT)); + cmd(win, ".f.t tag configure " + s + " -fg black -bg " + rgb); + tags[i] = byte 1; + } + return s; +} + +hex(i: int): int +{ + if(i < 10) + return i+'0'; + else + return i-10+'A'; +} + +hex2(i: int): string +{ + s := "00"; + s[0] = hex(i/16); + s[1] = hex(i%16); + return s; +} + +wincfg := array[] of { + "frame .f", + "text .f.t -width 809 -height 500 -state disabled -wrap char -bg white -yscrollcommand {.f.s set}", + "scrollbar .f.s -orient vertical -command {.f.t yview}", + "frame .i", + "button .i.b -bitmap small_color_left.bit -command {send tkc b}", + "button .i.f -bitmap small_color_right.bit -command {send tkc f}", + "button .i.s -bitmap small_find.bit -command {send tkc s}", + "button .i.m -bitmap small_reload.bit -command {send tkc m}", + + "pack .i.b -side left", + "pack .i.f -side left", + "pack .i.s -side left", + "pack .i.m -side left", + + "pack .f.s -fill y -side left", + "pack .f.t -fill both -expand 1", + + "pack .i -fill x", + "pack .f -fill both -expand 1", + "pack propagate . 0", + + ".f.t tag configure notexec -fg white -bg red", + ".f.t tag configure halfexec -fg red -bg white", + + "update", +};
\ No newline at end of file diff --git a/appl/wm/date.b b/appl/wm/date.b new file mode 100644 index 00000000..72278b7a --- /dev/null +++ b/appl/wm/date.b @@ -0,0 +1,78 @@ +implement WmDate; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + +include "tk.m"; + tk: Tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "daytime.m"; + daytime: Daytime; + + +WmDate: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +tpid: int; + +init(ctxt: ref Draw->Context, nil: list of string) +{ + sys = load Sys Sys->PATH; + if (ctxt == nil) { + sys->fprint(sys->fildes(2), "date: no window context\n"); + raise "fail:bad context"; + } + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient= load Tkclient Tkclient->PATH; + daytime = load Daytime Daytime->PATH; + + sys->pctl(Sys->NEWPGRP, nil); + tkclient->init(); + (t, wmctl) := tkclient->toplevel(ctxt, "", "Date", 0); + + st := daytime->time()[0:19]; + tk->cmd(t, "label .d -label {"+st+"}"); + tk->cmd(t, "pack .d; pack propagate . 0"); + tkclient->onscreen(t, nil); + tkclient->startinput(t, "ptr"::nil); + tick := chan of int; + spawn timer(tick); + + for(;;) alt { + s := <-t.ctxt.kbd => + tk->keyboard(t, s); + s := <-t.ctxt.ptr => + tk->pointer(t, *s); + s := <-t.ctxt.ctl or + s = <-t.wreq or + s = <-wmctl => + tkclient->wmctl(t, s); + <-tick => + tk->cmd(t, ".d configure -label {"+daytime->time()[0:19]+"};update"); + } +} + +timer(c: chan of int) +{ + tpid = sys->pctl(0, nil); + for(;;) { + c <-= 1; + sys->sleep(1000); + } +} + +kill(pid: int) +{ + fd := sys->open("#p/"+string pid+"/ctl", sys->OWRITE); + if(fd != nil) + sys->fprint(fd, "kill"); +} diff --git a/appl/wm/deb.b b/appl/wm/deb.b new file mode 100644 index 00000000..fa8208b0 --- /dev/null +++ b/appl/wm/deb.b @@ -0,0 +1,1444 @@ +implement WmDebugger; + +include "sys.m"; + sys: Sys; + stderr: ref Sys->FD; + +include "string.m"; + str: String; + +include "arg.m"; + arg: Arg; + +include "readdir.m"; + readdir: Readdir; + +include "draw.m"; + draw: Draw; + +include "tk.m"; + tk: Tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "dialog.m"; + dialog: Dialog; + +include "selectfile.m"; + selectfile: Selectfile; + +include "tabs.m"; + tabs: Tabs; + +include "debug.m"; + debug: Debug; + Prog, Exp, Module, Src, Sym: import debug; + +include "wmdeb.m"; + debdata: DebData; + Vars: import debdata; + debsrc: DebSrc; + opendir, Mod: import debsrc; + +WmDebugger: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +icondir : con "debug/"; + +tkconfig := array[] of { + "frame .m -relief raised -bd 1", + "frame .p -padx 2", + "frame .ctls -padx 2", + "frame .body", + + # menu bar + "menubutton .m.file -text File -menu .m.file.menu", + "menubutton .m.search -text Search -menu .m.search.menu", + "button .m.stack -text Stack -command {send m stack}", + "pack .m.file .m.search .m.stack -side left", + + # file menu + "menu .m.file.menu", + ".m.file.menu add command -label Open... -command {send m open}", + ".m.file.menu add command -label Thread... -command {send m pickup}", + ".m.file.menu add command -label Options... -command {send m options}", + ".m.file.menu add separator", + + # search menu + "menu .m.search.menu", + ".m.search.menu add command -state disabled"+ + " -label Look -command {send m look}", + ".m.search.menu add command -state disabled"+ + " -label {Search For} -command {send m search}", + + # program control + "image create bitmap Detach -file "+icondir+ + "detach.bit -maskfile "+icondir+"detach.mask", + "image create bitmap Kill -file "+icondir+ + "kill.bit -maskfile "+icondir+"kill.mask", + "image create bitmap Run -file "+icondir+ + "run.bit -maskfile "+icondir+"run.mask", + "image create bitmap Stop -file "+icondir+ + "stop.bit -maskfile "+icondir+"stop.mask", + "image create bitmap Bpt -file "+icondir+ + "break.bit -maskfile "+icondir+"break.mask", + "image create bitmap Stepop -file "+icondir+ + "stepop.bit -maskfile "+icondir+"stepop.mask", + "image create bitmap Stepin -file "+icondir+ + "stepin.bit -maskfile "+icondir+"stepin.mask", + "image create bitmap Stepout -file "+icondir+ + "stepout.bit -maskfile "+icondir+"stepout.mask", + "image create bitmap Stepover -file "+icondir+ + "stepover.bit -maskfile "+icondir+"stepover.mask", + "button .p.kill -image Kill -command {send m killall}"+ + " -state disabled -relief sunken", + "bind .p.kill <Enter> +{.p.status configure -text {kill current process}}", + "bind .p.kill <Leave> +{.p.status configure -text {}}", + "button .p.detach -image Detach -command {send m detach}"+ + " -state disabled -relief sunken", + "bind .p.detach <Enter> +{.p.status configure -text {stop debugging current process}}", + "bind .p.detach <Leave> +{.p.status configure -text {}}", + "button .p.run -image Run -command {send m run}"+ + " -state disabled -relief sunken", + "bind .p.run <Enter> +{.p.status configure -text {run to breakpoint}}", + "bind .p.run <Leave> +{.p.status configure -text {}}", + "button .p.step -image Stepop -command {send m step}"+ + " -state disabled -relief sunken", + "bind .p.step <Enter> +{.p.status configure -text {step one operation}}", + "bind .p.step <Leave> +{.p.status configure -text {}}", + "button .p.stmt -image Stepin -command {send m stmt}"+ + " -state disabled -relief sunken", + "bind .p.stmt <Enter> +{.p.status configure -text {step one statement}}", + "bind .p.stmt <Leave> +{.p.status configure -text {}}", + "button .p.over -image Stepover -command {send m over}"+ + " -state disabled -relief sunken", + "bind .p.over <Enter> +{.p.status configure -text {step over calls}}", + "bind .p.over <Leave> +{.p.status configure -text {}}", + "button .p.out -image Stepout -command {send m out}"+ + " -state disabled -relief sunken", + "bind .p.out <Enter> +{.p.status configure -text {step out of fn}}", + "bind .p.out <Leave> +{.p.status configure -text {}}", + "button .p.bpt -image Bpt -command {send m setbpt}"+ + " -state disabled -relief sunken", + "bind .p.bpt <Enter> +{.p.status configure -text {set/clear breakpoint}}", + "bind .p.bpt <Leave> +{.p.status configure -text {}}", + "frame .p.steps", + "label .p.status -anchor w", + "pack .p.step .p.stmt .p.over .p.out -in .p.steps -side left -fill y", + "pack .p.kill .p.detach .p.run .p.steps .p.bpt -side left -padx 5 -fill y", + "pack .p.status -side left -expand 1 -fill x", + + # progs + "frame .prog", + "label .prog.l -text Threads", + "canvas .prog.d -height 1 -width 1 -relief sunken -bd 2", + "frame .prog.v", + ".prog.d create window 0 0 -window .prog.v -anchor nw", + "pack .prog.l -side top -anchor w", + "pack .prog.d -side left -fill both -expand 1", + + # breakpoints + "frame .bpt", + "label .bpt.l -text Break", + "canvas .bpt.d -height 1 -width 1 -relief sunken -bd 2", + "frame .bpt.v", + ".bpt.d create window 0 0 -window .bpt.v -anchor nw", + "pack .bpt.l -side top -anchor w", + "pack .bpt.d -side left -fill both -expand 1", + + "pack .prog .bpt -side top -fill both -expand 1 -in .ctls", + + # test body + "frame .body.ft -bd 1 -relief sunken -width 60w -height 20h", + "scrollbar .body.scy", + "pack .body.scy -side right -fill y", + + "pack .body.ft -side top -expand 1 -fill both", + "pack propagate .body.ft 0", + + "pack .m .p -side top -fill x", + "pack .ctls -side left -fill y", + + "scrollbar .body.scx -orient horizontal", + "pack .body.scx -side bottom -fill x", + + "pack .body -expand 1 -fill both", + + "pack propagate . 0", + + "raise .; update; cursor -default" +}; + +# commands for disabling or enabling buttons +searchoff := array[] of { + ".m.search.menu entryconfigure 0 -state disabled", + ".m.search.menu entryconfigure 1 -state disabled", + ".m.search.menu entryconfigure 2 -state disabled", +}; +searchon := array[] of { + ".m.search.menu entryconfigure 0 -state normal", + ".m.search.menu entryconfigure 1 -state normal", + ".m.search.menu entryconfigure 2 -state normal", +}; +tkstopped := array[] of { + ".p.bpt configure -state normal -relief raised", + ".p.detach configure -state normal -relief raised", + ".p.kill configure -state normal -relief raised", + ".p.out configure -state normal -relief raised", + ".p.over configure -state normal -relief raised", + ".p.run configure -state normal -relief raised -image Run -command {send m run}", + ".p.step configure -state normal -relief raised", + ".p.stmt configure -state normal -relief raised", +}; +tkrunning := array[] of { + ".p.bpt configure -state normal -relief raised", + ".p.detach configure -state normal -relief raised", + ".p.kill configure -state normal -relief raised", + ".p.out configure -state disabled -relief sunken", + ".p.over configure -state disabled -relief sunken", + ".p.run configure -state normal -relief raised -image Stop -command {send m stop}", + ".p.step configure -state disabled -relief sunken", + ".p.stmt configure -state disabled -relief sunken", +}; +tkexited := array[] of { + ".p.bpt configure -state normal -relief raised", + ".p.detach configure -state normal -relief raised", + ".p.kill configure -state normal -relief raised", + ".p.out configure -state disabled -relief sunken", + ".p.over configure -state disabled -relief sunken", + ".p.run configure -state disabled -relief sunken -image Run -command {send m run}", + ".p.step configure -state disabled -relief sunken", + ".p.stmt configure -state disabled -relief sunken", + ".p.stop configure -state disabled -relief sunken", +}; +tkloaded := array[] of { + ".p.bpt configure -state normal -relief raised", + ".p.detach configure -state disabled -relief sunken", + ".p.kill configure -state disabled -relief sunken", + ".p.out configure -state disabled -relief sunken", + ".p.over configure -state disabled -relief sunken", + ".p.run configure -state normal -relief raised -image Run -command {send m run}", + ".p.step configure -state disabled -relief sunken", + ".p.stmt configure -state disabled -relief sunken", +}; +tknobody := array[] of { + ".p.bpt configure -state disabled -relief sunken", + ".p.detach configure -state disabled -relief sunken", + ".p.kill configure -state disabled -relief sunken", + ".p.out configure -state disabled -relief sunken", + ".p.over configure -state disabled -relief sunken", + ".p.run configure -state disabled -relief sunken -image Run -command {send m run}", + ".p.step configure -state disabled -relief sunken", + ".p.stmt configure -state disabled -relief sunken", +}; + +#tk option dialog +tkoptpack := array[] of { + "frame .buts", + + "pack .opts -side left -padx 10 -pady 5", +}; + +tkoptions := array[] of { + # general options + "frame .gen", + "frame .mod", + "label .modlab -text 'Source of executable module", + "entry .modent", + "pack .modlab -in .mod -anchor w", + "pack .modent -in .mod -fill x", + + "frame .arg", + "label .arglab -text 'Program Arguments", + "entry .argent -width 300", + "pack .arglab -in .arg -anchor w", + "pack .argent -in .arg -fill x", + + "frame .wd", + "label .wdlab -text 'Working Directory", + "entry .wdent", + "pack .wdlab -in .wd -anchor w", + "pack .wdent -in .wd -fill x", + + "pack .mod .arg .wd -fill x -anchor w -pady 10 -in .gen", + + # thread control options + "frame .prog", + "frame .new", + "radiobutton .new.run -variable new -value r -text 'Run new threads", + "radiobutton .new.block -variable new -value b -text 'Block new threads", + "pack .new.block .new.run -anchor w", + "frame .x", + "radiobutton .x.kill -variable exit -value k -text 'Kill threads on exit", + "radiobutton .x.detach -variable exit -value d -text 'Detach threads on exit", + "pack .x.kill .x.detach -anchor w", + "pack .new .x -expand 1 -anchor w -in .prog", + + # layout options + "frame .layout", + "frame .line", + "radiobutton .line.wrap -variable wrap -value w -text 'Wrap lines", + "radiobutton .line.scroll -variable wrap -value s -text 'Horizontal scroll", + "pack .line.wrap .line.scroll -anchor w", + "frame .crlf", + "radiobutton .crlf.no -variable crlf -value n -text 'CR/LF as is", + "radiobutton .crlf.yes -variable crlf -value y -text 'CR/LF -> LF", + "pack .crlf.no .crlf.yes -anchor w", + "pack .line .crlf -expand 1 -anchor w -in .layout", +}; + +tkopttabs := array[] of { + ("General", ".gen"), + ("Thread", ".prog"), + ("Layout", ".layout"), +}; + +# prog listing dialog box +tkpicktab := array[] of { + "frame .progs", + "scrollbar .progs.s -command '.progs.p yview", + "listbox .progs.p -width 35w -yscrollcommand '.progs.s set", + "bind .progs.p <Double-Button-1> 'send cmd prog", + "pack .progs.s -side right -fill y", + "pack .progs.p -fill both -expand 1", + + "frame .buts", + "button .buts.prog -text {Add Thread} -command 'send cmd prog", + "button .buts.grp -text {Add Group} -command 'send cmd group", + "pack .buts.prog .buts.grp -expand 1 -side left -fill x -padx 4 -pady 4", + + "pack .progs -fill both -expand 1", + "pack .buts -fill x", + "pack propagate . 0", +}; + +Bpt: adt +{ + id: int; + m: ref Mod; + pc: int; +}; + +Recv, Send, Alt, Running, Stopped, Exited, Broken, Killing, Killed: con iota; +status := array[] of +{ + Running => "Running", + Recv => "Receive", + Send => "Send", + Alt => "Alt", + Stopped => "Stopped", + Exited => "Exited", + Broken => "Broken", + Killing => "Killed", + Killed => "Killed", +}; + +tktools : array of array of string; +toolstate : array of string; + +KidGrab, KidStep, KidStmt, KidOver, KidOut, KidKill, KidRun: con iota; +Kid: adt +{ + state: int; + prog: ref Prog; + watch: int; # pid of watching prog + run: int; # pid of stepping prog + pickup: int; # picking up this kid? + cmd: chan of int; + stack: ref Vars; +}; + +Options: adt +{ + start: string; # src of module to start + mod: ref Mod; # module to start + wm: int; # program is a wm program? + path: array of string;# search path for .src and .sbl + args: list of string; # argument for starting a kid + dir: string; # . for kid + tabs: int; # options to show + nrun: int; # run new kids? + xkill: int; # kill kids on exit? + xscroll: int; # horizontal scrolling + remcr: int; # CR/LF -> LF +}; + +tktop: ref Tk->Toplevel; +kids: list of ref Kid; +kid: ref Kid; +kidctxt: ref Draw->Context; +kidack: chan of (ref Kid, string); +kidevent: chan of (ref Kid, string); +bpts: list of ref Bpt; +bptid:= 1; +title: string; +runok := 0; +context: ref Draw->Context; +opts: ref Options; +dbpid: int; +searchfor: string; +initsrc: string; + +badmodule(p: string) +{ + sys->fprint(sys->fildes(2), "deb: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init(ctxt: ref Draw->Context, argv: list of string) +{ + sys = load Sys Sys->PATH; + if (ctxt == nil) { + sys->fprint(sys->fildes(2), "deb: no window context\n"); + raise "fail:bad context"; + } + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + if(tkclient == nil) + badmodule(Tkclient->PATH); + selectfile = load Selectfile Selectfile->PATH; + if(selectfile == nil) + badmodule(Selectfile->PATH); + dialog = load Dialog Dialog->PATH; + if(dialog == nil) + badmodule(Dialog->PATH); + tabs = load Tabs Tabs->PATH; + if(tabs == nil) + badmodule(Tabs->PATH); + str = load String String->PATH; + if(str == nil) + badmodule(String->PATH); + readdir = load Readdir Readdir->PATH; + if(readdir == nil) + badmodule(Readdir->PATH); + debug = load Debug Debug->PATH; + if(debug == nil) + badmodule(Debug->PATH); + debdata = load DebData DebData->PATH; + if(debdata == nil) + badmodule(DebData->PATH); + debsrc = load DebSrc DebSrc->PATH; + if(debsrc == nil) + badmodule(DebSrc->PATH); + arg = load Arg Arg->PATH; + if(arg == nil) + badmodule(Arg->PATH); + dbpid = sys->pctl(Sys->NEWPGRP, nil); + opts = ref Options; + opts.tabs = 0; + opts.nrun = 0; + opts.xkill = 1; + opts.xscroll = 0; + opts.remcr = 0; + readopts(opts); + sysnam := sysname(); + context = ctxt; + + grabpids: list of int; + arg->init(argv); + arg->setusage("wmdeb [-p pid]"); + while((opt := arg->opt()) != 0){ + case opt { + 'f' => + initsrc = arg->earg(); + 'p' => + grabpids = int arg->earg() :: grabpids; + * => + arg->usage(); + } + } + for(argv = arg->argv(); argv != nil; argv = tl argv) + grabpids = int hd argv :: grabpids; + arg = nil; + + pickdummy := chan of int; + pickchan := pickdummy; + optdummy := chan of ref Options; + optchan := optdummy; + + tktools = array[] of { + Running => tkrunning, + Recv => tkrunning, + Send => tkrunning, + Alt => tkrunning, + Stopped => tkstopped, + Exited => tkexited, + Broken => tkexited, + Killing => tkexited, + Killed => tkexited, + }; + + + tkclient->init(); + selectfile->init(); + dialog->init(); + tabs->init(); + + title = sysnam+":Wmdeb"; + titlebut := chan of string; + (tktop, titlebut) = tkclient->toplevel(context, nil, title, Tkclient->Appl); + tkcmd("cursor -bitmap cursor.wait"); + + debug->init(); + kidctxt = ctxt; + + stderr = sys->fildes(2); + + debsrc->init(context, tktop, tkclient, selectfile, dialog, str, debug, opts.xscroll, opts.remcr); + (datatop, datactl, datatitle) := debdata->init(context, nil, debsrc, str, debug); + + m := chan of string; + tk->namechan(tktop, m, "m"); + toolstate = tknobody; + tkcmds(tktop, tkconfig); + if(!opts.xscroll){ + tkcmd("pack forget .body.scx"); + tkcmd("pack .body -expand 1 -fill both; update"); + } + + tkcmd("cursor -default"); + tkclient->onscreen(tktop, nil); + tkclient->startinput(tktop, "kbd" :: "ptr" :: nil); + + kids = nil; + kid = nil; + kidack = chan of (ref Kid, string); + kidevent = chan of (ref Kid, string); + + # pick up a src file, a kid? + if(initsrc != nil) + open1(initsrc); + else if(grabpids != nil) + for(; grabpids != nil; grabpids = tl grabpids) + pickup(hd grabpids); + + for(exiting := 0; !exiting || kids != nil; ){ + tkcmd("update"); + alt { + c := <-tktop.ctxt.kbd => + tk->keyboard(tktop, c); + p := <-tktop.ctxt.ptr => + tk->pointer(tktop, *p); + s := <-tktop.ctxt.ctl or + s = <-tktop.wreq or + s = <-titlebut => + case s{ + "exit" => + if(!exiting){ + if(opts.xkill) + killkids(); + else + detachkids(); + tkcmd("destroy ."); + } + exiting = 1; + break; + "task" => + spawn task(tktop); + * => + tkclient->wmctl(tktop, s); + } + c := <-datatop.ctxt.kbd => + tk->keyboard(datatop, c); + p := <-datatop.ctxt.ptr => + tk->pointer(datatop, *p); + s := <-datactl => + debdata->ctl(s); + s := <-datatop.wreq or + s = <-datatop.ctxt.ctl or + s = <-datatitle => + case s{ + "task" => + spawn debdata->wmctl(s); + * => + debdata->wmctl(s); + } + o := <-optchan => + if(o != nil && checkopts(o)) + opts = o; + optchan = optdummy; + p := <-pickchan => + if(p < 0){ + pickchan = pickdummy; + break; + } + k := pickup(p); + if(k != nil && k != kid){ + kid = k; + refresh(k); + } + s := <-m => + case s { + "open" => + open(); + "pickup" => + if(pickchan == pickdummy){ + pickchan = chan of int; + spawn pickprog(pickchan); + } + "options" => + if(optchan == optdummy){ + optchan = chan of ref Options; + spawn options(opts, optchan); + } + "step" => + step(kid, KidStep); + "over" => + step(kid, KidOver); + "out" => + step(kid, KidOut); + "stmt" => + step(kid, KidStmt); + "run" => + step(kid, KidRun); + "stop" => + if(kid != nil) + kid.prog.stop(); + "killall" => + killkids(); + "kill" => + killkid(kid); + "detach" => + detachkid(kid); + "setbpt" => + setbpt(); + "look" => + debsrc->search(debsrc->snarf()); + "search" => + s = dialog->getstring(context, tktop.image, "Search For"); + if(s == ""){ + tkcmd(".m.search.menu delete 2"); + }else{ + if(searchfor == "") + tkcmd(".m.search.menu add command -command {send m research}"); + tkcmd(".m.search.menu entryconfigure 2 -label '/"+s); + debsrc->search(s); + } + searchfor = s; + "research" => + debsrc->search(searchfor); + "stack" => + if(debdata != nil) + debdata->raisex(); + * => + if(str->prefix("open ", s)) + debsrc->showstrsrc(s[len "open ":]); + else if(str->prefix("seeprog ", s)) + seekid(int s[len "seeprog ":]); + else if(str->prefix("seebpt ", s)) + seebpt(int s[len "seebpt ":]); + } + (k, s) := <-kidevent => + case s{ + "recv" => + if(k.state == Running) + k.state = Recv; + "send" => + if(k.state == Running) + k.state = Send; + "alt" => + if(k.state == Running) + k.state = Alt; + "run" => + if(k.state == Recv || k.state == Send || k.state == Alt) + k.state = Running; + "exited" => + k.state = Exited; + "interrupted" or + "killed" => + alert("Thread "+string k.prog.id+" "+s); + k.state = Exited; + * => + if(str->prefix("new ", s)){ + nk := newkid(int s[len "new ":]); + if(opts.nrun) + step(nk, KidRun); + break; + } + if(str->prefix("load ", s)){ + s = s[len "load ":]; + if(s != nil && s[0] != '$') + loaded(s); + break; + } + if(str->prefix("child: ", s)) + s = s[len "child: ":]; + + if(str->prefix("broken: ", s)) + k.state = Broken; + alert("Thread "+string k.prog.id+" "+s); + } + if(k == kid && k.state != Running) + refresh(k); + k = nil; + (k, s) := <-kidack => + if(k.state == Killing){ + k.state = Killed; + k.cmd <-= KidKill; + k = nil; + break; + } + if(k.state == Killed){ + delkid(k); + k = nil; + break; + } + case s{ + "" or "child: breakpoint" or "child: stopped" => + k.state = Stopped; + k.prog.unstop(); + "prog broken" => + k.state = Broken; + * => + if(!str->prefix("child: ", s)) + alert("Debugger error "+status[k.state]+" "+string k.prog.id+" '"+s+"'"); + } + if(k == kid) + refresh(k); + if(k.pickup && opts.nrun){ + k.pickup = 0; + if(k.state == Stopped) + step(k, KidRun); + } + k = nil; + } + } + exitdb(); +} + +task(top: ref Tk->Toplevel) +{ + tkclient->wmctl(top, "task"); +} + +open() +{ + pattern := list of { + "*.b (Limbo source files)", + "* (All files)" + }; + + file := selectfile->filename(context, tktop.image, "Open source file", pattern, opendir); + if(file != nil) + open1(file); +} + +open1(file: string) +{ + (opendir, nil) = str->splitr(file, "/"); + if(opendir == "") + opendir = "."; + m := debsrc->loadsrc(file, 1); + if(m == nil){ + alert("Can't open "+file); + return; + } + debsrc->showmodsrc(m, ref Src((file, 1, 0), (file, 1, 0))); + kidstate(); + if(opts.start == nil){ + opts.start = file; + opts.mod = m; + } + if(opts.dir == "") + opts.dir = opendir; +} + +options(oo: ref Options, r: chan of ref Options) +{ + (t, titlebut) := tkclient->toplevel(context, nil, "Wmdeb Options", tkclient->OK); + + tkcmds(t, tkoptions); + tabsctl := tabs->mktabs(t, ".opts", tkopttabs, oo.tabs); + tkcmds(t, tkoptpack); + + o := ref *oo; + if(o.start != nil) + tk->cmd(t, ".modent insert end '"+o.start); + args := ""; + for(oa := o.args; oa != nil; oa = tl oa){ + if(args == "") + args = hd oa; + else + args += " " + hd oa; + } + tk->cmd(t, ".argent insert end '"+args); + tk->cmd(t, ".wdent insert end '"+o.dir); + if(o.xkill) + tk->cmd(t, ".x.kill invoke"); + else + tk->cmd(t, ".x.detach invoke"); + if(o.nrun) + tk->cmd(t, ".new.run invoke"); + else + tk->cmd(t, ".new.block invoke"); + if(o.xscroll) + tk->cmd(t, ".line.scroll invoke"); + else + tk->cmd(t, ".line.wrap invoke"); + if(o.remcr) + tk->cmd(t, ".crlf.yes invoke"); + else + tk->cmd(t, ".crlf.no invoke"); + + tk->cmd(t, ".killkids configure -command 'send cmd kill"); + tk->cmd(t, ".runkids configure -command 'send cmd run"); + tkclient->onscreen(t, nil); + tkclient->startinput(t, "ptr" :: "kbd" :: nil); + +out: for(;;){ + tk->cmd(t, "update"); + alt{ + c := <-t.ctxt.kbd => + tk->keyboard(t, c); + m := <-t.ctxt.ptr => + tk->pointer(t, *m); + s := <-tabsctl => + o.tabs = tabs->tabsctl(t, ".opts", tkopttabs, o.tabs, s); + s := <-t.ctxt.ctl or + s = <-t.wreq or + s = <-titlebut => + case s{ + "exit" => + r <-= nil; + exit; + "ok" => + break out; + } + tkclient->wmctl(t, s); + } + } + xscroll := o.xscroll; + o.start = tk->cmd(t, ".modent get"); + (nil, o.args) = sys->tokenize(tk->cmd(t, ".argent get"), " \t\n"); + o.dir = tk->cmd(t, ".wdent get"); + case tk->cmd(t, "variable new"){ + "r" => o.nrun = 1; + "b" => o.nrun = 0; + } + case tk->cmd(t, "variable exit"){ + "k" => o.xkill = 1; + "d" => o.xkill = 0; + } + case tk->cmd(t, "variable wrap"){ + "s" => o.xscroll = 1; + "w" => o.xscroll = 0; + } + case tk->cmd(t, "variable crlf"){ + "y" => o.remcr = 1; + "n" => o.remcr = 0; + } + if(o.xscroll != xscroll){ + if(o.xscroll) + tkcmd("pack .body.scx -side bottom -fill x"); + else + tkcmd("pack forget .body.scx"); + tkcmd("pack .body -expand 1 -fill both; update"); + } + debsrc->reinit(o.xscroll, o.remcr); + writeopts(o); + r <-= o; +} + +checkopts(o: ref Options): int +{ + if(o.start != ""){ + o.mod = debsrc->loadsrc(o.start, 1); + if(o.mod == nil) + o.start = ""; + } + return 1; +} + +pickprog(c: chan of int) +{ + (t, titlebut) := tkclient->toplevel(context, nil, "Wmdeb Thread List", 0); + cmd := chan of string; + tk->namechan(t, cmd, "cmd"); + + tkcmds(t, tkpicktab); + tk->cmd(t, "update"); + ids := addpickprogs(t); + tkclient->onscreen(t, nil); + tkclient->startinput(t, "ptr" :: "kbd" :: nil); + + for(;;){ + tk->cmd(t, "update"); + alt{ + key := <-t.ctxt.kbd => + tk->keyboard(t, key); + m := <-t.ctxt.ptr => + tk->pointer(t, *m); + s := <-t.ctxt.ctl or + s = <-t.wreq or + s = <-titlebut => + if(s == "exit"){ + c <-= -1; + exit; + } + tkclient->wmctl(t, s); + s := <-cmd => + case s{ + "ok" => + c <-= -1; + exit; + "prog" => + sel := tk->cmd(t, ".progs.p curselection"); + if(sel == "") + break; + pid := int tk->cmd(t, ".progs.p get "+sel); + c <-= pid; + "group" => + sel := tk->cmd(t, ".progs.p curselection"); + if(sel == "") + break; + nid := int sel; + if(nid > len ids || nid < 0) + break; + (nil, gid) := ids[nid]; + nid = len ids; + for(i := 0; i < nid; i++){ + (p, g) := ids[i]; + if(g == gid) + c <-= p; + } + } + } + } +} + +addpickprogs(t: ref Tk->Toplevel): array of (int, int) +{ + (d, n) := readdir->init("/prog", Readdir->NONE); + if(n <= 0) + return nil; + a := array[n] of { * => (-1, -1) }; + for(i := 0; i < n; i++){ + (p, nil) := debug->prog(int d[i].name); + if(p == nil) + continue; + (grp, nil, st, code) := debug->p.status(); + if(grp < 0) + continue; + a[i] = (p.id, grp); + tk->cmd(t, ".progs.p insert end '"+ + sys->sprint("%4d %4d %8s %s", p.id, grp, st, code)); + } + return a; +} + +step(k: ref Kid, cmd: int) +{ + if(k == nil){ + if(kids != nil){ + alert("No current thread"); + return; + } + k = spawnkid(opts); + kid = k; + if(k != nil) + refresh(k); + return; + } + case k.state{ + Stopped => + k.cmd <-= cmd; + k.state = Running; + if(k == kid) + kidstate(); + Running or Send or Recv or Alt or Exited or Broken => + ; + * => + sys->print("bad debug step state %d\n", k.state); + } +} + +setbpt() +{ + (m, pc) := debsrc->getsel(); + if(m == nil) + return; + s := m.sym.pctosrc(pc); + if(s == nil){ + alert("No pc is appropriate"); + return; + } + + # if the breakpoint is already there, delete it + for(bl := bpts; bl != nil; bl = tl bl){ + b := hd bl; + if(b.m == m && b.pc == pc){ + bpts = delbpt(b, bpts); + return; + } + } + + b := ref Bpt(bptid++, m, pc); + bpts = b :: bpts; + debsrc->attachdis(m); + for(kl := kids; kl != nil; kl = tl kl){ + k := hd kl; + k.prog.setbpt(m.dis, pc); + } + + # mark the breakpoint text + tkcmd(m.tk+" tag add bpt "+string s.start.line+"."+string s.start.pos+" "+string s.stop.line+"."+string s.stop.pos); + + # add the kid to the breakpoint window + me := ".bpt.v."+string b.id; + tkcmd("label "+me+" -text "+string b.id); + tkcmd("pack "+me+" -side top -fill x"); + tkcmd("bind "+me+" <ButtonRelease-1> {send m seebpt "+string b.id+"}"); + updatebpts(); +} + +seebpt(bpt: int) +{ + for(bl := bpts; bl != nil; bl = tl bl){ + b := hd bl; + if(b.id == bpt){ + s := b.m.sym.pctosrc(b.pc); + debsrc->showmodsrc(b.m, s); + return; + } + } +} + +delbpt(b: ref Bpt, bpts: list of ref Bpt): list of ref Bpt +{ + if(bpts == nil) + return nil; + hb := hd bpts; + tb := tl bpts; + if(b == hb){ + # remove mark from breakpoint text + s := b.m.sym.pctosrc(b.pc); + tkcmd(b.m.tk+" tag remove bpt "+string s.start.line+"."+string s.start.pos+" "+string s.stop.line+"."+string s.stop.pos); + + # remove the breakpoint window + tkcmd("destroy .bpt.v."+string b.id); + + # remove from kids + disablebpt(b); + return tb; + } + return hb :: delbpt(b, tb); + +} + +disablebpt(b: ref Bpt) +{ + for(kl := kids; kl != nil; kl = tl kl){ + k := hd kl; + k.prog.delbpt(b.m.dis, b.pc); + } +} + +updatebpts() +{ +tkcmd("update"); + tkcmd(".bpt.d configure -scrollregion {0 0 [.bpt.v cget -width] [.bpt.v cget -height]}"); +} + +seekid(pid: int) +{ + for(kl := kids; kl != nil; kl = tl kl){ + k := hd kl; + if(k.prog.id == pid){ + kid = k; + kid.stack.show(); + refresh(kid); + return; + } + } +} + +delkid(k: ref Kid) +{ + kids = rdelkid(k, kids); + if(kid == k){ + if(kids == nil){ + kid = nil; + kidstate(); + }else{ + kid = hd kids; + refresh(kid); + } + } +} + +rdelkid(k: ref Kid, kids: list of ref Kid): list of ref Kid +{ + if(kids == nil) + return nil; + hk := hd kids; + t := tl kids; + if(k == hk){ + # remove kid from display + k.stack.delete(); + tkcmd("destroy .prog.v."+string k.prog.id); + updatekids(); + return t; + } + return hk :: rdelkid(k, t); +} + +updatekids() +{ +tkcmd("update"); + tkcmd(".prog.d configure -scrollregion {0 0 [.prog.v cget -width] [.prog.v cget -height]}"); +} + +killkids() +{ + for(kl := kids; kl != nil; kl = tl kl) + killkid(hd kl); +} + +killkid(k: ref Kid) +{ + if(k.watch >= 0){ + killpid(k.watch); + k.watch = -1; + } + case k.state{ + Exited or Broken or Stopped => + k.cmd <-= KidKill; + k.state = Killed; + Running or Send or Recv or Alt or Killing => + k.prog.kill(); + k.state = Killing; + * => + sys->print("unknown state %d in killkid\n", k.state); + } +} + +freekids(): int +{ + r := 0; + for(kl := kids; kl != nil; kl = tl kl){ + k := hd kl; + if(k.state == Exited || k.state == Killing || k.state == Killed){ + r ++; + detachkid(k); + } + } + return r; +} + +detachkids() +{ + for(kl := kids; kl != nil; kl = tl kl) + detachkid(hd kl); +} + +detachkid(k: ref Kid) +{ + if(k == nil){ + alert("No current thread"); + return; + } + if(k.state == Exited){ + killkid(k); + return; + } + + # kill off the debugger progs + killpid(k.watch); + killpid(k.run); + err := k.prog.start(); + if(err != "") + alert("Detaching thread: "+err); + + delkid(k); +} + +kidstate() +{ + ts : array of string; + if(kid == nil){ + tkcmd(".Wm_t.title configure -text '"+title); + if(debsrc->packed == nil){ + tkcmds(tktop, searchoff); + ts = tknobody; + }else{ + ts = tkloaded; + tkcmds(tktop, searchon); + } + }else{ + tkcmd(".Wm_t.title configure -text '"+title+" "+string kid.prog.id+" "+status[kid.state]); + ts = tktools[kid.state]; + tkcmds(tktop, searchon); + } + if(ts != toolstate){ + toolstate = ts; + tkcmds(tktop, ts); + } +} + +# +# update the stack an src displays +# to reflect the current state of k +# +refresh(k: ref Kid) +{ + if(k.state == Killing || k.state == Killed){ + kidstate(); + return; + } + (s, err) := k.prog.stack(); + if(s == nil && err == "") + err = "No stack"; + if(err != ""){ + kidstate(); + return; + } + for(i := 0; i < len s; i++){ + debsrc->findmod(s[i].m); + s[i].findsym(); + } + err = s[0].findsym(); + src := s[0].src(); + kidstate(); + m := s[0].m; + if(src == nil && len s > 1){ + dis := s[0].m.dis(); + if(len dis > 0 && dis[0] == '$'){ + m = s[1].m; + s[1].findsym(); + src = s[1].src(); + } + } + debsrc->showmodsrc(debsrc->findmod(m), src); + k.stack.refresh(s); + k.stack.show(); +} + +pickup(pid: int): ref Kid +{ + for(kl := kids; kl != nil; kl = tl kl) + if((hd kl).prog.id == pid) + return hd kl; + k := newkid(pid); + if(k == nil) + return nil; + k.cmd <-= KidGrab; + k.state = Running; + k.pickup = 1; + if(kid == nil){ + kid = k; + refresh(kid); + } + return k; +} + +loaded(s: string) +{ + for(bl := bpts; bl != nil; bl = tl bl){ + b := hd bl; + debsrc->attachdis(b.m); + if(s == b.m.dis){ + for(kl := kids; kl != nil; kl = tl kl) + (hd kl).prog.setbpt(s, b.pc); + } + } +} + +Enofd: con "no free file descriptors\n"; + +newkid(pid: int): ref Kid +{ + (p, err) := debug->prog(pid); + if(err != ""){ + n := len err - len Enofd; + if(n >= 0 && err[n: ] == Enofd && freekids()){ + (p, err) = debug->prog(pid); + if(err == "") + return mkkid(p); + } + alert("Can't pick up thread "+err); + return nil; + } + return mkkid(p); +} + +mkkid(p: ref Prog): ref Kid +{ + for(bl := bpts; bl != nil; bl = tl bl){ + b := hd bl; + debsrc->attachdis(b.m); + p.setbpt(b.m.dis, b.pc); + } + k := ref Kid(Stopped, p, -1, -1, 0, chan of int, Vars.create()); + kids = k :: kids; + c := chan of int; + spawn kidslave(k, c); + k.run = <- c; + spawn kidwatch(k, c); + k.watch = <-c; + me := ".prog.v."+string p.id; + tkcmd("label "+me+" -text "+string p.id); + tkcmd("pack "+me+" -side top -fill x"); + tkcmd("bind "+me+" <ButtonRelease-1> {send m seeprog "+string p.id+"}"); + tkcmd(".prog.d configure -scrollregion {0 0 [.prog.v cget -width] [.prog.v cget -height]}"); + return k; +} + +spawnkid(o: ref Options): ref Kid +{ + m := o.mod; + if(m == nil){ + alert("No module to run"); + return nil; + } + + if(!debsrc->attachdis(m)){ + alert("Can't load Dis file "+m.dis); + return nil; + } + + (p, err) := debug->startprog(m.dis, o.dir, kidctxt, m.dis :: o.args); + if(err != nil){ + alert(m.dis+" is not a debuggable Dis command module: "+err); + return nil; + } + + return mkkid(p); +} + +xlate := array[] of { + KidStep => Debug->StepExp, + KidStmt => Debug->StepStmt, + KidOver => Debug->StepOver, + KidOut => Debug->StepOut, +}; + +kidslave(k: ref Kid, me: chan of int) +{ + me <-= sys->pctl(0, nil); + me = nil; + for(;;){ + c := <-k.cmd; + case c{ + KidGrab => + err := k.prog.grab(); + kidack <-= (k, err); + KidStep or KidStmt or KidOver or KidOut => + err := k.prog.step(xlate[c]); + kidack <-= (k, err); + KidKill => + err := "kill "+k.prog.kill(); + k.prog.kill(); # kill again to slay blocked progs + kidack <-= (k, err); + exit; + KidRun => + err := k.prog.cont(); + kidack <-= (k, err); + * => + sys->print("kidslave: bad command %d\n", c); + exit; + } + } +} + +kidwatch(k: ref Kid, me: chan of int) +{ + me <-= sys->pctl(0, nil); + me = nil; + for(;;) + kidevent <-= (k, k.prog.event()); +} + +alert(m: string) +{ + dialog->prompt(context, tktop.image, "warning -fg yellow", + "Debugger Alert", m, 0, "Dismiss"::nil); +} + +tkcmd(cmd: string): string +{ + s := tk->cmd(tktop, cmd); +# if(len s != 0 && s[0] == '!') +# sys->print("%s '%s'\n", s, cmd); + return s; +} + +sysname(): string +{ + fd := sys->open("#c/sysname", sys->OREAD); + if(fd == nil) + return "Anon"; + buf := array[128] of byte; + n := sys->read(fd, buf, len buf); + if(n < 0) + return "Anon"; + return string buf[:n]; +} + +tkcmds(top: ref Tk->Toplevel, cmds: array of string) +{ + for(i := 0; i < len cmds; i++) + tk->cmd(top, cmds[i]); +} + +exitdb() +{ + fd := sys->open("#p/"+string dbpid+"/ctl", sys->OWRITE); + if(fd != nil) + sys->fprint(fd, "killgrp"); + exit; +} + +killpid(pid: int) +{ + fd := sys->open("#p/"+string pid+"/ctl", sys->OWRITE); + if(fd != nil) + sys->fprint(fd, "kill"); +} + +getuser(): string +{ + fd := sys->open("/dev/user", Sys->OREAD); + if(fd == nil) + return ""; + buf := array[128] of byte; + n := sys->read(fd, buf, len buf); + if(n < 0) + return ""; + return string buf[0:n]; +} + +debconf(): string +{ + return "/usr/" + getuser() + "/lib/deb"; +} + +readopts(o: ref Options) +{ + fd := sys->open(debconf(), Sys->OREAD); + if(fd == nil) + return; + b := array[4] of byte; + if(sys->read(fd, b, 4) != 4) + return; + o.nrun = int b[0]-'0'; + o.xkill = int b[1]-'0'; + o.xscroll = int b[2]-'0'; + o.remcr = int b[3]-'0'; +} + +writeopts(o: ref Options) +{ + fd := sys->create(debconf(), Sys->OWRITE, 8r660); + if(fd == nil) + return; + b := array[4] of byte; + b[0] = byte (o.nrun+'0'); + b[1] = byte (o.xkill+'0'); + b[2] = byte (o.xscroll+'0'); + b[3] = byte (o.remcr+'0'); + sys->write(fd, b, 4); +} diff --git a/appl/wm/debdata.b b/appl/wm/debdata.b new file mode 100644 index 00000000..1f5b6752 --- /dev/null +++ b/appl/wm/debdata.b @@ -0,0 +1,418 @@ +implement DebData; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + +include "string.m"; + str: String; + +include "tk.m"; + tk: Tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "dialog.m"; + +include "selectfile.m"; + +include "debug.m"; + debug: Debug; + Sym, Src, Exp, Module: import debug; + +include "wmdeb.m"; + debsrc: DebSrc; + +DatumSize: con 32; +WalkWidth: con "20"; + +context: ref Draw->Context; +tktop: ref Tk->Toplevel; +var: ref Vars; +vid: int; +tkids := 1; # increasing id of tk pieces + +icondir : con "debug/"; + +tkconfig := array[] of { + "frame .body -width 400 -height 400", + "pack .Wm_t -side top -fill x", + "pack .body -expand 1 -fill both", + "pack propagate . 0", + "update", + "image create bitmap Itemopen -file "+icondir+ + "open.bit -maskfile "+icondir+"open.mask", + "image create bitmap Itemclosed -file "+icondir+ + "closed.bit -maskfile "+icondir+"closed.mask", +}; + +init(acontext: ref Draw->Context, + geom: string, + adebsrc: DebSrc, + astr: String, + adebug: Debug): (ref Tk->Toplevel, chan of string, chan of string) +{ + context = acontext; + debsrc = adebsrc; + sys = load Sys Sys->PATH; + tk = load Tk Tk->PATH; + str = astr; + debug = adebug; + + tkclient = load Tkclient Tkclient->PATH; + + tkclient->init(); + titlebut: chan of string; + (tktop, titlebut) = tkclient->toplevel(context, geom, "Stack", Tkclient->Resize); + buts := chan of string; + tk->namechan(tktop, buts, "buts"); + + for(i := 0; i < len tkconfig; i++) + tk->cmd(tktop, tkconfig[i]); + + tkcmd("update"); + tkclient->onscreen(tktop, nil); + tkclient->startinput(tktop, "kbd" :: "ptr" :: nil); + return (tktop, buts, titlebut); +} + +ctl(s: string) +{ + if(var == nil) + return; + arg := s[1:]; + case s[0]{ + 'o' => + var.expand(arg); + var.update(); + 'c' => + var.contract(arg); + var.update(); + 'y' => + var.scrolly(arg); + 's' => + var.showsrc(arg); + } + tkcmd("update"); +} + +wmctl(s: string) +{ + if(s == "exit"){ + tkcmd(". unmap"); + return; + } + tkclient->wmctl(tktop, s); + tkcmd("update"); +} + +Vars.create(): ref Vars +{ + t := ".body.v"+string vid++; + + tkcmd("frame "+t); + tkcmd("canvas "+t+".cvar -width 2 -height 2 -yscrollcommand {"+t+".sy set} -xscrollcommand {"+t+".sxvar set}"); + tkcmd("frame "+t+".f0"); + + tkcmd(t+".cvar create window 0 0 -window "+t+".f0 -anchor nw"); + tkcmd("scrollbar "+t+".sxvar -orient horizontal -command {"+t+".cvar xview}"); + + tkcmd("scrollbar "+t+".sy -command {send buts y}"); + tkcmd("pack "+t+".sy -side right -fill y -in "+t); + tkcmd("pack "+t+".sxvar -fill x -side bottom -in "+t); + tkcmd("pack "+t+".cvar -expand 1 -fill both -in "+t); + + return ref Vars(t, 0, nil); +} + +Vars.show(v: self ref Vars) +{ + if(v == var) + return; + if(var != nil) + tkcmd("pack forget "+var.tk); + var = v; + tkcmd("pack "+var.tk+" -expand 1 -fill both"); + v.update(); +} + +Vars.delete(v: self ref Vars) +{ + if(var == v) + var = nil; + tkcmd("destroy "+v.tk); + tkcmd("update"); +} + +Vars.refresh(v: self ref Vars, ea: array of ref Exp) +{ + nea := len ea; + newd := array[nea] of ref Datum; + da := v.d; + nd := len da; + n := nea; + if(n > nd) + n = nd; + for(i := 0; i < n; i++){ + d := da[nd-i-1]; + if(!sameexp(ea[nea-i-1], d.e, 1)) + break; + newd[nea-i-1] = d; + } + n = nea-i; + for(; i < nd; i++) + da[nd-i-1].destroy(); + v.d = nil; + for(i = 0; i < n; i++){ + debsrc->findmod(ea[i].m); + ea[i].findsym(); + newd[i] = mkkid(ea[i], v.tk, "0", string tkids++, nil, nil, -1, ""); + } + for(; i < nea; i++){ + debsrc->findmod(ea[i].m); + ea[i].findsym(); + d := newd[i]; + newd[i] = mkkid(ea[i], v.tk, "0", d.tkid, d.kids, d.val, d.canwalk, ""); + } + v.d = newd; + v.update(); +} + +Vars.update(v: self ref Vars) +{ + tkcmd("update"); + tkcmd(v.tk+".cvar configure -scrollregion {0 0 ["+v.tk+".f0 cget -width] ["+v.tk+".f0 cget -height]}"); + tkcmd("update"); +} + +Vars.scrolly(v: self ref Vars, pos: string) +{ + tkcmd(v.tk+".cvar yview"+pos); +} + +Vars.showsrc(v: self ref Vars, who: string) +{ + (sid, kids) := str->splitl(who[1:], "."); + showsrc(v.d, sid, kids); +} + +showsrc(da: array of ref Datum, id, kids: string) +{ + if(da == nil) + return; + for(i := 0; i < len da; i++){ + d := da[i]; + if(d.tkid != id) + continue; + if(kids == "") + d.showsrc(); + else{ + sid : string; + (sid, kids) = str->splitl(kids[1:], "."); + showsrc(d.kids, sid, kids); + } + break; + } +} + +Vars.expand(v: self ref Vars, who: string) +{ + (sid, kids) := str->splitl(who[1:], "."); + v.d = expandkid(v.d, sid, kids, who); +} + +expandkid(da: array of ref Datum, id, kids, who: string): array of ref Datum +{ + if(da == nil) + return nil; + for(i := 0; i < len da; i++){ + d := da[i]; + if(d.tkid != id) + continue; + if(kids == "") + da[i] = d.expand(nil, who); + else{ + sid : string; + (sid, kids) = str->splitl(kids[1:], "."); + d.kids = expandkid(d.kids, sid, kids, who); + } + break; + } + return da; +} + +Vars.contract(v: self ref Vars, who: string) +{ + (sid, kids) := str->splitl(who[1:], "."); + v.d = contractkid(v.d, sid, kids, who); +} + +contractkid(da: array of ref Datum, id, kids, who: string): array of ref Datum +{ + if(da == nil) + return nil; + for(i := 0; i < len da; i++){ + d := da[i]; + if(d.tkid != id) + continue; + if(kids == "") + da[i] = d.contract(who); + else{ + sid : string; + (sid, kids) = str->splitl(kids[1:], "."); + d.kids = contractkid(d.kids, sid, kids, who); + } + break; + } + return da; +} + +Datum.contract(d: self ref Datum, who: string): ref Datum +{ + vtk := d.vtk; + tkid := d.tkid; + if(tkid == "") + return d; + kids := d.kids; + if(kids == nil){ + tkcmd(vtk+".v"+tkid+".b configure -image Itemclosed -command {send buts o"+who+"}"); + return d; + } + + for(i := 0; i < len kids; i++) + kids[i].destroy(); + d.kids = nil; + tkcmd("destroy "+vtk+".f"+tkid); + tkcmd(vtk+".v"+tkid+".b configure -image Itemclosed -command {send buts o"+who+"}"); + + return d; +} + +Datum.showsrc(d: self ref Datum) +{ + debsrc->showmodsrc(debsrc->findmod(d.e.m), d.e.src()); +} + +Datum.destroy(d: self ref Datum) +{ + kids := d.kids; + for(i := 0; i < len kids; i++) + kids[i].destroy(); + vtk := d.vtk; + tkid := string d.tkid; + if(d.kids != nil){ + tkcmd("destroy "+vtk+".f"+tkid); + } + d.kids = nil; + tkcmd("destroy "+vtk+".v"+tkid); +} + +mkkid(e: ref Exp, vtk, parent, me: string, okids: array of ref Datum, oval:string, owalk: int, who: string): ref Datum +{ + (val, walk) := e.val(); + + who = who+"."+me; + + # make the tk goo + if(walk != owalk){ + if(owalk == -1){ + tkcmd("frame "+vtk+".v"+me); + tkcmd("label "+vtk+".v"+me+".l -text '"+e.name); + tkcmd("bind "+vtk+".v"+me+".l <ButtonRelease-1> 'send buts s"+who); + }else{ + tkcmd("destroy "+vtk+".v"+me+".b"); + } + if(walk) + tkcmd("button "+vtk+".v"+me+".b -image Itemclosed -command 'send buts o"+who); + else + tkcmd("frame "+vtk+".v"+me+".b -width "+WalkWidth); + } + + n := 16 - len e.name; + if(n < 4) + n = 4; + pad := " "[:n]; + + # tk value goo + if(val == "") + val = " "; + if(oval != ""){ + if(val != oval) + tkcmd(vtk+".v"+me+".val configure -text '"+pad+val); + }else + tkcmd("label "+vtk+".v"+me+".val -text '"+pad+val); + + tkcmd("pack "+vtk+".v"+me+".b "+vtk+".v"+me+".l "+vtk+".v"+me+".val -side left"); + tkcmd("pack "+vtk+".v"+me+" -side top -anchor w -in "+vtk+".f"+parent); + + d := ref Datum(me, parent, vtk, e, val, walk, nil); + if(okids != nil){ + if(walk) + return d.expand(okids, who); + for(i := 0; i < len okids; i++) + okids[i].destroy(); + } + return d; +} + +Datum.expand(d: self ref Datum, okids: array of ref Datum, who: string): ref Datum +{ + e := d.e.expand(); + if(e == nil) + return d; + + vtk := d.vtk; + + me := d.tkid; + + # make the tk goo for holding kids + needtk := okids == nil; + if(needtk){ + tkcmd("frame "+vtk+".f"+me); + tkcmd("frame "+vtk+".f"+me+".x -width "+WalkWidth); + tkcmd("frame "+vtk+".f"+me+".v"); + tkcmd("pack "+vtk+".f"+me+".x "+vtk+".f"+me+".v -side left -fill y -expand 1"); + } + + kids := array[len e] of ref Datum; + for(i := 0; i < len e; i++){ + if(i >= len okids) + break; + ok := okids[i]; + if(!sameexp(e[i], ok.e, 0)) + break; + kids[i] = mkkid(e[i], vtk, me, ok.tkid, ok.kids, ok.val, ok.canwalk, who); + } + for(oi := i; oi < len okids; oi++) + okids[oi].destroy(); + for(; i < len e; i++) + kids[i] = mkkid(e[i], vtk, me, string tkids++, nil, nil, -1, who); + + tkcmd("pack "+vtk+".f"+me+" -side top -anchor w -after "+vtk+".v"+me); + tkcmd(vtk+".v"+me+".b configure -image Itemopen -command {send buts c"+who+"}"); + + d.kids = kids; + return d; +} + +sameexp(e, f: ref Exp, offmatch: int): int +{ + if(e.m != f.m || e.p != f.p || e.name != f.name) + return 0; + return !offmatch || e.offset == f.offset; +} + +tkcmd(cmd: string): string +{ + s := tk->cmd(tktop, cmd); +# if(len s != 0 && s[0] == '!') +# sys->print("%s '%s'\n", s, cmd); + return s; +} + +raisex() +{ + tkcmd(". map; raise .; update"); +} diff --git a/appl/wm/debsrc.b b/appl/wm/debsrc.b new file mode 100644 index 00000000..57f26218 --- /dev/null +++ b/appl/wm/debsrc.b @@ -0,0 +1,633 @@ +implement DebSrc; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + +include "string.m"; + str: String; + +include "tk.m"; + tk: Tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "dialog.m"; + dialog: Dialog; + +include "selectfile.m"; + selectfile: Selectfile; + +include "debug.m"; + debug: Debug; + Sym, Src, Exp, Module: import debug; + +include "wmdeb.m"; + +include "plumbmsg.m"; + plumbmsg: Plumbmsg; + Msg: import plumbmsg; + +include "workdir.m"; + workdir: Workdir; + +include "dis.m"; + dism: Dis; + +mods: list of ref Mod; +tktop: ref Tk->Toplevel; +context: ref Draw->Context; +opendir = "."; +srcid: int; +xscroll, remcr: int; + +sblpath := array[] of +{ + ("/dis/", "/appl/"), + ("/dis/", "/appl/cmd/"), + # ("/dis/mux/", "/appl/mux/"), + # ("/dis/lib/", "/appl/lib/"), + # ("/dis/wm/", "/appl/wm/"), + ("/dis/sh.", "/appl/cmd/sh/sh."), +}; + +plumbed := 0; +but3: chan of string; + +plumbbind := array[] of +{ + "<ButtonPress-3> {send but3 pressed}", + "<ButtonRelease-3> {send but3 released %x %y}", + "<Motion-Button-3> {}", + "<Double-Button-3> {}", + "<Double-ButtonRelease-3> {}", +}; + +init(acontext: ref Draw->Context, + atktop: ref Tk->Toplevel, + atkclient: Tkclient, + aselectfile: Selectfile, + adialog: Dialog, + astr: String, + adebug: Debug, + xscr: int, + rcr: int) +{ + context = acontext; + tktop = atktop; + sys = load Sys Sys->PATH; + tk = load Tk Tk->PATH; + tkclient = atkclient; + selectfile = aselectfile; + dialog = adialog; + str = astr; + debug = adebug; + xscroll = xscr; + remcr = rcr; + + plumbmsg = load Plumbmsg Plumbmsg->PATH; + if(plumbmsg->init(1, nil, 0) >= 0){ + plumbed = 1; + workdir = load Workdir Workdir->PATH; + } +} + +reinit(xscr: int, rcr: int) +{ + if(xscroll == xscr && remcr == rcr) + return; + xscroll = xscr; + remcr = rcr; + for(ml := mods; ml != nil; ml = tl ml){ + m := hd ml; + if(xscroll) + tkcmd(m.tk+" configure -wrap none"); + else + tkcmd(m.tk+" configure -wrap char"); + tkcmd("update"); + fd := sys->open(m.src, sys->OREAD); + if(fd != nil) + loadfile(m.tk, fd); + } +} + +# +# make a Mod with a text widget for source file src +# +loadsrc(src: string, addpath: int): ref Mod +{ + if(src == "") + return nil; + + m : ref Mod = nil; + for(ml := mods; ml != nil; ml = tl ml){ + m = hd ml; + if(m.src == src || filesuffix(src, m.src)) + break; + } + + if(ml == nil || m.tk == nil){ + if(ml == nil) + m = ref Mod(src, nil, nil, nil, 0, 1); + fd := sys->open(src, sys->OREAD); + if(fd == nil) + return nil; + (dir, file) := str->splitr(src, "/"); + m.tk = ".t."+tk->quote(file)+string srcid++; + if(xscroll) + tkcmd("text "+m.tk+" -bd 0 -state disabled -wrap none"); + else + tkcmd("text "+m.tk+" -bd 0 -state disabled"); + if (but3 == nil) { + but3 = chan of string; + spawn but3proc(); + } + tk->namechan(tktop, but3, "but3"); + for (i := 0; i < len plumbbind; i++) + tkcmd("bind "+m.tk+" "+plumbbind[i]); + tkcmd(m.tk+" configure -insertwidth 2"); + opack := packed; + packm(m); + if(!loadfile(m.tk, fd)){ + fd = nil; + packm(opack); + tkcmd("destroy "+m.tk); + return nil; + } + fd = nil; + tkcmd(m.tk+" tag configure bpt -foreground #c00"); + tkcmd(".m.file.menu add command -label "+src+" -command {send m open "+src+"}"); + if(ml == nil) + mods = m :: mods; + + if(addpath) + addsearch(dir); + } + return m; +} + +addsearch(dir: string) +{ + for(i := 0; i < len searchpath; i++) + if(searchpath[i] == dir) + return; + s := array[i+1] of string; + s[0:] = searchpath; + s[i] = dir; + searchpath = s; +} + +# +# bring up the widget for src, if it exists +# +showstrsrc(src: string) +{ + m : ref Mod = nil; + for(ml := mods; ml != nil; ml = tl ml){ + m = hd ml; + if(m.src == src) + break; + } + if(ml == nil) + return; + + packm(m); +} + +# +# bring up the widget for module +# at position s +# +showmodsrc(m: ref Mod, s: ref Src) +{ + if(s == nil) + return; + + src := s.start.file; + if(src != s.stop.file) + s.stop = s.start; + + if(m == nil || m.tk == nil || m.src != src){ + m1 := findsrc(src); + if(m1 == nil) + return; + if(m1.dis == nil) + m1.dis = m.dis; + if(m1.sym == nil) + m1.sym = m.sym; + m = m1; + } + + tkcmd(m.tk+" mark set insert "+string s.start.line+"."+string s.start.pos); + tkcmd(m.tk+" tag remove sel 0.0 end"); + tkcmd(m.tk+" tag add sel insert "+string s.stop.line+"."+string s.stop.pos); + tkcmd(m.tk+" see insert"); + + packm(m); +} + +packm(m: ref Mod) +{ + if(packed != m && packed != nil){ + tkcmd(packed.tk+" configure -xscrollcommand {}"); + tkcmd(packed.tk+" configure -yscrollcommand {}"); + tkcmd(".body.scx configure -command {}"); + tkcmd(".body.scy configure -command {}"); + tkcmd("pack forget "+packed.tk); + } + + if(packed != m && m != nil){ + tkcmd(m.tk+" configure -xscrollcommand {.body.scx set}"); + tkcmd(m.tk+" configure -yscrollcommand {.body.scy set}"); + tkcmd(".body.scx configure -command {"+m.tk+" xview}"); + tkcmd(".body.scy configure -command {"+m.tk+" yview}"); + tkcmd("pack "+m.tk+" -expand 1 -fill both -in .body.ft"); + } + packed = m; +} + +# +# find the dis file associated with m +# we know that m has a valid src +# +attachdis(m: ref Mod): int +{ + c := load Diss m.dis; + if(c == nil){ + m.dis = repsuff(m.src, ".b", ".dis"); + c = load Diss m.dis; + } + if(c == nil && m.sym != nil){ + m.dis = repsuff(m.sym.path, ".sbl", ".dis"); + c = load Diss m.dis; + } + if(c != nil){ + # if m.dis in /appl, prefer one in /dis if it exists (!) + nd := len m.dis; + for(i := 0; i < len sblpath; i++){ + (disd, srcd) := sblpath[i]; + ns := len srcd; + if(nd > ns && m.dis[:ns] == srcd){ + dis := disd + m.dis[ns:]; + d := load Diss dis; + if(d != nil) + m.dis = dis; + break; + } + } + } + if(c == nil){ + (dir, file) := str->splitr(repsuff(m.src, ".b", ".dis"), "/"); + pat := list of { + file+" (Dis VM module)", + "*.dis (Dis VM module)" + }; + m.dis = selectfile->filename(context, tktop.image, "Locate Dis file", pat, dir); + c = load Diss m.dis; + } + return c != nil; +} + +# +# load the symbol file for m +# works best if m has an associated source file +# +attachsym(m: ref Mod) +{ + if(m.sym != nil) + return; + sbl := repsuff(m.src, ".b", ".sbl"); + err : string; + tk->cmd(tktop, "cursor -bitmap cursor.wait"); + (m.sym, err) = debug->sym(sbl); + tk->cmd(tktop, "cursor -default"); + if(m.sym != nil) + return; + if(!str->prefix("Can't open", err)){ + alert(err); + return; + } + (dir, file) := str->splitr(sbl, "/"); + + pat := list of { + file+" (Symbol table file)", + "*.sbl (Symbol table file)" + }; + sbl = selectfile->filename(context, tktop.image, "Locate Symbol file", pat, dir); + tk->cmd(tktop, "cursor -bitmap cursor.wait"); + (m.sym, err) = debug->sym(sbl); + tk->cmd(tktop, "cursor -default"); + if(m.sym != nil) + return; + if(!str->prefix("Can't open", err)){ + alert(err); + return; + } +} + +# +# get the current selection +# +getsel(): (ref Mod, int) +{ + m := packed; + if(m == nil || m.src == nil) + return (nil, 0); + attachsym(m); + if(m.sym == nil){ + alert("No symbol file for "+m.src); + return (nil, 0); + } + index := tkcmd(m.tk+" index insert"); + if(len index == 0 || index[0] == '!') + return (nil, 0); + (sline, spos) := str->splitl(index, "."); + line := int sline; + pos := int spos[1:]; + pc := m.sym.srctopc(ref Src((m.src, line, pos), (m.src, line, pos))); + s := m.sym.pctosrc(pc); + if(s == nil){ + alert("No pc is appropriate"); + return (nil, 0); + } + return (m, pc); +} + +# +# return the selected string +# +snarf(): string +{ + if(packed == nil) + return ""; + s := tk->cmd(tktop, packed.tk+" get sel.first sel.last"); + if(len s > 0 && s[0] == '!') + s = ""; + return s; +} + +plumbit(x, y: string) +{ + if (packed == nil) + return; + s := tk->cmd(tktop, packed.tk+" index @"+x+","+y); + if (s == nil || s[0] == '!') + return; + (nil, l) := sys->tokenize(s, "."); + msg := ref Msg( + "WmDeb", + "", + workdir->init(), + "text", + nil, + array of byte (packed.src+":"+hd l)); + if(msg.send() < 0) + sys->fprint(sys->fildes(2), "deb: plumbing write error: %r\n"); +} + +but3proc() +{ + button3 := 0; + for (;;) { + s := <-but3; + if(s == "pressed"){ + button3 = 1; + continue; + } + if(plumbed == 0 || button3 == 0) + continue; + button3 = 0; + (nil, l) := sys->tokenize(s, " "); + plumbit(hd tl l, hd tl tl l); + } +} + +# +# search for another occurance of s; +# return if s was found +# +search(s: string): int +{ + if(packed == nil || s == "") + return 0; + pos := " sel.last"; + sel := tk->cmd(tktop, packed.tk+" get sel.last"); + if(len sel > 0 && sel[0] == '!') + pos = " insert"; + pos = tk->cmd(tktop, packed.tk+" search -- "+tk->quote(s)+pos); + if((len pos > 0 && pos[0] == '1') || pos == "") + return 0; + tkcmd(packed.tk+" mark set insert "+pos); + tkcmd(packed.tk+" tag remove sel 0.0 end"); + tkcmd(packed.tk+" tag add sel insert "+pos+"+"+string len s+"c"); + tkcmd(packed.tk+" see insert"); + return 1; +} + +# +# make a Mod for debugger module mod +# +findmod(mod: ref Module): ref Mod +{ + dis := mod.dis(); + if(dis == "") + return nil; + m: ref Mod; + for(ml := mods; ml != nil; ml = tl ml){ + m = hd ml; + if(m.dis == dis || filesuffix(dis, m.dis)) + break; + } + if(ml == nil){ + if(len dis > 0 && dis[0] != '$') + m = findsrc(repsuff(dis, ".dis", ".b")); + if(m == nil) + mods = ref Mod("", "", dis, nil, 0, 0) :: mods; + } + if(m != nil){ + m.srcask = 0; + m.dis = dis; + if(m.symask){ + attachsym(m); + m.symask = 0; + } + mod.addsym(m.sym); + } + return m; +} + +# log(s: string) +# { +# fd := sys->open("/usr/jrf/debug", Sys->OWRITE); +# sys->seek(fd, 0, Sys->SEEKEND); +# sys->fprint(fd, "%s\n", s); +# fd = nil; +# } + +findbm(dis: string): ref Mod +{ + if(dism == nil){ + dism = load Dis Dis->PATH; + if(dism != nil) + dism->init(); + } + if(dism != nil && (b := dism->src(dis)) != nil) + return loadsrc(b, 1); + return nil; +} + +findsrc(src: string): ref Mod +{ + m := loadsrc(src, 1); + if(m != nil) + return m; + m = findbm(repsuff(src, ".b", ".dis")); + if(m != nil) + return m; + (dir, file) := str->splitr(src, "/"); + for(i := 0; i < len searchpath; i++){ + if(dir != "" && dir[0] != '/') + m = loadsrc(searchpath[i] + src, 0); + if(m != nil) + return m; + m = loadsrc(searchpath[i] + file, 0); + if(m != nil) + return m; + } + + ns := len src; + for(i = 0; i < len sblpath; i++){ + (disd, srcd) := sblpath[i]; + nd := len disd; + if(ns > nd && src[:nd] == disd){ + m = loadsrc(srcd + src[nd:], 0); + if(m != nil) + return m; + } + } + + (dir, file) = str->splitr(src, "/"); + opdir := dir; + if(opdir == "" || opdir[0] != '/') + opdir = opendir; + + pat := list of { + file+" (Limbo source)", + "*.b (Limbo source)" + }; + + src = selectfile->filename(context, tktop.image, "Locate Limbo Source", pat, opdir); + if(src == nil) + return nil; + (opendir, nil) = str->splitr(src, "/"); + if(opendir == "") + opendir = "."; + m = loadsrc(src, 1); + if(m != nil + && dir != "" && dir[0] != '/' + && suffix(dir, opendir)) + addsearch(opendir[:len opendir - len dir]); + else if(m != nil) # remember anyway + addsearch(opendir); + return m; +} + +suffix(suff, s: string): int +{ + if(len suff > len s) + return 0; + return suff == s[len s - len suff:]; +} + +# +# load the contents of fd into tkt +# +loadfile(tkt: string, fd: ref Sys->FD): int +{ + buf := array[512] of byte; + i := 0; + + (ok, d) := sys->fstat(fd); + if(ok < 0) + return 0; + tk->cmd(tktop, "cursor -bitmap cursor.wait"); + length := int d.length; + whole := array[length] of byte; + cr := 0; + for(;;){ + if(cr){ + buf[0] = byte '\r'; + n := sys->read(fd, buf[1:], len buf - 1); + n++; + } + else + n := sys->read(fd, buf, len buf); + if(n <= 0) + break; + if(remcr){ + for(k := 0; k < n-1; ){ + if(buf[k] == byte '\r' && buf[k+1] == byte '\n') + buf[k:] = buf[k+1:n--]; + else + k++; + } + if(buf[n-1] == byte '\r'){ + n--; + cr = 1; + } + } + j := i+n; + if(j > length) + break; + whole[i:] = buf[:n]; + i += n; + } + tk->cmd(tktop, tkt+" delete 1.0 end;"+tkt+" insert end '"+string whole[:i]); + tk->cmd(tktop, "update; cursor -default"); + return 1; +} + +delmod(mods: list of ref Mod, m: ref Mod): list of ref Mod +{ + if(mods == nil) + return nil; + mh := hd mods; + if(mh == m) + return tl mods; + return mh :: delmod(tl mods, m); +} + +# +# replace an occurance in name of suffix old with new +# +repsuff(name, old, new: string): string +{ + no := len old; + nn := len name; + if(nn >= no && name[nn-no:] == old) + return name[:nn-no] + new; + return name; +} + +filesuffix(suf, s: string): int +{ + nsuf := len suf; + ns := len s; + return ns > nsuf + && suf[0] != '/' + && s[ns-nsuf-1] == '/' + && s[ns-nsuf:] == suf; +} + +alert(m: string) +{ + dialog->prompt(context, tktop.image, "warning -fg yellow", + "Debugger Alert", m, 0, "Dismiss"::nil); +} + +tkcmd(s: string): string +{ + return tk->cmd(tktop, s); +} diff --git a/appl/wm/dir.b b/appl/wm/dir.b new file mode 100644 index 00000000..c4bcfe01 --- /dev/null +++ b/appl/wm/dir.b @@ -0,0 +1,511 @@ +implement WmDir; + +include "sys.m"; + sys: Sys; + Dir: import sys; + +include "draw.m"; + draw: Draw; + ctxt: ref Draw->Context; + +include "tk.m"; + tk: Tk; + Toplevel: import tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "dialog.m"; + dialog: Dialog; + +include "readdir.m"; + readdir: Readdir; + +include "daytime.m"; + daytime: Daytime; + +include "plumbmsg.m"; + plumbmsg: Plumbmsg; + Msg: import plumbmsg; + +Fontwidth: con 8; +Xwidth: con 50; + +WmDir: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +Wm: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +Ft: adt +{ + ext: string; + cmd: string; + tkname: string; + icon: string; + loaded: int; + givearg: int; +}; + +dirwin_cfg := array[] of { + # Lay out the screen + "frame .fc", + "scrollbar .fc.scroll -command {.fc.c yview}", + "canvas .fc.c -relief sunken -yscrollincrement 25"+ + " -borderwidth 2 -width 10c -height 300"+ + " -yscrollcommand {.fc.scroll set}"+ + " -font /fonts/misc/latin1.8x13.font", + "frame .mbar", + "menubutton .mbar.opt -text {Options} -menu .opt", + "pack .mbar.opt -side left", + "pack .fc.scroll -side right -fill y", + "pack .fc.c -fill both -expand 1", + "pack .mbar -fill x", + "pack .fc -fill both -expand 1", + "pack propagate . 0", + + # prepare cursor + "image create bitmap waiting -file cursor.wait", + + # Build the options menu + "menu .opt", + ".opt add radiobutton -text {by name}"+ + " -variable sort -value n -command {send opt sort}", + ".opt add radiobutton -text {by access}"+ + " -variable sort -value a -command {send opt sort}", + ".opt add radiobutton -text {by modify}"+ + " -variable sort -value m -command {send opt sort}", + ".opt add radiobutton -text {by size}"+ + " -variable sort -value s -command {send opt sort}", + ".opt add separator", + ".opt add radiobutton -text {use icons}"+ + " -variable show -value i -command {send opt icon}", + ".opt add radiobutton -text {use text}" + +" -variable show -value t -command {send opt text}", + ".opt add separator", + ".opt add checkbutton -text {Walk} -command {send opt walk}", +}; + +key := Readdir->NAME; +walk: int; +path: string; +usetext: int; +cmdname: string; +sysnam: string; +nde: int; +now: int; +plumbed := 0; +de: array of ref Sys->Dir; + +filetypes: array of ref Ft; +deftype: ref Ft; +dirtype: ref Ft; + +inittypes() +{ + deftype = ref Ft("", "/dis/wm/edit.dis", "WmDir_Dis", "file", 0, 1); + dirtype = ref Ft("", nil, "WmDir_Dir", "dir", 0, 1); + filetypes = array[] of { + ref Ft("dis", nil, "WmDis_Pic", "dis", 0, 0), + ref Ft("bit", "/dis/wm/view.dis", "WmDir_Pic", "pic", 0, 1), + ref Ft("gif", "/dis/wm/view.dis", "WmDir_Pic", "pic", 0, 1), + ref Ft("jpg", "/dis/wm/view.dis", "WmDir_Pic", "pic", 0, 1), + ref Ft("jpeg", "/dis/wm/view.dis", "WmDir_Pic", "pic", 0, 1), + ref Ft("mask", "/dis/wm/view.dis", "WmDir_Pic", "pic", 0, 1), + }; +} + +init(env: ref Draw->Context, argv: list of string) +{ + ctxt = env; + + sys = load Sys Sys->PATH; + if (ctxt == nil) { + sys->fprint(sys->fildes(2), "dir: no window context\n"); + raise "fail:bad context"; + } + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + dialog = load Dialog Dialog->PATH; + readdir = load Readdir Readdir->PATH; + plumbmsg = load Plumbmsg Plumbmsg->PATH; + if(plumbmsg != nil && plumbmsg->init(1, nil, 0) >= 0) + plumbed = 1; + + tkclient->init(); + dialog->init(); + inittypes(); + + cmdname = hd argv; + sysnam = sysname()+":"; + + (t, wmctl) := tkclient->toplevel(ctxt, "", "", Tkclient->Appl); + + tk->cmd(t, "cursor -image waiting"); + + filecmd := chan of string; + tk->namechan(t, filecmd, "fc"); + conf := chan of string; + tk->namechan(t, conf, "cf"); + opt := chan of string; + tk->namechan(t, opt, "opt"); + + argv = tl argv; + if(argv == nil) + getdir(t, ""); + else + getdir(t, hd argv); + for (c:=0; c<len dirwin_cfg; c++) + tk->cmd(t, dirwin_cfg[c]); + drawdir(t); + tk->cmd(t, "update; cursor -default"); + tk->cmd(t, "bind . <Configure> {send cf conf}"); + tkclient->onscreen(t, nil); + tkclient->startinput(t, "kbd"::"ptr"::nil); + + menu := ""; + +f: for(;;) alt { + s := <-t.ctxt.kbd => + tk->keyboard(t, s); + s := <-t.ctxt.ptr => + tk->pointer(t, *s); + s := <-t.ctxt.ctl or + s = <-t.wreq or + s = <-wmctl => + if (s == "exit") + exit; + tkclient->wmctl(t, s); + <-conf => + # + # Only recompute contents if the size changed + # + if(menu[0] != 's') + break; + tk->cmd(t, ".fc.c delete all"); + drawdir(t); + tk->cmd(t, ".fc.c yview moveto 0; update"); + mopt := <-opt => + case mopt { + "sort" => + case tk->cmd(t, "variable sort") { + "n" => key = readdir->NAME; + "a" => key = readdir->ATIME; + "m" => key = readdir->MTIME; + "s" => key = readdir->SIZE; + } + (de, nde) = readdir->sortdir(de, key); + "walk" => + walk = !walk; + continue f; + "text" => + usetext = 1; + "icon" => + usetext = 0; + } + tk->cmd(t, ".fc.c delete all"); + drawdir(t); + tk->cmd(t, ".fc.c yview moveto 0; update"); + action := <-filecmd => + nd := int action[1:]; + if(nd > len de) + break; + case action[0] { + '1' => + button1(t, de[nd]); + '3' => + button3(t, de[nd]); + } + } +} + +getdir(t: ref Toplevel, dir: string) +{ + if(dir == "") + dir = "/"; + + path = dir; + if (path[len path - 1] != '/') + path[len path] = '/'; + + (de, nde) = readdir->init(path, key); + if(nde < 0) { + dialog->prompt(ctxt, t.image, "error -fg red", + "Read directory", + sys->sprint("Error reading \"%s\"\n%r", path), + 0, "Exit"::nil); + exit; + } + + if(path != "/") { + (ok, d) := sys->stat(".."); + if(ok >= 0) { + dot := array[nde+1] of ref Dir; + dot[0] = ref d; + dot[0].name = ".."; + dot[1:] = de; + de = dot; + nde++; + } + } + + for(i := 0; i < nde; i++) { + s := de[i].name; + l := len s; + if(l > 4 && s[l-4:] == ".dis") + de[i].mode |= 8r111; + } + tkclient->settitle(t, sysnam+path); +} + +defcursor(t: ref Toplevel) +{ + tk->cmd(t, "cursor -default"); +} + +button1(t: ref Toplevel, item: ref Dir) +{ + mod: Wm; + + tk->cmd(t, "cursor -image waiting"); + npath := path; + name := item.name + "/"; + if(item.name == "..") { + i := len path - 2; + while(i > 0 && path[i] != '/') + i--; + npath = path[0:i]; + name = "/"; + } + + exec := npath+name[0:len name-1]; + ft := filetype(t, item, exec); + + if(item.mode & Sys->DMDIR) { + if(walk != 0) { + path = npath; + getdir(t, npath+name); + tk->cmd(t, ".fc.c delete all"); + drawdir(t); + tk->cmd(t, ".fc.c yview moveto 0; update"); + defcursor(t); + return; + } + mod = load Wm "/dis/wm/dir.dis"; + defcursor(t); + if(mod == nil) { + dialog->prompt(ctxt, t.image, "error -fg red", "Load Dir module", + sys->sprint("Error: %r"), + 0, "Continue"::nil); + return; + } + args := npath+name :: nil; + args = cmdname :: args; + spawn mod->init(ctxt, args); + return; + } + + cmd := ft.cmd; + if(cmd == nil) + cmd = npath+name; + + mod = load Wm cmd; + defcursor(t); + if(mod == nil) { + dialog->prompt(ctxt, t.image, "error -fg red", "Load Module", + sys->sprint("Trying to load \"%s\"\n%r", cmd), + 0, "Continue"::nil); + return; + } + if(ft.givearg) + spawn applinit(mod, ctxt, item.name :: exec :: nil); + else + spawn applinit(mod, ctxt, item.name :: nil); +} + +applinit(mod: Wm, ctxt: ref Draw->Context, args: list of string) +{ + sys->pctl(sys->NEWPGRP|sys->FORKFD, nil); + spawn mod->init(ctxt, args); +} + + +button3(nil: ref Toplevel, stat: ref Sys->Dir) +{ + if(!plumbed) + return; + msg := ref Msg( + "WmDir", + "", + path, + "text", + "", + array of byte stat.name); + + msg.send(); +} + +filetype(t: ref Toplevel, d: ref Dir, path: string): ref Ft +{ + if(d.mode & Sys->DMDIR) + return loadtype(t, dirtype); + + suffix := ""; + for(j := len path-2; j >= 0; j--) { + if(path[j] == '.') { + suffix = path[j+1:]; + break; + } + } + + if(suffix == "") + return loadtype(t, deftype); + + if(suffix[0] >= 'A' && suffix[0] <= 'Z') { + for(j = 0; j < len suffix; j++) + suffix[j] += ('A' - 'a'); + } + + for(i := 0; i<len filetypes; i++) { + if(suffix == filetypes[i].ext) + return loadtype(t, filetypes[i]); + } + + return loadtype(t, deftype); +} + +loadtype(t: ref Toplevel, ft: ref Ft): ref Ft +{ + if(ft.loaded) + return ft; + + s := sys->sprint("image create bitmap %s -file %s.bit -maskfile %s.mask", + ft.tkname, ft.icon, ft.icon); + tk->cmd(t, s); + + ft.loaded = 1; + return ft; +} + +drawdir(t: ref Toplevel) +{ + if(usetext) + drawdirtxt(t); + else + drawdirico(t); +} + +drawdirtxt(t: ref Toplevel) +{ + if(daytime == nil) { + daytime = load Daytime Daytime->PATH; + if(daytime == nil) { + dialog->prompt(ctxt, t.image, "error -fg red", "Load Module", + sys->sprint("Trying to load \"%s\"\n%r", Daytime->PATH), + 0, "Continue"::nil); + return; + } + now = daytime->now(); + } + + y := 10; + for(i := 0; i < nde; i++) { + tp := "file"; + if(de[i].mode & Sys->DMDIR) + tp = "dir "; + else + if(de[i].mode & 8r111) + tp = "exe "; + s := sys->sprint("%s %7bd %s %s", + tp, + de[i].length, + daytime->filet(now, de[i].mtime), + de[i].name); + id := tk->cmd(t, ".fc.c create text 10 "+string y+ + " -anchor w -text {"+s+"}"); + + base := ".fc.c bind "+id; + tk->cmd(t, base+" <Double-Button-1> {send fc %b "+string i+"}"); + tk->cmd(t, base+" <Button-3> {send fc %b "+string i+"}"); + tk->cmd(t, base+" <Motion-Button-3> {}"); + y += 15; + } + + x := int tk->cmd(t, ".fc.c cget actwidth"); + tk->cmd(t, ".fc.c configure -scrollregion { 0 0 "+string x+" "+string y+"}"); +} + +drawdirico(t: ref Toplevel) +{ + w := int tk->cmd(t, ".fc.c cget actwidth"); + + longest := 0; + for(i := 0; i < nde; i++) { + l := len de[i].name; + if(l > longest) + longest = l; + } + longest += 2; + + minw := (longest*Fontwidth); + if( w < minw ){ + w = minw + int tk->cmd(t, ".fc.scroll cget actwidth"); + tk->cmd(t, ". configure -width "+string w); + w = minw; + } + + xwid := Xwidth; + x := w/minw; + x = w/x; + if(x > xwid) + xwid = x; + + x = xwid/2; + y := 20; + + for(i = 0; i < nde; i++) { + sx := string x; + ft := filetype(t, de[i], de[i].name); + img := ft.tkname; + + id := tk->cmd(t, ".fc.c create image "+sx+" "+ + string y+" -image "+img); + tk->cmd(t, ".fc.c create text "+sx+ + " "+string (y+25)+" -text "+de[i].name); + + base := ".fc.c bind "+id; + tk->cmd(t, base+" <Double-Button-1> {send fc %b "+string i+"}"); + tk->cmd(t, base+" <Button-2> {send fc %b "+string i+"}"); + tk->cmd(t, base+" <Motion-Button-2> {}"); + tk->cmd(t, base+" <Button-3> {send fc %b "+string i+"}"); + tk->cmd(t, base+" <Motion-Button-3> {}"); + x += xwid; + if(x > w) { + x = xwid/2; + y += 50; + } + } + y += 50; + x = int tk->cmd(t, ".fc.c cget actwidth"); + tk->cmd(t, ".fc.c configure -scrollregion { 0 0 "+string x+" "+string y+"}"); +} + +sysname(): string +{ + syspath := "#c"; + if ( cmdname == "wmdir" ) + syspath = "/n/dev"; + fd := sys->open(syspath+"/sysname", sys->OREAD); + if(fd == nil) + return "Anon"; + buf := array[128] of byte; + n := sys->read(fd, buf, len buf); + if(n < 0) + return "Anon"; + return string buf[0:n]; +} diff --git a/appl/wm/drawmux/dmview.b b/appl/wm/drawmux/dmview.b new file mode 100644 index 00000000..92fdaa2d --- /dev/null +++ b/appl/wm/drawmux/dmview.b @@ -0,0 +1,163 @@ +implement DMView; + +include "sys.m"; +include "draw.m"; +include "tk.m"; +include "tkclient.m"; + +DMView : module { + init : fn (ctxt : ref Draw->Context, args : list of string); +}; + +DMPORT : con 9998; + +sys : Sys; +draw : Draw; +tk : Tk; +tkclient : Tkclient; + +Display, Image, Screen, Point, Rect, Chans : import draw; + +display : ref Display; +screen : ref Screen; + + +init(ctxt : ref Draw->Context, args : list of string) +{ + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + if (tk == nil) + fail(sys->sprint("cannot load %s: %r", Tk->PATH), "init"); + + tkclient = load Tkclient Tkclient->PATH; + if (tkclient == nil) + fail(sys->sprint("cannot load %s: %r", Tkclient->PATH), "init"); + + args = tl args; + if (args == nil) + fail("usage: dmview netaddr", "usage"); + addr := hd args; + args = tl args; + + display = ctxt.display; + screen = ctxt.screen; + + tkclient ->init(); + + (ok, nc) := sys->dial("tcp!"+addr+"!" + string DMPORT, nil); + if (ok < 0) + fail(sys->sprint("could not connect: %r"), "init"); + + info := array [2 * 12] of byte; + if (sys->read(nc.dfd, info, len info) != len info) { + sys->print("protocol error\n"); + return; + } + dispw := int string info[0:12]; + disph := int string info[12:24]; + info = nil; + + (tktop, wmctl) := tkclient->toplevel(ctxt, "", "dmview: "+addr, Tkclient->Hide); + if (tktop == nil) + fail("cannot create window", "init"); + + cpos := mkframe(tktop, dispw, disph); + winr := Rect((0, 0), (dispw, disph)); + newwin := display.newimage(winr, display.image.chans, 0, Draw->White); + # newwin := screen.newwindow(winr, Draw->Refbackup, Draw->White); + if (newwin == nil) { + sys->print("failed to create window: %r\n"); + return; + } + tk->putimage(tktop, ".c", newwin, nil); + tk->cmd(tktop, ".c dirty"); + tk->cmd(tktop, "update"); + winr = winr.addpt(cpos); + newwin.origin(Point(0,0), winr.min); + + pubscr := Screen.allocate(newwin, ctxt.display.black, 1); + if (pubscr == nil) { + sys->print("failed to create public screen: %r\n"); + return; + } + + msg := array of byte sys->sprint("%11d %11s ", pubscr.id, newwin.chans.text()); + sys->write(nc.dfd, msg, len msg); + msg = nil; + + pidc := chan of int; + spawn srv(nc.dfd, wmctl, pidc); + srvpid := <- pidc; + + tkclient->onscreen(tktop, nil); + tkclient->startinput(tktop, nil); + + for (;;) { + cmd := <- wmctl; + case cmd { + "srvexit" => +sys->print("srv exit: %r\n"); + srvpid = -1; + "exit" => + if (srvpid != -1) + kill(srvpid); + return; + "move" => + newwin.origin(Point(0,0), display.image.r.max); + tkclient->wmctl(tktop, cmd); + x := int tk->cmd(tktop, ".c cget -actx"); + y := int tk->cmd(tktop, ".c cget -acty"); + newwin.origin(Point(0,0), Point(x, y)); + "task" => + newwin.origin(Point(0,0), display.image.r.max); + tkclient->wmctl(tktop, cmd); + x := int tk->cmd(tktop, ".c cget -actx"); + y := int tk->cmd(tktop, ".c cget -acty"); + newwin.origin(Point(0,0), Point(x, y)); + * => + tkclient->wmctl(tktop, cmd); + } + } +} + +srv(fd : ref Sys->FD, done : chan of string, pidc : chan of int) +{ + pidc <-= sys->pctl(Sys->FORKNS, nil); + sys->bind("/dev/draw", "/", Sys->MREPL); + sys->export(fd, "/", Sys->EXPWAIT); + done <-= "srvexit"; +} + +fail(msg, exc : string) +{ + sys->print("%s\n", msg); + raise "fail:"+exc; +} + +mkframe(t : ref Tk->Toplevel, w, h : int) : Point +{ + tk->cmd(t, "panel .c -width " + string w + " -height " + string h); + tk->cmd(t, "frame .f -borderwidth 3 -relief groove"); + tk->cmd(t, "pack .c -in .f"); + tk->cmd(t, "pack .f"); + tk->cmd(t, "update"); + + x := int tk->cmd(t, ".c cget -actx"); + y := int tk->cmd(t, ".c cget -acty"); + + return Point(x, y); +} + +kill(pid: int) +{ + if ((pctl := sys->open("/prog/" + string pid + "/ctl", Sys->OWRITE)) != nil) + sys->fprint(pctl, "kill"); +} + +tkcmd(t : ref Tk->Toplevel, c : string) +{ + s := tk->cmd(t, c); + if (s != nil) + sys->print("%s ERROR: %s\n", c, s); +} diff --git a/appl/wm/drawmux/dmwm.b b/appl/wm/drawmux/dmwm.b new file mode 100644 index 00000000..45d80f8a --- /dev/null +++ b/appl/wm/drawmux/dmwm.b @@ -0,0 +1,207 @@ +implement Dmwm; +include "sys.m"; + sys: Sys; +include "draw.m"; + draw: Draw; + Screen, Display, Image, Rect, Point, Wmcontext, Pointer: import draw; +include "drawmux.m"; + dmux : Drawmux; +include "wmsrv.m"; + wmsrv: Wmsrv; + Window, Client: import wmsrv; +include "tk.m"; +include "wmclient.m"; + wmclient: Wmclient; +include "string.m"; + str: String; +include "dialog.m"; + dialog: Dialog; +include "arg.m"; + +Wm: module { + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +Dmwm: module { + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +Background: con int 16r777777FF; + +screen: ref Screen; +display: ref Display; + +badmodule(p: string) +{ + sys->fprint(sys->fildes(2), "wm: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init(ctxt: ref Draw->Context, argv: list of string) +{ + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + if(draw == nil) + badmodule(Draw->PATH); + + str = load String String->PATH; + if(str == nil) + badmodule(String->PATH); + + wmsrv = load Wmsrv Wmsrv->PATH; + if(wmsrv == nil) + badmodule(Wmsrv->PATH); + + wmclient = load Wmclient Wmclient->PATH; + if(wmclient == nil) + badmodule(Wmclient->PATH); + wmclient->init(); + + dialog = load Dialog Dialog->PATH; + if (dialog == nil) badmodule(Dialog->PATH); + dialog->init(); + + sys->pctl(Sys->NEWPGRP|Sys->FORKNS, nil); + if (ctxt == nil) + ctxt = wmclient->makedrawcontext(); + display = ctxt.display; + + dmux = load Drawmux Drawmux->PATH; + if (dmux != nil) { + (err, disp) := dmux->init(); + if (err != nil) { + dmux = nil; + sys->fprint(stderr(), "wm: cannot start drawmux: %s\n", err); + } + else + display = disp; + } + + buts := Wmclient->Appl; + if(ctxt.wm == nil) + buts = Wmclient->Plain; + # win := wmclient->window(ctxt, "Wm", buts); + # wmclient->win.onscreen("place"); + # wmclient->win.startinput("kbd" :: "ptr" :: nil); + + # screen = makescreen(win.image); + + (clientwm, join, req) := wmsrv->init(); + clientctxt := ref Draw->Context(display, nil, nil); + + sync := chan of string; + argv = tl argv; + if(argv == nil) + argv = "wm/toolbar" :: nil; + argv = "wm/wm" :: argv; + spawn command(clientctxt, argv, sync); + if((e := <-sync) != nil) + fatal("cannot run command: " + e); + + dmuxrequest := chan of (string, ref Sys->FD); + if (dmux != nil) + spawn dmuxlistener(dmuxrequest); + + for(;;) alt { + (name, fd) := <- dmuxrequest => + spawn dmuxask(ctxt, name, fd); + } +} + +makescreen(img: ref Image): ref Screen +{ + screen = Screen.allocate(img, img.display.color(Background), 0); + img.draw(img.r, screen.fill, nil, screen.fill.r.min); + return screen; +} + +kill(pid: int, note: string): int +{ + fd := sys->open("/prog/"+string pid+"/ctl", Sys->OWRITE); + if(fd == nil || sys->fprint(fd, "%s", note) < 0) + return -1; + return 0; +} + +fatal(s: string) +{ + sys->fprint(sys->fildes(2), "wm: %s\n", s); + kill(sys->pctl(0, nil), "killgrp"); + raise "fail:error"; +} + +command(ctxt: ref Draw->Context, args: list of string, sync: chan of string) +{ + fds := list of {0, 1, 2}; + pid := sys->pctl(sys->NEWFD, fds); + + cmd := hd args; + file := cmd; + + if(len file<4 || file[len file-4:]!=".dis") + file += ".dis"; + + c := load Wm file; + if(c == nil) { + err := sys->sprint("%r"); + if(err != "permission denied" && err != "access permission denied" && file[0]!='/' && file[0:2]!="./"){ + c = load Wm "/dis/"+file; + if(c == nil) + err = sys->sprint("%r"); + } + if(c == nil){ + sync <-= sys->sprint("%s: %s\n", cmd, err); + exit; + } + } + sync <-= nil; + c->init(ctxt, args); +} + +dmuxlistener(newclient : chan of (string, ref Sys->FD)) +{ + (aok, c) := sys->announce("tcp!*!9998"); + if (aok < 0) { + sys->print("cannot announce drawmux port: %r\n"); + return; + } + buf := array [Sys->ATOMICIO] of byte; + for (;;) { + (ok, nc) := sys->listen(c); + if (ok < 0) { + sys->fprint(stderr(), "wm: dmux listen failed: %r\n"); + return; + } + fd := sys->open(nc.dir+"/remote", Sys->OREAD); + name := "unknown"; + if (fd == nil) + sys->fprint(stderr(), "wm: dmux cannot access remote address: %r\n"); + else { + n := sys->read(fd, buf, len buf); + if (n > 0) { + name = string buf[0:n]; + for (i := len name -1; i > 0; i--) + if (name[i] == '!') + break; + if (i != 0) + name = name[0:i]; + } + } + fd = sys->open(nc.dir+"/data", Sys->ORDWR); + if (fd != nil) + newclient <-= (name, fd); + } +} + +dmuxask(ctxt: ref Draw->Context, name : string, fd : ref Sys->FD) +{ + msg := sys->sprint("Screen snoop request\nAddress: %s\n\nProceed?", name); + labs := "Ok" :: "No way!" :: nil; + if (1 || dialog->prompt(ctxt, nil, nil, "Snoop!", msg, 1, labs) == 0) + dmux->newviewer(fd); +} + +stderr(): ref Sys->FD +{ + return sys->fildes(2); +} diff --git a/appl/wm/drawmux/drawmux.b b/appl/wm/drawmux/drawmux.b new file mode 100644 index 00000000..121efec0 --- /dev/null +++ b/appl/wm/drawmux/drawmux.b @@ -0,0 +1,1827 @@ +implement Drawmux; + +include "sys.m"; +include "draw.m"; +include "drawmux.m"; + +include "drawoffs.m"; + +sys : Sys; +draw : Draw; + +Display, Point, Rect, Chans : import draw; + +Ehungup : con "Hangup"; + +drawR: Draw->Rect; +drawchans: Draw->Chans; +drawop := Draw->SoverD; +drawfd: ref Sys->FD; +images: ref Imageset; +screens: ref Screenset; +viewers: list of ref Viewer; +drawlock: chan of chan of int; +readdata: array of byte; +nhangups := 0; +prevnhangups := 0; + +init() : (string, ref Draw->Display) +{ + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + + if (draw == nil) + return (sys->sprint("cannot load %s: %r", Draw->PATH), nil); + drawlock = chan of chan of int; + images = Imageset.new(); + screens = Screenset. new(); + res := chan of (string, ref Draw->Display); + spawn getdisp(res); + r := <- res; + return r; +} + +newviewer(fd : ref Sys->FD) +{ + reply := array of byte sys->sprint("%.11d %.11d ", drawR.max.x - drawR.min.x, drawR.max.y - drawR.min.y); + if (sys->write(fd, reply, len reply) != len reply) { +# sys->print("viewer hangup\n"); + return; + } + + buf := array [Sys->ATOMICIO] of byte; + n := sys->read(fd, buf, len buf); + if (n < 24) + return; + pubscr := int string buf[0:12]; + chans := Chans.mk(string buf[12:24]); + + sys->pctl(Sys->FORKNS, nil); + sys->mount(fd, nil, "/", Sys->MREPL, nil); + cfd := sys->open("/new", Sys->OREAD); + sys->read(cfd, buf, len buf); + cnum := int string buf[0:12]; + cdata := sys->sprint("/%d/data", cnum); + datafd := sys->open(cdata, Sys->ORDWR); + + if (datafd == nil) { +# sys->print("cannot open viewer data file: %r\n"); + return; + } + Viewer.new(datafd, pubscr, chans); +} + +getdisp(result : chan of (string, ref Draw->Display)) +{ + sys->pctl(Sys->FORKNS, nil); + sys->bind("#i", "/dev", Sys->MREPL); + sys->bind("#s", "/dev/draw", Sys->MBEFORE); + newio := sys->file2chan("/dev/draw", "new"); + if (newio == nil) { + result <- = ("cannot create /dev/new file2chan", nil); + return; + } + spawn srvnew(newio); + disp := Display.allocate(nil); + if (disp == nil) { + result <-= (sys->sprint("%r"), nil); + return; + } + + draw->disp.image.draw(disp.image.r, disp.rgb(0,0,0), nil, Point(0,0)); + result <- = (nil, disp); +} + +srvnew(newio : ref Sys->FileIO) +{ + for (;;) alt { + (offset, count, fid, rc) := <- newio.read => + if (rc != nil) { + c := chan of (string, ref Sys->FD); + fd := sys->open("#i/draw/new", Sys->OREAD); + # +1 because of a sprint() nasty in devdraw.c + buf := array [(12 * 12)+1] of byte; + nn := sys->read(fd, buf, len buf); + cnum := int string buf[0:12]; + drawchans = Chans.mk(string buf[24:36]); + # repl is at [36:48] + drawR.min.x = int string buf[48:60]; + drawR.min.y = int string buf[60:72]; + drawR.max.x = int string buf[72:84]; + drawR.max.y = int string buf[84:96]; + + bwidth := bytesperline(drawR, drawchans); + img := ref Image (0, 0, 0, 0, drawchans, 0, drawR, drawR, Draw->Black, nil, drawR.min, bwidth, 0, ""); + images.add(0, img); + + cdir := sys->sprint("/dev/draw/%d", cnum); + dpath := sys->sprint("#i/draw/%d/data", cnum); + drawfd = sys->open(dpath, Sys->ORDWR); + fd = nil; + if (drawfd == nil) { + rc <-= (nil, sys->sprint("%r")); + return; + } + sys->bind("#s", cdir, Sys->MBEFORE); + drawio := sys->file2chan(cdir, "data"); + spawn drawclient(drawio); + rc <- = (buf, nil); + return; + } + (offset, data, fid, wc) := <- newio.write => + if (wc != nil) + writereply(wc, (0, "permission denied")); + } +} + +# for simplicity make the file 'exclusive use' +drawclient(drawio : ref Sys->FileIO) +{ + activefid := -1; + closecount := 2; + + for (;closecount;) { + alt { + unlock := <- drawlock => + <- unlock; + + (offset, count, fid, rc) := <- drawio.read => + if (activefid == -1) + activefid = fid; + + if (rc == nil) { + closecount--; + continue; + } + if (fid != activefid) { + rc <-= (nil, "file busy"); + continue; + } + if (readdata == nil) { + rc <-= (nil, nil); + continue; + } + if (count > len readdata) + count = len readdata; + rc <- = (readdata[0:count], nil); + readdata = nil; + + (offset, data, fid, wc) := <- drawio.write => + if (wc == nil) { + closecount--; + continue; + } + writereply(wc, process(data)); + } + if (nhangups != prevnhangups) { + ok : list of ref Viewer; + for (ok = nil; viewers != nil; viewers = tl viewers) { + v := hd viewers; + if (!v.hungup) + ok = v :: ok; + else { +# sys->print("shutting down Viewer\n"); + v.output <- = (nil, nil); + } + } + viewers = ok; + prevnhangups = nhangups; + } + } +# sys->print("DRAWIO DONE!\n"); +} + +writereply(wc : chan of (int, string), val : (int, string)) +{ + alt { + wc <-= val => + ; + * => + ; + } +} + +Image: adt { + id: int; + refc: int; + screenid: int; + refresh: int; + chans: Draw->Chans; + repl: int; + R: Draw->Rect; + clipR: Draw->Rect; + rrggbbaa: int; + font: ref Font; + lorigin: Draw->Point; + bwidth: int; + dirty: int; + name: string; +}; + +Screen: adt { + id: int; + imageid: int; + fillid: int; + windows: array of int; + + setz: fn (s: self ref Screen, z: array of int, top: int); + addwin: fn (s: self ref Screen, wid: int); + delwin: fn (s: self ref Screen, wid: int); +}; + +Font: adt { + ascent: int; + chars: array of ref Fontchar; +}; + +Fontchar: adt { + srcid: int; + R: Draw->Rect; + P: Draw->Point; + left: int; + width: int; +}; + +Idpair: adt { + key: int; + val: int; + next: cyclic ref Idpair; +}; + +Idmap: adt { + buckets: array of ref Idpair; + + new: fn (): ref Idmap; + add: fn (m: self ref Idmap, key, val: int); + del: fn (m: self ref Idmap, key: int); + lookup: fn (m: self ref Idmap, key: int): int; +}; + +Imageset: adt { + images: array of ref Image; + ixmap: ref Idmap; + freelist: list of int; + new: fn (): ref Imageset; + add: fn (s: self ref Imageset, id: int, img: ref Image); + del: fn (s: self ref Imageset, id: int); + lookup: fn (s: self ref Imageset, id: int): ref Image; + findname: fn(s: self ref Imageset, name: string): ref Image; +}; + +Screenset: adt { + screens: array of ref Screen; + ixmap: ref Idmap; + freelist: list of int; + new: fn (): ref Screenset; + add: fn (s: self ref Screenset, scr: ref Screen); + del: fn (s: self ref Screenset, id: int); + lookup: fn (s: self ref Screenset, id: int): ref Screen; +}; + + +Drawreq: adt { + data: array of byte; + pick { +# a => # allocate image +# id: int; +# screenid: int; +# refresh: int; +# ldepth: int; +# repl: int; +# R: Draw->Rect; +# clipR: Draw->Rect; +# value: int; + b => # new allocate image + id: int; + screenid: int; + refresh: int; + chans: Draw->Chans; + repl: int; + R: Draw->Rect; + clipR: Draw->Rect; + rrggbbaa: int; + A => # allocate screen + id: int; + imageid: int; + fillid: int; + c => # set clipr and repl + dstid: int; + repl: int; + clipR: Draw->Rect; +# x => # move cursor +# C => # set cursor image and hotspot +# _: int; + d => # general draw op + dstid: int; + srcid: int; + maskid: int; + D => # debug mode + _: int; + e => # draw ellipse + dstid: int; + srcid: int; + f => # free image + id: int; + img: ref Image; # helper for Viewers + F => # free screen + id: int; + i => # convert image to font + fontid: int; + nchars: int; + ascent: int; + l => # load a char into font + fontid: int; + srcid: int; + index: int; + R: Draw->Rect; + P: Draw->Point; + left: int; + width: int; + L => # draw line + dstid: int; + srcid: int; + n => # attach to named image + dstid: int; + name: string; + N => # name image + dstid: int; + in: int; + name: string; + o => # set window origins + id: int; + rmin: Draw->Point; + screenrmin: Draw->Point; + O => # set next compositing op + op: int; + p => # draw polygon + dstid: int; + srcid: int; + r => # read pixels + id: int; + R: Draw->Rect; + s => # draw text + dstid: int; + srcid: int; + fontid: int; + x => # draw text with bg + dstid: int; + srcid: int; + fontid: int; + bgid: int; + S => # import public screen + t => # adjust window z order + top: int; + ids: array of int; + v => # flush updates to display + y => # write pixels + id: int; + R: Draw->Rect; + } +}; + +getreq(data : array of byte, ix : int) : (ref Drawreq, string) +{ + mlen := 0; + err := "short draw message"; + req : ref Drawreq; + + case int data[ix] { + 'b' => # alloc image + mlen = 1+4+4+1+4+1+(4*4)+(4*4)+4; + if (mlen+ix <= len data) { + data = data[ix:ix+mlen]; + r := ref Drawreq.b; + r.data = data; + r.id = get4(data, OPb_id); + r.screenid = get4(data, OPb_screenid); + r.refresh = get1(data, OPb_refresh); + r.chans = Draw->Chans(get4(data, OPb_chans)); + r.repl = get1(data, OPb_repl); + r.R = getR(data, OPb_R); + r.clipR = getR(data, OPb_clipR); + r.rrggbbaa = get4(data, OPb_rrggbbaa); + req = r; + } + 'A' => # alloc screen + mlen = 1+4+4+4+1; + if (mlen+ix <= len data) { + data = data[ix:ix+mlen]; + r := ref Drawreq.A; + r.data = data; + r.id = get4(data, OPA_id); + r.imageid = get4(data, OPA_imageid); + r.fillid = get4(data, OPA_fillid); + req = r; + } + 'c' => # set clipR + mlen = 1+4+1+(4*4); + if (mlen+ix <= len data) { + data = data[ix:ix+mlen]; + r := ref Drawreq.c; + r.data = data; + r.dstid = get4(data, OPc_dstid); + r.repl = get1(data, OPc_repl); + r.clipR = getR(data, OPc_clipR); + req = r; + } + 'd' => # draw + mlen = 1+4+4+4+(4*4)+(2*4)+(2*4); + if (mlen+ix <= len data) { + data = data[ix:ix+mlen]; + r := ref Drawreq.d; + r.data = data; + r.dstid = get4(data, OPd_dstid); + r.srcid = get4(data, OPd_srcid); + r.maskid = get4(data, OPd_maskid); + req = r; + } + 'D' => + # debug mode + mlen = 1+1; + if (mlen+ix <= len data) { + req = ref Drawreq.v; + req.data = data[ix:ix+mlen]; + } + 'e' or + 'E' => # ellipse + mlen = 1+4+4+(2*4)+4+4+4+(2*4)+4+4; + if (mlen+ix <= len data) { + data = data[ix:ix+mlen]; + r := ref Drawreq.e; + r.data = data; + r.dstid = get4(data, OPe_dstid); + r.srcid = get4(data, OPe_srcid); + req = r; + } + 'f' => # free image + mlen = 1+4; + if (mlen+ix <= len data) { + data = data[ix:ix+mlen]; + r := ref Drawreq.f; + r.data = data; + r.id = get4(data, OPf_id); + req = r; + } + 'F' => # free screen + mlen = 1+4; + if (mlen+ix <= len data) { + data = data[ix:ix+mlen]; + r := ref Drawreq.f; + r.data = data; + r.id = get4(data, OPF_id); + req = r; + } + 'i' => # alloc font + mlen = 1+4+4+1; + if (mlen+ix <= len data) { + data = data[ix:ix+mlen]; + r := ref Drawreq.i; + r.data = data; + r.fontid = get4(data, OPi_fontid); + r.nchars = get4(data, OPi_nchars); + r.ascent = get1(data, OPi_ascent); + req = r; + } + 'l' => # load font char + mlen = 1+4+4+2+(4*4)+(2*4)+1+1; + if (mlen+ix <= len data) { + data = data[ix:ix+mlen]; + r := ref Drawreq.l; + r.data = data; + r.fontid = get4(data, OPl_fontid); + r.srcid = get4(data, OPl_srcid); + r.index = get2(data, OPl_index); + r.R = getR(data, OPl_R); + r.P = getP(data, OPl_P); + r.left = get1(data, OPl_left); + r.width = get1(data, OPl_width); + req = r; + } + 'L' => # line + mlen = 1+4+(2*4)+(2*4)+4+4+4+4+(2*4); + if (mlen+ix <= len data) { + data = data[ix:ix+mlen]; + r := ref Drawreq.L; + r.data = data; + r.dstid = get4(data, OPL_dstid); + r.srcid = get4(data, OPL_srcid); + req = r; + } + 'n' => # attach to named image + mlen = 1+4+1; + if (mlen+ix < len data) { + mlen += get1(data, ix+OPn_j); + if (mlen+ix <= len data) { + data = data[ix:ix+mlen]; + r := ref Drawreq.n; + r.data = data; + r.dstid = get4(data, OPn_dstid); + r.name = string data[OPn_name:]; + req = r; + } + } + 'N' => # name image + mlen = 1+4+1+1; + if (mlen+ix < len data) { + mlen += get1(data, ix+OPN_j); + if (mlen+ix <= len data) { + data = data[ix:ix+mlen]; + r := ref Drawreq.N; + r.data = data; + r.dstid = get4(data, OPN_dstid); + r.in = get1(data, OPN_in); + r.name = string data[OPN_name:]; + req = r; + } + } + 'o' => # set origins + mlen = 1+4+(2*4)+(2*4); + if (mlen+ix <= len data) { + data = data[ix:ix+mlen]; + r := ref Drawreq.o; + r.data = data; + r.id = get4(data, OPo_id); + r.rmin = getP(data, OPo_rmin); + r.screenrmin = getP(data, OPo_screenrmin); + req = r; + } + 'O' => # set next compop + mlen = 1+1; + if (mlen+ix <= len data) { + data = data[ix:ix+mlen]; + r := ref Drawreq.O; + r.data = data; + r.op = get1(data, OPO_op); + req = r; + } + 'p' or + 'P' => # polygon + mlen = 1+4+2+4+4+4+4+(2*4); + if (mlen + ix <= len data) { + n := get2(data, ix+OPp_n); + nb := coordslen(data, ix+OPp_P0, 2*(n+1)); + if (nb == -1) + err = "bad coords"; + else { + mlen += nb; + if (mlen+ix <= len data) { + data = data[ix:ix+mlen]; + r := ref Drawreq.p; + r.data = data; + r.dstid = get4(data, OPp_dstid); + r.srcid = get4(data, OPp_srcid); + req = r; + } + } + } + 'r' => # read pixels + mlen = 1+4+(4*4); + if (mlen+ix <= len data) { + data = data[ix:ix+mlen]; + r := ref Drawreq.r; + r.data = data; + r.id = get4(data, OPr_id); + r.R = getR(data, OPr_R); + req = r; + } + 's' => # text + mlen = 1+4+4+4+(2*4)+(4*4)+(2*4)+2; + if (ix+mlen <= len data) { + ni := get2(data, ix+OPs_ni); + mlen += (2*ni); + if (mlen+ix <= len data) { + data = data[ix:ix+mlen]; + r := ref Drawreq.s; + r.data = data; + r.dstid = get4(data, OPs_dstid); + r.srcid = get4(data, OPs_srcid); + r.fontid = get4(data, OPs_fontid); + req = r; + } + } + 'x' => # text with bg img + mlen = 1+4+4+4+(2*4)+(4*4)+(2*4)+2+4+(2*4); + if (ix+mlen <= len data) { + ni := get2(data, ix+OPx_ni); + mlen += (2*ni); + if (mlen+ix <= len data) { + data = data[ix:ix+mlen]; + r := ref Drawreq.x; + r.data = data; + r.dstid = get4(data, OPx_dstid); + r.srcid = get4(data, OPx_srcid); + r.fontid = get4(data, OPx_fontid); + r.bgid = get4(data, OPx_bgid); + req = r; + } + } + 'S' => # import public screen + mlen = 1+4+4; + if (mlen+ix <= len data) { + data = data[ix:ix+mlen]; + req = ref Drawreq.S; + req.data = data; + } + 't' => # adjust window z order + mlen = 1+1+2; + if (ix+mlen<= len data) { + nw := get2(data, ix+OPt_nw); + mlen += (4*nw); + if (mlen+ix <= len data) { + data = data[ix:ix+mlen]; + r := ref Drawreq.t; + r.data = data; + r.top = get1(data, OPt_top); + r.ids = array [nw] of int; + for (n := 0; n < nw; n++) + r.ids[n] = get4(data, OPt_id + 4*n); + req = r; + } + } + 'v' => # flush + req = ref Drawreq.v; + req.data = data[ix:ix+1]; + 'y' or + 'Y' => # write pixels + mlen = 1+4+(4*4); + if (ix+mlen <= len data) { + imgid := get4(data, ix+OPy_id); + img := images.lookup(imgid); + compd := data[ix] == byte 'Y'; + r := getR(data, ix+OPy_R); + n := imglen(img, data, ix+mlen, r, compd); + if (n == -1) + err ="bad image data"; + mlen += n; + if (mlen+ix <= len data) + req = ref Drawreq.y (data[ix:ix+mlen], imgid, r); + } + * => + err = "bad draw command"; + } + + if (req == nil) + return (nil, err); + return (req, nil); +} + +process(data : array of byte) : (int, string) +{ + offset := 0; + while (offset < len data) { + (req, err) := getreq(data, offset); + if (err != nil) + return (0, err); + offset += len req.data; + n := sys->write(drawfd, req.data, len req.data); + if (n <= 0) + return (n, sys->sprint("[%c] %r", int req.data[0])); + + readn := 0; + sendtoviews := 1; + + # actions that must be done before sending to Viewers + pick r := req { + b => # allocate image + bwidth := bytesperline(r.R, r.chans); + img := ref Image (r.id, 0, r.screenid, r.refresh, r.chans, r.repl, r.R, r.clipR, r.rrggbbaa, nil, r.R.min, bwidth, 0, ""); + images.add(r.id, img); + if (r.screenid != 0) { + scr := screens.lookup(r.screenid); + scr.addwin(r.id); + } + + A => # allocate screen + scr := ref Screen (r.id, r.imageid, r.fillid, nil); + screens.add(scr); + # we never allocate public screens on our Viewers + put1(r.data, OPA_public, 0); + dirty(r.imageid, 0); + + c => # set clipr and repl + img := images.lookup(r.dstid); + img.repl = r.repl; + img.clipR = r.clipR; + + d => # general draw op + dirty(r.dstid, 1); + drawop = Draw->SoverD; + + e => # draw ellipse + dirty(r.dstid, 1); + drawop = Draw->SoverD; + + f => # free image + # help out Viewers, real work is done later + r.img = images.lookup(r.id); + + L => # draw line + dirty(r.dstid, 1); + drawop = Draw->SoverD; + + n => # attach to named image + img := images.findname(r.name); + images.add(r.dstid, img); + + N => # name image + img := images.lookup(r.dstid); + if (r.in) + img.name = r.name; + else + img.name = nil; + + o => # set image origins + img := images.lookup(r.id); + deltax := img.lorigin.x - r.rmin.x; + deltay := img.lorigin.y - r.rmin.y; + w := img.R.max.x - img.R.min.x; + h := img.R.max.y - img.R.min.y; + + img.R = Draw->Rect(r.screenrmin, (r.screenrmin.x + w, r.screenrmin.y + h)); + img.clipR = Draw->Rect((img.clipR.min.x - deltax, img.clipR.min.y - deltay), (img.clipR.max.x - deltax, img.clipR.max.y - deltay)); + img.lorigin = r.rmin; + + O => # set compositing op + drawop = r.op; + + p => # draw polygon + dirty(r.dstid, 1); + drawop = Draw->SoverD; + + r => # read pixels + img := images.lookup(r.id); + bpl := bytesperline(r.R, img.chans); + readn = bpl * (r.R.max.y - r.R.min.y); + + s => # draw text + dirty(r.dstid, 1); + drawop = Draw->SoverD; + + x => # draw text with bg + dirty(r.dstid, 1); + drawop = Draw->SoverD; + + t => # adjust window z order + if (r.ids != nil) { + img := images.lookup(r.ids[0]); + scr := screens.lookup(img.screenid); + scr.setz(r.ids, r.top); + } + + y => # write pixels + dirty(r.id, 1); + } + + if (readn) { + rdata := array [readn] of byte; + if (sys->read(drawfd, rdata, readn) == readn) + readdata = rdata; + } + + for (vs := viewers; vs != nil; vs = tl vs) { + v := hd vs; + v.process(req); + } + + # actions that must only be done after sending to Viewers + pick r := req { + f => # free image + img := images.lookup(r.id); + if (img.screenid != 0) { + scr := screens.lookup(img.screenid); + scr.delwin(img.id); + } + images.del(r.id); + + F => # free screen + scr := screens.lookup(r.id); + for (i := 0; i < len scr.windows; i++) { + img := images.lookup(scr.windows[i]); + img.screenid = 0; + } + screens.del(r.id); + + i => # convert image to font + img := images.lookup(r.fontid); + font := ref Font; + font.ascent = r.ascent; + font.chars = array[r.nchars] of ref Fontchar; + img.font = font; + + l => # load a char into font + img := images.lookup(r.fontid); + font := img.font; + fc := ref Fontchar(r.srcid, r.R, r.P, r.left, r.width); + font.chars[r.index] = fc; + } + } + return (offset, nil); +} + +coordslen(data : array of byte, ix, n : int) : int +{ + start := ix; + dlen := len data; + if (ix == dlen) + return -1; + while (ix < dlen && n) { + n--; + if ((int data[ix++]) & 16r80) + ix += 2; + } + if (n) + return -1; + return ix - start; +} + + +imglen(i : ref Image, data : array of byte, ix : int, r : Draw->Rect, comp : int) : int +{ + bpl := bytesperline(r, i.chans); + if (!comp) + return (r.max.y - r.min.y) * bpl; + y := r.min.y; + lineix := byteaddr(i, r.min); + elineix := lineix+bpl; + start := ix; + eix := len data; + for (;;) { + if (lineix == elineix) { + if (++y == r.max.y) + break; + lineix = byteaddr(i, Point(r.min.x, y)); + elineix = lineix+bpl; + } + if (ix == eix) # buffer too small + return -1; + c := int data[ix++]; + if (c >= 128) { + for (cnt := c-128+1; cnt != 0; --cnt) { + if (ix == eix) # buffer too small + return -1; + if (lineix == elineix) # phase error + return -1; + lineix++; + ix++; + } + } else { + if (ix == eix) # short buffer + return -1; + ix++; + for (cnt := (c >> 2)+3; cnt != 0; --cnt) { + if (lineix == elineix) # phase error + return -1; + lineix++; + } + } + } + return ix-start; +} + +byteaddr(i: ref Image, p: Point): int +{ + x := p.x - i.lorigin.x; + y := p.y - i.lorigin.y; + bits := i.chans.depth(); + if (bits == 0) + # invalid chans + return 0; + return (y*i.bwidth)+(x<<3)/bits; +} + +bytesperline(r: Draw->Rect, chans: Draw->Chans): int +{ + d := chans.depth(); + l, t: int; + + if(r.min.x >= 0){ + l = (r.max.x*d+8-1)/8; + l -= (r.min.x*d)/8; + }else{ # make positive before divide + t = (-r.min.x*d+8-1)/8; + l = t+(r.max.x*d+8-1)/8; + } + return l; +} + +get1(data : array of byte, ix : int) : int +{ + return int data[ix]; +} + +put1(data : array of byte, ix, val : int) +{ + data[ix] = byte val; +} + +get2(data : array of byte, ix : int) : int +{ + return int data[ix] | ((int data[ix+1]) << 8); +} + +put2(data : array of byte, ix, val : int) +{ + data[ix] = byte val; + data[ix+1] = byte (val >> 8); +} + +get4(data : array of byte, ix : int) : int +{ + return int data[ix] | ((int data[ix+1]) << 8) | ((int data[ix+2]) << 16) | ((int data[ix+3]) << 24); +} + +put4(data : array of byte, ix, val : int) +{ + data[ix] = byte val; + data[ix+1] = byte (val >> 8); + data[ix+2] = byte (val >> 16); + data[ix+3] = byte (val >> 24); +} + +getP(data : array of byte, ix : int) : Draw->Point +{ + x := int data[ix] | ((int data[ix+1]) << 8) | ((int data[ix+2]) << 16) | ((int data[ix+3]) << 24); + ix += 4; + y := int data[ix] | ((int data[ix+1]) << 8) | ((int data[ix+2]) << 16) | ((int data[ix+3]) << 24); + return Draw->Point(x, y); +} + +putP(data : array of byte, ix : int, P : Draw->Point) +{ + val := P.x; + data[ix] = byte val; + data[ix+1] = byte (val >> 8); + data[ix+2] = byte (val >> 16); + data[ix+3] = byte (val >> 24); + val = P.y; + ix += 4; + data[ix] = byte val; + data[ix+1] = byte (val >> 8); + data[ix+2] = byte (val >> 16); + data[ix+3] = byte (val >> 24); +} + +getR(data : array of byte, ix : int) : Draw->Rect +{ + minx := int data[ix] | ((int data[ix+1]) << 8) | ((int data[ix+2]) << 16) | ((int data[ix+3]) << 24); + ix += 4; + miny := int data[ix] | ((int data[ix+1]) << 8) | ((int data[ix+2]) << 16) | ((int data[ix+3]) << 24); + ix += 4; + maxx := int data[ix] | ((int data[ix+1]) << 8) | ((int data[ix+2]) << 16) | ((int data[ix+3]) << 24); + ix += 4; + maxy := int data[ix] | ((int data[ix+1]) << 8) | ((int data[ix+2]) << 16) | ((int data[ix+3]) << 24); + + return Draw->Rect(Draw->Point(minx, miny), Draw->Point(maxx, maxy)); +} + +putR(data : array of byte, ix : int , R : Draw->Rect) +{ + val := R.min.x; + data[ix] = byte val; + data[ix+1] = byte (val >> 8); + data[ix+2] = byte (val >> 16); + data[ix+3] = byte (val >> 24); + val = R.min.y; + ix += 4; + data[ix] = byte val; + data[ix+1] = byte (val >> 8); + data[ix+2] = byte (val >> 16); + data[ix+3] = byte (val >> 24); + val = R.max.x; + ix += 4; + data[ix] = byte val; + data[ix+1] = byte (val >> 8); + data[ix+2] = byte (val >> 16); + data[ix+3] = byte (val >> 24); + val = R.max.y; + ix += 4; + data[ix] = byte val; + data[ix+1] = byte (val >> 8); + data[ix+2] = byte (val >> 16); + data[ix+3] = byte (val >> 24); +} + +dirty(id, v : int) +{ + img := images.lookup(id); + img.dirty = v; +} + +Screen.setz(s : self ref Screen, z : array of int, top : int) +{ + old := s.windows; + nw := array [len old] of int; + # use a dummy idmap to ensure uniqueness; + ids := Idmap.new(); + ix := 0; + if (top) { + for (i := 0; i < len z; i++) { + if (ids.lookup(z[i]) == -1) { + ids.add(z[i], 0); + nw[ix++] = z[i]; + } + } + } + for (i := 0; i < len old; i++) { + if (ids.lookup(old[i]) == -1) { + ids.add(old[i], 0); + nw[ix++] = old[i]; + } + } + if (!top) { + for (i = 0; i < len z; i++) { + if (ids.lookup(z[i]) == -1) { + ids.add(z[i], 0); + nw[ix++] = z[i]; + } + } + } + s.windows = nw; +} + +Screen.addwin(s : self ref Screen, wid : int) +{ + nw := array [len s.windows + 1] of int; + nw[0] = wid; + nw[1:] = s.windows; + s.windows = nw; +} + +Screen.delwin(s : self ref Screen, wid : int) +{ + if (len s.windows == 1) { + # assert s.windows[0] == wid + s.windows = nil; + return; + } + nw := array [len s.windows - 1] of int; + ix := 0; + for (i := 0; i < len s.windows; i++) { + if (s.windows[i] == wid) + continue; + nw[ix++] = s.windows[i]; + } + s.windows = nw; +} + +Idmap.new() : ref Idmap +{ + m := ref Idmap; + m.buckets = array[256] of ref Idpair; + return m; +} + +Idmap.add(m : self ref Idmap, key, val : int) +{ + h := key & 16rff; + m.buckets[h] = ref Idpair (key, val, m.buckets[h]); +} + +Idmap.del(m : self ref Idmap, key : int) +{ + h := key &16rff; + prev := m.buckets[h]; + if (prev == nil) + return; + if (prev.key == key) { + m.buckets[h] = m.buckets[h].next; + return; + } + for (idp := prev.next; idp != nil; idp = idp.next) { + if (idp.key == key) + break; + prev = idp; + } + if (idp != nil) + prev.next = idp.next; +} + +Idmap.lookup(m :self ref Idmap, key : int) : int +{ + h := key &16rff; + for (idp := m.buckets[h]; idp != nil; idp = idp.next) { + if (idp.key == key) + return idp.val; + } + return -1; +} + +Imageset.new() : ref Imageset +{ + s := ref Imageset; + s.images = array [32] of ref Image; + s.ixmap = Idmap.new(); + for (i := 0; i < len s.images; i++) + s.freelist = i :: s.freelist; + return s; +} + +Imageset.add(s: self ref Imageset, id: int, img: ref Image) +{ + if (s.freelist == nil) { + n := 2 * len s.images; + ni := array [n] of ref Image; + ni[:] = s.images; + for (i := len s.images; i < n; i++) + s.freelist = i :: s.freelist; + s.images = ni; + } + ix := hd s.freelist; + s.freelist = tl s.freelist; + s.images[ix] = img; + s.ixmap.add(id, ix); + img.refc++; +} + +Imageset.del(s: self ref Imageset, id: int) +{ + ix := s.ixmap.lookup(id); + if (ix == -1) + return; + img := s.images[ix]; + if (img != nil) + img.refc--; + s.images[ix] = nil; + s.freelist = ix :: s.freelist; + s.ixmap.del(id); +} + +Imageset.lookup(s : self ref Imageset, id : int ) : ref Image +{ + ix := s.ixmap.lookup(id); + if (ix == -1) + return nil; + return s.images[ix]; +} + +Imageset.findname(s: self ref Imageset, name: string): ref Image +{ + for (ix := 0; ix < len s.images; ix++) { + img := s.images[ix]; + if (img != nil && img.name == name) + return img; + } + return nil; +} + +Screenset.new() : ref Screenset +{ + s := ref Screenset; + s.screens = array [32] of ref Screen; + s.ixmap = Idmap.new(); + for (i := 0; i < len s.screens; i++) + s.freelist = i :: s.freelist; + return s; +} + +Screenset.add(s : self ref Screenset, scr : ref Screen) +{ + if (s.freelist == nil) { + n := 2 * len s.screens; + ns := array [n] of ref Screen; + ns[:] = s.screens; + for (i := len s.screens; i < n; i++) + s.freelist = i :: s.freelist; + s.screens = ns; + } + ix := hd s.freelist; + s.freelist = tl s.freelist; + s.screens[ix] = scr; + s.ixmap.add(scr.id, ix); +} + +Screenset.del(s : self ref Screenset, id : int) +{ + ix := s.ixmap.lookup(id); + if (ix == -1) + return; + s.screens[ix] = nil; + s.freelist = ix :: s.freelist; + s.ixmap.del(id); +} + +Screenset.lookup(s : self ref Screenset, id : int ) : ref Screen +{ + ix := s.ixmap.lookup(id); + if (ix == -1) + return nil; + return s.screens[ix]; +} + + +Viewer : adt { + imgmap: ref Idmap; + scrmap: ref Idmap; + chanmap: ref Idmap; # maps to 1 for images that require chan conversion + + imageid: int; + screenid: int; + whiteid: int; + hungup: int; + dchans: Draw->Chans; # chans.desc of remote display img + + # temporary image for chan conversion + tmpid: int; + tmpR: Draw->Rect; + + output: chan of (array of byte, chan of string); + + new: fn(fd: ref Sys->FD, pubscr: int, chans: Draw->Chans): string; + process: fn(v: self ref Viewer, req: ref Drawreq); + getimg: fn(v: self ref Viewer, id: int): int; + getscr: fn(v: self ref Viewer, id, win: int): (int, int); + copyimg: fn(v: self ref Viewer, img: ref Image, id: int); + chanconv: fn(v: self ref Viewer, img: ref Image, id: int, r: Rect, ymsg: array of byte); +}; + +vwriter(fd : ref Sys->FD, datac : chan of array of byte, nc : chan of string) +{ + for (;;) { + data := <- datac; + if (data == nil) + return; + n := sys->write(fd, data, len data); + if (n != len data) { +# sys->print("[%c]: %r\n", int data[0]); +# sys->print("[%c] datalen %d got %d error: %r\n", int data[0], len data, n); + nc <-= sys->sprint("%r"); + } else { +# sys->print("[%c]", int data[0]); + nc <-= nil; + } + } +} + +vbmsg : adt { + data : array of byte; + rc : chan of string; + next : cyclic ref vbmsg; +}; + +vbuffer(v : ref Viewer, fd : ref Sys->FD) +{ + ioc := v.output; + datac := chan of array of byte; + errc := chan of string; + spawn vwriter(fd, datac, errc); + fd = nil; + + msghd : ref vbmsg; + msgtl : ref vbmsg; + +Loop: + for (;;) alt { + (data, rc) := <- ioc => + if (data == nil) + break Loop; + if (msgtl != nil) { + if (msgtl != msghd && msgtl.rc == nil && (len msgtl.data + len data) <= Sys->ATOMICIO) { + ndata := array [len msgtl.data + len data] of byte; + ndata[:] = msgtl.data; + ndata[len msgtl.data:] = data; + msgtl.data = ndata; + msgtl.rc = rc; + } else { + msgtl.next = ref vbmsg (data, rc, nil); + msgtl = msgtl.next; + } + } else { + msghd = ref vbmsg (data, rc, nil); + msgtl = msghd; + datac <-= data; + } + err := <- errc => + if (msghd.rc != nil) + msghd.rc <- = err; + msghd = msghd.next; + if (msghd != nil) + datac <-= msghd.data; + else + msgtl = nil; + if (err == Ehungup) { + nhangups++; + v.hungup = 1; + } + } + # shutdown vwriter (may be blocked sending on errc) + for (;;) alt { + <- errc => + ; + datac <- = nil => + return; + } +} + +Viewer.new(fd: ref Sys->FD, pubscr: int, chans: Draw->Chans): string +{ + v := ref Viewer; + v.output = chan of (array of byte, chan of string); + spawn vbuffer(v, fd); + + v.imgmap = Idmap.new(); + v.scrmap = Idmap.new(); + v.chanmap = Idmap.new(); + v.imageid = 0; + v.screenid = pubscr; + v.hungup = 0; + v.dchans = chans; + v.tmpid = 0; + v.tmpR = Rect((0,0), (0,0)); + +#D := array[1+1] of byte; +#D[0] = byte 'D'; +#D[1] = byte 1; +#v.output <-= (D, nil); + + reply := chan of string; + # import remote public screen into our remote draw client + S := array [1+4+4] of byte; + S[0] = byte 'S'; + put4(S, OPS_id, pubscr); + put4(S, OPS_chans, chans.desc); + v.output <-= (S, reply); + err := <- reply; + if (err != nil) { + v.output <-= (nil, nil); + return err; + } + + # create remote window + dispid := ++v.imageid; + b := array [1+4+4+1+4+1+(4*4)+(4*4)+4] of byte; + b[0] = byte 'b'; + put4(b, OPb_id, dispid); + put4(b, OPb_screenid, pubscr); + put1(b, OPb_refresh, 0); + put4(b, OPb_chans, chans.desc); + put1(b, OPb_repl, 0); + putR(b, OPb_R, drawR); + putR(b, OPb_clipR, drawR); + put4(b, OPb_rrggbbaa, Draw->White); + v.output <-= (b, reply); + err = <- reply; + if (err != nil) { + v.output <-= (nil, nil); + return err; + } + + # map local display image id to remote window image id + v.imgmap.add(0, dispid); + if (!drawchans.eq(chans)) + # writepixels on this image must be chan converted + v.chanmap.add(0, 1); + + # create 'white' repl image for use as mask + v.whiteid = ++v.imageid; + put4(b, OPb_id, v.whiteid); + put4(b, OPb_screenid, 0); + put1(b, OPb_refresh, 0); + put4(b, OPb_chans, (Draw->RGBA32).desc); + put1(b, OPb_repl, 1); + putR(b, OPb_R, Rect((0,0), (1,1))); + putR(b, OPb_clipR, Rect((-16r3FFFFFFF, -16r3FFFFFFF), (16r3FFFFFFF, 16r3FFFFFFF))); + put4(b, OPb_rrggbbaa, Draw->White); + v.output <-= (b, reply); + err = <- reply; + if (err != nil) { + v.output <-= (nil, nil); + return err; + } + + img := images.lookup(0); + key := chan of int; + drawlock <- = key; + v.copyimg(img, dispid); + + O := array [1+1] of byte; + O[0] = byte 'O'; + O[1] = byte drawop; + v.output <-= (O, nil); + + flush := array [1] of byte; + flush[0] = byte 'v'; + v.output <- = (flush, nil); + viewers = v :: viewers; + key <-= 1; + return nil; +} + +Viewer.process(v : self ref Viewer, req : ref Drawreq) +{ + data := req.data; + pick r := req { + b => # allocate image + imgid := ++v.imageid; + if (r.screenid != 0) { + (scrid, mapchans) := v.getscr(r.screenid, 0); + put4(data, OPb_screenid, scrid); + if (mapchans) { + put4(data, OPb_chans, v.dchans.desc); + v.chanmap.add(r.id, 1); + } + } + v.imgmap.add(r.id, imgid); + put4(data, OPb_id, imgid); + + A => # allocate screen + imgid := v.getimg(r.imageid); + put4(data, OPA_fillid, v.getimg(r.fillid)); + put4(data, OPA_imageid, imgid); + reply := chan of string; + for (i := 0; i < 25; i++) { + put4(data, OPA_id, ++v.screenid); + v.output <-= (data, reply); + if (<-reply == nil) { + v.scrmap.add(r.id, v.screenid); + return; + } + } + return; + + c => # set clipr and repl + put4(data, OPc_dstid, v.getimg(r.dstid)); + + d => # general draw op + dstid := v.imgmap.lookup(r.dstid); + if (dstid == -1) { + # don't do draw op as getimg() will do a writepixels + v.getimg(r.dstid); + return; + } + put4(data, OPd_maskid, v.getimg(r.maskid)); + put4(data, OPd_srcid, v.getimg(r.srcid)); + put4(data, OPd_dstid, dstid); + + e => # draw ellipse + dstid := v.imgmap.lookup(r.dstid); + if (dstid == -1) { + # don't do draw op as getimg() will do a writepixels + v.getimg(r.dstid); + return; + } + put4(data, OPe_srcid, v.getimg(r.srcid)); + put4(data, OPe_dstid, dstid); + + f => # free image + id := v.imgmap.lookup(r.img.id); + if (id == -1) + # Viewer has never seen this image - ignore + return; + v.imgmap.del(r.id); + # Viewers alias named images - only delete if last reference + if (r.img.refc > 1) + return; + v.chanmap.del(r.img.id); + put4(data, OPf_id, id); + + F => # free screen + id := v.scrmap.lookup(r.id); + scr := screens.lookup(r.id); + # image and fill are free'd separately + #v.imgmap.del(scr.imageid); + #v.imgmap.del(scr.fillid); + if (id == -1) + return; + put4(data, OPF_id, id); + + i => # convert image to font + put4(data, OPi_fontid, v.getimg(r.fontid)); + + l => # load a char into font + put4(data, OPl_srcid, v.getimg(r.srcid)); + put4(data, OPl_fontid, v.getimg(r.fontid)); + + L => # draw line + dstid := v.imgmap.lookup(r.dstid); + if (dstid == -1) { + # don't do draw op as getimg() will do a writepixels + v.getimg(r.dstid); + return; + } + put4(data, OPL_srcid, v.getimg(r.srcid)); + put4(data, OPL_dstid, dstid); + +# n => # attach to named image +# N => # name +# Handled by id remapping to avoid clashes in namespace of remote viewers. +# If it is a name we know then the id is remapped within the images Imageset +# Otherwise, there is nothing we can do other than ignore all ops related to the id. + + o => # set image origins + id := v.imgmap.lookup(r.id); + if (id == -1) + # Viewer has never seen this image - ignore + return; + put4(data, OPo_id, id); + + O => # set next compositing op + ; + + p => # draw polygon + dstid := v.imgmap.lookup(r.dstid); + if (dstid == -1) { + # don't do draw op as getimg() will do a writepixels + v.getimg(r.dstid); + return; + } + put4(data, OPp_srcid, v.getimg(r.srcid)); + put4(data, OPp_dstid, dstid); + + s => # draw text + dstid := v.imgmap.lookup(r.dstid); + if (dstid == -1) { + # don't do draw op as getimg() will do a writepixels + v.getimg(r.dstid); + return; + } + put4(data, OPs_fontid, v.getimg(r.fontid)); + put4(data, OPs_srcid, v.getimg(r.srcid)); + put4(data, OPs_dstid, dstid); + + x => # draw text with bg + dstid := v.imgmap.lookup(r.dstid); + if (dstid == -1) { + # don't do draw op as getimg() will do a writepixels + v.getimg(r.dstid); + return; + } + put4(data, OPx_fontid, v.getimg(r.fontid)); + put4(data, OPx_srcid, v.getimg(r.srcid)); + put4(data, OPx_bgid, v.getimg(r.bgid)); + put4(data, OPx_dstid, dstid); + + t => # adjust window z order + for (i := 0; i < len r.ids; i++) + put4(data, OPt_id + 4*i, v.getimg(r.ids[i])); + + v => # flush updates to display + ; + + y => # write pixels + id := v.imgmap.lookup(r.id); + if (id == -1) { + # don't do draw op as getimg() will do a writepixels + v.getimg(r.id); + return; + } + if (!drawchans.eq(v.dchans) && v.chanmap.lookup(r.id) != -1) { + # chans clash + img := images.lookup(r.id); + # copy data as other Viewers may alter contents + copy := (array [len data] of byte)[:] = data; + v.chanconv(img, id, r.R, copy); + return; + } + put4(data, OPy_id, id); + + * => + return; + } + # send out a copy of the data as other Viewers may alter contents + copy := array [len data] of byte; + copy[:] = data; + v.output <-= (copy, nil); +} + +Viewer.getimg(v: self ref Viewer, localid: int) : int +{ + remid := v.imgmap.lookup(localid); + if (remid != -1) + return remid; + + img := images.lookup(localid); + if (img.id != localid) { + # attached via name, see if we have the aliased image + remid = v.imgmap.lookup(img.id); + if (remid != -1) { + # we have it, add mapping to save us this trouble next time + v.imgmap.add(localid, remid); + return remid; + } + } + # is the image a window? + scrid := 0; + mapchans := 0; + if (img.screenid != 0) + (scrid, mapchans) = v.getscr(img.screenid, img.id); + + vid := ++v.imageid; + # create the image + # note: clipr for image creation has to be based on screen co-ords + clipR := img.clipR.subpt(img.lorigin); + clipR = clipR.addpt(img.R.min); + b := array [1+4+4+1+4+1+(4*4)+(4*4)+4] of byte; + b[0] = byte 'b'; + put4(b, OPb_id, vid); + put4(b, OPb_screenid, scrid); + put1(b, OPb_refresh, 0); + if (mapchans) + put4(b, OPb_chans, v.dchans.desc); + else + put4(b, OPb_chans, img.chans.desc); + put1(b, OPb_repl, img.repl); + putR(b, OPb_R, img.R); + putR(b, OPb_clipR, clipR); + put4(b, OPb_rrggbbaa, img.rrggbbaa); + v.output <-= (b, nil); + + v.imgmap.add(img.id, vid); + if (mapchans) + v.chanmap.add(img.id, 1); + + # set the origin + if (img.lorigin.x != img.R.min.x || img.lorigin.y != img.R.min.y) { + o := array [1+4+(2*4)+(2*4)] of byte; + o[0] = byte 'o'; + put4(o, OPo_id, vid); + putP(o, OPo_rmin, img.lorigin); + putP(o, OPo_screenrmin, img.R.min); + v.output <-= (o, nil); + } + + # is the image a font? + if (img.font != nil) { + f := img.font; + i := array [1+4+4+1] of byte; + i[0] = byte 'i'; + put4(i, OPi_fontid, vid); + put4(i, OPi_nchars, len f.chars); + put1(i, OPi_ascent, f.ascent); + v.output <-= (i, nil); + + for (index := 0; index < len f.chars; index++) { + ch := f.chars[index]; + if (ch == nil) + continue; + l := array [1+4+4+2+(4*4)+(2*4)+1+1] of byte; + l[0] = byte 'l'; + put4(l, OPl_fontid, vid); + put4(l, OPl_srcid, v.getimg(ch.srcid)); + put2(l, OPl_index, index); + putR(l, OPl_R, ch.R); + putP(l, OPl_P, ch.P); + put1(l, OPl_left, ch.left); + put1(l, OPl_width, ch.width); + v.output <-= (l, nil); + } + } + + # if 'dirty' then writepixels + if (img.dirty) + v.copyimg(img, vid); + + return vid; +} + +Viewer.copyimg(v : self ref Viewer, img : ref Image, id : int) +{ + dx := img.R.max.x - img.R.min.x; + dy := img.R.max.y - img.R.min.y; + srcR := Rect (img.lorigin, (img.lorigin.x + dx, img.lorigin.y + dy)); + bpl := bytesperline(srcR, img.chans); + rlen : con 1+4+(4*4); + ystep := (Sys->ATOMICIO - rlen)/ bpl; + minx := srcR.min.x; + maxx := srcR.max.x; + maxy := srcR.max.y; + + chanconv := 0; + if (!drawchans.eq(v.dchans) && v.chanmap.lookup(img.id) != -1) + chanconv = 1; + + for (y := img.lorigin.y; y < maxy; y += ystep) { + if (y + ystep > maxy) + ystep = (maxy - y); + R := Draw->Rect((minx, y), (maxx, y+ystep)); + r := array [rlen] of byte; + r[0] = byte 'r'; + put4(r, OPr_id, img.id); + putR(r, OPr_R, R); + if (sys->write(drawfd, r, len r) != len r) + break; + + nb := bpl * ystep; + ymsg := array [1+4+(4*4)+nb] of byte; + ymsg[0] = byte 'y'; +# put4(ymsg, OPy_id, id); + putR(ymsg, OPy_R, R); + n := sys->read(drawfd, ymsg[OPy_data:], nb); + if (n != nb) + break; + if (chanconv) + v.chanconv(img, id, R, ymsg); + else { + put4(ymsg, OPy_id, id); + v.output <-= (ymsg, nil); + } + } +} + +Viewer.chanconv(v: self ref Viewer, img: ref Image, id: int, r: Rect, ymsg: array of byte) +{ + # check origin matches and enough space in conversion image + if (!(img.lorigin.eq(v.tmpR.min) && r.inrect(v.tmpR))) { + # create new tmp image + if (v.tmpid != 0) { + f := array [1+4] of byte; + f[0] = byte 'f'; + put4(f, OPf_id, v.tmpid); + v.output <-= (f, nil); + } + v.tmpR = Rect((0,0), (img.R.dx(), img.R.dy())).addpt(img.lorigin); + v.tmpid = ++v.imageid; + b := array [1+4+4+1+4+1+(4*4)+(4*4)+4] of byte; + b[0] = byte 'b'; + put4(b, OPb_id, v.tmpid); + put4(b, OPb_screenid, 0); + put1(b, OPb_refresh, 0); + put4(b, OPb_chans, drawchans.desc); + put1(b, OPb_repl, 0); + putR(b, OPb_R, v.tmpR); + putR(b, OPb_clipR, v.tmpR); + put4(b, OPb_rrggbbaa, Draw->Nofill); + v.output <-= (b, nil); + } + # writepixels to conversion image + put4(ymsg, OPy_id, v.tmpid); + v.output <-= (ymsg, nil); + + # ensure that drawop is Draw->S + if (drawop != Draw->S) { + O := array [1+1] of byte; + O[0] = byte 'O'; + put1(O, OPO_op, Draw->S); + v.output <-= (O, nil); + } + # blit across to real target + d := array [1+4+4+4+(4*4)+(2*4)+(2*4)] of byte; + d[0] = byte 'd'; + put4(d, OPd_dstid, id); + put4(d, OPd_srcid, v.tmpid); + put4(d, OPd_maskid, v.whiteid); + putR(d, OPd_R, r); + putP(d, OPd_P0, r.min); + putP(d, OPd_P1, r.min); + v.output <-= (d, nil); + + # restore drawop if necessary + if (drawop != Draw->S) { + O := array [1+1] of byte; + O[0] = byte 'O'; + put1(O, OPO_op, drawop); + v.output <-= (O, nil); + } +} + +# returns (rid, map) +# rid == remote screen id +# map indicates that chan mapping is required for windows on this screen + +Viewer.getscr(v : self ref Viewer, localid, winid : int) : (int, int) +{ + remid := v.scrmap.lookup(localid); + if (remid != -1) { + if (drawchans.eq(v.dchans)) + return (remid, 0); + scr := screens.lookup(localid); + if (v.chanmap.lookup(scr.imageid) == -1) + return (remid, 0); + return (remid, 1); + } + + scr := screens.lookup(localid); + imgid := v.getimg(scr.imageid); + fillid := v.getimg(scr.fillid); + A := array [1+4+4+4+1] of byte; + A[0] = byte 'A'; + put4(A, OPA_imageid, imgid); + put4(A, OPA_fillid, fillid); + put1(A, OPA_public, 0); + + reply := chan of string; + for (i := 0; i < 25; i++) { + put4(A, OPA_id, ++v.screenid); + v.output <-= (A, reply); + if (<-reply != nil) + continue; + v.scrmap.add(localid, v.screenid); + break; + } + # if i == 25 then we have a problem + # ... + if (i == 25) { +# sys->print("failed to create remote screen\n"); + return (0, 0); + } + + # pre-construct the windows on this screen + for (ix := len scr.windows -1; ix >=0; ix--) + if (scr.windows[ix] != winid) + v.getimg(scr.windows[ix]); + + if (drawchans.eq(v.dchans)) + return (v.screenid, 0); + if (v.chanmap.lookup(scr.imageid) == -1) + return (v.screenid, 0); + return (v.screenid, 1); +} diff --git a/appl/wm/drawmux/drawmux.m b/appl/wm/drawmux/drawmux.m new file mode 100644 index 00000000..cf641207 --- /dev/null +++ b/appl/wm/drawmux/drawmux.m @@ -0,0 +1,6 @@ +Drawmux: module { + PATH: con "/dis/lib/drawmux.dis"; + + init: fn(): (string, ref Draw->Display); + newviewer: fn(fd: ref Sys->FD); +}; diff --git a/appl/wm/drawmux/drawoffs.m b/appl/wm/drawmux/drawoffs.m new file mode 100644 index 00000000..ce5a28a2 --- /dev/null +++ b/appl/wm/drawmux/drawoffs.m @@ -0,0 +1,185 @@ +# allocate image (old) +#OPa_id : con 1; +#OPa_screenid : con 5; +#OPa_refresh : con 9; +#OPa_ldepth : con 10; +#OPa_repl : con 12; +#OPa_R : con 13; +#OPa_clipR : con 29; +#OPa_value : con 45; + +# allocate image (new) +OPb_id : con 1; +OPb_screenid : con 5; +OPb_refresh : con 9; +OPb_chans : con 10; +OPb_repl : con 14; +OPb_R : con 15; +OPb_clipR : con 31; +OPb_rrggbbaa : con 47; + +# allocate screen +OPA_id : con 1; +OPA_imageid : con 5; +OPA_fillid : con 9; +OPA_public : con 13; + +# set repl & clipr +OPc_dstid : con 1; +OPc_repl : con 5; +OPc_clipR : con 6; + +# set cursor image and hotspot +#OPC_id : con 1; +#OPC_hotspot : con 5; + +# the primitive draw op +OPd_dstid : con 1; +OPd_srcid : con 5; +OPd_maskid : con 9; +OPd_R : con 13; +OPd_P0 : con 29; +OPd_P1 : con 37; + +# enable debug messages +OPD_val : con 1; + +# ellipse +OPe_dstid : con 1; +OPe_srcid : con 5; +OPe_center : con 9; +OPe_a : con 17; +OPe_b : con 21; +OPe_thick : con 25; +OPe_sp : con 29; +OPe_alpha : con 37; +OPe_phi : con 41; + +# filled ellipse +OPE_dstid : con 1; +OPE_srcid : con 5; +OPE_center : con 9; +OPE_a : con 17; +OPE_b : con 21; +OPE_thick : con 25; +OPE_sp : con 29; +OPE_alpha : con 37; +OPE_phi : con 41; + +# free image +OPf_id : con 1; + +# free screen +OPF_id : con 1; + +# init font +OPi_fontid : con 1; +OPi_nchars : con 5; +OPi_ascent : con 9; + +# load font char +OPl_fontid : con 1; +OPl_srcid : con 5; +OPl_index : con 9; +OPl_R : con 11; +OPl_P : con 27; +OPl_left : con 35; +OPl_width : con 36; + +# line +OPL_dstid : con 1; +OPL_P0 : con 5; +OPL_P1 : con 13; +OPL_end0 : con 21; +OPL_end1 : con 25; +OPL_radius : con 29; +OPL_srcid : con 33; +OPL_sp : con 37; + +# attach to named image +OPn_dstid : con 1; +OPn_j : con 5; +OPn_name : con 6; + +# name image +OPN_dstid : con 1; +OPN_in : con 5; +OPN_j : con 6; +OPN_name : con 7; + +# set window origins +OPo_id : con 1; +OPo_rmin : con 5; +OPo_screenrmin : con 13; + +# set next compositing operator +OPO_op : con 1; + +# polygon +OPp_dstid : con 1; +OPp_n : con 5; +OPp_end0 : con 7; +OPp_end1 : con 11; +OPp_radius : con 15; +OPp_srcid : con 19; +OPp_sp : con 23; +OPp_P0 : con 31; +OPp_dp : con 39; + +# filled polygon +OPP_dstid : con 1; +OPP_n : con 5; +OPP_wind : con 7; +OPP_ignore : con 11; +OPP_srcid : con 19; +OPP_sp : con 23; +OPP_P0 : con 31; +OPP_dp : con 39; + +# read +OPr_id : con 1; +OPr_R : con 5; + +# string +OPs_dstid : con 1; +OPs_srcid : con 5; +OPs_fontid : con 9; +OPs_P : con 13; +OPs_clipR : con 21; +OPs_sp : con 37; +OPs_ni : con 45; +OPs_index : con 47; + +# stringbg +OPx_dstid : con 1; +OPx_srcid : con 5; +OPx_fontid : con 9; +OPx_P : con 13; +OPx_clipR : con 21; +OPx_sp : con 37; +OPx_ni : con 45; +OPx_bgid : con 47; +OPx_bgpt : con 51; +OPx_index : con 59; + +# attach to public screen +OPS_id : con 1; +OPS_chans : con 5; + +# visible +# top or bottom windows +OPt_top : con 1; +OPt_nw : con 2; +OPt_id : con 4; + +#OPv no fields + +# write +OPy_id : con 1; +OPy_R : con 5; +OPy_data : con 21; + +# write compressed +OPY_id : con 1; +OPY_R : con 5; +OPY_data : con 21; diff --git a/appl/wm/drawmux/mkfile b/appl/wm/drawmux/mkfile new file mode 100644 index 00000000..f4c8d7ec --- /dev/null +++ b/appl/wm/drawmux/mkfile @@ -0,0 +1,37 @@ +<../../../mkconfig + +TARG=\ + dmview.dis\ + dmwm.dis\ + +LIBTARG=\ + drawmux.dis\ + +MODULES=\ + drawmux.m\ + drawoffs.m\ + +SYSMODULES=\ + arg.m\ + draw.m\ + sh.m\ + sys.m\ + tk.m\ + wmlib.m\ + +DISBIN=$ROOT/dis/wm +DISLIB=$ROOT/dis/lib + +all:V: $TARG $LIBTARG + +install:V: $DISBIN/dmview.dis $DISBIN/dmwm.dis $DISLIB/drawmux.dis + +<$ROOT/mkfiles/mkdis + +nuke:V: nuke-lib + +nuke-lib:V: + cd $DISLIB; rm -f $LIBTARG + +$DISLIB/%.dis: %.dis + rm -f $DISLIB/$stem.dis && cp $stem.dis $DISLIB/$stem.dis diff --git a/appl/wm/edit.b b/appl/wm/edit.b new file mode 100644 index 00000000..dd6e8229 --- /dev/null +++ b/appl/wm/edit.b @@ -0,0 +1,730 @@ +# +# Copyright © 1996-1999 Lucent Technologies Inc. All rights reserved. +# Modified version of edit +# D.B.Knudsen +# Revisions Copyright © 2000-2002 Vita Nuova Holdings Limited. All rights reserved. +# +implement WmEdit; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + Rect, Screen: import draw; + +include "tk.m"; + tk: Tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "dialog.m"; + dialog: Dialog; + +include "selectfile.m"; + selectfile: Selectfile; + +WmEdit: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +ErrIco: con "error -fg red"; + +ed: ref Tk->Toplevel; +dirty := 0; + +BLUE : con "#0000ff"; +GREEN : con "#008800"; + +SEARCH, +SEARCHFOR, +REPLACE, +REPLACEWITH, +REPLACEALL, +NOSEE : con iota; + +ed_config := array[] of { + "frame .m -relief raised -bd 2", + "frame .b", + "menubutton .m.file -text File -menu .m.file.menu", + "menubutton .m.edit -text Edit -menu .m.edit.menu", + "menubutton .m.search -text Search -menu .m.search.menu", + "menubutton .m.options -text Options -menu .m.options.menu", +# "label .m.filename", + "pack .m.file .m.edit .m.search .m.options -side left", +# "pack .m.filename -padx 10 -side left", + "menu .m.file.menu", + ".m.file.menu add command -label New -command {send c new}", + ".m.file.menu add command -label Open... -command {send c open}", + ".m.file.menu add separator", + ".m.file.menu add command -label Save -command {send c save}", + ".m.file.menu add command -label {Save As...} -command {send c saveas}", + ".m.file.menu add separator", + ".m.file.menu add command -label {Exit} -command {send c exit}", + "menu .m.edit.menu", + ".m.edit.menu add command -label Cut -command {send c cut}", + ".m.edit.menu add command -label Copy -command {send c copy}", + ".m.edit.menu add command -label Paste -command {send c paste}", + "menu .m.search.menu", + ".m.search.menu add command -label {Find ...} " + + "-command {send c searchf}", + ".m.search.menu add command -label {Replace with...} " + + "-command {send c replacew}", + ".m.search.menu add command -label {Find Again} -command {send c search}", + ".m.search.menu add command -label {Find and Replace} " + + "-command {send c replace}", + ".m.search.menu add command -label {Find and Replace All} " + + "-command {send c replaceall}", + "menu .m.options.menu", + ".m.options.menu add checkbutton -text Limbo -command {send c limbo}", + ".m.options.menu add command -label Indent -command {send c indent}", + "text .b.t -yscrollcommand {.b.s set} -bg white", + "bind .b.t <Button-2> {.m.edit.menu post %X %Y}", + "bind .b.t <Key> +{send c dirtied {%A}}", + "bind .b.t <ButtonRelease-1> +{send c reindent}", + "scrollbar .b.s -command {.b.t yview}", + "pack .m -fill x", + "pack .b.s -fill y -side left", + "pack .b.t -fill both -expand 1", + "pack .b -fill both -expand 1", + "focus .b.t", + "pack propagate . 0", + ".b.t tag configure keyword -fg " + BLUE, + ".b.t tag configure comment -fg " + GREEN, + "update", +}; + +context : ref Draw->Context; +curfile := "(New)"; +snarf := ""; +searchfor := ""; +replacewith := ""; +path := "."; + +init(ctxt: ref Draw->Context, argv: list of string) +{ + wmctl: chan of string; + + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + selectfile = load Selectfile Selectfile->PATH; + dialog = load Dialog Dialog->PATH; + + sys->pctl(Sys->NEWPGRP, nil); + tkclient->init(); + selectfile->init(); + dialog->init(); + + context = ctxt; + + (ed, wmctl) = tkclient->toplevel(context, "", "Edit", Tkclient->Appl); + + argv = tl argv; + + c := chan of string; + tk->namechan(ed, c, "c"); + for (i := 0; i < len ed_config; i++) + cmd(ed, ed_config[i]); + + if (argv != nil) { + e := loadtfile(hd argv); + if(e != nil) + dialog->prompt(ctxt, ed.image, ErrIco, "Open file", e, 0, "Ok"::nil); + } + + tkclient->settitle(ed, "Edit " + curfile); + tkclient->onscreen(ed, nil); + tkclient->startinput(ed, "ptr" :: "kbd" :: nil); + cmd(ed, "update"); + + e := cmd(ed, "variable lasterror"); + if(e != "") { + sys->print("edit error: %s\n", e); + return; + } + + cmdloop: for(;;) { + alt { + key := <-ed.ctxt.kbd => + tk->keyboard(ed, key); + m := <-ed.ctxt.ptr => + tk->pointer(ed, *m); + s := <-ed.ctxt.ctl or + s = <-ed.wreq or + s = <-wmctl => + if(s == "exit") { + if (check_dirty()) + break cmdloop; + else + break; + } + task_title: string; + if (s == "task") { + if (curfile == "(New)") + task_title = tkclient->settitle(ed, "Edit"); + else + task_title = tkclient->settitle(ed, "Edit " + curfile); + cmd(ed, "update"); + } + tkclient->wmctl(ed, s); + if (s == "task") + tkclient->settitle(ed, task_title); + s := <-c => + if ( len s > 7 && s[:7] == "dirtied" ) { + set_dirty(); do_limbo_check(s); + } + else + case s { + "exit" => if ( check_dirty() ){ set_clean(); break cmdloop; } + "dirtied" => set_dirty(); do_limbo_check(s); + "new" => if ( check_dirty()) {set_clean(); do_new();} + "open" => if ( check_dirty() && do_open()) set_clean(); + "save" => do_save(0); + "saveas" => do_save(1); + "cut" => do_snarf(1); set_dirty(); + "copy" => do_snarf(0); + "paste" => do_paste(); set_dirty(); + "search" => do_search(SEARCH); + "searchf" => do_search(SEARCHFOR); + "replace" => do_replace(REPLACE); + "replacew" => do_replace(REPLACEWITH); + "replaceall" => do_replaceall(); + "limbo" => do_limbo(); + "indent" => do_indent(); + "reindent" => re_indent(); + } + cmd(ed, "focus .b.t"); + } + cmd(ed, "update"); + e = cmd(ed, "variable lasterror"); + if(e != "") { + sys->print("edit error: %s\n", e); + break cmdloop; + } + } +} + +check_dirty() : int +{ + if ( dirty == 0 ) + return 1; + if (dialog->prompt(context, ed.image, ErrIco, "Confirm", + "File was changed.\nDiscard changes?", + 0, "Yes" :: "No" :: nil) == 0 ) { + return 1; + } + return 0; +} + +set_dirty() +{ + if(!dirty){ + dirty = 1; + tkclient->settitle(ed, "Edit " + curfile + " (dirty)"); + cmd(ed, "update"); + } +# We want to just remove the binding, but Inferno's tk does not +# recognize the - in front of the command. To make it do so would +# require changes to utils.c and ebind.c in /tk +# cmd(ed, "bind .b.t <Key> -{send c dirtied}"); +} + +set_clean() +{ + if(dirty){ + dirty = 0; + tkclient->settitle(ed, "Edit " + curfile); + cmd(ed, "update"); + #cmd(ed, "bind .b.t <Key> +{send c dirtied}"); + } +} + +BLOCK, TEMP : con iota; +is_limbo := 0; # initially not limbo +this_word := ""; +last_keyword := ""; +in_comment := 0; +first_char := 1; +indent : list of int; +last_kw_is_block := 0; +tab := "\t"; +tabs := array[] of { + "", "\t", "\t\t", "\t\t\t", "\t\t\t\t", "\t\t\t\t\t", + "\t\t\t\t\t\t", "\t\t\t\t\t\t\t", "\t\t\t\t\t\t\t\t" +}; + +keywords := array[] of { + "adt", "alt", "array", "big", "break", + "byte", "case", "chan", "con", "continue", + "cyclic", "do", "else", "exit", "fn", + "for", "hd", "if", "implement", "import", + "include", "int", "len", "list", "load", + "module", "nil", "of", "or", "pick", + "real", "ref", "return", "self", "spawn", + "string", "tagof", "tl", "to", "type", + "while" +}; +block_keyword := (big 1 << 40 ) | big (1 << 17) | big (1 << 15) | + big (1 << 12) | big (1 << 11); + +do_limbo() +{ + is_limbo = !is_limbo; + if ( is_limbo ) + mark_keyw_comm(); + else { + cmd(ed, ".b.t tag remove comment 1.0 end"); + cmd(ed, ".b.t tag remove keyword 1.0 end"); + } +} + +do_limbo_check(s : string) +{ + if ( ! is_limbo ) + return; + if ( len s < 11 ) + return; +# +# Maybe we should actually remember where the insert point is. +# In general we can get it via .b.t index insert, but for most +# characters, we could maintain the position with simple arithmetic. +# +# Also, we need to insert code in cut and paste operations to keep +# track of various things when in limbo mode. Also need to catch +# text deletions via typeover of selection. +# + char := s[9]; + if ( char == '\\' && len s > 10 ) + char = s[10]; + case char { + ' ' or '\t' => + if ( ! in_comment ) + look_keyword(this_word); + this_word = "" ; + '\n' => + if ( in_comment ) { + # terminate current tag + cmd(ed, ".b.t tag remove comment insert-1chars"); + in_comment = 0; + } + else + look_keyword(this_word); + this_word = "" ; + if ( last_kw_is_block ) + indent = TEMP :: indent; + else while ( indent != nil && hd indent == TEMP ) + indent = tl indent; + last_kw_is_block = 0; + add_indent(); + first_char = 1; + return; + '{' => + indent = BLOCK :: indent; + last_kw_is_block = 0; + '}' => + if ( indent != nil ) + indent = tl indent; + last_kw_is_block = 0; + # If the line is just indentation plus '}', rewrite it + # to have one less indent. + if ( first_char ) { + current := int cmd(ed, ".b.t index insert"); + cmd(ed, ".b.t delete " + + string current + ".0 insert"); + add_indent(); + cmd(ed, ".b.t insert insert '}"); + } +# ';' => +# last_kw_is_block = 0; +# '\b' => # By the time we see this, the character has +# # already been wiped out, probably. +# # To know what it was we'd need a lastchar, +# # reset for each mouse button up and \b +# '\u007f' => # Here, we have to know what used to be ahead of the +# # insert point. + '#' => + # if ( ! in_quote ) { + # cmd(ed, ".b.t tag add comment insert-1chars"); + in_comment = 1; + # } + 'A' to 'Z' or 'a' to 'z' or '0' to '9' or '_' => + if ( ! in_comment ) + this_word[len this_word] = char; + * => + if ( ! in_comment ) + look_keyword(this_word); + this_word = ""; + } + if ( in_comment ) + cmd(ed, ".b.t tag add comment insert-1chars"); + first_char = 0; +} + +look_keyword(word : string) +{ + # compare this_word to all keywords + if ( is_keyword(word) ) { + cmd(ed, ".b.t tag add keyword insert-" + + string (len this_word + 1) + "chars insert-1chars"); + } +} + +is_keyword(word : string) : int +{ + l := len keywords; + for ( i := 0; i < l; i++ ) + if ( word == keywords[i] ) { + if ( i != 26 ) # don't set for 'nil' + last_kw_is_block = int (block_keyword >> i) & 1; + return 1; + } + return 0; +} + +do_new() +{ + cmd(ed, ".b.t delete 1.0 end"); + curfile = "(New)"; + tkclient->settitle(ed, "Edit " + curfile); +} + +do_open(): int +{ + for(;;) { + fname := selectfile->filename(context, ed.image, "", nil, path); + if(fname == "") + break; + cmd(ed, ".b.t delete 1.0 end"); + e := loadtfile(fname); + if(e == nil) { + basepath(fname); + return 1; + } + + options := list of { + "Cancel", + "Open another file" + }; + + if(dialog->prompt(context, ed.image, ErrIco, "Open file", e, 0, options) == 0) + break; + } + return 0; +} + +basepath(file: string) +{ + for(i := len file-1; i >= 0; i--) + if(file[i] == '/') { + path = file[0:i]; + break; + } +} + +do_save(prompt: int) +{ + fname := curfile; + + contents := tk->cmd(ed, ".b.t get 1.0 end"); + for(;;) { + if(prompt || curfile == "(New)") { + fname = dialog->getstring(context, ed.image, "File"); + if ( len fname > 0 && fname[0] != '/' && path != "" ) + fname = path + "/" + fname; + } + + if(savetfile(fname, contents)) { + set_clean(); + break; + } + + options := list of { + "Cancel", + "Try another file" + }; + + msg := sys->sprint("Trying to write file \"%s\"\n%r", fname); + if(dialog->prompt(context, ed.image, ErrIco, "Save file", msg, 0, options) == 0) + break; + + prompt = 1; + } +} + +do_snarf(del: int) +{ + range := cmd(ed, ".b.t tag nextrange sel 1.0"); + if(range == "" || (len range > 0 && range[0] == '!')) + return; + snarf = tk->cmd(ed, ".b.t get " + range); + if(del) + cmd(ed, ".b.t delete " + range); + tkclient->snarfput(snarf); +} + +do_paste() +{ + snarf = tkclient->snarfget(); + if(snarf == "") + return; + cmd(ed, ".b.t insert insert '" + snarf); +} + +do_search(prompt: int) : int +{ + if(prompt == SEARCHFOR) + searchfor = dialog->getstring(context, ed.image, "Search For"); + if(searchfor == "") + return 0; + cmd(ed, "cursor -bitmap cursor.wait"); + ix := cmd(ed, ".b.t search -- " + tk->quote(searchfor) + " insert+1c"); + if(ix != "" && len ix > 1 && ix[0] != '!') { + cmd(ed, ".b.t tag remove sel 0.0 end"); + cmd(ed, ".b.t mark set anchor " + ix); + cmd(ed, ".b.t mark set insert " + ix); + cmd(ed, ".b.t tag add sel " + ix + " " + ix + "+" + + string(len searchfor) + "c"); + if ( prompt != NOSEE ) + cmd(ed, ".b.t see " + ix); + cmd(ed, "cursor -default"); + return 1; + } + cmd(ed, "cursor -default"); + return 0; +} + +do_replace(prompt : int) +{ + range := ""; + if ( prompt == REPLACEWITH ) { + replacewith = dialog->getstring(context, ed.image, "Replacement String"); + + range = cmd(ed, ".b.t tag nextrange sel 1.0"); + if(range == "" || (len range > 0 && range[0] == '!')) + return; # nothing currently selected + } + if ( range != "" ) { # there's something selected + cmd(ed, ".b.t mark set insert sel.first"); + } + else { # have to find a string + if ( searchfor == "" ) { # no search string! + if ( do_search(SEARCHFOR) == 0 ) + return; + } + else if ( do_search(SEARCH) == 0 ) + return; + } + cmd(ed, ".b.t delete sel.first sel.last"); + cmd(ed, ".b.t insert insert " + tk->quote(replacewith)); +} + +do_replaceall() +{ + cur := cmd(ed, ".b.t index insert"); + if ( cur == "" || cur[0] == '!' ) + return; + dirt := 0; + if ( searchfor == "" ) # no search string + searchfor = dialog->getstring(context, ed.image, "Search For"); + if ( searchfor == "" ) # still no search string + return; + srch := tk->quote(searchfor); + repl := tk->quote(replacewith); + for ( ix := "1.0"; len ix > 0 && ix[0] != '!'; ) { + ix = cmd(ed, ".b.t search -- " + srch + " " + ix + " end"); + if ( ix == "" || len ix <= 1 || ix[0] == '!') + break; + cmd(ed, ".b.t delete " + ix + " " + ix + "+" + + string(len searchfor) + "c"); + if ( replacewith != "" ) { + cmd(ed, ".b.t insert " + ix + " " + repl); + ix = cmd(ed, ".b.t index " + ix + "+" + + string(len replacewith) + "c"); + } + dirt++; + } + cmd(ed, ".b.t mark set insert " + cur); + if ( dirt > 0 ) + set_dirty(); +} + + +loadtfile(path: string): string +{ + if ( path != nil && path[0] == '/' ) + basepath(path); + fd := sys->open(path, sys->OREAD); + if(fd == nil) + return "Can't open "+path+", the error was:\n"+sys->sprint("%r"); + (ok, d) := sys->fstat(fd); + if(ok < 0) + return "Can't stat "+path+", the error was:\n"+sys->sprint("%r"); + if(d.mode & Sys->DMDIR) + return path+" is a directory"; + + cmd(ed, "cursor -bitmap cursor.wait"); + BLEN: con 8192; + buf := array[BLEN+Sys->UTFmax] of byte; + inset := 0; + for(;;) { + n := sys->read(fd, buf[inset:], BLEN); + if(n <= 0) + break; + n += inset; + nutf := sys->utfbytes(buf, n); + s := string buf[0:nutf]; + # move any partial rune to beginning of buffer + inset = n-nutf; + buf[0:] = buf[nutf:n]; + cmd(ed, ".b.t insert end '" + s); + } + if ( is_limbo ) + mark_keyw_comm(); + curfile = path; + tkclient->settitle(ed, "Edit " + curfile); + cmd(ed, "cursor -default"); + cmd(ed, "update"); + return ""; +} + +savetfile(path: string, contents: string): int +{ + buf := array of byte contents; + n := len buf; + + fd := sys->create(path, sys->OWRITE, 8r664); + if(fd == nil) + return 0; + i := sys->write(fd, buf, n); + if(i != n) { + sys->print("savetfile only wrote %d of %d: %r\n", i, n); + return 0; + } + curfile = path; +# cmd(ed, ".m.filename configure -text '" + curfile); + tkclient->settitle(ed, "Edit " + curfile); + + return 1; +} + +mark_keyw_comm() +{ + quote := 0; + start : int; + notkey := 0; + word : string; + + last := int cmd(ed, ".b.t index end"); + for ( i := 1; i <= last; i++ ) { + quote = 0; + word = ""; + line := tk->cmd(ed, ".b.t get " + string i + ".0 " + + string (i+1) + ".0"); + l := len line; +ll : for ( j := 0; j < l; j++ ) { + c := line[j]; + if ( quote && (c = line[j]) != quote ) + continue; + case c { + '#' => + cmd(ed, sys->sprint(".b.t tag add comment" + + " %d.%d %d.%d", i, j, i, l)); + break ll; + '\'' or '\"' => + if ( j != 0 && line[j-1] == '\\' ) + break; + if ( c == quote ) + quote = 0; + else + quote = line[j]; + word = ""; + 'a' to 'z' => + if ( word == "" ) + start = j; + word[len word] = c; + 'A' to 'Z' or '_' => + notkey = 1; + continue; + * => + if ( ! notkey && is_keyword(word) ) + cmd(ed, ".b.t tag add keyword " + + sys->sprint("%d.%d %d.%d", + i, start, i, j)); + word = ""; + notkey = 0; + } + } + } +} + +do_indent() +{ + for ( ; ; ) { + tab = dialog->getstring(context, ed.image, "single indent"); + break; + } + for ( i := 1; i <= 8; i++ ) { + s := ""; + for ( j := i; j > 0; j-- ) + s += tab; + tabs[i] = collapse(s); + } +} + +collapse(s : string) : string +{ + if ( len s >= 8 && s[0:8] == " " ) + return "\t" + collapse(s[8:]); + return s; +} + +add_indent() +{ + for ( i := len indent; i >= 8; i -= 8 ) + cmd(ed, ".b.t insert insert '" + tabs[8]); + cmd(ed, ".b.t insert insert '" + tabs[i]); +} +# +# We should also look at the previous line, maybe. +# And the line after. That may be too much. +# +# This is also the logical place to check if we are in a keyword, +# reinitialize this_word (which presents problems if we are in the +# middle of a word, etc.) Also check if we are in a comment or not. +# +re_indent() +{ + pos := cmd(ed, ".b.t index insert"); + (n, lc) := sys->tokenize(pos, "."); + if ( n < 2 ) + return; + init := tk->cmd(ed, ".b.t get " + hd lc + ".0 insert"); + l := len init; + for ( i := 8; i > 0; i-- ) { + lt := len tabs[i]; + if ( l >= lt && init[:lt] == tabs[i] ) + break; + } + for ( indent = nil; len indent < i; indent = 0 :: indent) ; + + in_comment = 0; # Are we in a comment? + for ( i = len tabs[i]; i < l; i++ ) + if ( init[i] == '#' ) { + in_comment = 1; + break; + } +} + +cmd(win: ref Tk->Toplevel, s: string): string +{ +# sys->print("%s\n", s); + r := tk->cmd(win, s); + if (r != nil && r[0] == '!') { + sys->print("wm/edit: error executing '%s': %s\n", s, r); + } + return r; +} diff --git a/appl/wm/filename.b b/appl/wm/filename.b new file mode 100644 index 00000000..56b1203a --- /dev/null +++ b/appl/wm/filename.b @@ -0,0 +1,74 @@ +implement Filename; + +include "sys.m"; + sys: Sys; + stderr: ref Sys->FD; +include "draw.m"; + draw: Draw; + Rect: import draw; +include "tk.m"; +include "selectfile.m"; + selectfile: Selectfile; + +include "arg.m"; + +Filename: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +usage() +{ + sys->fprint(stderr, "usage: filename [-g geom] [-d startdir] [pattern...]\n"); + raise "fail:usage"; +} + +init(ctxt: ref Draw->Context, argv: list of string) +{ + sys = load Sys Sys->PATH; + stderr = sys->fildes(2); + draw = load Draw Draw->PATH; + selectfile = load Selectfile Selectfile->PATH; + if (selectfile == nil) { + sys->fprint(stderr, "selectfile: cannot load %s: %r\n", Selectfile->PATH); + raise "fail:bad module"; + } + arg := load Arg Arg->PATH; + if (arg == nil) { + sys->fprint(stderr, "filename: cannot load %s: %r\n", Arg->PATH); + raise "fail:bad module"; + } + + if (ctxt == nil) { + sys->fprint(stderr, "filename: no window context\n"); + raise "fail:bad context"; + } + + sys->pctl(Sys->NEWPGRP, nil); + selectfile->init(); + + startdir := "."; +# geom := "-x " + string (ctxt.screen.image.r.dx() / 5) + +# " -y " + string (ctxt.screen.image.r.dy() / 5); + title := "Select a file"; + arg->init(argv); + while (opt := arg->opt()) { + case opt { +# 'g' => +# geom = arg->arg(); + 'd' => + startdir = arg->arg(); + 't' => + title = arg->arg(); + * => + sys->fprint(stderr, "filename: unknown option -%c\n", opt); + usage(); + } + } + if (startdir == nil || title == nil) + usage(); +# top := tk->toplevel(ctxt.screen, geom); + argv = arg->argv(); + arg = nil; + sys->print("%s\n", selectfile->filename(ctxt, nil, title, argv, startdir)); +} diff --git a/appl/wm/ftree/cptree.b b/appl/wm/ftree/cptree.b new file mode 100644 index 00000000..5af59fff --- /dev/null +++ b/appl/wm/ftree/cptree.b @@ -0,0 +1,136 @@ +implement Cptree; + +include "sys.m"; + sys: Sys; +include "draw.m"; +include "readdir.m"; + readdir: Readdir; +include "cptree.m"; + +init() +{ + sys = load Sys Sys->PATH; + readdir = load Readdir Readdir->PATH; +} + +Context: adt { + progressch: chan of string; + warningch: chan of (string, chan of int); + finishedch: chan of string; +}; + +# recursively copy file/directory f into directory d; +# the name remains the same. +copyproc(f, d: string, progressch: chan of string, + warningch: chan of (string, chan of int), + finishedch: chan of string) +{ + ctxt := ref Context(progressch, warningch, finishedch); + (fok, fstat) := sys->stat(f); + if (fok == -1) + error(ctxt, sys->sprint("cannot stat '%s': %r", f)); + (dok, dstat) := sys->stat(d); + if (dok == -1) + error(ctxt, sys->sprint("cannot stat '%s': %r", d)); + if ((dstat.mode & Sys->DMDIR) == 0) + error(ctxt, sys->sprint("'%s' is not a directory", d)); + if (fstat.qid.path == dstat.qid.path) + error(ctxt, sys->sprint("'%s' and '%s' are identical", f, d)); + + c := d + "/" + fname(f); + (cok, cstat) := sys->stat(c); + if (cok == 0) + error(ctxt, sys->sprint("'%s' already exists", c)); + rcopy(ctxt, f, ref fstat, c); + finishedch <-= nil; +} + +rcopy(ctxt: ref Context, src: string, srcstat: ref Sys->Dir, dst: string) +{ + omode := Sys->OWRITE; + perm := srcstat.mode; + if (perm & Sys->DMDIR) { + omode = Sys->OREAD; + perm |= 8r300; + } + + dstfd := sys->create(dst, omode, perm); + if (dstfd == nil) { + warning(ctxt, sys->sprint("cannot create '%s': %r", dst)); + return; + } + if (srcstat.mode & Sys->DMDIR) { + (entries, n) := readdir->init(src, Readdir->NAME | Readdir->COMPACT); + if (n == -1) + warning(ctxt, sys->sprint("cannot read dir '%s': %r", src)); + for (i := 0; i < len entries; i++) { + e := entries[i]; + rcopy(ctxt, src + "/" + e.name, e, dst + "/" + e.name); + } + if (perm != srcstat.mode) { + (ok, nil) := sys->fstat(dstfd); + if (ok != -1) { + dststat := sys->nulldir; + dststat.mode = srcstat.mode; + sys->fwstat(dstfd, dststat); + } + } + } else { + srcfd := sys->open(src, Sys->OREAD); + if (srcfd == nil) { + sys->remove(dst); + warning(ctxt, sys->sprint("cannot open '%s': %r", src)); + return; + } + ctxt.progressch <-= "copying " + src; + buf := array[Sys->ATOMICIO] of byte; + while ((n := sys->read(srcfd, buf, len buf)) > 0) { + if (sys->write(dstfd, buf, n) != n) { + sys->remove(dst); + warning(ctxt, sys->sprint("error writing '%s': %r", dst)); + return; + } + } + if (n == -1) { + sys->remove(dst); + warning(ctxt, sys->sprint("error reading '%s': %r", src)); + return; + } + } +} + +warning(ctxt: ref Context, msg: string) +{ + r := chan of int; + ctxt.warningch <-= (msg, r); + if (!<-r) + exit; +} + +error(ctxt: ref Context, msg: string) +{ + ctxt.finishedch <-= msg; + exit; +} + +fname(f: string): string +{ + f = cleanname(f); + for (i := len f - 1; i >= 0; i--) + if (f[i] == '/') + break; + return f[i+1:]; +} + +cleanname(s: string): string +{ + t := ""; + i := 0; + while (i < len s) + if ((t[len t] = s[i++]) == '/') + while (i < len s && s[i] == '/') + i++; + if (len t > 1 && t[len t - 1] == '/') + t = t[0:len t - 1]; + return t; +} diff --git a/appl/wm/ftree/cptree.m b/appl/wm/ftree/cptree.m new file mode 100644 index 00000000..874a66a1 --- /dev/null +++ b/appl/wm/ftree/cptree.m @@ -0,0 +1,8 @@ +Cptree: module { + PATH: con "/dis/lib/ftree/cptree.dis"; + init: fn(); + copyproc: fn(f, d: string, progressch: chan of string, + warningch: chan of (string, chan of int), + finishedch: chan of string); +}; + diff --git a/appl/wm/ftree/ftree.b b/appl/wm/ftree/ftree.b new file mode 100644 index 00000000..d70629d0 --- /dev/null +++ b/appl/wm/ftree/ftree.b @@ -0,0 +1,873 @@ +implement Ftree; + +include "sys.m"; + sys: Sys; +include "draw.m"; + draw: Draw; + Point, Rect: import draw; +include "tk.m"; + tk: Tk; +include "tkclient.m"; + tkclient: Tkclient; +include "readdir.m"; + readdir: Readdir; +include "items.m"; + items: Items; + Item, Expander: import items; +include "plumbmsg.m"; + plumbmsg: Plumbmsg; + Msg: import plumbmsg; +include "sh.m"; + sh: Sh; +include "popup.m"; + popup: Popup; +include "cptree.m"; + cptree: Cptree; +include "string.m"; + str: String; +include "arg.m"; + arg: Arg; + +stderr: ref Sys->FD; + +Ftree: module { + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +Tree: adt { + fname: string; + pick { + L => + N => + e: ref Expander; + sub: cyclic array of ref Tree; + } +}; + +tkcmds := array[] of { + "frame .top", + "label .top.l -text |", + "pack .top.l -side left -expand 1 -fill x", + "frame .f", + "canvas .c -yscrollcommand {.f.s set}", + "scrollbar .f.s -command {.c yview}", + "pack .f.s -side left -fill y", + "pack .c -side top -in .f -fill both -expand 1", + "pack .top -anchor w", + "pack .f -fill both -expand 1", + "pack propagate . 0", + ".top.l configure -text {}", +}; + +badmodule(p: string) +{ + sys->fprint(stderr, "ftree: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +tkwin: ref Tk->Toplevel; +root := "/"; + +cpfile := ""; + +usage() +{ + sys->fprint(stderr, "usage: ftree [-e] [-E] [-p] [-d] [root]\n"); + raise "fail:usage"; +} + +plumbinprogress := 0; +disallow := 1; +plumbed: chan of int; +roottree: ref Tree.N; +rootitem: Item; +runplumb := 1; + +init(ctxt: ref Draw->Context, argv: list of string) +{ + loadmods(); + if (ctxt == nil) { + sys->fprint(sys->fildes(2), "ftree: no window context\n"); + raise "fail:bad context"; + } + draw = load Draw Draw->PATH; + noexit := 0; + winopts := Tkclient->Resize | Tkclient->Hide; + arg->init(argv); + while ((opt := arg->opt()) != 0) { + case opt { + 'e' => + (noexit, winopts) = (1, Tkclient->Resize); + 'E' => + (noexit, winopts) = (1, 0); + 'p' => + (noexit, winopts) = (0, 0); + 'd' => + disallow = 0; + 'P' => + runplumb = 1; + * => + usage(); + } + } + argv = arg->argv(); + if (argv != nil && tl argv != nil) + usage(); + if (argv != nil) { + root = hd argv; + (ok, s) := sys->stat(root); + if (ok == -1) { + sys->fprint(stderr, "ftree: %s: %r\n", root); + raise "fail:bad root"; + } else if ((s.mode & Sys->DMDIR) == 0) { + sys->fprint(stderr, "ftree: %s is not a directory\n", root); + raise "fail:bad root"; + } + } + + sys->pctl(Sys->NEWPGRP|Sys->FORKNS, nil); + + (win, wmctl) := tkclient->toplevel(ctxt, nil, "Ftree", winopts); + tkwin = win; + for (i := 0; i < len tkcmds; i++) + cmd(win, tkcmds[i]); + fittoscreen(win); + cmd(win, "update"); + + event := chan of string; + tk->namechan(win, event, "event"); + + clickfile := chan of string; + tk->namechan(win, clickfile, "clickfile"); + + sys->bind("#s", "/chan", Sys->MBEFORE); + fio := sys->file2chan("/chan", "plumbstart"); + if (fio == nil) { + sys->fprint(stderr, "ftree: cannot make /chan/plumbstart: %r\n"); + raise "fail:error"; + } + nsfio := sys->file2chan("/chan", "nsupdate"); + if (nsfio == nil) { + sys->fprint(stderr, "ftree: cannot make /chan/nsupdate: %r\n"); + raise "fail:error"; + } + + if (runplumb){ + if((err := sh->run(ctxt, "plumber" :: "-n" :: "-w" :: "-c/chan/plumbstart" :: nil)) != nil) + sys->fprint(stderr, "ftree: can't start plumber: %s\n", err); + } + + plumbmsg = load Plumbmsg Plumbmsg->PATH; + if (plumbmsg != nil && plumbmsg->init(1, nil, 0) == -1) { + sys->fprint(stderr, "ftree: no plumber\n"); + plumbmsg = nil; + } + + nschanged := chan of string; + roottree = ref Tree.N("/", Expander.new(win, ".c"), nil); + rootitem = roottree.e.make(items->maketext(win, ".c", "/", "/")); + cmd(win, ".c configure -width " + string rootitem.r.dx() + " -height " + string rootitem.r.dy() + + " -scrollregion {" + r2s(rootitem.r) + "}"); + sendevent("/", "expand"); + tkclient->onscreen(win, nil); + tkclient->startinput(win, "ptr"::nil); + cmd(win, "update"); + + plumbed = chan of int; + for (;;) alt { + key := <-win.ctxt.kbd => + tk->keyboard(win, key); + m := <-win.ctxt.ptr => + tk->pointer(win, *m); + s := <-win.ctxt.ctl or + s = <-win.wreq or + s = <-wmctl => + if (noexit && s == "exit") + s = "task"; + tkclient->wmctl(win, s); + s := <-event => + (target, ev) := eventtarget(s); + sendevent(target, ev); + m := <-clickfile => + (n, toks) := sys->tokenize(m, " "); + (b, s) := (hd toks, hd tl toks); + if (b == "menu") { + c := chan of (ref Tree, Item, chan of Item); + nsu := chan of string; + spawn menuproc(c, nsu); + found := operate(s, c); + if (found) { + if ((upd := <-nsu) != nil) + updatens(upd); + } + } else if (b == "plumb") + plumbit(s); + ok := <-plumbed => + colour := "#00ff00"; + if (!ok) + colour = "red"; + cmd(tkwin, ".c itemconfigure highlight -fill " + colour); + cmd(tkwin, "update"); + plumbinprogress = 0; + s := <-nschanged => + sys->print("got nschanged: %s\n", s); + updatens(s); + (nil, nil, nil, rc) := <-nsfio.read => + if (rc != nil) + readreply(rc, nil, "permission denied"); + (nil, data, nil, wc) := <-nsfio.write => + if (wc == nil) + break; + s := cleanname(string data); + if (len s >= len root && s[0:len root] == root) { + s = s[len root:]; + if (s == nil) + s = "/"; + if (s[0] == '/') + updatens(s); + } + writereply(wc, len data, nil); + (nil, nil, nil, rc) := <-fio.read => + if (rc != nil) + readreply(rc, nil, "permission denied"); + (nil, data, nil, wc) := <-fio.write => + if (wc == nil) + break; + s := string data; + if (len s == 0 || s[0] != 's') + writereply(wc, 0, "invalid write"); + cmd := str->unquoted(s); + if (cmd == nil || tl cmd == nil || tl tl cmd == nil) { + writereply(wc, 0, "invalid write"); + } else { + if (hd tl tl cmd == "+ftree") + runsubftree(ctxt, tl tl tl cmd); + else + sh->run(ctxt, "{$* &}" :: tl tl cmd); + writereply(wc, len data, nil); + } + } +} + +runsubftree(ctxt: ref Draw->Context, c: list of string) +{ + if (len c < 2) { + return; + } + cmd(tkwin, ". unmap"); + sh->run(ctxt, c); + cmd(tkwin, ". map"); +} + +sendevent(target, ev: string) +{ + c := chan of (ref Tree, Item, chan of Item); + spawn sendeventproc(ev, c); + operate(target, c); + cmd(tkwin, "update"); +} + +# non-blocking reply to read request, in case client has gone away. +readreply(reply: Sys->Rread, data: array of byte, err: string) +{ + alt { + reply <-= (data, err) =>; + * =>; + } +} + +# non-blocking reply to write request, in case client has gone away. +writereply(reply: Sys->Rwrite, count: int, err: string) +{ + alt { + reply <-= (count, err) =>; + * =>; + } +} + +plumbit(f: string) +{ + if (!plumbinprogress) { + highlight(f, "yellow", 2000); + spawn plumbproc(root + f, plumbed); + plumbinprogress = 1; + } +} + +plumbproc(f: string, plumbed: chan of int) +{ + if (plumbmsg == nil || (ref Msg("browser", nil, nil, "text", nil, array of byte f)).send() == -1) { + sys->fprint(stderr, "ftree: cannot plumb %s\n", f); + plumbed <-= 0; + } else + plumbed <-= 1; +} + +loadmods() +{ + sys = load Sys Sys->PATH; + stderr = sys->fildes(2); + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + if (tkclient == nil) + badmodule(Tkclient->PATH); + tkclient->init(); + + readdir = load Readdir Readdir->PATH; + if (readdir == nil) + badmodule(Readdir->PATH); + + str = load String String->PATH; + if (str == nil) + badmodule(String->PATH); + + items = load Items Items->PATH; + if (items == nil) + badmodule(Items->PATH); + items->init(); + + sh = load Sh Sh->PATH; + if (sh == nil) + badmodule(Sh->PATH); + + popup = load Popup Popup->PATH; + if (popup == nil) + badmodule(Popup->PATH); + popup->init(); + + cptree = load Cptree Cptree->PATH; + if (cptree == nil) + badmodule(Cptree->PATH); + cptree->init(); + + arg = load Arg Arg->PATH; + if (arg == nil) + badmodule(Arg->PATH); +} + +updatens(s: string) +{ + sys->print("updatens(%s)\n", s); + (target, ev) := eventtarget(s); + spawn rereadproc(c := chan of (ref Tree, Item, chan of Item)); + operate(target, c); + cmd(tkwin, "update"); +} + +nsupdatereaderproc(fd: ref Sys->FD, path: string, nschanged: chan of string) +{ + buf := array[Sys->ATOMICIO] of byte; + while ((n := sys->read(fd, buf, len buf)) > 0) { + s := string buf[0:n]; + nschanged <-= path + string buf[0:n-1]; + } + sys->print("nsupdate gave eof: (%r)\n"); +} + +sendeventproc(ev: string, c: chan of (ref Tree, Item, chan of Item)) +{ + (tree, it, replyc) := <-c; + if (replyc == nil) + return; + pick t := tree { + N => + if (ev == "expand") + expand(t, it); + else if (ev == "contract") + t.sub = nil; + it = t.e.event(it, ev); + } + replyc <-= it; +} + +Open, Copy, Paste, Remove: con iota; + +menu := array[] of { +Open => "Open", +Copy => "Copy", +Paste => "Paste into", +Remove => "Remove", +}; + +screenx(cvs: string, x: int): int +{ + return x - int cmd(tkwin, cvs + " canvasx 0"); +} + +screeny(cvs: string, y: int): int +{ + return y - int cmd(tkwin, cvs + " canvasy 0"); +} + +menuproc(c: chan of (ref Tree, Item, chan of Item), nsu: chan of string) +{ + (tree, it, replyc) := <-c; + if (replyc == nil) + return; + + p := Point(screenx(".c", it.r.min.x), screeny(".c", it.r.min.y)); + m := array[len menu] of string; + for (i := 0; i < len m; i++) + m[i] = menu[i] + " " + tree.fname; + n := post(tkwin, p, m, 0); + upd: string; + if (n >= 0) { + case n { + Copy => + cpfile = it.name; + Paste => + if (cpfile == nil) + notice("no file in snarf buffer"); + else { + cp(cpfile, it.name); + upd = it.name; + } + Remove => + if ((e := rm(it.name)) != nil) + notice(e); + upd = parent(it.name); + Open => + plumbit(it.name); + } + } + +# id := cmd(tkwin, ".c create rectangle " + r2s(it.r) + " -fill yellow"); + replyc <-= it; + nsu <-= upd; +} + +post(win: ref Tk->Toplevel, p: Point, a: array of string, n: int): int +{ + rc := popup->post(win, p, a, n); + for(;;)alt{ + r := <-rc => + return r; + key := <-win.ctxt.kbd => + tk->keyboard(win, key); + m := <-win.ctxt.ptr => + tk->pointer(win, *m); + s := <-win.ctxt.ctl or + s = <-win.wreq => + tkclient->wmctl(win, s); + } +} + +highlight(f: string, colour: string, time: int) +{ + spawn highlightproc(c := chan of (ref Tree, Item, chan of Item), colour, time); + operate(f, c); + tk->cmd(tkwin, "update"); +} + +unhighlight() +{ + cmd(tkwin, ".c delete highlight"); + tk->cmd(tkwin, "update"); +} + +hpid := -1; +highlightproc(c: chan of (ref Tree, Item, chan of Item), colour: string, time: int) +{ + (tree, it, replyc) := <-c; + if (replyc == nil) + return; + r: Rect; + pick t := tree { + N => + r = t.e.titleitem.r.addpt(it.r.min); + L => + r = it.r; + } + id := cmd(tkwin, ".c create rectangle " + r2s(r) + " -fill " + colour + " -tags highlight"); + cmd(tkwin, ".c lower " + id); + kill(hpid); + sync := chan of int; + spawn highlightsleepproc(sync, time); + hpid = <-sync; + replyc <-= it; +} + +highlightsleepproc(sync: chan of int, time: int) +{ + sync <-= sys->pctl(0, nil); + sys->sleep(time); + cmd(tkwin, ".c delete highlight"); + cmd(tkwin, "update"); +} + +operate(towhom: string, c: chan of (ref Tree, Item, chan of Item)): int +{ + towhom = cleanname(towhom); + (ok, it) := operate1(roottree, rootitem, towhom, towhom, c); + if (!it.eq(rootitem)) { + cmd(tkwin, ".c configure -width " + string it.r.dx() + " -height " + string it.r.dy() + + " -scrollregion {" + r2s(it.r) + "}"); + rootitem = it; + } + if (!ok) + c <-= (nil, it, nil); + return ok; +} + +blankitem: Item; +operate1(tree: ref Tree, it: Item, towhom, below: string, + c: chan of (ref Tree, Item, chan of Item)): (int, Item) +{ +# sys->print("operate on %s, towhom: %s, below: %s\n", it.name, towhom, below); + n: ref Tree.N; + replyc := chan of Item; + if (it.name != towhom) { + pick t := tree { + L => + return (0, it); + N => + n = t; + } + below = dropelem(below); + if (below == nil) + return (0, it); + path := pathcat(it.name, below); + if (len n.e.children != len n.sub) { + sys->fprint(stderr, "inconsistent children in %s (%d vs sub %d)\n", it.name, len n.e.children, len n.sub); + return (0, it); + } + for (i := 0; i < len n.e.children; i++) { + f := n.e.children[i].name; +# sys->print("checking %s against child %s\n", path, f); + if (len path >= len f && path[0:len f] == f && + (len path == len f || path[len f] == '/')) { + break; + } + } + if (i == len n.e.children) + return (0, it); + oldit := n.e.children[i].addpt(it.r.min); + (ok, nit) := operate1(n.sub[i], oldit, towhom, below, c); + if (nit.eq(oldit)) + return (ok, it); +# sys->print("childchanged({%s, [%s]}, %d, {%s, [%s]})\n", +# it.name, r2s(it.r), i, nit.name, r2s(nit.r)); + n.e.children[i] = nit.subpt(it.r.min); + return (ok, n.e.childrenchanged(it)); + } + c <-= (tree, it, replyc); + return (1, <-replyc); +} + + +dropelem(below: string): string +{ + if (below[0] == '/') + return below[1:]; + for (i := 1; i < len below; i++) + if (below[i] == '/') + break; + if (i == len below) + return nil; + return below[i+1:]; +} + +cleanname(s: string): string +{ + t := ""; + i := 0; + while (i < len s) + if ((t[len t] = s[i++]) == '/') + while (i < len s && s[i] == '/') + i++; + if (len t > 1 && t[len t - 1] == '/') + t = t[0:len t - 1]; + return t; +} + +pathcat(s1, s2: string): string +{ + if (s1 == nil || s2 == nil) + return s1 + s2; + if (s1[len s1 - 1] != '/' && s2[0] != '/') + return s1 + "/" + s2; + return s1 + s2; +} + +# read the directory referred to by t. +expand(t: ref Tree.N, it: Item) +{ + (d, n) := readdir->init(root + it.name, Readdir->NAME|Readdir->COMPACT); + if (d == nil) { + sys->print("readdir failed: %r\n"); + d = array[0] of ref Sys->Dir; + } + sortit(d); + t.sub = array[len d] of ref Tree; + t.e.children = array[len d] of Item; + for (i := 0; i < len d; i++) { + tagname := pathcat(it.name, d[i].name); + (t.sub[i], t.e.children[i]) = makenode(d[i].mode & Sys->DMDIR, d[i].name, tagname); + # make coords relative to parent + t.e.children[i] = t.e.children[i].subpt(it.r.min); + } +} + +makenode(isdir: int, title, tagname: string): (ref Tree, Item) +{ + tree: ref Tree; + it: Item; + if (isdir) { + e := Expander.new(tkwin, ".c"); + tree = ref Tree.N(title, e, nil); + it = e.make(items->maketext(tkwin, ".c", tagname, title)); + cmd(tkwin, ".c bind " + e.titleitem.name + + " <Button-1> {send clickfile menu " + tagname + "}"); + } else { + tree = ref Tree.L(title); + it = items->maketext(tkwin, ".c", tagname, title); + cmd(tkwin, ".c bind " + tagname + + " <ButtonRelease-2> {send clickfile plumb " + tagname + "}"); + cmd(tkwin, ".c bind " + tagname + + " <Button-1> {send clickfile menu " + tagname + "}"); + } + return (tree, it); +} + +rereadproc(c: chan of (ref Tree, Item, chan of Item)) +{ + (tree, it, replyc) := <-c; + if (replyc == nil) + return; + pick t := tree { + L => + replyc <-= it; + N => + replyc <-= reread(t, it); + } +} + +# re-read tree & update recursively as necessary. +# _it_ is the tree's Item, in absolute coords. +reread(tree: ref Tree.N, it: Item): Item +{ + (d, n) := readdir->init(root + it.name, Readdir->NAME|Readdir->COMPACT); + sortit(d); + sys->print("re-reading %s (was %d, now %d)\n", it.name, len tree.sub, len d); + + sub := tree.sub; + newsub := array[len d] of ref Tree; + newchildren := array[len d] of Item; + i := j := 0; + while (i < len sub || j < len d) { + cmp: int; + if (i >= len sub) + cmp = 1; + else if (j >= len d) + cmp = -1; + else { + cmp = entrycmp(sub[i].fname, tagof(sub[i]) == tagof(Tree.N), + d[j].name, d[j].mode & Sys->DMDIR); + } + if (cmp == 0) { + # entry remains the same, but maybe it's changed type. + if ((tagof(sub[i]) == tagof(Tree.N)) != ((d[j].mode & Sys->DMDIR) != 0)) { + # delete old item and make new one... + tagname := tree.e.children[i].name; + cmd(tkwin, ".c delete " + tagname); + (newsub[j], newchildren[j]) = + makenode(d[j].mode & Sys->DMDIR, d[j].name, tagname); + newchildren[j] = newchildren[j].subpt(it.r.min); + } else { + nit := tree.e.children[i]; + pick t := sub[i] { + N => + if (t.e.expanded) + nit = reread(t, nit.addpt(it.r.min)).subpt(it.r.min); + } + (newsub[j], newchildren[j]) = (sub[i], nit); + } + i++; + j++; + } else if (cmp > 0) { + # new entry, d[j] + tagname := pathcat(it.name, d[j].name); + (newsub[j], newchildren[j]) = + makenode(d[j].mode & Sys->DMDIR, d[j].name, tagname); + newchildren[j] = newchildren[j].subpt(it.r.min); + j++; + } else { + # entry has been deleted, sub[i] + cmd(tkwin, ".c delete " + tree.e.children[i].name); + i++; + } + } + (tree.sub, tree.e.children) = (newsub, newchildren); + return tree.e.childrenchanged(it); +} + +entrycmp(s1: string, isdir1: int, s2: string, isdir2: int): int +{ + if (!isdir1 == !isdir2) { + if (s1 > s2) + return 1; + else if (s1 < s2) + return -1; + else + return 0; + } else if (isdir1) + return -1; + else + return 1; +} + +sortit(d: array of ref Sys->Dir) +{ + da := array[len d] of ref Sys->Dir; + fa := array[len d] of ref Sys->Dir; + nd := nf := 0; + for (i := 0; i < len d; i++) { + if (d[i].mode & Sys->DMDIR) + da[nd++] = d[i]; + else + fa[nf++] = d[i]; + } + d[0:] = da[0:nd]; + d[nd:] = fa[0:nf]; +} + +eventtarget(s: string): (string, string) +{ + for (i := 0; i < len s; i++) + if (s[i] == ' ') + return (s[0:i], s[i+1:]); + return (s, nil); +} + +cmd(top: ref Tk->Toplevel, s: string): string +{ + e := tk->cmd(top, s); + if (e != nil && e[0] == '!') + sys->fprint(sys->fildes(2), "ftree: tk error %s on '%s'\n", e, s); + return e; +} + +r2s(r: Rect): string +{ + return string r.min.x + " " + string r.min.y + " " + + string r.max.x + " " + string r.max.y; +} + +p2s(p: Point): string +{ + return string p.x + " " + string p.y; +} + +fittoscreen(win: ref Tk->Toplevel) +{ + Point: import draw; + if (win.image == nil || win.image.screen == nil) + return; + r := win.image.screen.image.r; + scrsize := Point((r.max.x - r.min.x), (r.max.y - r.min.y)); + bd := int cmd(win, ". cget -bd"); + winsize := Point(int cmd(win, ". cget -actwidth") + bd * 2, int cmd(win, ". cget -actheight") + bd * 2); + if (winsize.x > scrsize.x) + cmd(win, ". configure -width " + string (scrsize.x - bd * 2)); + if (winsize.y > scrsize.y) + cmd(win, ". configure -height " + string (scrsize.y - bd * 2)); + actr: Rect; + actr.min = Point(int cmd(win, ". cget -actx"), int cmd(win, ". cget -acty")); + actr.max = actr.min.add((int cmd(win, ". cget -actwidth") + bd*2, + int cmd(win, ". cget -actheight") + bd*2)); + (dx, dy) := (actr.dx(), actr.dy()); + if (actr.max.x > r.max.x) + (actr.min.x, actr.max.x) = (r.min.x - dx, r.max.x - dx); + if (actr.max.y > r.max.y) + (actr.min.y, actr.max.y) = (r.min.y - dy, r.max.y - dy); + if (actr.min.x < r.min.x) + (actr.min.x, actr.max.x) = (r.min.x, r.min.x + dx); + if (actr.min.y < r.min.y) + (actr.min.y, actr.max.y) = (r.min.y, r.min.y + dy); + cmd(win, ". configure -x " + string actr.min.x + " -y " + string actr.min.y); +} + +cp(src, dst: string) +{ + if(disallow){ + notice("permission denied"); + return; + } + progressch := chan of string; + warningch := chan of (string, chan of int); + finishedch := chan of string; + spawn cptree->copyproc(root + src, root + dst, progressch, warningch, finishedch); +loop: for (;;) alt { + m := <-progressch => + status(m); + (m, r) := <-warningch => + notice("warning: " + m); + sys->sleep(1000); + r <-= 1; + m := <-finishedch => + status(m); + break loop; + } +} + +parent(f: string): string +{ + f = cleanname(f); + for (i := len f - 1; i >= 0; i--) + if (f[i] == '/') + break; + if (i > 0) + f = f[0:i]; + return f; +} + +notice(s: string) +{ + status(s); +} + +status(s: string) +{ + cmd(tkwin, ".top.l configure -text '" + s); + cmd(tkwin, "update"); +} + +rm(name: string): string +{ + if(disallow) + return "permission denied"; + name = root + name; + if(sys->remove(name) < 0) { + e := sys->sprint("%r"); + (ok, d) := sys->stat(name); + if(ok >= 0 && (d.mode & Sys->DMDIR) != 0) + return rmdir(name); + return e; + } + return nil; +} + +rmdir(name: string): string +{ + (d, n) := readdir->init(name, Readdir->NONE|Readdir->COMPACT); + for(i := 0; i < n; i++) { + path := name+"/"+d[i].name; + e: string; + if(d[i].mode & Sys->DMDIR) + e = rmdir(path); + else if (sys->remove(path) == -1) + e = sys->sprint("cannot remove %s: %r", path); + if (e != nil) + return e; + } + if (sys->remove(name) == -1) + return sys->sprint("cannot remove %s: %r", name); + return nil; +} + +kill(pid: int) +{ + if ((fd := sys->open("/prog/"+string pid+"/ctl", Sys->OWRITE)) != nil) + sys->write(fd, array of byte "kill", 4); +} diff --git a/appl/wm/ftree/items.b b/appl/wm/ftree/items.b new file mode 100644 index 00000000..023e3d33 --- /dev/null +++ b/appl/wm/ftree/items.b @@ -0,0 +1,326 @@ +implement Items; + +include "sys.m"; + sys: Sys; +include "draw.m"; + draw: Draw; + Point, Rect: import draw; +include "tk.m"; + tk: Tk; +include "items.m"; + +Taglen: con 5; +Titletaglen: con 10; +Spotdiam: con 10; +Lineopts: con " -width 1 -fill gray"; +Ovalopts: con " -outline gray"; +Crossopts: con " -fill red"; + +init() +{ + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; +} + +blankexpander: Expander; +Expander.new(win: ref Tk->Toplevel, cvs: string): ref Expander +{ + e := ref blankexpander; + e.win = win; + e.cvs = cvs; + return e; +} + +moveto(win: ref Tk->Toplevel, cvs: string, tag: string, bbox: Rect, p: Point) +{ + if (!bbox.min.eq(p)) + cmd(win, cvs + " move " + tag + " " + p2s(p.sub(bbox.min))); +} + +bbox(win: ref Tk->Toplevel, cvs, w: string): Rect +{ + return s2r(cmd(win, cvs + " bbox " + w)); +} + +rename(win: ref Tk->Toplevel, it: Item, newname: string): Item +{ + (nil, itl) := sys->tokenize(cmd(win, ".c find withtag " + it.name), " "); + cmd(win, ".c dtag " + it.name + " " + it.name); + for (; itl != nil; itl = tl itl) + cmd(win, ".c addtag " + newname + " withtag " + hd itl); + it.name = newname; + return it; +} + +Expander.make(e: self ref Expander, titleitem: Item): Item +{ + name := titleitem.name; + tag := " -tags " + name; + + e.titleitem = rename(e.win, titleitem, "!!." + name); + cmd(e.win, e.cvs + " addtag " + name + " withtag !!." + name); + sc := spotcentre((0, 0), dxy(e.titleitem.r)); + spotr := Rect(sc, sc).inset(-Spotdiam/2); + + p := (spotr.max.x + Titletaglen, 0); + moveto(e.win, e.cvs, e.titleitem.name, e.titleitem.r, p); + e.titleitem.r = rmoveto(e.titleitem.r, p); + it := Item(name, ((0, 0), (spotr.max.x + Titletaglen + titleitem.r.dx(), titleitem.r.dy())), (0, 0)); + + # make line to the right of spot + cmd(e.win, e.cvs + " create line " + + p2s((spotr.max.x, sc.y)) + " " + p2s((spotr.max.x+Titletaglen, sc.y)) + tag + Lineopts); + + # make spot + spotid := cmd(e.win, e.cvs + " create oval " + + r2s(spotr) + Ovalopts + tag); + if (e.expanded) + cmd(e.win, e.cvs + " bind " + spotid + " <ButtonRelease-1>" + + " {send event " + name + " contract}"); + else + cmd(e.win, e.cvs + " bind " + spotid + " <ButtonRelease-1>" + + " {send event " + name + " expand}"); + + cmd(e.win, e.cvs + " raise " + spotid); + e.spotid = int spotid; + + it.attach = (0, sc.y); + it.r.max = (e.titleitem.r.dx() + spotr.max.x + Titletaglen, e.titleitem.r.dy()); + + if (!e.expanded) { + addcross(e, it, name); + return it; + } + + it.r = placechildren(e, it, name); + return it; +} + +rmoveto(r: Rect, p: Point): Rect +{ + return r.addpt(p.sub(r.min)); +} + +# place all children of e appropriately. +# assumes that the canvas items of all children are already made. +# return bbox rectangle of whole thing. +placechildren(e: ref Expander, it: Item, tags: string): Rect +{ + ltag := " -tags {"+ tags + " !." + it.name + "}"; + titlesize := dxy(e.titleitem.r); + sc := spotcentre(it.r.min, titlesize); + maxwidth := 0; + y := it.r.min.y + titlesize.y; + lasty := 0; + for (i := 0; i < len e.children; i++) { + c := e.children[i]; + if (c.r.dx() > maxwidth) + maxwidth = c.r.dx(); + c.r = c.r.addpt(it.r.min); + r: Rect; + r.min = (sc.x + Taglen, y); + r.max = r.min.add(dxy(c.r)); + moveto(e.win, e.cvs, c.name, c.r, r.min); + + # make item coords relative to parent + e.children[i].r = r.subpt(it.r.min); + cmd(e.win, e.cvs + " addtag " + it.name + " withtag " + c.name); + + # horizontal attachment + cmd(e.win, e.cvs + " create line " + + p2s((sc.x, y + c.attach.y)) + " " + + p2s((sc.x + Taglen + c.attach.x, y + c.attach.y)) + + ltag + Lineopts); + lasty = y + c.attach.y; + y += r.dy(); + } + + # vertical attachment (if there were any children) + if (i > 0) { + id := cmd(e.win, e.cvs + " create line " + + p2s((sc.x, sc.y + Spotdiam/2)) + " " + p2s((sc.x, lasty)) + ltag + Lineopts); + cmd(e.win, e.cvs + " bind " + id + " <Button-1>"+ + " {send event " + it.name + " see}"); + } + r := Rect(it.r.min, + (max(sc.x+Spotdiam/2+Titletaglen+titlesize.x, sc.x+Taglen+maxwidth), + y)); + return r; +} + +Expander.event(e: self ref Expander, it: Item, ev: string): Item +{ + case ev { + "expand" => + if (e.expanded) { + sys->print("item %s is already expanded\n", it.name); + return it; + } + e.expanded = 1; + tags := gettags(e.win, e.cvs, string e.spotid); + cmd(e.win, e.cvs + " delete !." + it.name); + cmd(e.win, e.cvs + " bind " + string e.spotid + " <ButtonRelease-1>" + + + " {send event " + it.name + " contract}"); + it.r = placechildren(e, it, tags); + "contract" => + if (!e.expanded) { + sys->print("item %s is already contracted\n", it.name); + return it; + } + e.expanded = 0; + cmd(e.win, e.cvs + " delete !." + it.name); + for (i := 0; i < len e.children; i++) + cmd(e.win, e.cvs + " delete " + e.children[i].name); + cmd(e.win, e.cvs + " bind " + string e.spotid + " <ButtonRelease-1>" + + + " {send event " + it.name + " expand}"); + tags := gettags(e.win, e.cvs, string e.spotid); + addcross(e, it, tags); + titlesize := dxy(e.titleitem.r); + it.r.max = it.r.min.add((Taglen * 2 + Spotdiam + titlesize.x, titlesize.y)); + e.children = nil; + "see" => + cmd(e.win, e.cvs + " see " + p2s(it.r.min)); + * => + sys->print("unknown event '%s' on item %s\n", ev, it.name); + } + return it; +} + +Expander.childrenchanged(e: self ref Expander, it: Item): Item +{ + cmd(e.win, e.cvs + " delete !." + it.name); + tags := gettags(e.win, e.cvs, string e.spotid); + it.r = placechildren(e, it, tags); + return it; +} + +gettags(win: ref Tk->Toplevel, cvs: string, name: string): string +{ + tags := cmd(win, cvs + " gettags " + name); + (n, tagl) := sys->tokenize(tags, " "); + ntags := ""; + for (; tagl != nil; tagl = tl tagl) { + t := hd tagl; + if (t[0] != '!' && (t[0] < '0' || t[0] > '9')) + ntags += " " + t; + } + return ntags; +} + +spotcentre(origin, titlesize: Point): Point +{ + return (origin.x + Spotdiam / 2, origin.y + titlesize.y / 2); +} + +addcross(e: ref Expander, it: Item, tags: string) +{ + p := spotcentre(it.r.min, dxy(e.titleitem.r)); + crosstags := " -tags {" + tags + " !." + it.name + "}"; + + id1 := cmd(e.win, e.cvs + " create line " + + p2s((p.x-Spotdiam/2, p.y)) + " " + + p2s((p.x+Spotdiam/2, p.y)) + crosstags + Crossopts); + id2 := cmd(e.win, e.cvs + " create line " + + p2s((p.x, p.y-Spotdiam/2)) + " " + + p2s((p.x, p.y+Spotdiam/2)) + crosstags + Crossopts); + cmd(e.win, e.cvs + " lower " + id1 + ";" + e.cvs + " lower " + id2); +} + +knownfont: string; +knownfontheight: int; +fontheight(win: ref Tk->Toplevel, font: string): int +{ + Font: import draw; + if (font == knownfont) + return knownfontheight; + if (win.image == nil) # can happen if we run out of image memory + return -1; + f := Font.open(win.image.display, font); + if (f == nil) + return -1; + knownfont = font; + knownfontheight = f.height; + return f.height; +} + +maketext(win: ref Tk->Toplevel, cvs: string, name: string, text: string): Item +{ + tag := " -tags " + name; + it := Item(name, ((0, 0), (0, 0)), (0, 0)); + ttid := cmd(win, cvs + " create text 0 0 " + + " -anchor nw" + tag + + " -text '" + text); + it.r = bbox(win, cvs, ttid); + h := fontheight(win, cmd(win, cvs + " itemcget " + ttid + " -font")); + if (h != -1) { + dh := it.r.dy() - h; + it.r.min.y += dh / 2; + it.r.max.y -= dh / 2; + } + it.attach = (0, it.r.dy() / 2); + return it; +} + +cmd(top: ref Tk->Toplevel, s: string): string +{ + e := tk->cmd(top, s); + if (e != nil && e[0] == '!') + sys->fprint(sys->fildes(2), "items: tk error %s on '%s'\n", e, s); + return e; +} + +r2s(r: Rect): string +{ + return string r.min.x + " " + string r.min.y + " " + + string r.max.x + " " + string r.max.y; +} + +s2r(s: string): Rect +{ + (n, toks) := sys->tokenize(s, " "); + if (n != 4) { + sys->print("'%s' is not a rectangle!\n", s); + raise "bad conversion"; + } + r: Rect; + (r.min.x, toks) = (int hd toks, tl toks); + (r.min.y, toks) = (int hd toks, tl toks); + (r.max.x, toks) = (int hd toks, tl toks); + (r.max.y, toks) = (int hd toks, tl toks); + return r; +} + +Item.eq(i: self Item, j: Item): int +{ + return i.r.eq(j.r) && i.attach.eq(j.attach) && i.name == j.name; +} + +Item.addpt(i: self Item, p: Point): Item +{ + i.r = i.r.addpt(p); + return i; +} + +Item.subpt(i: self Item, p: Point): Item +{ + i.r = i.r.subpt(p); + return i; +} + +p2s(p: Point): string +{ + return string p.x + " " + string p.y; +} + +dxy(r: Rect): Point +{ + return r.max.sub(r.min); +} + +max(a, b: int): int +{ + if (a > b) + return a; + return b; +} diff --git a/appl/wm/ftree/items.m b/appl/wm/ftree/items.m new file mode 100644 index 00000000..7af34d12 --- /dev/null +++ b/appl/wm/ftree/items.m @@ -0,0 +1,30 @@ +Items: module { + PATH: con "/dis/lib/ftree/items.dis"; + + Item: adt { + name: string; # tag held in common by all canvas items in this Item. + r: Rect; # relative to parent's Item when stored in children + attach: Point; # attachment point relative to r.min + + eq: fn(i: self Item, j: Item): int; + addpt: fn(i: self Item, p: Point): Item; + subpt: fn(i: self Item, p: Point): Item; + }; + + Expander: adt { + titleitem: Item; + expanded: int; + children: array of Item; + win: ref Tk->Toplevel; + cvs: string; + spotid: int; + + new: fn(win: ref Tk->Toplevel, cvs: string): ref Expander; + make: fn(e: self ref Expander, it: Item): Item; + event: fn(e: self ref Expander, it: Item, ev: string): Item; + childrenchanged: fn(e: self ref Expander, it: Item): Item; + }; + + init: fn(); + maketext: fn(win: ref Tk->Toplevel, cvs: string, name: string, text: string): Item; +}; diff --git a/appl/wm/ftree/mkfile b/appl/wm/ftree/mkfile new file mode 100644 index 00000000..4f4c5f39 --- /dev/null +++ b/appl/wm/ftree/mkfile @@ -0,0 +1,36 @@ +<../../../mkconfig + +TARG=\ + items.dis\ + cptree.dis\ + ftree.dis + +MODULES=\ + items.m\ + cptree.m\ + +SYSMODULES=\ + arg.m\ + draw.m\ + plumbmsg.m\ + popup.m\ + readdir.m\ + sh.m\ + string.m\ + sys.m\ + tk.m\ + tkclient.m\ + +DISBIN=$ROOT/dis/lib/ftree + +all:V: ftree.dis $TARG + +$ROOT/dis/wm/ftree.dis: ftree.dis + rm -f $ROOT/dis/wm/ftree.dis && cp ftree.dis $ROOT/dis/wm/ftree.dis + +<$ROOT/mkfiles/mkdis + +install:V: $ROOT/dis/wm/ftree.dis + +nuke:V: nuke-std + cd $ROOT/dis/wm; rm -f ftree.dis diff --git a/appl/wm/ftree/wmsetup b/appl/wm/ftree/wmsetup new file mode 100644 index 00000000..e229e63c --- /dev/null +++ b/appl/wm/ftree/wmsetup @@ -0,0 +1,48 @@ +# /dis/sh script +# wm defines "menu" and "delmenu" builtins +load std +prompt='% ' '' +fn % {$*} +autoload=std +home=/usr/^"{cat /dev/user} + +if {! {~ wm ${loaded}}} { + echo wmsetup must run under wm >[1=2] + raise usage +} + +fn wmrun { + args := $* + { + pctl newpgrp + fn wmrun + $args + } >[2] /chan/wmstderr & +} + +fn cd { + builtin cd $*; echo cwd `{pwd} > /chan/shctl +} + +menu Shell {wmrun wm/sh} +menu Acme {wmrun acme} +menu Edit {wmrun wm/edit} +menu Charon {wmrun charon} +menu Manual {wmrun wm/man} +menu Files {if {ftest -d $home} {wmrun wm/dir $home} {wmrun wm/dir /}} +menu '' '' +menu System 'Debugger' {wmrun wm/deb} +menu System 'Module manager' {wmrun wm/rt} +menu System 'Task manager' {wmrun wm/task} +menu System 'Memory monitor' {wmrun wm/memory} +menu System 'About' {wmrun wm/about} +menu Misc 'Tetris' {wmrun wm/tetris} +menu Misc 'Coffee' {wmrun wm/coffee} +menu Misc 'Colours' {wmrun wm/colors} +menu Misc 'Winctl' {wmrun wm/winctl} +menu Misc 'Clock' {wmrun wm/date} + +if {ftest -f $home/lib/wmsetup} {run $home/lib/wmsetup} {} + +builtin cd /usr/rog/limbo/browser +wmrun ftree diff --git a/appl/wm/getauthinfo.b b/appl/wm/getauthinfo.b new file mode 100644 index 00000000..0e03cc85 --- /dev/null +++ b/appl/wm/getauthinfo.b @@ -0,0 +1,291 @@ +implement WmGetauthinfo; + +include "sys.m"; + sys: Sys; + +include "security.m"; + login: Login; + +include "draw.m"; + draw: Draw; + +include "tk.m"; + tk: Tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "dialog.m"; + dialog: Dialog; + +include "keyring.m"; + kr: Keyring; + +include "string.m"; + +include "sh.m"; + +# +# Tk version of getauthinfo command +# +WmGetauthinfo: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +Wm: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +cfg := array[] of { + "frame .all -borderwidth 2 -relief raised", + + "frame .u", + "label .u.l -text {User } -anchor w", + "entry .u.e", + "pack .u.l .u.e -side left -in .u -expand 1", + "bind .u.e <Key-\n> {send cmd u}", + "focus .u.e", + + "frame .p", + "label .p.l -text {Password} -anchor w", + "entry .p.e -show *", + "pack .p.l .p.e -side left -in .p -expand 1", + "bind .p.e <Key-\n> {send cmd p}", + + "frame .s", + "label .s.l -text {Signer } -anchor w", + "entry .s.e", + "pack .s.l .s.e -side left -in .s -expand 1", + "bind .s.e <Key-\n> {send cmd s}", + + "frame .f", + "label .f.l -text {Save key} -anchor w", + "entry .f.e", + "pack .f.l .f.e -side left -in .f -expand 1", + "bind .f.e <Key-\n> {send cmd f}", + + "frame .b", + "radiobutton .b.p -variable save -value p -anchor w -text '" + "Permanent", + "radiobutton .b.t -variable save -value t -anchor w -text '" + "Temporary", + "pack .b.p .b.t -side right -in .b -expand 1", + ".b.p invoke", + "pack .u .p .s .f .b -in .all", + "pack .Wm_t .all -fill x -expand 1", + "update" +}; + +about : con "Generate keys and\n" + + "request certificate for\n" + + "mounting remote server"; + + +init(ctxt: ref Draw->Context, nil: list of string) +{ + sys = load Sys Sys->PATH; + if (ctxt == nil) { + sys->fprint(sys->fildes(2), "getauthinfo: no window context\n"); + raise "fail:bad context"; + } + kr = load Keyring Keyring->PATH; + str := load String String->PATH; + + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + + tkclient = load Tkclient Tkclient->PATH; + dialog = load Dialog Dialog->PATH; + tkclient->init(); + dialog->init(); + + (top, wmctl) := tkclient->toplevel(ctxt, "", + "Obtain Certificate for Server", Tkclient->Help); + for (c:=0; c<len cfg; c++) + tk->cmd(top, cfg[c]); + cmd := chan of string; + tk->namechan(top, cmd, "cmd"); + + login = load Login Login->PATH; + if(login == nil){ + dialog->prompt(ctxt, top.image, "error -fg red", "Error", + "Cannot load " + Login->PATH, 0, "Exit"::nil); + exit; + } + + # start interactive + usr := user(); + passwd := ""; + signer := defaultsigner(); + dir:= ""; + file := "net!"; + path := ""; + tk->cmd(top, ".u.e insert end '" + usr); + tk->cmd(top, ".s.e insert end '" + signer); + tk->cmd(top, "update"); + tkclient->onscreen(top, nil); + tkclient->startinput(top, "kbd"::"ptr"::nil); + info : ref Keyring->Authinfo; + for(;;){ + alt { + s := <-top.ctxt.kbd => + tk->keyboard(top, s); + s := <-top.ctxt.ptr => + tk->pointer(top, *s); + s := <-top.ctxt.ctl or + s = <-top.wreq => + tkclient->wmctl(top, s); + menu := <-wmctl => + case menu { + "exit" => + exit; + "help" => + dialog->prompt(ctxt, top.image, "info -fg green", "About", + about, 0, "OK"::nil); + } + tkclient->wmctl(top, menu); + rdy := <-cmd => + case (rdy[0]) { + 'u' => + usr = tk->cmd(top, ".u.e get"); + if(usr == "") + tk->cmd(top, "focus .u.e; update"); + else { + dir = "/usr/" + usr + "/keyring/"; + path = dir + file; + tk->cmd(top, ".f.e delete 0 end"); + tk->cmd(top, ".f.e insert end '" + path); + tk->cmd(top, "focus .p.e; update"); + } + continue; + 'p' => + passwd = tk->cmd(top, ".p.e get"); + if(passwd == "") + tk->cmd(top, "focus .p.e; update"); + else + tk->cmd(top, "focus .s.e; update"); + continue; + 's' => + signer = tk->cmd(top, ".s.e get"); + if(signer == "") + tk->cmd(top, "focus .s.e"); + else { + file = "net!" + signer; + path = dir + file; + tk->cmd(top, ".f.e delete 0 end"); + tk->cmd(top, ".f.e insert end " + path); + tk->cmd(top, "focus .f.e; update"); + } + continue; + 'f' => + path = tk->cmd(top, ".f.e get"); + if(path == "") { + tk->cmd(top, "focus .f.e; update"); + continue; + } + + # start encrypt key exchange + addr := "net!"+signer+"!inflogin"; + tk->cmd(top, "cursor -bitmap cursor.wait"); + err: string; + (err, info) = login->login(usr, passwd, addr); + tk->cmd(top, "cursor -default"); + if(info == nil){ + dialog->prompt(ctxt, top.image, "warning -fg yellow", "Warning", + err, 0, "Continue"::nil); + tk->cmd(top, ".p.e delete 0 end"); + tk->cmd(top, "focus .p.e"); + continue; + } + + # save the info for later access + save := tk->cmd(top, "variable save"); + (dir, file) = str->splitr(path, "/"); + if(save[0] == 't') + spawn save2file(dir, file); + + tk->cmd(top, "cursor -default"); + if(kr->writeauthinfo(path, info) < 0){ + dialog->prompt(ctxt, top.image, "error -fg red", "Error", + "Can't write to " + path, 0, "Exit"::nil); + exit; + } + if(save[0] == 'p') + dialog->prompt(ctxt, top.image, "info -fg green", "Notice", + "Authentication information is\nsaved in file:\n" + + path, 0, "OK"::nil); + else + dialog->prompt(ctxt, top.image, "info -fg green", "Notice", + "Authentication information is\nheld in a temporary file:\n" + + path, 0, "OK"::nil); + + return; + + } + } + } +} + + +user(): string +{ + sys = load Sys Sys->PATH; + + fd := sys->open("/dev/user", sys->OREAD); + if(fd == nil) + return ""; + + buf := array[Sys->NAMEMAX] of byte; + n := sys->read(fd, buf, len buf); + if(n < 0) + return ""; + + return string buf[0:n]; +} + +save2file(dir, file: string) +{ + if(sys->bind("#s", dir, Sys->MBEFORE) < 0) + exit; + fileio := sys->file2chan(dir, file); + if(fileio != nil) + exit; + + sys->pctl(Sys->NEWPGRP, nil); + + infodata := array[0] of byte; + + for(;;) alt { + (off, nbytes, fid, rc) := <-fileio.read => + if(rc == nil) + break; + if(off > len infodata){ + rc <-= (infodata[off:off], nil); + } else { + if(off + nbytes > len infodata) + nbytes = len infodata - off; + rc <-= (infodata[off:off+nbytes], nil); + } + + (off, data, fid, wc) := <-fileio.write => + if(wc == nil) + break; + + if(off != len infodata){ + wc <-= (0, "cannot be rewritten"); + } else { + nid := array[len infodata+len data] of byte; + nid[0:] = infodata; + nid[len infodata:] = data; + infodata = nid; + wc <-= (len data, nil); + } + data = nil; + } +} + +# get default signer server name +defaultsigner(): string +{ + return "$SIGNER"; +} diff --git a/appl/wm/hebrew.m b/appl/wm/hebrew.m new file mode 100644 index 00000000..63515aa7 --- /dev/null +++ b/appl/wm/hebrew.m @@ -0,0 +1,30 @@ +hebrewtab := array[] of { + Remaptab(' ', ' '), + Remaptab('t', 16r5d0+0), + Remaptab('c', 16r5d0+1), + Remaptab('d', 16r5d0+2), + Remaptab('s', 16r5d0+3), + Remaptab('v', 16r5d0+4), + Remaptab('u', 16r5d0+5), + Remaptab('z', 16r5d0+6), + Remaptab('j', 16r5d0+7), + Remaptab('y', 16r5d0+8), + Remaptab('h', 16r5d0+9), + Remaptab('l', 16r5d0+10), + Remaptab('f', 16r5d0+11), + Remaptab('k', 16r5d0+12), + Remaptab('o', 16r5d0+13), + Remaptab('n', 16r5d0+14), + Remaptab('i', 16r5d0+15), + Remaptab('b', 16r5d0+16), + Remaptab('x', 16r5d0+17), + Remaptab('g', 16r5d0+18), + Remaptab(';', 16r5d0+19), + Remaptab('p', 16r5d0+20), + Remaptab('.', 16r5d0+21), + Remaptab('m', 16r5d0+22), + Remaptab('e', 16r5d0+23), + Remaptab('r', 16r5d0+24), + Remaptab('a', 16r5d0+25), + Remaptab(',', 16r5d0+26) +}; diff --git a/appl/wm/keyboard.b b/appl/wm/keyboard.b new file mode 100644 index 00000000..7e257826 --- /dev/null +++ b/appl/wm/keyboard.b @@ -0,0 +1,511 @@ +implement Keybd; + +# +# extensive revision of code originally by N. W. Knauft +# +# Copyright © 1997 Lucent Technologies Inc. All rights reserved. +# Revisions Copyright © 1998 Vita Nuova Limited. All rights reserved. +# Rewritten code Copyright © 2001 Vita Nuova Holdings Limited. All rights reserved. +# +# To do: +# input from file +# calculate size + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + Rect, Point: import draw; + +include "tk.m"; + tk: Tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "arg.m"; + +include "keyboard.m"; + +Keybd: module +{ + init: fn(nil: ref Draw->Context, nil: list of string); +}; + +FONT: con "/fonts/lucidasans/boldlatin1.6.font"; +SPECFONT: con "/fonts/lucidasans/unicode.6.font"; + +# size in pixels +#KEYSIZE: con 16; +KEYSIZE: con 13; +KEYSPACE: con 2; +KEYBORDER: con 1; +KEYGAP: con KEYSPACE - (2 * KEYBORDER); +#ENDGAP: con 2 - KEYBORDER; +ENDGAP: con 0; + +Key: adt { + name: string; + val: int; + size: int; + x: list of int; + on: int; +}; + +background: con "#dddddd"; + +Backspace, Tab, Backslash, CapsLock, Return, Shift, Ctrl, Esc, Alt, Space: con iota; + +specials := array[] of { +Backspace => Key("<-", '\b', 28, nil, 0), +Tab => Key("Tab", '\t', 26, nil, 0), +Backslash => Key("\\\\", '\\', KEYSIZE, nil, 0), +CapsLock => Key("Caps", Keyboard->Caps, 40, nil, 0), +Return => Key("Enter", '\n', 36, nil, 0), +Shift => Key("Shift", Keyboard->LShift, 45, nil, 0), +Esc => Key("Esc", 8r33, 21, nil, 0), +Ctrl => Key("Ctrl", Keyboard->LCtrl, 36, nil, 0), +Alt => Key("Alt", Keyboard->LAlt, 22, nil, 0), +Space => Key(" ", ' ', 140, nil, 0), +Space+1 => Key("Return", '\n', 36, nil, 0), +}; + +keys:= array[] of { + # unshifted + array[] of { + "Esc", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\\\\", "`", nil, + "Tab", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "<-", nil, + "Ctrl", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "Enter", nil, + "Shift", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "Shift", nil, + "Caps", "Alt", " ", "Alt", nil, + }, + + # shifted + array[] of { + "Esc", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "|", "~", nil, + "Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "\\{", "\\}", "<-", nil, + "Ctrl", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":", "\"", "Return", nil, + "Shift", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", "Shift", nil, + "Caps", "Alt", " ", "Alt", nil, + }, +}; + +keyvals: array of array of int; +noexit := 0; + +init(ctxt: ref Draw->Context, args: list of string) +{ + sys = load Sys Sys->PATH; + if (ctxt == nil) { + sys->fprint(sys->fildes(2), "keyboard: no window context\n"); + raise "fail:bad context"; + } + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + arg := load Arg Arg->PATH; + + taskbar := 0; + winopts := Tkclient->Hide; + arg->init(args); + while ((opt := arg->opt()) != 0) { + case opt { + 't' => + taskbar = 1; + 'e' => + noexit = 1; + winopts = 0; + * => + sys->fprint(sys->fildes(2), "usage: keyboard [-et]\n"); + raise "fail:usage"; + } + } + + sys->pctl(Sys->NEWPGRP, nil); + tkclient->init(); + + keyvals = array[] of { + array[len keys[0]] of int, + array[len keys[1]] of int, + }; + setindex(keys[0], keyvals[0], specials); + setindex(keys[1], keyvals[1], specials); + + + (t, wcmd) := tkclient->toplevel(ctxt, "", "Kbd", winopts); + cmd(t, ". configure -bd 0 -relief flat"); + + for(i := 0; i < len keys[0]; i++) + if(keys[0][i] != nil) + cmd(t, sys->sprint("button .b%d -takefocus 0 -font %s -width %d -height %d -bd %d -activebackground %s -text {%s} -command 'send keypress %d", + i, FONT, KEYSIZE, KEYSIZE, KEYBORDER, background, keys[0][i], keyvals[0][i])); + + for(i = 0; i < len specials; i++) { + k := specials[i]; + for(xl := k.x; xl != nil; xl = tl xl) + cmd(t, sys->sprint(".b%d configure -font %s -width %d", hd xl, SPECFONT, k.size)); + } + + # pack buttons in rows + i = 0; + for(j:=0; i < len keys[0]; j++){ + rowf := sys->sprint(".f%d", j); + cmd(t, "frame "+rowf); + cmd(t, sys->sprint("frame .pad%d -height %d", j, KEYGAP)); + if(ENDGAP){ + cmd(t, rowf + ".pad -width " + string ENDGAP); + cmd(t, "pack " + rowf + ".pad -side left"); + } + for(; keys[0][i] != nil; i++){ + label := keys[0][i]; + expand := label != "\\\\" && len label > 1; + cmd(t, "pack .b" + string i + " -in "+ rowf + " -side left -fill x -expand "+string expand); + if(keys[0][i+1] != nil && KEYGAP > 0){ + padf := sys->sprint("%s.pad%d", rowf, i); + cmd(t, "frame " + padf + " -width " + string KEYGAP); + cmd(t, "pack " + padf + " -side left"); + } + } + if(ENDGAP){ + padf := sys->sprint("%s.pad%d", rowf, i); + cmd(t, "frame " + padf + " -width " + string ENDGAP); + cmd(t, "pack " + padf + " -side left"); + } + i++; + } + nrow := j; + + # pack rows in frame + for(j = 0; j < nrow; j++) + cmd(t, sys->sprint("pack .f%d .pad%d -fill x -in .", j, j)); + + (w, h) := (int cmd(t, ". cget -width"), int cmd(t, ". cget -height")); + r := t.screenr; + off := (r.dx()-w)/2; + cmd(t, sys->sprint(". configure -x %d -y %d", r.min.x+off, r.max.y-h)); + tkclient->onscreen(t, nil); + tkclient->startinput(t, "ptr" :: nil); + + spawn handle_keyclicks(t, wcmd, taskbar); +} + +setindex(keys: array of string, keyvals: array of int, spec: array of Key) +{ + for(i := 0; i < len keys; i++){ + if(keys[i] == nil) + continue; + val := keys[i][0]; + if(len keys[i] > 1 && val == '\\') + val = keys[i][1]; + for(j := 0; j < len spec; j++) + if(spec[j].name == keys[i]){ + if(!inlist(i, spec[j].x)) + spec[j].x = i :: spec[j].x; + val = spec[j].val; + break; + } + keyvals[i] = val; + } +} + +inlist(i: int, l: list of int): int +{ + for(; l != nil; l = tl l) + if(hd l == i) + return 1; + return 0; +} + +handle_keyclicks(t: ref Tk->Toplevel, wcmd: chan of string, taskbar: int) +{ + keypress := chan of string; + tk->namechan(t, keypress, "keypress"); + + if(taskbar) + tkclient->wmctl(t, "task"); + + cmd(t,"update"); + + collecting := 0; + collected := ""; + for(;;)alt { + k := <-keypress => + c := int k; + case c { + Keyboard->Caps => + active(t, Ctrl, 0); + active(t, Shift, 0); + active(t, Alt, 0); + active(t, CapsLock, -1); + redraw(t); + Keyboard->LShift => + active(t, Shift, -1); + redraw(t); + Keyboard->LCtrl => + active(t, Alt, 0); + active(t, Ctrl, -1); + active(t, Shift, 0); + redraw(t); + Keyboard->LAlt => + active(t, Alt, -1); + active(t, Ctrl, 0); + active(t, Shift, 0); + redraw(t); + if(specials[Alt].on){ + collecting = 1; + collected = ""; + }else + collecting = 0; + * => + if(collecting){ + collected[len collected] = c; + c = latin1(collected); + if(c < -1) + continue; + collecting = 0; + if(c == -1){ + for(i := 0; i < len collected; i++) + sendkey(t, collected[i]); + continue; + } + } + show := specials[Ctrl].on | specials[Alt].on | specials[Shift].on; + if(specials[Ctrl].on) + c &= 16r1F; + active(t, Ctrl, 0); + active(t, Alt, 0); + active(t, Shift, 0); + if(show) + redraw(t); + sendkey(t, c); + } + m := <-t.ctxt.ptr => + tk->pointer(t, *m); + s := <-t.ctxt.ctl or + s = <-t.wreq or + s = <-wcmd => + if (s == "exit" && noexit) + s = "task"; + tkclient->wmctl(t, s); + } +} + +sendkey(t: ref Tk->Toplevel, c: int) +{ + sys->fprint(t.ctxt.connfd, "key %d", c); +} + +active(t: ref Tk->Toplevel, keyno: int, on: int) +{ + key := specials[keyno:]; + if(on < 0) + key[0].on ^= 1; + else + key[0].on = on; + for(xl := key[0].x; xl != nil; xl = tl xl){ + col := background; + if(key[0].on) + col = "white"; + cmd(t, ".b"+string hd xl+" configure -bg "+col+ " -activebackground "+col); + } +} + +redraw(t: ref Tk->Toplevel) +{ + shifted := specials[Shift].on; + bank := keys[shifted]; + vals := keyvals[shifted]; + for(i:=0; i<len bank; i++) { + key := bank[i]; + val := vals[i]; + if(key != nil){ + if(specials[CapsLock].on && len key == 1){ + if(key[0]>='A' && key[0]<='Z') # true if also shifted + key[0] += 'a'-'A'; + else if(key[0] >= 'a' && key[0]<='z') + key[0] += 'A'-'a'; + val = key[0]; + } + cmd(t, ".b" + string i + " configure -text {" + key + "} -command 'send keypress " + string val); + } + } + cmd(t, "update"); +} + +# +# The code makes two assumptions: strlen(ld) is 1 or 2; latintab[i].ld can be a +# prefix of latintab[j].ld only when j<i. +# +Cvlist: adt +{ + ld: string; # must be seen before using this conversion + si: string; # options for last input characters + so: string; # the corresponding Rune for each si entry +}; +latintab: array of Cvlist = array[] of { + (" ", " i", "␣ı"), + ("!~", "-=~", "≄≇≉"), + ("!", "!<=>?bmp", "¡≮≠≯‽⊄∉⊅"), + ("\"*", "IUiu", "ΪΫϊϋ"), + ("\"", "\"AEIOUYaeiouy", "¨ÄËÏÖÜŸäëïöüÿ"), + ("$*", "fhk", "ϕϑϰ"), + ("$", "BEFHILMRVaefglopv", "ℬℰℱℋℐℒℳℛƲɑℯƒℊℓℴ℘ʋ"), + ("\'\"", "Uu", "Ǘǘ"), + ("\'", "\'ACEILNORSUYZacegilnorsuyz", "´ÁĆÉÍĹŃÓŔŚÚÝŹáćéģíĺńóŕśúýź"), + ("*", "*ABCDEFGHIKLMNOPQRSTUWXYZabcdefghiklmnopqrstuwxyz", "∗ΑΒΞΔΕΦΓΘΙΚΛΜΝΟΠΨΡΣΤΥΩΧΗΖαβξδεφγθικλμνοπψρστυωχηζ"), + ("+", "-O", "±⊕"), + (",", ",ACEGIKLNORSTUacegiklnorstu", "¸ĄÇĘĢĮĶĻŅǪŖŞŢŲąçęģįķļņǫŗşţų"), + ("-*", "l", "ƛ"), + ("-", "+-2:>DGHILOTZbdghiltuz~", "∓ƻ÷→ÐǤĦƗŁ⊖ŦƵƀðǥℏɨłŧʉƶ≂"), + (".", ".CEGILOZceglz", "·ĊĖĠİĿ⊙Żċėġŀż"), + ("/", "Oo", "Øø"), + ("1", "234568", "½⅓¼⅕⅙⅛"), + ("2", "-35", "ƻ⅔⅖"), + ("3", "458", "¾⅗⅜"), + ("4", "5", "⅘"), + ("5", "68", "⅚⅝"), + ("7", "8", "⅞"), + (":", ")-=", "☺÷≔"), + ("<!", "=~", "≨⋦"), + ("<", "-<=>~", "←«≤≶≲"), + ("=", ":<=>OV", "≕⋜≡⋝⊜⇒"), + (">!", "=~", "≩⋧"), + (">", "<=>~", "≷≥»≳"), + ("?", "!?", "‽¿"), + ("@\'", "\'", "ъ"), + ("@@", "\'EKSTYZekstyz", "ьЕКСТЫЗекстыз"), + ("@C", "Hh", "ЧЧ"), + ("@E", "Hh", "ЭЭ"), + ("@K", "Hh", "ХХ"), + ("@S", "CHch", "ЩШЩШ"), + ("@T", "Ss", "ЦЦ"), + ("@Y", "AEOUaeou", "ЯЕЁЮЯЕЁЮ"), + ("@Z", "Hh", "ЖЖ"), + ("@c", "h", "ч"), + ("@e", "h", "э"), + ("@k", "h", "х"), + ("@s", "ch", "щш"), + ("@t", "s", "ц"), + ("@y", "aeou", "яеёю"), + ("@z", "h", "ж"), + ("@", "ABDFGIJLMNOPRUVXabdfgijlmnopruvx", "АБДФГИЙЛМНОПРУВХабдфгийлмнопрувх"), + ("A", "E", "Æ"), + ("C", "ACU", "⋂ℂ⋃"), + ("Dv", "Zz", "DŽDž"), + ("D", "-e", "Ð∆"), + ("G", "-", "Ǥ"), + ("H", "-H", "Ħℍ"), + ("I", "-J", "ƗIJ"), + ("L", "&-Jj|", "⋀ŁLJLj⋁"), + ("N", "JNj", "NJℕNj"), + ("O", "*+-./=EIcoprx", "⊛⊕⊖⊙⊘⊜ŒƢ©⊚℗®⊗"), + ("P", "P", "ℙ"), + ("Q", "Q", "ℚ"), + ("R", "R", "ℝ"), + ("S", "123S", "¹²³§"), + ("T", "-u", "Ŧ⊨"), + ("V", "=", "⇐"), + ("Y", "R", "Ʀ"), + ("Z", "-ACSZ", "Ƶℤ"), + ("^", "ACEGHIJOSUWYaceghijosuwy", "ÂĈÊĜĤÎĴÔŜÛŴŶâĉêĝĥîĵôŝûŵŷ"), + ("_\"", "AUau", "ǞǕǟǖ"), + ("_,", "Oo", "Ǭǭ"), + ("_.", "Aa", "Ǡǡ"), + ("_", "AEIOU_aeiou", "ĀĒĪŌŪ¯āēīōū"), + ("`\"", "Uu", "Ǜǜ"), + ("`", "AEIOUaeiou", "ÀÈÌÒÙàèìòù"), + ("a", "ben", "↔æ∠"), + ("b", "()+-0123456789=bknpqru", "₍₎₊₋₀₁₂₃₄₅₆₇₈₉₌♝♚♞♟♛♜•"), + ("c", "$Oagu", "¢©∩≅∪"), + ("dv", "z", "dž"), + ("d", "-adegz", "ð↓‡°†ʣ"), + ("e", "$lmns", "€⋯—–∅"), + ("f", "a", "∀"), + ("g", "$-r", "¤ǥ∇"), + ("h", "-v", "ℏƕ"), + ("i", "-bfjps", "ɨ⊆∞ij⊇∫"), + ("l", "\"$&\'-jz|", "“£∧‘łlj⋄∨"), + ("m", "iou", "µ∈×"), + ("n", "jo", "nj¬"), + ("o", "AOUaeiu", "Å⊚Ůåœƣů"), + ("p", "Odgrt", "℗∂¶∏∝"), + ("r", "\"\'O", "”’®"), + ("s", "()+-0123456789=abnoprstu", "⁽⁾⁺⁻⁰ⁱ⁴⁵⁶⁷⁸⁹⁼ª⊂ⁿº⊃√ß∍∑"), + ("t", "-efmsu", "ŧ∃∴™ς⊢"), + ("u", "-AEGIOUaegiou", "ʉĂĔĞĬŎŬ↑ĕğĭŏŭ"), + ("v\"", "Uu", "Ǚǚ"), + ("v", "ACDEGIKLNORSTUZacdegijklnorstuz", "ǍČĎĚǦǏǨĽŇǑŘŠŤǓŽǎčďěǧǐǰǩľňǒřšťǔž"), + ("w", "bknpqr", "♗♔♘♙♕♖"), + ("x", "O", "⊗"), + ("y", "$", "¥"), + ("z", "-", "ƶ"), + ("|", "Pp|", "Þþ¦"), + ("~!", "=", "≆"), + ("~", "-=AINOUainou~", "≃≅ÃĨÑÕŨãĩñõũ≈"), +}; + +# +# Given 5 characters k[0]..k[4], find the rune or return -1 for failure. +# +unicode(k: string): int +{ + c := 0; + for(i:=1; i<5; i++){ + r := k[i]; + c <<= 4; + if('0'<=r && r<='9') + c += r-'0'; + else if('a'<=r && r<='f') + c += 10 + r-'a'; + else if('A'<=r && r<='F') + c += 10 + r-'A'; + else + return -1; + } + return c; +} + +# +# Given n characters k[0]..k[n-1], find the corresponding rune or return -1 for +# failure, or something < -1 if n is too small. In the latter case, the result +# is minus the required n. +# +latin1(k: string): int +{ + n := len k; + if(k[0] == 'X' || n>1 && k[0] == 'x' && k[1]!='O') # 'x' to avoid having to Shift as well + if(n>=5) + return unicode(k); + else + return -5; + for(i := 0; i < len latintab; i++){ + l := latintab[i]; + if(k[0] == l.ld[0]){ + if(n == 1) + return -2; + c := 0; + if(len l.ld == 1) + c = k[1]; + else if(l.ld[1] != k[1]) + continue; + else if(n == 2) + return -3; + else + c = k[2]; + for(p:=0; p < len l.si; p++) + if(l.si[p] == c && p < len l.so) + return l.so[p]; + return -1; + } + } + return -1; +} + +cmd(top: ref Tk->Toplevel, c: string): string +{ + e := tk->cmd(top, c); + if (e != nil && e[0] == '!') + sys->fprint(sys->fildes(2), "keyboard: tk error on '%s': %s\n", c, e); + return e; +} diff --git a/appl/wm/logon.b b/appl/wm/logon.b new file mode 100644 index 00000000..00643e87 --- /dev/null +++ b/appl/wm/logon.b @@ -0,0 +1,339 @@ +implement WmLogon; +# +# Logon program for Wm environment +# +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + Screen, Display, Image, Context, Point, Rect: import draw; + ctxt: ref Context; + +include "tk.m"; + tk: Tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "readdir.m"; + +include "arg.m"; +include "sh.m"; +include "newns.m"; +include "keyring.m"; +include "security.m"; + +WmLogon: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +cfg := array[] of { + "label .p -bitmap @/icons/inferno.bit -borderwidth 2 -relief raised", + "frame .l -bg red", + "label .l.u -fg black -bg silver -text {User Name:} -anchor w", + "pack .l.u -fill x", + "frame .e", + "entry .e.u -bg white", + "pack .e.u -fill x", + "frame .f -borderwidth 2 -relief raised", + "pack .l .e -side left -in .f", + "pack .p .f -fill x", + "bind .e.u <Key-\n> {send cmd ok}", + "focus .e.u" +}; + +listcfg := array[] of { + "frame .f", + "listbox .f.lb -yscrollcommand {.f.sb set}", + "scrollbar .f.sb -orient vertical -command {.f.lb yview}", + "button .login -text {Login} -command {send cmd login}", + "pack .f.sb .f.lb -in .f -side left -fill both -expand 1", + "pack .f -side top -anchor center -fill y -expand 1", + "pack .login -side top", +# "pack propagate . 0", +}; + +init(actxt: ref Draw->Context, args: list of string) +{ + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + if(tkclient == nil){ + sys->fprint(stderr(), "logon: cannot load %s: %r\n", Tkclient->PATH); + raise "fail:bad module"; + } + sys->pctl(Sys->NEWPGRP|Sys->FORKFD, nil); + tkclient->init(); + ctxt = actxt; + + dolist := 0; + usr := ""; + nsfile := "namespace"; + arg := load Arg Arg->PATH; + if(arg != nil){ + arg->init(args); + arg->setusage("logon [-l] [-n namespace] [-u user]"); + while((opt := arg->opt()) != 0){ + case opt{ + 'u' => + usr = arg->earg(); + 'l' => + dolist = 1; + 'n' => + nsfile = arg->earg(); + * => + arg->usage(); + } + } + args = arg->argv(); + arg = nil; + } else + args = nil; + if(ctxt == nil) + sys->fprint(stderr(), "logon: must run under a window manager\n"); + + (ctlwin, nil) := tkclient->toplevel(ctxt, nil, nil, Tkclient->Plain); + if(sys->fprint(ctlwin.ctxt.connfd, "request") == -1){ + sys->fprint(stderr(), "logon: must be run as principal wm application\n"); + raise "fail:lack of control"; + } + + if(dolist) + usr = chooseuser(ctxt); + + if (usr == nil || !logon(usr)) { + (panel, cmd) := makepanel(ctxt, cfg); + stop := chan of int; + spawn tkclient->handler(panel, stop); + for(;;) { + tk->cmd(panel, "focus .e.u; update"); + <-cmd; + usr = tk->cmd(panel, ".e.u get"); + if(usr == "") { + notice("You must supply a user name to login"); + continue; + } + if(logon(usr)) { + panel = nil; + stop <-= 1; + break; + } + tk->cmd(panel, ".e.u delete 0 end"); + } + } + ok: int; + if(nsfile != nil){ + (ok, nil) = sys->stat(nsfile); + if(ok < 0){ + nsfile = nil; + (ok, nil) = sys->stat("namespace"); + } + }else + (ok, nil) = sys->stat("namespace"); + if(ok >= 0) { + ns := load Newns Newns->PATH; + if(ns == nil) + notice("failed to load namespace builder"); + else if ((nserr := ns->newns(nil, nsfile)) != nil) + notice("namespace error:\n"+nserr); + } + tkclient->wmctl(ctlwin, "endcontrol"); + errch := chan of string; + spawn exec(ctxt, args, errch); + err := <-errch; + if (err != nil) { + sys->fprint(stderr(), "logon: %s\n", err); + raise "fail:exec failed"; + } +} + +makepanel(ctxt: ref Draw->Context, cmds: array of string): (ref Tk->Toplevel, chan of string) +{ + (t, nil) := tkclient->toplevel(ctxt, "-bg silver", nil, Tkclient->Plain); + + cmd := chan of string; + tk->namechan(t, cmd, "cmd"); + + for(i := 0; i < len cmds; i++) + tk->cmd(t, cmds[i]); + err := tk->cmd(t, "variable lasterr"); + if(err != nil) { + sys->fprint(stderr(), "logon: tk error: %s\n", err); + raise "fail:config error"; + } + tk->cmd(t, "update"); + centre(t); + tkclient->startinput(t, "kbd" :: "ptr" :: nil); + tkclient->onscreen(t, "onscreen"); + return (t, cmd); +} + +exec(ctxt: ref Draw->Context, argv: list of string, errch: chan of string) +{ + sys->pctl(sys->NEWFD, 0 :: 1 :: 2 :: nil); + { + argv = "/dis/wm/toolbar.dis" :: nil; + cmd := load Command hd argv; + if (cmd == nil) { + errch <-= sys->sprint("cannot load %s: %r", hd argv); + } else { + errch <-= nil; + spawn cmd->init(ctxt, argv); + } + }exception{ + "fail:*" => + exit; + } +} + +logon(user: string): int +{ + userdir := "/usr/"+user; + if(sys->chdir(userdir) < 0) { + notice("There is no home directory for \""+ + user+"\"\nmounted on this machine"); + return 0; + } + + chmod("/chan", Sys->DMDIR|8r777); + chmod("/chan/wmrect", 8r666); + chmod("/chan/wmctl", 8r666); + + # + # Set the user id + # + fd := sys->open("/dev/user", sys->OWRITE); + if(fd == nil) { + notice(sys->sprint("failed to open /dev/user: %r")); + return 0; + } + b := array of byte user; + if(sys->write(fd, b, len b) < 0) { + notice("failed to write /dev/user\nwith error "+sys->sprint("%r")); + return 0; + } + + return 1; +} + +chmod(file: string, mode: int): int +{ + d := sys->nulldir; + d.mode = mode; + if(sys->wstat(file, d) < 0){ + notice(sys->sprint("failed to chmod %s: %r", file)); + return -1; + } + return 0; +} + +chooseuser(ctxt: ref Draw->Context): string +{ + (t, cmd) := makepanel(ctxt, listcfg); + usrlist := getusers(); + if(usrlist == nil) + usrlist = "inferno" :: nil; + for(; usrlist != nil; usrlist = tl usrlist) + tkcmd(t, ".f.lb insert end '" + hd usrlist); + tkcmd(t, "update"); + stop := chan of int; + spawn tkclient->handler(t, stop); + u := ""; + for(;;){ + <-cmd; + sel := tkcmd(t, ".f.lb curselection"); + if(sel == nil) + continue; + u = tkcmd(t, ".f.lb get " + sel); + if(u != nil) + break; + } + stop <-= 1; + return u; +} + +getusers(): list of string +{ + readdir := load Readdir Readdir->PATH; + if(readdir == nil) + return nil; + (dirs, nil) := readdir->init("/usr", Readdir->NAME); + n: list of string; + for (i := len dirs -1; i >=0; i--) + if (dirs[i].qid.qtype & Sys->QTDIR) + n = dirs[i].name :: n; + return n; +} + +notecmd := array[] of { + "frame .f", + "label .f.l -bitmap error -foreground red", + "button .b -text Continue -command {send cmd done}", + "focus .f", + "bind .f <Key-\n> {send cmd done}", + "pack .f.l .f.m -side left -expand 1", + "pack .f .b", + "pack propagate . 0", +}; + +centre(t: ref Tk->Toplevel) +{ + org: Point; + ir := tk->rect(t, ".", Tk->Border|Tk->Required); + org.x = t.screenr.dx() / 2 - ir.dx() / 2; + org.y = t.screenr.dy() / 3 - ir.dy() / 2; +#sys->print("ir: %d %d %d %d\n", ir.min.x, ir.min.y, ir.max.x, ir.max.y); + if (org.y < 0) + org.y = 0; + tk->cmd(t, ". configure -x " + string org.x + " -y " + string org.y); +} + +notice(message: string) +{ + (t, nil) := tkclient->toplevel(ctxt, "-borderwidth 2 -relief raised", nil, Tkclient->Plain); + cmd := chan of string; + tk->namechan(t, cmd, "cmd"); + tk->cmd(t, "label .f.m -anchor nw -text '"+message); + for(i := 0; i < len notecmd; i++) + tk->cmd(t, notecmd[i]); + centre(t); + tkclient->onscreen(t, "onscreen"); + tkclient->startinput(t, "kbd"::"ptr"::nil); + stop := chan of int; + spawn tkclient->handler(t, stop); + tk->cmd(t, "update; cursor -default"); + <-cmd; + stop <-= 1; +} + +tkcmd(t: ref Tk->Toplevel, cmd: string): string +{ + s := tk->cmd(t, cmd); + if (s != nil && s[0] == '!') { + sys->print("%s\n", cmd); + sys->print("tk error: %s\n", s); + } + return s; +} + +stderr(): ref Sys->FD +{ + return sys->fildes(2); +} + +rf(path: string) : string +{ + fd := sys->open(path, sys->OREAD); + if(fd == nil) + return nil; + + buf := array[512] of byte; + n := sys->read(fd, buf, len buf); + if(n <= 0) + return nil; + + return string buf[0:n]; +} diff --git a/appl/wm/logwindow.b b/appl/wm/logwindow.b new file mode 100644 index 00000000..4d0326b4 --- /dev/null +++ b/appl/wm/logwindow.b @@ -0,0 +1,187 @@ +implement Logwindow; + +# +# Copyright © 1999 Vita Nuova Limited. All rights reserved. +# + +include "sys.m"; + sys: Sys; + stderr: ref Sys->FD; +include "draw.m"; + draw: Draw; +include "tk.m"; + tk: Tk; + cmd: import tk; +include "tkclient.m"; + tkclient: Tkclient; +include "arg.m"; + +Logwindow: module { + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +cfg := array[] of { + "frame .bf", + "checkbutton .bf.scroll -text Scroll -variable scroll -command {send cmd scroll}", + ".bf.scroll select", + "checkbutton .bf.popup -text {Pop up} -variable popup -command {send cmd popup}", + ".bf.popup select", + "pack .bf.scroll .bf.popup -side left", + "frame .t", + "scrollbar .t.scroll -command {.t.t yview}", + "text .t.t -height 7c -yscrollcommand {.t.scroll set}", + "pack .t.scroll -side left -fill y", + "pack .t.t -fill both -expand 1", + "pack .Wm_t -fill x", + "pack .bf -anchor w", + "pack .t -fill both -expand 1", + "pack propagate . 0", +}; + +eflag := 0; + +badmodule(p: string) +{ + sys->fprint(stderr, "logwindow: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init(ctxt: ref Draw->Context, argv: list of string) +{ + sys = load Sys Sys->PATH; + stderr = sys->fildes(2); + + tkclient = load Tkclient Tkclient->PATH; + if (tkclient == nil) + badmodule(Tkclient->PATH); + tkclient->init(); + + tk = load Tk Tk->PATH; + if (tk == nil) + badmodule(Tk->PATH); + + arg := load Arg Arg->PATH; + if (arg == nil) + badmodule(Arg->PATH); + + if (ctxt == nil) { + sys->fprint(stderr, "logwindow: nil Draw->Context\n"); + raise "fail:no draw context"; + } + gflag := 0; + title := "Log Window"; + arg->init(argv); + while ((opt := arg->opt()) != 0) { + case opt { + 'e' => + eflag = 1; + 'g' => + gflag = 1; + * => + sys->fprint(stderr, "usage: logwindow [-ge] [title]\n"); + raise "fail:usage"; + } + } + argv = arg->argv(); + if (argv != nil) + title = hd argv; + + if (!gflag) + sys->pctl(Sys->NEWPGRP, nil); + + (top, wmchan) := tkclient->toplevel(ctxt, "", title, Tkclient->Hide|Tkclient->Resize); + if (top == nil) { + sys->fprint(stderr, "logwindow: couldn't make window\n"); + raise "fail: no window"; + } + cmd(top, ". unmap"); + + for (c:=0; c<len cfg; c++) + tk->cmd(top, cfg[c]); + if ((err := tk->cmd(top, "variable lasterror")) != nil) { + sys->fprint(stderr, "logwindow: tk error: %s\n", err); + raise "fail: tk error"; + } + + logwin(sys->fildes(0), top, wmchan); +} + +scrolling := 1; +popup := 1; + +logwin(fd: ref Sys->FD, top: ref Tk->Toplevel, wmchan: chan of string) +{ + cmd := chan of string; + tk->namechan(top, cmd, "cmd"); + raised := 0; + ichan := chan of int; + spawn inputmon(fd, top, ichan); + tkclient->onscreen(top, nil); + tkclient->startinput(top, "kbd"::"ptr"::nil); + tkclient->wmctl(top, "task"); + for (;;) alt { + s := <-top.ctxt.kbd => + tk->keyboard(top, s); + s := <-top.ctxt.ptr => + tk->pointer(top, *s); + s := <-top.ctxt.ctl or + s = <-top.wreq or + s = <-wmchan => + case s { + "task" => + raised = 0; + "untask" => + raised = 1; + } + tkclient->wmctl(top, s); + e := <-ichan => + if (e == 0 && eflag) { + tkclient->wmctl(top, "exit"); + exit; + } + if (!raised && popup) + tkclient->wmctl(top, "untask"); + msg := <-cmd => + case msg { + "scroll" => + scrolling = int tk->cmd(top, "variable scroll"); + "popup" => + popup = int tk->cmd(top, "variable popup"); + } + } +} + +inputmon(fd: ref Sys->FD, top: ref Tk->Toplevel, ichan: chan of int) +{ + buf := array[Sys->ATOMICIO] of byte; + t := 0; + while ((n := sys->read(fd, buf[t:], len buf-t)) > 0) { + t += n; + cl := 0; + for (i := t - 1; i >= 0; i--) { + (nil, cl, nil) = sys->byte2char(buf, i); + if (cl > 0) + break; + } + if (cl == 0) + continue; + logmsg(top, ichan, string buf[0:i+cl]); + buf[0:] = buf[i+cl:t]; + t -= i + cl; + } + if (n < 0) + logmsg(top, ichan, sys->sprint("Input error: %r\n")); + else + logmsg(top, ichan, "Got EOF\n"); + if (eflag) + ichan <-= 0; +} + +logmsg(top: ref Tk->Toplevel, ichan: chan of int, m: string) +{ + tk->cmd(top, ".t.t insert end '"+m); + if (scrolling) + tk->cmd(top, ".t.t see end"); + tk->cmd(top, "update"); + ichan <-= 1; +} diff --git a/appl/wm/man.b b/appl/wm/man.b new file mode 100644 index 00000000..89b4d12f --- /dev/null +++ b/appl/wm/man.b @@ -0,0 +1,769 @@ +implement WmMan; + +include "sys.m"; + sys: Sys; +include "draw.m"; + draw: Draw; +include "tk.m"; + tk: Tk; +include "tkclient.m"; + tkclient: Tkclient; +include "plumbmsg.m"; +include "man.m"; + man: Man; + +WmMan: module { + init: fn (ctxt: ref Draw->Context, argv: list of string); +}; + +window: ref Tk->Toplevel; + +W: adt { + textwidth: fn(nil: self ref W, text: Parseman->Text): int; +}; + +ROMAN: con "/fonts/lucidasans/unicode.7.font"; +BOLD: con "/fonts/lucidasans/typelatin1.7.font"; +ITALIC: con "/fonts/lucidasans/italiclatin1.7.font"; +HEADING1: con "/fonts/lucidasans/boldlatin1.7.font"; +HEADING2: con "/fonts/lucidasans/italiclatin1.7.font"; +rfont, bfont, ifont, h1font, h2font: ref Draw->Font; + +GOATTR: con Parseman->ATTR_LAST << iota; +MANPATH: con "/man/1/man"; +INDENT: con 40; + +metrics: Parseman->Metrics; +parser: Parseman; + + +tkconfig := array [] of { + "frame .input", + "frame .view", + "text .view.t -state disabled -width 0 -height 0 -bg white -yscrollcommand {.view.yscroll set} -xscrollcommand {.view.xscroll set}", + "scrollbar .view.yscroll -orient vertical -command {.view.t yview}", + "scrollbar .view.xscroll -orient horizontal -command {.view.t xview}", + "entry .input.e -bg white", + "button .input.back -state disabled -bitmap small_color_left.bit -command {send nav b}", + "button .input.forward -state disabled -bitmap small_color_right.bit -command {send nav f}", + + "pack .input.back .input.forward -side left -anchor w", + "pack .input.e -expand 1 -fill x", + + "pack .view.yscroll -fill y -side left", + "pack .view.t -expand 1 -fill both", + + "bind .input.e <Key-\n> {send nav e}", + "bind .input.e <Button-1> +{grab set .input.e}", + "bind .input.e <ButtonRelease-1> +{grab release .input.e}", + "bind .view.t <Button-1> +{grab set .view.t}", + "bind .view.t <ButtonRelease-1> +{grab release .view.t}", + "bind .view.t <ButtonRelease-3> {send plumb %x %y}", + + "pack .input -fill x", + "pack .view -expand 1 -fill both", + "pack propagate . 0", + ". configure -width 500 -height 500", + "focus .input.e", +}; + +History: adt { + prev: cyclic ref History; + next: cyclic ref History; + topline: string; + searchstart: string; + searchend: string; + pick { + Search => + search: list of string; + Go => + path: string; + } +}; + +history: ref History; + + +init(ctxt: ref Draw->Context, argv: list of string) +{ + doplumb := 0; + + sys = load Sys Sys->PATH; + if (ctxt == nil) { + sys->fprint(sys->fildes(2), "man: no window context\n"); + raise "fail:bad context"; + } + sys->pctl(Sys->NEWPGRP, nil); + + draw = load Draw Draw->PATH; + if (draw == nil) + loaderr("Draw"); + + tk = load Tk Tk->PATH; + if (tk == nil) + loaderr(Tk->PATH); + + man = load Man Man->PATH; + if (man == nil) + loaderr(Man->PATH); + + tkclient = load Tkclient Tkclient->PATH; + if (tkclient == nil) + loaderr(Tkclient->PATH); + + parser = load Parseman Parseman->PATH; + if (parser == nil) + loaderr(Parseman->PATH); + parser->init(); + + plumber := load Plumbmsg Plumbmsg->PATH; + if (plumber != nil) { + if (plumber->init(1, nil, 0) >= 0) + doplumb = 1; + } + + argv = tl argv; + + rfont = draw->(Draw->Font).open(ctxt.display, ROMAN); + bfont = draw->(Draw->Font).open(ctxt.display, BOLD); + ifont = draw->(Draw->Font).open(ctxt.display, ITALIC); + h1font = draw->(Draw->Font).open(ctxt.display, HEADING1); + h2font = draw->(Draw->Font).open(ctxt.display, HEADING2); + + em := draw->rfont.width("m"); + en := draw->rfont.width("n"); + metrics = Parseman->Metrics(490, 80, em, en, 14, 40, 20); + + tkclient->init(); + buts := Tkclient->Resize | Tkclient->Hide; + winctl: chan of string; + (window, winctl) = tkclient->toplevel(ctxt, nil, "Man", buts); + nav := chan of string; + plumb := chan of string; + tk->namechan(window, nav, "nav"); + tk->namechan(window, plumb, "plumb"); + for(tc:=0; tc<len tkconfig; tc++) + tkcmd(window, tkconfig[tc]); + if ((err := tkcmd(window, "variable lasterror")) != nil) { + sys->fprint(sys->fildes(2), "man: tk initialization failed: %s\n", err); + raise "fail:tk"; + } + fittoscreen(window); + tkcmd(window, "update"); + mktags(); + + vw := int tkcmd(window, ".view.t cget -actwidth") - 10; + if (vw <= 0) + vw = 1; + metrics.pagew = vw; + + linechan := chan of list of (int, Parseman->Text); + man->loadsections(nil); + + pidc := chan of int; + + if (argv != nil) { + if (hd argv == "-f") { + first: ref History; + for (argv = tl argv; argv != nil; argv = tl argv) { + hnode := ref History.Go(history, nil, "", "", "", hd argv); + if (history != nil) + history.next = hnode; + history = hnode; + if (first == nil) + first = history; + } + history = first; + } else + history = ref History.Search(nil, nil, "", "", "", argv); + } + + if (history == nil) + history = ref History.Go(nil, nil, "", "", "", MANPATH); + + setbuttons(); + spawn printman(pidc, linechan, history); + layoutpid := <- pidc; + tkclient->onscreen(window, nil); + tkclient->startinput(window, "kbd"::"ptr"::nil); + for (;;) alt { + s := <-window.ctxt.kbd => + tk->keyboard(window, s); + s := <-window.ctxt.ptr => + tk->pointer(window, *s); + s := <-window.ctxt.ctl or + s = <-window.wreq or + s = <-winctl => + e := tkclient->wmctl(window, s); + if (e == nil && s[0] == '!') { + topline := tkcmd(window, ".view.t yview"); + (nil, toptoks) := sys->tokenize(topline, " "); + if (toptoks != nil) + history.topline = hd toptoks; + vw = int tkcmd(window, ".view.t cget -actwidth") - 10; + if (vw <= 0) + vw = 1; + if (vw != metrics.pagew) { + if (layoutpid != -1) + kill(layoutpid); + metrics.pagew = vw; + tkcmd(window, ".view.t delete 1.0 end"); + tkcmd(window, "update"); + spawn printman(pidc, linechan, history); + layoutpid = <- pidc; + } + } + line := <- linechan => + if (line == nil) { + # layout done + if (history.topline != "") { + topline := tkcmd(window, ".view.t yview"); + (nil, toptoks) := sys->tokenize(topline, " "); + if (toptoks != nil) + if (hd toptoks == "0") + tkcmd(window, ".view.t yview moveto " + history.topline); + } + tkcmd(window, "update"); + } else + setline(line); + go := <- nav => + topline := tkcmd(window, ".view.t yview"); + (nil, toptoks) := sys->tokenize(topline, " "); + if (toptoks != nil) + history.topline = hd toptoks; + case go[0] { + 'f' => + # forward + history = history.next; + setbuttons(); + if (layoutpid != -1) + kill(layoutpid); + tkcmd(window, ".view.t delete 1.0 end"); + tkcmd(window, "update"); + spawn printman(pidc, linechan, history); + layoutpid = <- pidc; + 'b' => + # back + history = history.prev; + setbuttons(); + if (layoutpid != -1) + kill(layoutpid); + tkcmd(window, ".view.t delete 1.0 end"); + tkcmd(window, "update"); + spawn printman(pidc, linechan, history); + layoutpid = <- pidc; + 'e' or 'l' => + t := ""; + if (go[0] == 'l') { + # link + t = go[1:]; + } else { + # entry + t = tkcmd(window, ".input.e get"); + for (i := 0; i < len t; i++) + if (!(t[i] == ' ' || t[i] == '\t')) + break; + if (i == len t) + break; + t = t[i:]; + if (t[0] == '/' || t[0] == '?') { + search(t); + break; + } + } + (n, toks) := sys->tokenize(t, " \t"); + if (n == 0) + continue; + h := ref History.Search(history, nil, "", "", "", toks); + history.next = h; + history = h; + setbuttons(); + if (layoutpid != -1) + kill(layoutpid); + tkcmd(window, ".view.t delete 1.0 end"); + tkcmd(window, "update"); + spawn printman(pidc, linechan, history); + layoutpid = <- pidc; + 'g' => + # goto file + h := ref History.Go(history, nil, "", "", "", go[1:]); + history.next = h; + history = h; + setbuttons(); + if (layoutpid != 0) + kill(layoutpid); + tkcmd(window, ".view.t delete 1.0 end"); + tkcmd(window, "update"); + spawn printman(pidc, linechan, history); + layoutpid = <- pidc; + } + p := <- plumb => + if (!doplumb) + break; + (nil, l) := sys->tokenize(p, " "); + x := int hd l; + y := int hd tl l; + index := tkcmd(window, ".view.t index @"+string x+","+string y); + selindex := tkcmd(window, ".view.t tag ranges sel"); + insel := 0; + if(selindex != "") + insel = tkcmd(window, ".view.t compare sel.first <= "+index)=="1" && + tkcmd(window, ".view.t compare sel.last >= "+index)=="1"; + text := ""; + attr := ""; + if (insel) + text = tkcmd(window, ".view.t get sel.first sel.last"); + else{ + # have line with text in it + # now extract whitespace-bounded string around click + (nil, w) := sys->tokenize(index, "."); + charno := int hd tl w; + left := tkcmd(window, ".view.t index {"+index+" linestart}"); + right := tkcmd(window, ".view.t index {"+index+" lineend}"); + line := tkcmd(window, ".view.t get "+left+" "+right); + for(i:=charno; i>0; --i) + if(line[i-1]==' ' || line[i-1]=='\t') + break; + for(j:=charno; j<len line; j++) + if(line[j]==' ' || line[j]=='\t') + break; + text = line[i:j]; + attr = "click="+string (charno-i); + } + msg := ref Plumbmsg->Msg( + "WmMan", + "", + "", + "text", + attr, + array of byte text); + plumber->msg.send(); + + layoutpid = <- pidc => + ; + } +} + +search(pat: string) +{ + dir: string; + start: string; + if (pat[0] == '/') { + dir = "-forwards"; + start = history.searchend; + } else { + dir = "-backwards"; + start = history.searchstart; + } + pat = pat[1:]; + if (start == "") + start = "1.0"; + r := tkcmd(window, ".view.t search " + dir + " -- " + tk->quote(pat) + " " + start); + if (r != nil) { + history.searchstart = r; + history.searchend = r + "+" + string len pat + "c"; + tkcmd(window, ".view.t tag remove sel 1.0 end"); + tkcmd(window, ".view.t tag add sel " + history.searchstart + " " + history.searchend); + tkcmd(window, ".view.t see " + r); + tkcmd(window, "update"); + } +} + +setbuttons() +{ + if (history.prev == nil) + tkcmd(window, ".input.back configure -state disabled"); + else + tkcmd(window, ".input.back configure -state normal"); + if (history.next == nil) + tkcmd(window, ".input.forward configure -state disabled"); + else + tkcmd(window, ".input.forward configure -state normal"); +} + +dolayout(linechan: chan of list of (int, Parseman->Text), path: string) +{ + fd := sys->open(path, Sys->OREAD); + if (fd == nil) { + layouterror(linechan, sys->sprint("cannot open file %s: %r", path)); + return; + } + w: ref W; + parser->parseman(fd, metrics, 0, w, linechan); +} + +printman(pidc: chan of int, linechan: chan of list of (int, Parseman->Text), h: ref History) +{ + pidc <-= sys->pctl(0, nil); + args: list of string; + pick hp := h { + Search => + args = hp.search; + Go => + dolayout(linechan, hp.path); + pidc <-= -1; + return; + } + sections: list of string; + argstext := ""; + addsections := 1; + keywords: list of string; + for (; args != nil; args = tl args) { + arg := hd args; + if (arg == nil) + continue; + if (addsections && !isint(arg)) { + addsections = 0; + keywords = args; + } + if (addsections) + sections = arg :: sections; + argstext = argstext + " " + arg; + } + manpages := man->getfiles(sections, keywords); + pagelist := sortpages(manpages); + if (len pagelist == 1) { + (nil, path, nil) := hd pagelist; + dolayout(linechan, path); + pidc <-= -1; + return; + } + + tt := Parseman->Text(Parseman->FONT_ROMAN, 0, "Search:", 1, nil); + at := Parseman->Text(Parseman->FONT_BOLD, 0, argstext, 0, nil); + linechan <-= (0, tt)::(0, at)::nil; + tt.text = ""; + linechan <-= (0, tt)::nil; + + if (pagelist == nil) { + donet := Parseman->Text(Parseman->FONT_ROMAN, 0, "No matches", 0, nil); + linechan <-= (INDENT, donet) :: nil; + linechan <-= nil; + pidc <-= -1; + return; + } + + linelist: list of list of Parseman->Text; + pathlist: list of Parseman->Text; + + maxkwlen := 0; + comma := Parseman->Text(Parseman->FONT_ROMAN, 0, ", ", 0, ""); + for (; pagelist != nil; pagelist = tl pagelist) { + (n, p, kwl) := hd pagelist; + l := 0; + keywords: list of Parseman->Text = nil; + for (; kwl != nil; kwl = tl kwl) { + kw := hd kwl; + kwt := Parseman->Text(Parseman->FONT_ITALIC, GOATTR, kw, 0, p); + nt := Parseman->Text(Parseman->FONT_ROMAN, GOATTR, "(" + string n + ")", 0, p); + l += textwidth(kwt) + textwidth(nt); + if (keywords != nil) { + l += textwidth(comma); + keywords = nt :: kwt :: comma :: keywords; + } else + keywords = nt :: kwt :: nil; + } + if (l > maxkwlen) + maxkwlen = l; + linelist = keywords :: linelist; + ptext := Parseman->Text(Parseman->FONT_ROMAN, GOATTR, p, 0, ""); + pathlist = ptext :: pathlist; + } + + for (; pathlist != nil; (pathlist, linelist) = (tl pathlist, tl linelist)) { + line := (10 + INDENT + maxkwlen, hd pathlist) :: nil; + for (ll := hd linelist; ll != nil; ll = tl ll) { + litem := hd ll; + if (tl ll == nil) + line = (INDENT, litem) :: line; + else + line = (0, litem) :: line; + } + linechan <-= line; + } + linechan <-= nil; + pidc <-= -1; +} + +layouterror(linechan: chan of list of (int, Parseman->Text), msg: string) +{ + text := "ERROR: " + msg; + t := Parseman->Text(Parseman->FONT_ROMAN, 0, text, 0, nil); + linechan <-= (0, t)::nil; + linechan <-= nil; +} + +loaderr(modname: string) +{ + sys->print("cannot load %s module: %r\n", modname); + raise "fail:init"; +} + +W.textwidth(nil: self ref W, text: Parseman->Text): int +{ + return textwidth(text); +} + +textwidth(text: Parseman->Text): int +{ + f: ref Draw->Font; + if (text.heading == 1) + f = h1font; + else if (text.heading == 2) + f = h2font; + else { + case text.font { + Parseman->FONT_ROMAN => + f = rfont; + Parseman->FONT_BOLD => + f = bfont; + Parseman->FONT_ITALIC => + f = ifont; + * => + return 8 * len text.text; + } + } + return draw->f.width(text.text); +} + +lnum := 0; + +setline(line: list of (int, Parseman->Text)) +{ + tabstr := ""; + linestr := ""; + lastoff := 0; + curfont := Parseman->FONT_ROMAN; + curlink := ""; + curgtag := ""; + curheading := 0; + fonttext := ""; + + for (l := line; l != nil; l = tl l) { + (offset, nil) := hd l; + if (offset != 0) { + lastoff = offset; + if (tabstr != "") + tabstr[len tabstr] = ' '; + tabstr = tabstr + string offset; + } + } + # fudge up tabs for rest of line + if (lastoff != 0) + tabstr = tabstr + " " + string lastoff + " " + string (lastoff + INDENT); + ttag := ""; + gtag := ""; + if (tabstr != nil) + ttag = tabtag(tabstr) + " "; + + for (l = line; l != nil; l = tl l) { + (offset, text) := hd l; + gtag = ""; + if (text.link != nil) { + if (text.attr & GOATTR) + gtag = gotag(text.link) + " "; + else { + gtag = linktag(text.link) + " "; + } + } + if (offset != 0) + fonttext[len fonttext] = '\t'; + if (text.font != curfont || text.link != curlink || text.heading != curheading || gtag != curgtag) { + # need to change tags + linestr = linestr + " " + tk->quote(fonttext) + " {" + ttag + curgtag + fonttag(curfont, curheading) + "}"; + ttag = ""; + curgtag = gtag; + fonttext = ""; + curfont = text.font; + curlink = text.link; + curheading = text.heading; + } + fonttext = fonttext + text.text; + } + if (fonttext != nil) + linestr = linestr + " " + tk->quote(fonttext) + " {" + ttag + curgtag + fonttag(curfont, curheading) + "}"; + tkcmd(window, ".view.t insert end " + linestr); + tkcmd(window, ".view.t insert end {\n}"); + # only update on every other line + if (lnum++ & 1) + tkcmd(window, "update"); +} + +mktags() +{ + tkcmd(window, ".view.t tag configure ROMAN -font " + ROMAN); + tkcmd(window, ".view.t tag configure BOLD -font " + BOLD); + tkcmd(window, ".view.t tag configure ITALIC -font " + ITALIC); + tkcmd(window, ".view.t tag configure H1 -font " + HEADING1); + tkcmd(window, ".view.t tag configure H2 -font " + HEADING2); +} + +fonttag(font, heading: int): string +{ + if (heading == 1) + return "H1"; + if (heading == 2) + return "H2"; + case font { + Parseman->FONT_ROMAN => + return "ROMAN"; + Parseman->FONT_BOLD => + return "BOLD"; + Parseman->FONT_ITALIC => + return "ITALIC"; + } + return nil; +} + +nexttag := 0; +lasttabstr := ""; +lasttagname := ""; + +tabtag(tabstr: string): string +{ + if (tabstr == lasttabstr) + return lasttagname; + lasttagname = "TAB" + string nexttag++; + lasttabstr = tabstr; + tkcmd(window, ".view.t tag configure " + lasttagname + " -tabs " + tk->quote(tabstr)); + return lasttagname; +} + +# optimise this! +gotag(path: string): string +{ + cmd := "{send nav g" + path + "}"; + name := "GO" + string nexttag++; + tkcmd(window, ".view.t tag bind " + name + " <ButtonRelease-1> +" + cmd); + tkcmd(window, ".view.t tag configure " + name + " -fg green"); + return name; +} + +# and this! +linktag(search: string): string +{ + cmd := tk->quote("send nav l" + search); + name := "LN" + string nexttag++; + tkcmd(window, ".view.t tag bind " + name + " <ButtonRelease-1> +" + cmd); + tkcmd(window, ".view.t tag configure " + name + " -fg green"); + return name; +} + +isint(s: string): int +{ + for (i := 0; i < len s; i++) + if (s[i] < '0' || s[i] > '9') + return 0; + return 1; +} + +kill(pid: int) +{ + pctl := sys->open("/prog/" + string pid + "/ctl", Sys->OWRITE); + if (pctl != nil) { + poison := array of byte "kill"; + sys->write(pctl, poison, len poison); + } +} + +revsortuniq(strlist: list of string): list of string +{ + strs := array [len strlist] of string; + for (i := 0; strlist != nil; (i, strlist) = (i+1, tl strlist)) + strs[i] = hd strlist; + + # simple sort (ascending) + for (i = 0; i < len strs - 1; i++) { + for (j := i+1; j < len strs; j++) + if (strs[i] < strs[j]) + (strs[i], strs[j]) = (strs[j], strs[i]); + } + + # construct list (result is descending) + r: list of string; + prev := ""; + for (i = 0; i < len strs; i++) { + if (strs[i] != prev) { + r = strs[i] :: r; + prev = strs[i]; + } + } + return r; +} + +sortpages(pagelist: list of (int, string, string)): list of (int, string, list of string) +{ + pages := array [len pagelist] of (int, string, string); + for (i := 0; pagelist != nil; (i, pagelist) = (i+1, tl pagelist)) + pages[i] = hd pagelist; + + for (i = 0; i < len pages - 1; i++) { + for (j := i+1; j < len pages; j++) { + (nil, nil, ipath) := pages[i]; + (nil, nil, jpath) := pages[j]; + if (ipath > jpath) + (pages[i], pages[j]) = (pages[j], pages[i]); + } + } + + r: list of (int, string, list of string); + filecmds: list of string; + lastfile := ""; + lastsect := 0; + for (i = 0; i < len pages; i++) { + (section, cmd, file) := pages[i]; + if (lastfile == "") { + lastfile = file; + lastsect = section; + } + + if (file != lastfile) { + r = (lastsect, lastfile, filecmds) :: r; + lastfile = file; + lastsect = section; + filecmds = nil; + } + filecmds = cmd :: filecmds; + } + if (filecmds != nil) + r = (lastsect, lastfile, revsortuniq(filecmds)) :: r; + return r; +} + +fittoscreen(win: ref Tk->Toplevel) +{ + Point, Rect: import draw; + if (win.image == nil || win.image.screen == nil) + return; + r := win.image.screen.image.r; + scrsize := Point((r.max.x - r.min.x), (r.max.y - r.min.y)); + bd := int tkcmd(win, ". cget -bd"); + winsize := Point(int tkcmd(win, ". cget -actwidth") + bd * 2, int tkcmd(win, ". cget -actheight") + bd * 2); + if (winsize.x > scrsize.x) + tkcmd(win, ". configure -width " + string (scrsize.x - bd * 2)); + if (winsize.y > scrsize.y) + tkcmd(win, ". configure -height " + string (scrsize.y - bd * 2)); + actr: Rect; + actr.min = Point(int tkcmd(win, ". cget -actx"), int tkcmd(win, ". cget -acty")); + actr.max = actr.min.add((int tkcmd(win, ". cget -actwidth") + bd*2, + int tkcmd(win, ". cget -actheight") + bd*2)); + (dx, dy) := (actr.dx(), actr.dy()); + if (actr.max.x > r.max.x) + (actr.min.x, actr.max.x) = (r.max.x - dx, r.max.x); + if (actr.max.y > r.max.y) + (actr.min.y, actr.max.y) = (r.max.y - dy, r.max.y); + if (actr.min.x < r.min.x) + (actr.min.x, actr.max.x) = (r.min.x, r.min.x + dx); + if (actr.min.y < r.min.y) + (actr.min.y, actr.max.y) = (r.min.y, r.min.y + dy); + tkcmd(win, ". configure -x " + string actr.min.x + " -y " + string actr.min.y); +} + +tkcmd(top: ref Tk->Toplevel, s: string): string +{ + e := tk->cmd(top, s); + if (e != nil && e[0] == '!') { + sys->print("tk error %s on '%s'\n", e, s); + } + return e; +} diff --git a/appl/wm/mand.b b/appl/wm/mand.b new file mode 100644 index 00000000..7e060722 --- /dev/null +++ b/appl/wm/mand.b @@ -0,0 +1,839 @@ +implement Mand; + +# +# Copyright © 2000 Vita Nuova Limited. All rights reserved. +# + +# mandelbrot/julia fractal browser: +# button 1 - drag a rectangle to zoom into +# button 2 - (from mandel only) show julia at point +# button 3 - zoom out + +include "sys.m"; + sys : Sys; +include "draw.m"; + draw : Draw; + Point, Rect, Image, Context, Screen, Display : import draw; +include "tk.m"; + tk: Tk; +include "tkclient.m"; + tkclient: Tkclient; + +Mand : module +{ + init : fn(nil : ref Context, argv : list of string); +}; + +colours: array of ref Image; +stderr : ref Sys->FD; + +FIX: type big; + +Calc: adt { + xr, yr: array of FIX; + parx, pary: FIX; + # column order + dispbase: array of COL; # auxiliary display and border + imgch: chan of (ref Image, Rect); + img: ref Image; + maxx, maxy, supx, supy: int; + disp: int; # origin of auxiliary display + morj : int; + winr: Rect; + kdivisor: int; + pointsdone: int; +}; + +# BASE, LIMIT, MAXCOUNT, MINDELTA may be varied + +# +# calls with 256X128 on initial set +# --------------------------------- +# crawl 58 (5% of time) +# fillline 894 (6% of time) +# isblank 5012 (0% of time) +# mcount 6928 (55% of time) +# getcolour 52942 (11% of time) +# displayset 1 (15% of time) +# +WHITE : con 16r0; +BLACK : con 16rff; + +COL : type byte; + +BASE : con 60; # 28 +HBASE : con (BASE/2); +SCALE : con (big 1<<BASE); +TWO : con (big 1<<(BASE+1)); +FOUR : con (big 1<<(BASE+2)); +NEG : con (~((big 1<<(32-HBASE))-big 1)); +MINDELTA : con (big 1<<(HBASE-1)); # (1<<(HBASE-2)) + +SCHEDCOUNT: con 100; + +BLANK : con 0; # blank pixel +BORDER : con 255; # border pixel +LIMIT : con 4; # 4 or 5 + +# pointcolour() returns values in the range 1..MAXCOUNT+1 +# these must not clash with 0 or 255 +# hence 0 <= MAXCOUNT <= 253 +# +MAXCOUNT : con 253; # 92 64 + +# colour cube +R, G, B : int; + +# initial width and height +WIDTH: con 400; +HEIGHT: con 400; + +Fracpoint: adt { + x, y: real; +}; + +Fracrect: adt { + min, max: Fracpoint; +}; + +Params: adt { + r: Fracrect; + p: Fracpoint; + m: int; + kdivisor: int; + fill: int; +}; + +Usercmd: adt { + pick { + Zoomin => + r: Rect; + Julia => + p: Point; + Zoomout or + Restart => + # nothing + } +}; + +badmod(mod: string) +{ + sys->fprint(stderr, "mand: cannot load %s: %r\n", mod); + raise "fail:bad module"; +} + +win_config := array[] of { + "frame .f", + "label .f.dl -text Depth", + "entry .f.depth", + ".f.depth insert 0 1", + "checkbutton .f.fill -text {Fill} -command {send cmd fillchanged} -variable fill", + ".f.fill select", + "pack .f.dl -side left", + "pack .f.fill -side right", + "pack .f.depth -side top -fill x", + "frame .c -bd 3 -relief sunken -width " + string WIDTH + " -height " + string HEIGHT, + "pack .f -side top -fill x", + "pack .c -side bottom -fill both -expand 1", + "pack propagate . 0", + "bind .c <Button-1> {send cmd b1 %x %y}", + "bind .c <ButtonRelease-2> {send cmd b2 %x %y}", + "bind .c <ButtonRelease-1> {send cmd b1r %x %y}", + "bind .c <ButtonRelease-3> {send cmd b3 %x %y}", + + "bind .f.depth <Key-\n> {send cmd setkdivisor}", + "update", +}; + +mouseproc(win: ref Tk->Toplevel) +{ + for(;;) + tk->pointer(win, *<-win.ctxt.ptr); +} + +init(ctxt: ref Context, argv : list of string) +{ + sys = load Sys Sys->PATH; + stderr = sys->fildes(2); + + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + + tkclient = load Tkclient Tkclient->PATH; + if (tkclient == nil) badmod(Tkclient->PATH); + + tkclient->init(); + if (ctxt == nil) + ctxt = tkclient->makedrawcontext(); + (win, wmcmd) := tkclient->toplevel(ctxt, "", "Fractals", Tkclient->Appl); + sys->pctl(Sys->NEWPGRP, nil); + + cmdch := chan of string; + tk->namechan(win, cmdch, "cmd"); + for (i := 0; i < len win_config; i++) + cmd(win, win_config[i]); + tkclient->onscreen(win, nil); + tkclient->startinput(win, "kbd"::"ptr"::nil); + fittoscreen(win); + cmd(win, "update"); + spawn mouseproc(win); + + R = G = B = 6; + argv = tl argv; + if (argv != nil) { (R, argv) = (int hd argv, tl argv); if (R <= 0) R = 1; } + if (argv != nil) { (G, argv) = (int hd argv, tl argv); if (G <= 0) G = 1; } + if (argv != nil) { (B, argv) = (int hd argv, tl argv); if (B <= 0) B = 1; } + colours = array[256] of ref Image; + for (i = 0; i < len colours; i++) + # colours[i] = ctxt.display.color(i); + colours[i] = ctxt.display.rgb(col(i/(G*B), R), + col(i/(1*B), G), + col(i/(1*1), B)); + specr := Fracrect((-2.0, -1.5), (1.0, 1.5)); + p := Params( + correctratio(specr, win), + (0.0, 0.0), + 1, # m + 1, # kdivisor + int cmd(win, "variable fill") + ); + pid := -1; + sync := chan of int; + imgch := chan of (ref Image, Rect); + canvr := canvposn(win); + spawn docalculate(sync, p, imgch); + pid = <-sync; + imgch <-= (win.image, canvr); + + stack: list of (Fracrect, Params); + for(;;){ + restart := 0; + alt { + s := <-win.ctxt.kbd => + tk->keyboard(win, s); + c := <-win.ctxt.ctl or + c = <-win.wreq or + c = <-wmcmd => + if(c[0] == '!'){ + if(pid != -1) + restart = winreq(win, c, imgch, sync); + else + restart = winreq(win, c, nil, nil); + }else{ + tkclient->wmctl(win, c); + if(c == "task" && pid != -1){ + kill(pid); + pid = -1; + } + } + press := <-cmdch => + (nil, toks) := sys->tokenize(press, " "); + ucmd: ref Usercmd = nil; + case hd toks { + "start" => + ucmd = ref Usercmd.Restart; + "b1" or "b2" or "b3" => + #cmd(win, "grab set .c"); + #fiximage(win); + ucmd = trackmouse(win, cmdch, hd toks, Point(int hd tl toks, int hd tl tl toks)); + #cmd(win, "grab release .c"); + "fillchanged" => + p.fill = int cmd(win, "variable fill"); + ucmd = ref Usercmd.Restart; + "setkdivisor" => + p.kdivisor = int cmd(win, ".f.depth get"); + if (p.kdivisor < 1) + p.kdivisor = 1; + ucmd = ref Usercmd.Restart; + } + if (ucmd != nil) { + pick u := ucmd { + Zoomin => + # sys->print("zoomin to %s\n", r2s(u.r)); + if (u.r.dx() > 0 && u.r.dy() > 0) { + stack = (specr, p) :: stack; + specr.min = pt2real(u.r.min, win, p.r); + specr.max = pt2real(u.r.max, win, p.r); + (specr.min.y, specr.max.y) = (specr.max.y, specr.min.y); # canonicalise + restart = 1; + } + Zoomout => + if (stack != nil) { + ((specr, p), stack) = (hd stack, tl stack); + cmd(win, ".f.depth delete 0 end"); + cmd(win, ".f.depth insert 0 " + string p.kdivisor); + if (p.fill) + cmd(win, ".f.fill select"); + else + cmd(win, ".f.fill deselect"); + cmd(win, "update"); + restart = 1; + } + Julia => + # pt := pt2real(u.p, win, p.r); + if (p.m) { + stack = (specr, p) :: stack; + p.p = pt2real(u.p, win, p.r); + specr = ((-2.0, -1.5), (1.0, 1.5)); + p.m = 0; + restart = 1; + } + Restart => + restart = 1; + } + } + <-sync => + win.image.flush(Draw->Flushon); + pid = -1; + } + if (restart) { + if (pid != -1) + kill(pid); + win.image.flush(Draw->Flushoff); + p.r = correctratio(specr, win); + sync = chan of int; + spawn docalculate(sync, p, imgch); + pid = <-sync; + imgch <-= (win.image, canvposn(win)); + } + } +} + +winreq(win: ref Tk->Toplevel, c: string, imgch: chan of (ref Image, Rect), terminated: chan of int): int +{ + oldimage := win.image; + if (imgch != nil) { + # halt calculation process + alt { + imgch <-= (nil, ((0,0), (0,0))) =>; + <-terminated => + imgch = nil; + } + } + tkclient->wmctl(win, c); + if(win.image != oldimage) + return 1; + if(imgch != nil) + imgch <-= (win.image, canvposn(win)); + return 0; +} + +correctratio(r: Fracrect, win: ref Tk->Toplevel): Fracrect +{ + # make sure calculation rectangle is in + # the same ratio as bitmap (also make sure that + # calculated area always includes desired area) + wr := canvposn(win); + (btall, atall) := (real wr.dy() / real wr.dx(), (r.max.y - r.min.y) / (r.max.x - r.min.x)); + if (btall > atall) { + # bitmap is taller than area, so expand area vertically + excess := (r.max.x - r.min.x) * btall - (r.max.y - r.min.y); + r.min.y -= excess / 2.0; + r.max.y += excess / 2.0; + } else { + # area is taller than bitmap, so expand area horizontally + excess := (r.max.y - r.min.y) / btall - (r.max.x - r.min.x); + r.min.x -= excess / 2.0; + r.max.x += excess / 2.0; + } + return r; +} + +pt2real(pt: Point, win: ref Tk->Toplevel, r: Fracrect): Fracpoint +{ + sz := Point(int cmd(win, ".c cget -actwidth"), int cmd(win, ".c cget -actheight")); + return (real pt.x / real sz.x * (r.max.x- r.min.x) + r.min.x, + real (sz.y - pt.y) / real sz.y * (r.max.y - r.min.y) + r.min.y); +} + +pt2s(pt: Point): string +{ + return string pt.x + " " + string pt.y; +} + +r2s(r: Rect): string +{ + return pt2s(r.min) + " " + pt2s(r.max); +} + +trackmouse(win: ref Tk->Toplevel, cmdch: chan of string, but: string, p: Point): ref Usercmd +{ + case but { + "b1" => + cr := canvposn(win); + display := win.image.display; + save := display.newimage(cr, win.image.chans, 0, Draw->Nofill); + save.draw(cr, win.image, nil, cr.min); + oclip := win.image.clipr; + win.image.clipr = cr; + + p = p.add(cr.min); + r := Rect(p, p); + win.image.border(r, 1, display.white, (0, 0)); + win.image.flush(Draw->Flushnow); + do { + but = <-cmdch; + (nil, toks) := sys->tokenize(but, " "); + but = hd toks; + if(but == "b1"){ + xr := r.canon(); + win.image.draw(xr, save, nil, xr.min); + (r.max.x, r.max.y) = (int hd tl toks + cr.min.x, int hd tl tl toks + cr.min.y); + win.image.border(r.canon(), 1, display.white, (0, 0)); + win.image.flush(Draw->Flushnow); + } + } while (but != "b1r"); + r = r.canon(); + win.image.draw(r, save, nil, r.min); + win.image.clipr = oclip; + r = r.subpt(cr.min); + return ref Usercmd.Zoomin(r); + "b2" => + return ref Usercmd.Julia(p); + "b3" => + return ref Usercmd.Zoomout; + } + return nil; +} + +poll(calc: ref Calc) +{ + calc.img.flush(Draw->Flushnow); + alt { + <-calc.imgch => + calc.img = nil; + (calc.img, calc.winr) = <-calc.imgch; + * =>; + } +} + +docalculate(sync: chan of int, p: Params, imgch: chan of (ref Image, Rect)) +{ + if (p.m) + ; # sys->print("mandel [[%g,%g],[%g,%g]]\n", r.min.x, r.min.y, r.max.x, r.max.y); + else + ; # sys->print("julia [[%g,%g],[%g,%g]] [%g,%g]\n", r.min.x, r.min.y, r.max.x, r.max.y, p.p.x, p.p.y); + sync <-= sys->pctl(0, nil); + calculate(p, imgch); + sync <-= 0; +} + +canvposn(win: ref Tk->Toplevel): Rect +{ + return tk->rect(win, ".c", Tk->Local); +} + +calculate(p: Params, imgch: chan of (ref Image, Rect)) +{ + calc := ref Calc; + (calc.img, calc.winr) = <-imgch; + r := calc.winr; + calc.maxx = r.dx(); + calc.maxy = r.dy(); + calc.supx = calc.maxx + 2; + calc.supy = calc.maxy + 2; + calc.imgch = imgch; + calc.xr = array[calc.maxx] of FIX; + calc.yr = array[calc.maxy] of FIX; + calc.morj = p.m; + initr(calc, p); + calc.img.drawop(r, calc.img.display.white, nil, (0,0), Draw->S); + + if (p.fill) { + calc.dispbase = array[calc.supx*calc.supy] of COL; # auxiliary display and border + calc.disp = calc.maxy + 3; + setdisp(calc); + displayset(calc); + } else { + for (x := 0; x < calc.maxx; x++) { + for (y := 0; y < calc.maxy; y++) + point(calc, calc.img, (x, y), pointcolour(calc, x, y)); + } + } +} + +setdisp(calc: ref Calc) +{ + d : int; + i : int; + + for (i = 0; i < calc.supx*calc.supy; i++) + calc.dispbase[i] = byte BLANK; + + i = 0; + for (d = 0; i < calc.supx; d += calc.supy) { + calc.dispbase[d] = byte BORDER; + i++; + } + i = 0; + for (d = 0; i < calc.supy; d++) { + calc.dispbase[d] = byte BORDER; + i++; + } + i = 0; + for (d = 0+calc.supx*calc.supy-1; i < calc.supx; d -= calc.supy) { + calc.dispbase[d] = byte BORDER; + i++; + } + i = 0; + for (d = 0+calc.supx*calc.supy-1; i < calc.supy; d--) { + calc.dispbase[d] = byte BORDER; + i++; + } +} + +initr(calc: ref Calc, p: Params): int +{ + r := p.r; + dp := real2fix((r.max.x-r.min.x)/(real calc.maxx)); + dq := real2fix((r.max.y-r.min.y)/(real calc.maxy)); + calc.xr[0] = real2fix(r.min.x)-(big calc.maxx*dp-(real2fix(r.max.x)-real2fix(r.min.x)))/big 2; + for (x := 1; x < calc.maxx; x++) + calc.xr[x] = calc.xr[x-1] + dp; + calc.yr[0] = real2fix(r.max.y)+(big calc.maxy*dq-(real2fix(r.max.y)-real2fix(r.min.y)))/big 2; + for (y := 1; y < calc.maxy; y++) + calc.yr[y] = calc.yr[y-1] - dq; + calc.parx = real2fix(p.p.x); + calc.pary = real2fix(p.p.y); + calc.kdivisor = p.kdivisor; + calc.pointsdone = 0; + return dp >= MINDELTA && dq >= MINDELTA; +} + +fillline(calc: ref Calc, x, y, d, dir, dird, col: int) +{ + x0 := x; + + while (calc.dispbase[d] == byte BLANK) { + calc.dispbase[d] = byte col; + x -= dir; + d -= dird; + } + if (0 && pointcolour(calc, (x0+x+dir)/2, y) != col) { # midpoint of line (island code) + # island - undo colouring or do properly + do { + d += dird; + x += dir; + # *d = BLANK; + calc.dispbase[d] = byte pointcolour(calc, x, y); + point(calc, calc.img, (x, y), int calc.dispbase[d]); + } while (x != x0); + return; # abort crawl ? + } + horizline(calc, calc.img, x0, x, y, col); +} + +crawlt(calc: ref Calc, x, y, d, col: int) +{ + yinc, dyinc : int; + + firstd := d; + xinc := 1; + dxinc := calc.supy; + + for (;;) { + if (getcolour(calc, x+xinc, y, d+dxinc) == col) { + x += xinc; + d += dxinc; + yinc = -xinc; + dyinc = -dxinc; + # if (isblank(x+xinc, y, d+dxinc)) + if (calc.dispbase[d+dxinc] == byte BLANK) + fillline(calc, x+xinc, y, d+dxinc, yinc, dyinc, col); + if (d == firstd) + break; + } + else { + yinc = xinc; + dyinc = dxinc; + } + if (getcolour(calc, x, y+yinc, d+yinc) == col) { + y += yinc; + d += yinc; + xinc = yinc; + dxinc = dyinc; + # if (isblank(x-xinc, y, d-dxinc)) + if (calc.dispbase[d-dxinc] == byte BLANK) + fillline(calc, x-xinc, y, d-dxinc, yinc, dyinc, col); + if (d == firstd) + break; + } + else { + xinc = -yinc; + dxinc = -dyinc; + } + } +} + +# spurious lines problem - disallow all acw paths +# +# 43---------> +# 12---------> +# +# 654------------> +# 7 3------------> +# 812------------> +# + +# Given a closed curve completely described by unit movements LRUD (left, +# right, up, and down), calculate the enclosed area. The description +# may be cw or acw and of arbitrary shape. +# +# Based on Green's Theorem :- area = integral ydx +# C +# area = 0; +# count = ARBITRARY_VALUE; +# while( moves_are_left() ){ +# move = next_move(); +# switch(move){ +# case L: +# area -= count; +# break; +# case R: +# area += count; +# break; +# case U: +# count++; +# break; +# case D: +# count--; +# break; +# } +# area = abs(area); + +crawlf(calc: ref Calc, x, y, d, col: int) +{ + xinc, yinc, dxinc, dyinc : int; + firstx, firsty : int; + firstd : int; + area := 0; + count := 0; + + firstx = x; + firsty = y; + firstd = d; + xinc = 1; + dxinc = calc.supy; + + # acw on success, cw on failure + for (;;) { + if (getcolour(calc, x+xinc, y, d+dxinc) == col) { + x += xinc; + d += dxinc; + yinc = -xinc; + dyinc = -dxinc; + area += xinc*count; + if (d == firstd) + break; + } else { + yinc = xinc; + dyinc = dxinc; + } + if (getcolour(calc, x, y+yinc, d+yinc) == col) { + y += yinc; + d += yinc; + xinc = yinc; + dxinc = dyinc; + count -= yinc; + if (d == firstd) + break; + } else { + xinc = -yinc; + dxinc = -dyinc; + } + } + if (area > 0) # cw + crawlt(calc, firstx, firsty, firstd, col); +} + +displayset(calc: ref Calc) +{ + edge : int; + last := BLANK; + d := calc.disp; + + for (x := 0; x < calc.maxx; x++) { + for (y := 0; y < calc.maxy; y++) { + col := calc.dispbase[d]; + if (col == byte BLANK) { + col = calc.dispbase[d] = byte pointcolour(calc, x, y); + point(calc, calc.img, (x, y), int col); + if (col == byte last) + edge++; + else { + last = int col; + edge = 0; + } + if (edge >= LIMIT) { + crawlf(calc, x, y-edge, d-edge, last); + # prevent further crawlf() + last = BLANK; + } + } + else { + if (col == byte last) + edge++; + else { + last = int col; + edge = 0; + } + } + d++; + } + last = BLANK; + d += 2; + } +} + +pointcolour(calc: ref Calc, x, y: int) : int +{ + if (++calc.pointsdone >= SCHEDCOUNT) { + calc.pointsdone = 0; + sys->sleep(0); + poll(calc); + } + if (calc.morj) + return mcount(calc, x, y) + 1; + else + return jcount(calc, x, y) + 1; +} + +mcount(calc: ref Calc, x_coord, y_coord: int): int +{ + (p, q) := (calc.xr[x_coord], calc.yr[y_coord]); + (x, y) := (calc.parx, calc.pary); + k := 0; + maxcount := MAXCOUNT * calc.kdivisor; + while (k < maxcount) { + if (x >= TWO || y >= TWO || x <= -TWO || y <= -TWO) + break; + + if (0) { + # x = (x < 0) ? (x>>HBASE)|NEG : x>>HBASE; + # y = (y < 0) ? (y>>HBASE)|NEG : y>>HBASE; + } + + x >>= HBASE; + y >>= HBASE; + t := y*y; + y = big 2*x*y+q; # possible unserious overflow when BASE == 28 + x *= x; + if (x+t >= FOUR) + break; + x -= t-p; + k++; + } + return k / calc.kdivisor; +} + +jcount(calc: ref Calc, x_coord, y_coord: int): int +{ + (x, y) := (calc.xr[x_coord], calc.yr[y_coord]); + (p, q) := (calc.parx, calc.pary); + k := 0; + maxcount := MAXCOUNT * calc.kdivisor; + while (k < maxcount) { + if (x >= TWO || y >= TWO || x <= -TWO || y <= -TWO) + break; + + if (0) { + # x = (x < 0) ? (x>>HBASE)|NEG : x>>HBASE; + # y = (y < 0) ? (y>>HBASE)|NEG : y>>HBASE; + } + + x >>= HBASE; + y >>= HBASE; + t := y*y; + y = big 2*x*y+q; # possible unserious overflow when BASE == 28 + x *= x; + if (x+t >= FOUR) + break; + x -= t-p; + k++; + } + return k / calc.kdivisor; +} + +getcolour(calc: ref Calc, x, y, d: int): int +{ + if (calc.dispbase[d] == byte BLANK) { + calc.dispbase[d] = byte pointcolour(calc, x, y); + point(calc, calc.img, (x, y), int calc.dispbase[d]); + } + return int calc.dispbase[d]; +} + +point(calc: ref Calc, d: ref Image, p: Point, col: int) +{ + d.draw(Rect(p, p.add((1,1))).addpt(calc.winr.min), colours[col], nil, (0,0)); +} + +horizline(calc: ref Calc, d: ref Image, x0, x1, y: int, col: int) +{ + if (x0 < x1) + r := Rect((x0, y), (x1, y+1)); + else + r = Rect((x1+1, y), (x0+1, y+1)); + d.draw(r.addpt(calc.winr.min), colours[col], nil, (0, 0)); + # r := Rect((x0, y), (x1, y)).canon(); + # r.max = r.max.add((1, 1)); +} + +real2fix(x: real): FIX +{ + return big (x * real SCALE); +} + +cmd(top: ref Tk->Toplevel, s: string): string +{ + e := tk->cmd(top, s); + if (e != nil && e[0] == '!') + sys->fprint(stderr, "mand: tk error on '%s': %s\n", s, e); + return e; +} + +kill(pid: int): int +{ + fd := sys->open("/prog/"+string pid+"/ctl", Sys->OWRITE); + if (fd == nil) + return -1; + if (sys->write(fd, array of byte "kill", 4) != 4) + return -1; + return 0; +} + +col(i, r : int) : int +{ + if (r == 1) + return 0; + return (255*(i%r))/(r-1); +} + +fittoscreen(win: ref Tk->Toplevel) +{ + Point: import draw; + if (win.image == nil || win.image.screen == nil) + return; + r := win.image.screen.image.r; + scrsize := Point((r.max.x - r.min.x), (r.max.y - r.min.y)); + bd := int cmd(win, ". cget -bd"); + winsize := Point(int cmd(win, ". cget -actwidth") + bd * 2, int cmd(win, ". cget -actheight") + bd * 2); + if (winsize.x > scrsize.x) + cmd(win, ". configure -width " + string (scrsize.x - bd * 2)); + if (winsize.y > scrsize.y) + cmd(win, ". configure -height " + string (scrsize.y - bd * 2)); + actr: Rect; + actr.min = Point(int cmd(win, ". cget -actx"), int cmd(win, ". cget -acty")); + actr.max = actr.min.add((int cmd(win, ". cget -actwidth") + bd*2, + int cmd(win, ". cget -actheight") + bd*2)); + (dx, dy) := (actr.dx(), actr.dy()); + if (actr.max.x > r.max.x) + (actr.min.x, actr.max.x) = (r.max.x - dx, r.max.x); + if (actr.max.y > r.max.y) + (actr.min.y, actr.max.y) = (r.max.y - dy, r.max.y); + if (actr.min.x < r.min.x) + (actr.min.x, actr.max.x) = (r.min.x, r.min.x + dx); + if (actr.min.y < r.min.y) + (actr.min.y, actr.max.y) = (r.min.y, r.min.y + dy); + cmd(win, ". configure -x " + string actr.min.x + " -y " + string actr.min.y); +} diff --git a/appl/wm/mash.b b/appl/wm/mash.b new file mode 100644 index 00000000..f83b347e --- /dev/null +++ b/appl/wm/mash.b @@ -0,0 +1,577 @@ +implement WmMash; + +include "sys.m"; + sys: Sys; + FileIO: import sys; + +include "draw.m"; + draw: Draw; + Context: import draw; + +include "tk.m"; + tk: Tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "plumbmsg.m"; + plumbmsg: Plumbmsg; + Msg: import plumbmsg; + +include "workdir.m"; + workdir: Workdir; + +WmMash: module +{ + init: fn(ctxt: ref Draw->Context, args: list of string); +}; + +Command: module +{ + tkinit: fn(ctxt: ref Draw->Context, t: ref Tk->Toplevel, args: list of string); +}; + +BS: con 8; # ^h backspace character +BSW: con 23; # ^w bacspace word +BSL: con 21; # ^u backspace line +EOT: con 4; # ^d end of file +ESC: con 27; # hold mode + +HIWAT: con 2000; # maximum number of lines in transcript +LOWAT: con 1500; # amount to reduce to after high water + +Name: con "Mash"; + +Rdreq: adt +{ + off: int; + nbytes: int; + fid: int; + rc: chan of (array of byte, string); +}; + +shwin_cfg := array[] of { + "menu .m", + ".m add command -text Cut -command {send edit cut}", + ".m add command -text Paste -command {send edit paste}", + ".m add command -text Snarf -command {send edit snarf}", + ".m add command -text Send -command {send edit send}", + "frame .b -bd 1 -relief ridge", + "frame .ft -bd 0", + "scrollbar .ft.scroll -width 14 -bd 0 -relief ridge -command {.ft.t yview}", + "text .ft.t -bd 1 -relief flat -width 520 -height 7c -yscrollcommand {.ft.scroll set}", + "pack .ft.scroll -side left -fill y", + "pack .ft.t -fill both -expand 1", + "pack .Wm_t -fill x", + "pack .b -anchor w -fill x", + "pack .ft -fill both -expand 1", + "pack propagate . 0", + "focus .ft.t", + "bind .ft.t <Key> {send keys {%A}}", + "bind .ft.t <Control-d> {send keys {%A}}", + "bind .ft.t <Control-h> {send keys {%A}}", + "bind .ft.t <Button-1> +{grab set .ft.t; send but1 pressed}", + "bind .ft.t <Double-Button-1> +{grab set .ft.t; send but1 pressed}", + "bind .ft.t <ButtonRelease-1> +{grab release .ft.t; send but1 released}", + "bind .ft.t <ButtonPress-2> {send but2 %X %Y}", + "bind .ft.t <Motion-Button-2-Button-1> {}", + "bind .ft.t <Motion-ButtonPress-2> {}", + "bind .ft.t <ButtonPress-3> {send but3 pressed}", + "bind .ft.t <ButtonRelease-3> {send but3 released %x %y}", + "bind .ft.t <Motion-Button-3> {}", + "bind .ft.t <Motion-Button-3-Button-1> {}", + "bind .ft.t <Double-Button-3> {}", + "bind .ft.t <Double-ButtonRelease-3> {}", + "update" +}; + +rdreq: list of Rdreq; +menuindex := "0"; +holding := 0; +plumbed := 0; +rawon := 0; +rawinput := ""; + +init(ctxt: ref Context, argv: list of string) +{ + s: string; + + sys = load Sys Sys->PATH; + if (ctxt == nil) { + sys->fprint(sys->fildes(2), "mash: no window context\n"); + raise "fail:bad context"; + } + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + plumbmsg = load Plumbmsg Plumbmsg->PATH; + + sys->pctl(Sys->FORKNS | Sys->NEWPGRP, nil); + + tkclient->init(); + + if(plumbmsg->init(1, nil, 0) >= 0){ + plumbed = 1; + workdir = load Workdir Workdir->PATH; + } + + argv = tl argv; # strip off command name + (t, titlectl) := tkclient->toplevel(ctxt, "", Name, Tkclient->Appl); + + edit := chan of string; + tk->namechan(t, edit, "edit"); +# mash := chan of string; +# tk->namechan(t, mash, "mash"); + + tkcmds(t, shwin_cfg); + + tkclient->onscreen(t, nil); + tkclient->startinput(t, "kbd"::"ptr"::nil); + + ioc := chan of (int, ref FileIO, ref FileIO, string); + spawn newsh(ctxt, t, ioc, argv); + + (pid, file, filectl, consfile) := <-ioc; + if(file == nil || filectl == nil) { + sys->print("newsh: %r\n"); + return; + } + + keys := chan of string; + tk->namechan(t, keys, "keys"); + + but1 := chan of string; + tk->namechan(t, but1, "but1"); + but2 := chan of string; + tk->namechan(t, but2, "but2"); + but3 := chan of string; + tk->namechan(t, but3, "but3"); + button1 := 0; + button3 := 0; + + rdrpc: Rdreq; + + # outpoint is place in text to insert characters printed by programs + tk->cmd(t, ".ft.t mark set outpoint end; .ft.t mark gravity outpoint left"); + + for(;;) alt { + c := <-t.ctxt.kbd => + tk->keyboard(t, c); + c := <-t.ctxt.ptr => + tk->pointer(t, *c); + c := <-t.ctxt.ctl or + c = <-t.wreq => + tkclient->wmctl(t, c); + menu := <-titlectl => + if(menu == "exit") { + kill(pid); + return; + } + tkclient->wmctl(t, menu); + tk->cmd(t, "focus .ft.t"); + + ecmd := <-edit => + editor(t, ecmd); + sendinput(t); + tk->cmd(t, "focus .ft.t"); + + c := <-keys => + cut(t, 1); + if(rawon) { + rawinput += c[1:2]; + rawinput = sendraw(rawinput); + break; + } + char := c[1]; + if(char == '\\') + char = c[2]; + update := ";.ft.t see insert;update"; + case char { + * => + tk->cmd(t, ".ft.t insert insert "+c+update); + '\n' or EOT => + tk->cmd(t, ".ft.t insert insert "+c+update); + sendinput(t); + BS => + tk->cmd(t, ".ft.t tkTextDelIns -c"+update); + BSL => + tk->cmd(t, ".ft.t tkTextDelIns -l"+update); + BSW => + tk->cmd(t, ".ft.t tkTextDelIns -w"+update); + ESC => + holding ^= 1; + color := "blue"; + if(!holding){ + color = "black"; + tkclient->settitle(t, Name); + sendinput(t); + }else + tkclient->settitle(t, Name+" (holding)"); + tk->cmd(t, ".ft.t configure -foreground "+color+update); + } + + c := <-but1 => + button1 = (c == "pressed"); + button3 = 0; # abort any pending button 3 action + + c := <-but2 => + if(button1){ + cut(t, 1); + tk->cmd(t, "update"); + break; + } + (nil, l) := sys->tokenize(c, " "); + x := int hd l - 50; + y := int hd tl l - int tk->cmd(t, ".m yposition "+menuindex) - 10; + tk->cmd(t, ".m activate "+menuindex+"; .m post "+string x+" "+string y+ + "; grab set .m; update"); + button3 = 0; # abort any pending button 3 action + + c := <-but3 => + if(c == "pressed"){ + button3 = 1; + if(button1){ + paste(t); + tk->cmd(t, "update"); + } + break; + } + if(plumbed == 0 || button3 == 0 || button1 != 0) + break; + button3 = 0; + # Plumb message triggered by release of button 3 + (nil, l) := sys->tokenize(c, " "); + x := int hd tl l; + y := int hd tl tl l; + index := tk->cmd(t, ".ft.t index @"+string x+","+string y); + selindex := tk->cmd(t, ".ft.t tag ranges sel"); + if(selindex != "") + insel := tk->cmd(t, ".ft.t compare sel.first <= "+index)=="1" && + tk->cmd(t, ".ft.t compare sel.last >= "+index)=="1"; + else + insel = 0; + attr := ""; + if(insel) + text := tk->cmd(t, ".ft.t get sel.first sel.last"); + else{ + # have line with text in it + # now extract whitespace-bounded string around click + (nil, w) := sys->tokenize(index, "."); + charno := int hd tl w; + left := tk->cmd(t, ".ft.t index {"+index+" linestart}"); + right := tk->cmd(t, ".ft.t index {"+index+" lineend}"); + line := tk->cmd(t, ".ft.t get "+left+" "+right); + for(i:=charno; i>0; --i) + if(line[i-1]==' ' || line[i-1]=='\t') + break; + for(j:=charno; j<len line; j++) + if(line[j]==' ' || line[j]=='\t') + break; + text = line[i:j]; + attr = "click="+string (charno-i); + } + msg := ref Msg( + "WmSh", + "", + workdir->init(), + "text", + attr, + array of byte text); + if(msg.send() < 0) + sys->fprint(sys->fildes(2), "sh: plumbing write error: %r\n"); + + rdrpc = <-filectl.read => + if(rdrpc.rc == nil) + continue; + rdrpc.rc <-= ( nil, "not allowed" ); + + (nil, data, nil, wc) := <-filectl.write => + if(wc == nil) { + # consctl closed - revert to cooked mode + rawon = 0; + continue; + } + (nc, cmdlst) := sys->tokenize(string data, " \n"); + if(nc == 1) { + case hd cmdlst { + "rawon" => + rawon = 1; + rawinput = ""; + # discard previous input + advance := string (len tk->cmd(t, ".ft.t get outpoint end") +1); + tk->cmd(t, ".ft.t mark set outpoint outpoint+" + advance + "chars"); + "rawoff" => + rawon = 0; + * => + wc <-= (0, "unknown consctl request"); + continue; + } + wc <-= (len data, nil); + continue; + } + wc <-= (0, "unknown consctl request"); + + rdrpc = <-file.read => + if(rdrpc.rc == nil) { + (ok, nil) := sys->stat(consfile); + if (ok < 0) + return; + continue; + } + append(rdrpc); + sendinput(t); + + (off, data, fid, wc) := <-file.write => + if(wc == nil) { + (ok, nil) := sys->stat(consfile); + if (ok < 0) + return; + continue; + } + cdata := stripbs(t, string data); + ncdata := string len cdata + "chars;"; + moveins := insat(t, "outpoint"); + tk->cmd(t, ".ft.t insert outpoint '"+ cdata); + wc <-= (len data, nil); + data = nil; + s = ".ft.t mark set outpoint outpoint+" + ncdata; + s += ".ft.t see outpoint;"; + if(moveins) + s += ".ft.t mark set insert insert+" + ncdata; + s += "update"; + tk->cmd(t, s); + nlines := int tk->cmd(t, ".ft.t index end"); + if(nlines > HIWAT){ + s = ".ft.t delete 1.0 "+ string (nlines-LOWAT) +".0;update"; + tk->cmd(t, s); + } + } +} + +RPCread: type (int, int, int, chan of (array of byte, string)); + +append(r: RPCread) +{ + t := r :: nil; + while(rdreq != nil) { + t = hd rdreq :: t; + rdreq = tl rdreq; + } + rdreq = t; +} + +insat(t: ref Tk->Toplevel, mark: string): int +{ + return tk->cmd(t, ".ft.t compare insert == "+mark) == "1"; +} + +insininput(t: ref Tk->Toplevel): int +{ + if(tk->cmd(t, ".ft.t compare insert >= outpoint") != "1") + return 0; + return tk->cmd(t, ".ft.t compare {insert linestart} == {outpoint linestart}") == "1"; +} + +isalnum(s: string): int +{ + if(s == "") + return 0; + c := s[0]; + if('a' <= c && c <= 'z') + return 1; + if('A' <= c && c <= 'Z') + return 1; + if('0' <= c && c <= '9') + return 1; + if(c == '_') + return 1; + if(c > 16rA0) + return 1; + return 0; +} + +stripbs(t: ref Tk->Toplevel, s: string): string +{ + l := len s; + for(i := 0; i < l; i++) + if(s[i] == '\b') { + pre := ""; + rem := ""; + if(i + 1 < l) + rem = s[i+1:]; + if(i == 0) { # erase existing character in line + if(tk->cmd(t, ".ft.t get " + + "{outpoint linestart} outpoint") != "") + tk->cmd(t, ".ft.t delete outpoint-1char"); + } else { + if(s[i-1] != '\n') # don't erase newlines + i--; + if(i) + pre = s[:i]; + } + s = pre + rem; + l = len s; + i = len pre - 1; + } + return s; +} + +editor(t: ref Tk->Toplevel, ecmd: string) +{ + s, snarf: string; + + case ecmd { + "cut" => + menuindex = "0"; + cut(t, 1); + + "paste" => + menuindex = "1"; + paste(t); + + "snarf" => + menuindex = "2"; + if(tk->cmd(t, ".ft.t tag ranges sel") == "") + break; + snarf = tk->cmd(t, ".ft.t get sel.first sel.last"); + tkclient->snarfput(snarf); + + "send" => + menuindex = "3"; + if(tk->cmd(t, ".ft.t tag ranges sel") != ""){ + snarf = tk->cmd(t, ".ft.t get sel.first sel.last"); + tkclient->snarfput(snarf); + }else + snarf = tkclient->snarfget(); + if(snarf != "") + s = snarf; + else + return; + if(s[len s-1] != '\n' && s[len s-1] != EOT) + s[len s] = '\n'; + tk->cmd(t, ".ft.t see end; .ft.t insert end '"+s); + tk->cmd(t, ".ft.t mark set insert end"); + tk->cmd(t, ".ft.t tag remove sel sel.first sel.last"); + } + tk->cmd(t, "update"); +} + +cut(t: ref Tk->Toplevel, snarfit: int) +{ + if(tk->cmd(t, ".ft.t tag ranges sel") == "") + return; + if(snarfit) + tkclient->snarfput(tk->cmd(t, ".ft.t get sel.first sel.last")); + tk->cmd(t, ".ft.t delete sel.first sel.last"); +} + +paste(t: ref Tk->Toplevel) +{ + snarf := tkclient->snarfget(); + if(snarf == "") + return; + cut(t, 0); + tk->cmd(t, ".ft.t insert insert '"+snarf); + tk->cmd(t, ".ft.t tag add sel insert-"+string len snarf+"chars insert"); + sendinput(t); +} + +sendinput(t: ref Tk->Toplevel) +{ + if(holding) + return; + input := tk->cmd(t, ".ft.t get outpoint end"); + slen := len input; + if(slen == 0 || rdreq == nil) + return; + + r := hd rdreq; + for(i := 0; i < slen; i++) + if(input[i] == '\n' || input[i] == EOT) + break; + + if(i >= slen && slen < r.nbytes) + return; + + if(i >= r.nbytes) + i = r.nbytes-1; + advance := string (i+1); + if(input[i] == EOT) + input = input[0:i]; + else + input = input[0:i+1]; + + rdreq = tl rdreq; + + alt { + r.rc <-= (array of byte input, "") => + tk->cmd(t, ".ft.t mark set outpoint outpoint+" + advance + "chars"); + * => + # requester has disappeared; ignore his request and try again + sendinput(t); + } +} + +sendraw(input : string) : string +{ + i := len input; + if(i == 0 || rdreq == nil) + return input; + + r := hd rdreq; + rdreq = tl rdreq; + + if(i > r.nbytes) + i = r.nbytes; + + alt { + r.rc <-= (array of byte input[0:i], "") => + input = input[i:]; + * => + ;# requester has disappeared; ignore his request and try again + } + return input; +} + +newsh(ctxt: ref Context, t: ref Tk->Toplevel, ioc: chan of (int, ref FileIO, ref FileIO, string), args: list of string) +{ + pid := sys->pctl(sys->NEWFD, nil); + + sh := load Command "/dis/mash.dis"; + if(sh == nil) { + ioc <-= (0, nil, nil, nil); + return; + } + + tty := "cons."+string pid; + + sys->bind("#s","/chan",sys->MBEFORE); + fio := sys->file2chan("/chan", tty); + fioctl := sys->file2chan("/chan", tty + "ctl"); + + ioc <-= (pid, fio, fioctl, "/chan/"+tty); + if(fio == nil || fioctl == nil) + return; + + sys->bind("/chan/"+tty, "/dev/cons", sys->MREPL); + sys->bind("/chan/"+tty+"ctl", "/dev/consctl", sys->MREPL); + + fd0 := sys->open("/dev/cons", sys->OREAD|sys->ORCLOSE); + fd1 := sys->open("/dev/cons", sys->OWRITE); + fd2 := sys->open("/dev/cons", sys->OWRITE); + + sh->tkinit(ctxt, t, "mash" :: args); +} + +kill(pid: int) +{ + fd := sys->open("#p/"+string pid+"/ctl", sys->OWRITE); + if(fd != nil) + sys->fprint(fd, "killgrp"); +} + +tkcmds(t: ref Tk->Toplevel, cfg: array of string) +{ + for(i := 0; i < len cfg; i++) + tk->cmd(t, cfg[i]); +} diff --git a/appl/wm/memory.b b/appl/wm/memory.b new file mode 100644 index 00000000..6bbfa68b --- /dev/null +++ b/appl/wm/memory.b @@ -0,0 +1,246 @@ +implement WmMemory; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + Display, Image, Rect: import draw; + +include "tk.m"; + tk: Tk; + t: ref Tk->Toplevel; + +include "tkclient.m"; + tkclient: Tkclient; + +WmMemory: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +Arena: adt +{ + name: string; + limit: int; + size: int; + hw: int; + allocs: int; + frees: int; + exts: int; + chunk: int; + y: int; + tag: string; + tagsz: string; + taghw: string; + tagiu: string; +}; +a := array[10] of Arena; + +mem_cfg := array[] of { + "canvas .c -width 240 -height 45", + "pack .c", + "update", +}; + +init(ctxt: ref Draw->Context, nil: list of string) +{ + spawn realinit(ctxt); +} + +realinit(ctxt: ref Draw->Context) +{ + sys = load Sys Sys->PATH; + if (ctxt == nil) { + sys->fprint(sys->fildes(2), "memory: no window context\n"); + raise "fail:bad context"; + } + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + sys->pctl(Sys->NEWPGRP, nil); + tkclient->init(); + + menubut := chan of string; + (t, menubut) = tkclient->toplevel(ctxt, "", "Memory", 0); + for(j := 0; j < len mem_cfg; j++) + cmd(t, mem_cfg[j]); + tkclient->startinput(t, "ptr"::nil); + tkclient->onscreen(t, nil); + + tick := chan of int; + spawn ticker(tick); + + mfd := sys->open("/dev/memory", sys->OREAD); + + n := getmem(mfd); + maxx := initdraw(n); + + pid: int; + for(;;) alt { + s := <-t.ctxt.ptr => + tk->pointer(t, *s); + s := <-t.ctxt.ctl or + s = <-t.wreq or + s = <-menubut => + if(s == "exit"){ + kill(pid); + return; + } + tkclient->wmctl(t, s); + pid = <-tick => + update(mfd); + for(i := 0; i < n; i++) { + if(a[i].limit <= 0) + continue; + x := int ((big a[i].size * big (230-maxx)) / big a[i].limit); + s := sys->sprint(".c coords %s %d %d %d %d", + a[i].tag, + maxx, + a[i].y + 4, + maxx + x, + a[i].y + 8); + cmd(t, s); + x = int ((big a[i].hw * big (230-maxx)) / big a[i].limit); + s = sys->sprint(".c coords %s %d %d %d %d", + a[i].taghw, + maxx, + a[i].y + 4, + maxx+x, + a[i].y + 8); + cmd(t, s); + s = sys->sprint(".c itemconfigure %s -text '%s", a[i].tagsz, string a[i].size); + cmd(t, s); + s = sys->sprint(".c itemconfigure %s -text '%d", a[i].tagiu, a[i].allocs-a[i].frees); + cmd(t, s); + } + cmd(t, "update"); + } +} + +ticker(c: chan of int) +{ + pid := sys->pctl(0, nil); + for(;;) { + c <-= pid; + sys->sleep(1000); + } +} + +initdraw(n: int): int +{ + y := 15; + maxx := 0; + for (i := 0; i < n; i++) { + id := cmd(t, ".c create text 5 "+string y+" -anchor w -text "+a[i].name); + r := s2r(cmd(t, ".c bbox " + id)); + if (r.max.x > maxx) + maxx = r.max.x; + y += 20; + } + maxx += 5; + y = 15; + for(i = 0; i < n; i++) { + s := sys->sprint(".c create rectangle %d %d 230 %d -fill white", maxx, y+4, y+8); + cmd(t, s); + s = sys->sprint(".c create rectangle %d %d 230 %d -fill white", maxx, y+4, y+8); + a[i].taghw = cmd(t, s); + s = sys->sprint(".c create rectangle %d %d 230 %d -fill red", maxx, y+4, y+8); + a[i].tag = cmd(t, s); + s = sys->sprint(".c create text 230 %d -anchor e -text '%s", y - 2, sizestr(a[i].limit)); + cmd(t, s); + s = sys->sprint(".c create text %d %d -anchor w -text '%s", maxx, y - 2, string a[i].size); + a[i].tagsz = cmd(t, s); + s = sys->sprint(".c create text 120 %d -fill red -anchor w -text '%d", y - 2, a[i].allocs-a[i].frees); + a[i].tagiu = cmd(t, s); + a[i].y = y; + y += 20; + } + cmd(t, ".c configure -height "+string y); + cmd(t, "update"); + return maxx; +} + +sizestr(n: int): string +{ + if ((n / 1024) % 1024 == 0) + return string (n / (1024 * 1024)) + "M"; + return string (n / 1024) + "K"; +} + +buf := array[8192] of byte; + +update(mfd: ref Sys->FD): int +{ + sys->seek(mfd, big 0, Sys->SEEKSTART); + n := sys->read(mfd, buf, len buf); + if(n <= 0) + exit; + (nil, l) := sys->tokenize(string buf[0:n], "\n"); + i := 0; + while(l != nil) { + s := hd l; + a[i].size = int s[0:]; + a[i].hw = int s[24:]; + a[i].allocs = int s[3*12:]; + a[i].frees = int s[4*12:]; + a[i].exts = int s[5*12:]; + a[i++].chunk = int s[6*12:]; + l = tl l; + } + return i; +} + +getmem(mfd: ref Sys->FD): int +{ + n := sys->read(mfd, buf, len buf); + if(n <= 0) + exit; + (nil, l) := sys->tokenize(string buf[0:n], "\n"); + i := 0; + while(l != nil) { + s := hd l; + a[i].size = int s[0:]; + a[i].limit = int s[12:]; + a[i].hw = int s[2*12:]; + a[i].allocs = int s[3*12:]; + a[i].frees = int s[4*12:]; + a[i].exts = int s[5*12:]; + a[i].chunk = int s[6*12:]; + a[i].name = s[7*12:]; + i++; + l = tl l; + } + return i; +} + +s2r(s: string): Rect +{ + (n, toks) := sys->tokenize(s, " "); + if (n != 4) { + sys->print("'%s' is not a rectangle!\n", s); + raise "bad conversion"; + } + r: Rect; + (r.min.x, toks) = (int hd toks, tl toks); + (r.min.y, toks) = (int hd toks, tl toks); + (r.max.x, toks) = (int hd toks, tl toks); + (r.max.y, toks) = (int hd toks, tl toks); + return r; +} + +kill(pid: int) +{ + fd := sys->open("#p/"+string pid+"/ctl", sys->OWRITE); + if(fd != nil) + sys->fprint(fd, "kill"); +} + + +cmd(top: ref Tk->Toplevel, s: string): string +{ + e := tk->cmd(top, s); + if (e != nil && e[0] == '!') + sys->print("memory: tk error on '%s': %s\n", s, e); + return e; +} diff --git a/appl/wm/minitel/README b/appl/wm/minitel/README new file mode 100644 index 00000000..82f3202e --- /dev/null +++ b/appl/wm/minitel/README @@ -0,0 +1,209 @@ +Minitel Emulation for Inferno + +This directory contains the source of `miniterm', a minitel emulator +for Inferno. Miniterm is written in Limbo. The main components are: + + miniterm.m - common constants + miniterm.b - terminal emulator, messaging and Minitel `protocol` + event.[mb] - inter-module message format + keyb.b - Minitel keyboard module + modem.b - Minitel modem module + screen.b - Minitel screen module + socket.b - Minitel socket module + arg.m - basic command line argument handling + mdisplay.[mb] - Videotex display module + swkeyb.[mb] - Minitel aware software keyboard + + fonts.tgz which expands into: + + fonts/minitel - external and subfont directory (`bind -b' into /fonts) + fonts/minitel/f40x25 - 40 column external font + fonts/minitel/14x17 + fonts/minitel/14x17xoe + fonts/minitel/14x17arrow + fonts/minitel/f40x25g1 - 40 column semigraphic external font + fonts/minitel/vid14x17 + fonts/minitel/f40x25h - 40 column double height external font + fonts/minitel/14x34 + fonts/minitel/14x34xoe + fonts/minitel/14x34arrow + fonts/minitel/f40x25w - 40 column double width external font + fonts/minitel/28x17 + fonts/minitel/28x17xoe + fonts/minitel/28x17arrow + fonts/minitel/f40x25s - 40 column double size external font + fonts/minitel/28x34xoe + fonts/minitel/28x34arrow + fonts/minitel/f80x25 - 80 column external font + fonts/minitel/8x12 + fonts/minitel/8x12xoe + fonts/minitel/8x12arrow + +The fonts subdirectory should be bound into /fonts: + bind -b fonts /fonts +or the directory fonts/minitel copied to /fonts/minitel before invoking the emulator. +The names of the external fonts are +known to the Videotex display module. Similarly, the files: + /dev/modem + /dev/modemctl +are known to the modem module, but you can ignore them if +(as is almost certain) you are using the Internet-minitel gateway +and you haven't got appropriate modem hardware anyway. + +To build + mkdir /usr/inferno/dis/wm/minitel + mk install + +The code models the structure outlined in the Minitel 1B specification +provided by France Telecom. However, much more interpretation was +required to display the majority of screens currently seen on Minitel. +Additional information (although sketchy) was found on the Internet by +searching for Minitel or Videotex and also by examination of the codes +sent by minitel servers and experimenting with replies. There must be +some more up to date information somewhere! + +We don't support downloadable fonts, but correctly filter them out. + +The file miniterm.b contains the code for the minitel `terminal' with +which the other modules communicate. The keyboard, modem, socket, +screen and terminal are run as separate threads which communicate by +calling: + send(e: ref Event) +The clue to the intermodule communication is in Terminal.run which +does something like: + for(;;) { + ev =<- t.in => + eva := protocol(ev); + while(len eva > 0) { + post(eva[0]); + eva = eva[1:]; + } + # then deliver any `posted' messages (without blocking) + } +An Event `ev' may typically be an Edata type (say from the modem) or +an Eproto type for internal interpretation. In the call: + eva := protocol(ev) +The function protocol() dissects Edata messages to produce an inline +sequence of Edata and Eproto messages. The function post() queues +messages for delivery to the appropriate modules. For example, data +from the modem might be destined for the screen and the socket module. +Messages are queued until they can be delivered. That way the line: + ev =<- t.in +is executed in a timely way and the other modules can be written to +make blocking writes (via send()) and to service reads when they are +ready. + +In many places in the code lines appear with comments like: + if(p.skip < 1 || p.skip > 127) # 5.0 +These refer to sections of the Minitel specification which explain the +code. + +The mdisplay code provides a Videotex display using Inferno +primitives. The screen, keyboard and modem modules interpret data as +described in the equivalent section of the Minitel specification. The +socket module has not been implemented but currently performs a `null' +function and could easily be added if required. + + +- Namespace +We always expect the fonts to appear in /fonts and the softmodem +to appear as /dev/modem and /dev/modemctl. + +- Invocation +If invoked with no argument, miniterm uses the France Telecom +internet gateway by default (tcp!193.252.252.250!513). +If the argument starts with `modem' then +a direct connection through /dev/modem will be established. + +An argument beginning with anything other than `modem' will +be assumed to be an address suitable for dial(). For example: + + wm/minitel/miniterm tcp!193.252.252.250!513 + +will connect to the current France Telecom internet server. + +For direct connections a modem `init' string and an optional +phone number can follow the modem prefix, as in: + + wm/minitel/miniterm modem!F3!3615 + +or + + wm/minitel/miniterm modem!F3!01133836431414 + +The `F3' is the code which instructs the softmodem to enable V.23 +and needs to be passed when connecting to the FT servers. +To use pulse dialing instead of tone dialing the phone number +can be prefixed with a 'P' as in: + + wm/minitel/miniterm modem!F3!P3614 + +If the parameter specifies a network connection or a direct connection +with a phone number the software will attempt to connect immediately. +If Cx/Fin is used to disconnect and then re-connect it will use the +same IP address for a network connection or prompt for a new +phone number in the case of a direct connection. When prompting +for a new number the top row of the screen is used to allow the user +to edit the last used number. Simple editing is available, and the minitel +keys do the obvious things. + + + +** Notes on the 15th December 1998 Release ** + +- Software keyboard +A version of the software keyboard which understands some of +the minitel keyboard mappings is included. For example, hitting 'A' results +in a capital 'A' on the screen in spite of the Videotex case mapping. + +- Minitel function keys +The minitel keys are displayed on the right hand side of the screen +in 40 column mode on a network connection +and can be swapped to the left hand side by hitting the <- key. +In direct dial mode and 80 column network mode the keys are +displayed at the bottom of the screen. +In network mode they are re-displayed as appropriate on 40 to 80 +column mode changes. + + +Known Omission +------------- +- Error Correction (direct dial only) +There is no screen button to enable error correction in the release. +If a server asks for error correction it will be enabled. It looks as though +we need to include a key to enable it. Without it direct dial screens are +occasionally corrupted. + +- Software Keyboard Handling +We need to add some code to update the software keyboard and +bring it to the foreground on a mode change. + +- Full 80 column support +I am aware of some screens which don't look correct in 80 column +mode (and others that do). See `EMAIL' then choose USENET and +press SUITE a few times. I believe it behaves as specified but as we +have seen with the 40 column Videotex mode the specification +is not sufficient to display most of the minitel screens correctly. +80 column support needs just a little more work. +It may be, too, that the 80 column font could be made much more +readable by utilising a few more pixels on the screen now that we +are able to cover the toolbar. + +- Full toolbar integration +Experimentation will show whether there needs to be more +integration with the toolbar. + +Known Bugs +---------- +- Softmodem disconnection +Often, the modem does not hangup correctly. + +- Choose `USA' from a network connection +USA (from a network connection) gives an `iC' in bottom left hand +corner of screen. Possibly a server issue. Doesn't occur when +connecting directly. The server is really sending this sequence. +Both the FT emulator and their explorer plug-in suffer from it too. + + +John Bates +Vita Nuova Limited diff --git a/appl/wm/minitel/event.b b/appl/wm/minitel/event.b new file mode 100644 index 00000000..f751f55b --- /dev/null +++ b/appl/wm/minitel/event.b @@ -0,0 +1,19 @@ +# +# Copyright © 1998 Vita Nuova Limited. All rights reserved. +# + +Event.str(ev: self ref Event) : string +{ + s := "?"; + pick e := ev { + Edata => + s = sprint("Edata %d = ", len e.data); + for(i:=0; i<len e.data; i++) + s += hex(int e.data[i], 2) + " "; + Equit => + s = "Equit"; + Eproto => + s = sprint("Eproto %ux (%s)", e.cmd, e.s); + } + return s; +} diff --git a/appl/wm/minitel/event.m b/appl/wm/minitel/event.m new file mode 100644 index 00000000..1b524363 --- /dev/null +++ b/appl/wm/minitel/event.m @@ -0,0 +1,19 @@ +# +# Copyright © 1998 Vita Nuova Limited. All rights reserved. +# + +Event: adt { + path: int; # path for delivery + from: int; # sending module (for reply) + pick { + Edata => + data: array of byte; + Eproto => + cmd: int; + s: string; + a0, a1, a2: int; # parameters + Equit => + } + + str: fn(e: self ref Event) : string; # convert to readable form +}; diff --git a/appl/wm/minitel/keyb.b b/appl/wm/minitel/keyb.b new file mode 100644 index 00000000..aba5485d --- /dev/null +++ b/appl/wm/minitel/keyb.b @@ -0,0 +1,367 @@ +# +# Copyright © 1998 Vita Nuova Limited. All rights reserved. +# + +# special keyboard operations +Extend, # enable cursor and editing keys and control chars +C0keys, # cursor keys send BS,HT,LF and VT +Invert # case inversion + : con 1 << iota; + +Keyb: adt { + m: ref Module; # common attributes + in: chan of ref Event; + + cmd: chan of string; # from Tk (keypresses and focus) + spec: int; # special keyboard extensions + + init: fn(k: self ref Keyb, toplevel: ref Tk->Toplevel); + reset: fn(k: self ref Keyb); + run: fn(k: self ref Keyb); + quit: fn(k: self ref Keyb); + map: fn(k: self ref Keyb, key:int): array of byte; +}; + +Keyb.init(k: self ref Keyb, toplevel: ref Tk->Toplevel) +{ + k.in = chan of ref Event; + k.cmd = chan of string; + tk->namechan(toplevel, k.cmd, "keyb"); # Tk -> keyboard + k.reset(); +} + +Keyb.reset(k: self ref Keyb) +{ + k.m = ref Module(Pmodem|Psocket, 0); +} + +ask(in: chan of string, out: chan of string) +{ + keys: string; + + T.mode = Videotex; + S.setmode(Videotex); +# clear(S); + prompt: con "Numéroter: "; + number := M.lastdialstr; + S.msg(prompt); + +Input: + for(;;) { + n := len prompt + len number; + # guard length must be > len prompt + if (n > 30) + n -= 30; + else + n = 0; + S.msg(prompt + number[n:]); + keys = <- in; + if (keys == nil) + return; + + keys = canoncmd(keys); + + case keys { + "connect" or "send" => + break Input; + "correct" => + if(len number > 0) + number = number[0: len number -1]; + "cancel" => + number = ""; + break Input; + "repeat" or "index" or "guide" or "next" or "previous" => + ; + * => + number += keys; + } + } + + S.msg(nil); + for (;;) alt { + out <- = number => + return; + keys = <- in => + if (keys == nil) + return; + } +} + +Keyb.run(k: self ref Keyb) +{ + dontask := chan of string; + askchan := dontask; + askkeys := chan of string; +Runloop: + for(;;){ + alt { + ev := <- k.in => + pick e := ev { + Equit => + break Runloop; + Eproto => + case e.cmd { + Creset => + k.reset(); + Cproto => + case e.a0 { + START => + case e.a1 { + LOWERCASE => + k.spec |= Invert; + } + STOP => + case e.a1 { + LOWERCASE => + k.spec &= ~Invert; + } + } + * => break; + } + } + cmd := <- k.cmd => + if(debug['k'] > 0) { + fprint(stderr, "Tk %s\n", cmd); + } + (n, args) := sys->tokenize(cmd, " "); + if(n >0) + case hd args { + "key" => + (key, nil) := toint(hd tl args, 16); + if(askchan != dontask) { + s := minikey(key); + if (s == nil) + s[0] = key; + askkeys <-= s; + break; + } + keys := k.map(key); + if(keys != nil) { + send(ref Event.Edata(k.m.path, Mkeyb, keys)); + } + "skey" => # minitel key hit (soft key) + if(hd tl args == "Exit") { + if(askchan != dontask) { + askchan = dontask; + askkeys <-= nil; + } + if(T.state == Online || T.state == Connecting) { + seq := keyseq("connect"); + if(seq != nil) { + send(ref Event.Edata(k.m.path, Mkeyb, seq)); + send(ref Event.Edata(k.m.path, Mkeyb, seq)); + } + send(ref Event.Eproto(Pmodem, Mkeyb, Cdisconnect, "", 0,0,0)); + } + send(ref Event.Equit(0, 0)); + break; + } + if(askchan != dontask) { + askkeys <-= hd tl args; + break; + } + case hd tl args { + "Connect" => + case T.state { + Local => + if(M.connect == Network) + send(ref Event.Eproto(Pmodem, Mkeyb, Cconnect, "", 0,0,0)); + else { + askchan = chan of string; + spawn ask(askkeys, askchan); + } + Connecting => + send(ref Event.Eproto(Pmodem, Mkeyb, Cdisconnect, "", 0,0,0)); + Online => + seq := keyseq("connect"); + if(seq != nil) + send(ref Event.Edata(k.m.path, Mkeyb, seq)); + } + * => + seq := keyseq(hd tl args); + if(seq != nil) + send(ref Event.Edata(k.m.path, Mkeyb, seq)); + } + "click" => # fetch a word from the display + x := int hd tl args; + y := int hd tl tl args; + word := disp->GetWord(Point(x, y)); + if(word != nil) { + if (askchan != dontask) { + askkeys <- = word; + break; + } + if (T.state == Local) { + if (canoncmd(word) == "connect") { + if(M.connect == Network) + send(ref Event.Eproto(Pmodem, Mkeyb, Cconnect, "", 0,0,0)); + else { + askchan = chan of string; + spawn ask(askkeys, askchan); + } + break; + } + } + seq := keyseq(word); + if(seq != nil) + send(ref Event.Edata(k.m.path, Mkeyb, seq)); + else { + send(ref Event.Edata(k.m.path, Mkeyb, array of byte word )); + send(ref Event.Edata(k.m.path, Mkeyb, keyseq("send"))); + } + } + + } + dialstr := <-askchan => + askchan = dontask; + if(dialstr != nil) { + M.dialstr = dialstr; + send(ref Event.Eproto(Pmodem, Mkeyb, Cconnect, "", 0,0,0)); + } + } + } + send(nil); +} + + +# Perform mode specific key translation +# returns nil on invalid keypress, +Keyb.map(nil: self ref Keyb, key: int): array of byte +{ + # hardware to minitel keyboard mapping + cmd := minikey(key); + if (cmd != nil) { + seq := keyseq(cmd); + if(seq != nil) + return seq; + } + + # alphabetic (with case mapping) + case T.mode { + Videotex => + if(key >= 'A' && key <= 'Z') + return array [] of { byte ('a' + (key - 'A'))}; + if(key >= 'a' && key <= 'z') + return array [] of {byte ('A' + (key - 'a'))}; + Mixed or Ascii => + if(key >= 'A' && key <= 'Z' || key >= 'a' && key <= 'z') + return array [] of {byte key}; + }; + + # Numeric + if(key >= '0' && key <= '9') + return array [] of {byte key}; + + # Control-A -> Control-Z, Esc - columns 0 and 1 + if(key >= 16r00 && key <=16r1f) + case T.mode { + Videotex => + return nil; + Mixed or Ascii => + return array [] of {byte key}; + } + + # miscellaneous key mapping + case key { + 16r20 => ; # space + 16ra3 => return array [] of { byte 16r19, byte 16r23 }; # pound + '!' or '"' or '#' or '$' + or '%' or '&' or '\'' or '(' or ')' + or '*' or '+' or ',' or '-' + or '.' or ':' or ';' or '<' + or '=' or '>' or '?' or '@' => ; + KF13 => # request for error correction - usually Fnct M + C + if((M.spec&Ecp) == 0 && T.state == Online && T.connect == Direct) { +fprint(stderr, "requesting Ecp\n"); + return array [] of { byte SEP, byte 16r4a }; + } + return nil; + * => return nil; + } + return array [] of {byte key}; +} + +Keyb.quit(k: self ref Keyb) +{ + if(k==nil); +} + +canoncmd(s : string) : string +{ + s = tolower(s); + case s { + "connect" or "cx/fin" or + "connexion" or "fin" => return "connect"; + "send" or "envoi" => return "send"; + "repeat" or "repetition" => return "repeat"; + "index" or "sommaire" or "somm" + => return "index"; + "guide" => return "guide"; + "correct" or "correction" => return "correct"; + "cancel" or "annulation" or "annul" or "annu" + => return "cancel"; + "next" or "suite" => return "next"; + "previous" or "retour" or "retou" + => return "previous"; + } + return s; +} + +# map softkey names to the appropriate byte sequences +keyseq(skey: string): array of byte +{ + b2 := 0; + asterisk := 0; + if(skey == nil || len skey == 0) + return nil; + if(skey[0] == '*') { + asterisk = 1; + skey = skey[1:]; + } + skey = canoncmd(skey); + case skey { + "connect" => b2 = 16r49; + "send" => b2 = 16r41; + "repeat" => b2 = 16r43; + "index" => b2 = 16r46; + "guide" => b2 = 16r44; + "correct" => b2 = 16r47; + "cancel" => b2 = 16r45; + "next" => b2 = 16r48; + "previous" => b2 = 16r42; + } + if(b2) { + if(asterisk) + return array [] of { byte '*', byte SEP, byte b2}; + else + return array [] of { byte SEP, byte b2}; + } else + return nil; +} + +# map hardware or software keyboard presses to minitel functions +minikey(key: int): string +{ + case key { + Kup or KupPC => + return"previous"; + Kdown or KdownPC => + return "next"; + Kenter => + return "send"; + Kback => + return "correct"; + Kesc => + return "cancel"; + KF1 => + return "guide"; + KF2 => + return "connect"; + KF3 => + return "repeat"; + KF4 => + return "index"; + * => + return nil; + } +}
\ No newline at end of file diff --git a/appl/wm/minitel/mdisplay.b b/appl/wm/minitel/mdisplay.b new file mode 100644 index 00000000..b3c629f9 --- /dev/null +++ b/appl/wm/minitel/mdisplay.b @@ -0,0 +1,799 @@ +implement MDisplay; + +# +# Copyright © 1998 Vita Nuova Limited. All rights reserved. +# +# - best viewed with acme! + +include "sys.m"; +include "draw.m"; +include "mdisplay.m"; + +sys : Sys; +draw : Draw; + +Context, Point, Rect, Font, Image, Display, Screen : import draw; + + +# len cell == number of lines +# len cell[0] == number of cellmap cells per char +# (x,y)*cellsize == font glyph clipr + +cellS := array [] of {array [] of {(0, 0)}}; +cellW := array [] of {array [] of {(0, 0), (1, 0)}}; +cellH := array [] of {array [] of {(0, 1)}, array [] of {(0, 0)}}; +cellWH := array [] of {array [] of {(0, 1), (1, 1)}, array [] of {(0, 0), (1, 0)}}; + +Cellinfo : adt { + font : ref Font; + ch, attr : int; + clipmod : (int, int); +}; + + +# current display attributes +display : ref Display; +window : ref Image; +frames := array [2] of ref Image; +update : chan of int; + +colours : array of ref Image; +bright : ref Image; + +# current mode attributes +cellmap : array of Cellinfo; +nrows : int; +ncols : int; +ulheight : int; +curpos : Point; +winoff : Point; +cellsize : Point; +modeattr : con fgWhite | bgBlack; +showC := 0; +delims := 0; +modbbox := Rect((0,0),(0,0)); +blankrow : array of Cellinfo; + +ctxt : ref Context; +font : ref Font; # g0 videotex font - extended with unicode g2 syms +fonth : ref Font; # double height version of font +fontw : ref Font; # double width +fonts : ref Font; # double size +fontg1 : ref Font; # semigraphic videotex font (ch+128=separated) +fontfr : ref Font; # french character set +fontusa : ref Font; # american character set + + +Init(c : ref Context) : string +{ + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + + if (c == nil || c.display == nil) + return "no display context"; + + ctxt = c; + disp := ctxt.display; + + black := disp.rgb2cmap(0, 0, 0); + blue := disp.rgb2cmap(0, 0, 255); + red := disp.rgb2cmap(255, 0, 0); + magenta := disp.rgb2cmap(255, 0, 255); + green := disp.rgb2cmap(0, 255, 0); + cyan := disp.rgb2cmap(0, 255, 255); + yellow := disp.rgb2cmap(255, 255, 0); + white := disp.rgb2cmap(240, 240, 240); + + iblack := disp.color(black); + iblue := disp.color(blue); + ired := disp.color(red); + imagenta := disp.color(magenta); + igreen := disp.color(green); + icyan := disp.color(cyan); + iyellow := disp.color(yellow); + iwhite := disp.color(white); + + colours = array [] of { iblack, iblue, ired, imagenta, + igreen, icyan, iyellow, iwhite}; + bright = disp.color(disp.rgb2cmap(255, 255, 255)); + + update = chan of int; + spawn Update(update); + display = disp; + return nil; +} + +Quit() +{ + if (update != nil) + update <- = QuitUpdate; + update = nil; + window = nil; + frames[0] = nil; + frames[1] = nil; + cellmap = nil; + display = nil; +} + +Mode(r : Draw->Rect, w, h, ulh, d : int, fontpath : string) : (string, ref Draw->Image) +{ + if (display == nil) + # module not properly Init()'d + return ("not initialized", nil); + + curpos = Point(-1, -1); + if (window != nil) + update <- = Pause; + + cellmap = nil; + window = nil; + (dx, dy) := (r.dx(), r.dy()); + if (dx == 0 || dy == 0) { + return (nil, nil); + } + + black := display.rgb2cmap(0, 0, 0); + window = ctxt.screen.newwindow(r, Draw->Refbackup, black); + if (window == nil) + return ("cannot create window", nil); + + window.origin(Point(0,0), r.min); + winr := Rect((0,0), (dx, dy)); + frames[0] = display.newimage(winr, window.chans, 0, black); + frames[1] = display.newimage(winr, window.chans, 0, black); + + if (window == nil || frames[0] == nil || frames[1] == nil) { + window = nil; + return ("cannot allocate display resources", nil); + } + + ncols = w; + nrows = h; + ulheight = ulh; + delims = d; + showC = 0; + + cellmap = array [ncols * nrows] of Cellinfo; + + font = Font.open(display, fontpath); + fontw = Font.open(display, fontpath + "w"); + fonth = Font.open(display, fontpath + "h"); + fonts = Font.open(display, fontpath + "s"); + fontg1 = Font.open(display, fontpath + "g1"); + fontfr = Font.open(display, fontpath + "fr"); + fontusa = Font.open(display, fontpath + "usa"); + + if (font != nil) + cellsize = Point(font.width(" "), font.height); + else + cellsize = Point(dx/ncols, dy / nrows); + + winoff.x = (dx - (cellsize.x * ncols)) / 2; + winoff.y = (dy - (cellsize.y * nrows)) /2; + if (winoff.x < 0) + winoff.x = 0; + if (winoff.y < 0) + winoff.y = 0; + + blankrow = array [ncols] of {* => Cellinfo(font, ' ', modeattr | fgWhite, (0,0))}; + for (y := 0; y < nrows; y++) { + col0 := y * ncols; + cellmap[col0:] = blankrow; + } + +# frames[0].clipr = frames[0].r; +# frames[1].clipr = frames[1].r; +# frames[0].draw(frames[0].r, colours[0], nil, Point(0,0)); +# frames[1].draw(frames[1].r, colours[0], nil, Point(0,0)); +# window.draw(window.r, colours[0], nil, Point(0,0)); + update <- = Continue; + return (nil, window); +} + +Cursor(pt : Point) +{ + if (update == nil || cellmap == nil) + # update thread (cursor/character flashing) not running + return; + + # normalize pt + pt.x--; + + curpos = pt; + update <- = CursorSet; +} + +Put(str : string, pt : Point, charset, attr, insert : int) +{ + if (cellmap == nil || str == nil) + # nothing to do + return; + + # normalize pt + pt.x--; + + f : ref Font; + cell := cellS; + + case charset { + videotex => + if (!(attr & attrD)) + attr &= (fgMask | attrF | attrH | attrW | attrP); + if (attr & attrW && attr & attrH) { + cell = cellWH; + f = fonts; + } else if (attr & attrH) { + cell = cellH; + f = fonth; + } else if (attr & attrW) { + cell = cellW; + f = fontw; + } else { + f = font; + } + + semigraphic => + f = fontg1; + if (attr & attrL) { + # convert to "separated" + newstr := ""; + for (ix := 0; ix < len str; ix++) + newstr[ix] = str[ix] + 16r80; + str = newstr; + } + # semigraphic charset does not support size / polarity attributes + # attrD always set later once field attr established + attr &= ~(attrD | attrH | attrW | attrP | attrL); + + french => f = fontfr; + american => f = fontusa; + * => f = font; + } + + update <- = Pause; + + txty := pt.y - (len cell - 1); + for (cellix := len cell - 1; cellix >= 0; cellix--) { + y := pt.y - cellix; + + if (y < 0) + continue; + if (y >= nrows) + break; + + col0 := y * ncols; + colbase := pt.y * ncols; + + if (delims && !(attr & attrD)) { + # seek back for a delimiter + mask : int; + delimattr := modeattr; + + # semigraphics only inherit attrC from current field + if (charset == semigraphic) + mask = attrC; + else + mask = bgMask | attrC | attrL; + + for (ix := pt.x-1; ix >= 0; ix--) { + cix := ix + col0; + if (cellmap[cix].attr & attrD) { + if (cellmap[cix].font == fontg1 && f != fontg1) + # don't carry over attrL from semigraphic field + mask &= ~attrL; + + delimattr = cellmap[cix].attr; + break; + } + } + attr = (attr & ~mask) | (delimattr & mask); + + # semigraphics validate background colour + if (charset == semigraphic) + attr |= attrD; + } + + strlen := len cell[0] * len str; + gfxwidth := cellsize.x * strlen; + srco := Point(pt.x*cellsize.x, y*cellsize.y); + + if (insert) { + # copy existing cells and display to new position + if (pt.x + strlen < ncols) { + for (destx := ncols -1; destx > pt.x; destx--) { + srcx := destx - strlen; + if (srcx < 0) + break; + cellmap[col0 + destx] = cellmap[col0 + srcx]; + } + + # let draw() do the clipping for us + dsto := Point(srco.x + gfxwidth, srco.y); + dstr := Rect((dsto.x, srco.y), (ncols * cellsize.x, srco.y + cellsize.y)); + + frames[0].clipr = frames[0].r; + frames[1].clipr = frames[1].r; + frames[0].draw(dstr, frames[0], nil, srco); + frames[1].draw(dstr, frames[1], nil, srco); + if (modbbox.dx() == 0) + modbbox = dstr; + else + modbbox = boundingrect(modbbox, dstr); + } + } + + # copy-in new string + x := pt.x; + for (strix := 0; x < ncols && strix < len str; strix++) { + for (clipix := 0; clipix < len cell[cellix]; (x, clipix) = (x+1, clipix+1)) { + if (x < 0) + continue; + if (x >= ncols) + break; + cmix := col0 + x; + cellmap[cmix].font = f; + cellmap[cmix].ch = str[strix]; + cellmap[cmix].attr = attr; + cellmap[cmix].clipmod = cell[cellix][clipix]; + } + } + + # render the new string + txto := Point(srco.x, txty * cellsize.y); + strr := Rect(srco, (srco.x + gfxwidth, srco.y + cellsize.y)); + if (strr.max.x > ncols * cellsize.x) + strr.max.x = ncols * cellsize.x; + + drawstr(str, f, strr, txto, attr); + + # redraw remainder of line until find cell not needing redraw + + # this could be optimised by + # spotting strings with same attrs, font and clipmod pairs + # and write out whole string rather than processing + # a char at a time + + attr2 := attr; + mask := bgMask | attrC | attrL; + s := ""; + for (; delims && x < ncols; x++) { + if (x < 0) + continue; + newattr := cellmap[col0 + x].attr; + + if (cellmap[col0 + x].font == fontg1) { + # semigraphics act as bg colour delimiter + attr2 = (attr2 & ~bgMask) | (newattr & bgMask); + mask &= ~attrL; + } else + if (newattr & attrD) + break; + + if ((attr2 & mask) == (newattr & mask)) + break; + newattr = (newattr & ~mask) | (attr2 & mask); + cellmap[col0 + x].attr = newattr; + s[0] = cellmap[col0 + x].ch; + (cx, cy) := cellmap[col0 + x].clipmod; + f2 := cellmap[col0 + x].font; + + cellpos := Point(x * cellsize.x, y * cellsize.y); + clipr := Rect(cellpos, cellpos.add(Point(cellsize.x, cellsize.y))); + drawpt := cellpos.sub(Point(cx*cellsize.x, cy*cellsize.y)); + drawstr(s, f2, clipr, drawpt, newattr); + } + } + update <- = Continue; +} + +Scroll(topline, nlines : int) +{ + if (cellmap == nil || nlines == 0) + return; + + blankr : Rect; + scr := Rect((0,topline * cellsize.y), (ncols * cellsize.x, nrows * cellsize.y)); + + update <- = Pause; + + frames[0].clipr = scr; + frames[1].clipr = scr; + dstr := scr.subpt(Point(0, nlines * cellsize.y)); + + frames[0].draw(dstr, frames[0], nil, frames[0].clipr.min); + frames[1].draw(dstr, frames[1], nil, frames[1].clipr.min); + + if (nlines > 0) { + # scroll up - copy up from top + if (nlines > nrows - topline) + nlines = nrows - topline; + for (y := nlines + topline; y < nrows; y++) { + srccol0 := y * ncols; + dstcol0 := (y - nlines) * ncols; + cellmap[dstcol0:] = cellmap[srccol0:srccol0+ncols]; + } + for (y = nrows - nlines; y < nrows; y++) { + col0 := y * ncols; + cellmap[col0:] = blankrow; + } + blankr = Rect(Point(0, scr.max.y - (nlines * cellsize.y)), scr.max); + } else { + # scroll down - copy down from bottom + nlines = -nlines; + if (nlines > nrows - topline) + nlines = nrows - topline; + for (y := (nrows - 1) - nlines; y >= topline; y--) { + srccol0 := y * ncols; + dstcol0 := (y + nlines) * ncols; + cellmap[dstcol0:] = cellmap[srccol0:srccol0+ncols]; + } + for (y = topline; y < nlines; y++) { + col0 := y * ncols; + cellmap[col0:] = blankrow; + } + blankr = Rect(scr.min, (scr.max.x, scr.min.y + (nlines * cellsize.y))); + } + frames[0].draw(blankr, colours[0], nil, Point(0,0)); + frames[1].draw(blankr, colours[0], nil, Point(0,0)); + if (modbbox.dx() == 0) + modbbox = scr; + else + modbbox = boundingrect(modbbox, scr); + update <- = Continue; +} + +Reveal(show : int) +{ + showC = show; + if (cellmap == nil) + return; + + update <- = Pause; + for (y := 0; y < nrows; y++) { + col0 := y * ncols; + for (x := 0; x < ncols; x++) { + attr := cellmap[col0+x].attr; + if (!(attr & attrC)) + continue; + + s := ""; + s[0] = cellmap[col0 + x].ch; + (cx, cy) := cellmap[col0 + x].clipmod; + f := cellmap[col0 + x].font; + cellpos := Point(x * cellsize.x, y * cellsize.y); + clipr := Rect(cellpos, cellpos.add(Point(cellsize.x, cellsize.y))); + drawpt := cellpos.sub(Point(cx*cellsize.x, cy*cellsize.y)); + + drawstr(s, f, clipr, drawpt, attr); + } + } + update <- = Continue; +} + +# expects that pt.x already normalized +wordchar(pt : Point) : int +{ + if (pt.x < 0 || pt.x >= ncols) + return 0; + if (pt.y < 0 || pt.y >= nrows) + return 0; + + col0 := pt.y * ncols; + c := cellmap[col0 + pt.x]; + + if (c.attr & attrC && !showC) + # don't let clicking on screen 'reveal' concealed chars! + return 0; + + if (c.font == fontg1) + return 0; + + if (c.attr & attrW) { + # check for both parts of character + (modx, nil) := c.clipmod; + if (modx == 1) { + # rhs of char - check lhs is the same + if (pt.x <= 0) + return 0; + lhc := cellmap[col0 + pt.x-1]; + (lhmodx, nil) := lhc.clipmod; + if (!((lhc.attr & attrW) && (lhc.font == c.font) && (lhc.ch == c.ch) && (lhmodx == 0))) + return 0; + } else { + # lhs of char - check rhs is the same + if (pt.x >= ncols - 1) + return 0; + rhc := cellmap[col0 + pt.x + 1]; + (rhmodx, nil) := rhc.clipmod; + if (!((rhc.attr & attrW) && (rhc.font == c.font) && (rhc.ch == c.ch) && (rhmodx == 1))) + return 0; + } + } + if (c.ch >= 16r30 && c.ch <= 16r39) + # digits + return 1; + if (c.ch >= 16r41 && c.ch <= 16r5a) + # capitals + return 1; + if (c.ch >= 16r61 && c.ch <= 16r7a) + # lowercase + return 1; + if (c.ch == '*' || c.ch == '/') + return 1; + return 0; +} + +GetWord(gfxpt : Point) : string +{ + if (cellmap == nil) + return nil; + + scr := Rect((0,0), (ncols * cellsize.x, nrows * cellsize.y)); + gfxpt = gfxpt.sub(winoff); + + if (!gfxpt.in(scr)) + return nil; + + x := gfxpt.x / cellsize.x; + y := gfxpt.y / cellsize.y; + col0 := y * ncols; + + s := ""; + + # seek back + for (sx := x; sx >= 0; sx--) + if (!wordchar(Point(sx, y))) + break; + + if (sx++ == x) + return nil; + + # seek forward, constructing s + for (; sx < ncols; sx++) { + if (!wordchar(Point(sx, y))) + break; + c := cellmap[col0 + sx]; + s[len s] = c.ch; + if (c.attr & attrW) + sx++; + } + return s; +} + +Refresh() +{ + if (window == nil || modbbox.dx() == 0) + return; + + if (update != nil) + update <- = Redraw; +} + +framecolours(attr : int) : (ref Image, ref Image, ref Image, ref Image) +{ + fg : ref Image; + fgcol := attr & fgMask; + if (fgcol == fgWhite && attr & attrB) + fg = bright; + else + fg = colours[fgcol / fgBase]; + + bg : ref Image; + bgcol := attr & bgMask; + if (bgcol == bgWhite && attr & attrB) + bg = bright; + else + bg = colours[bgcol / bgBase]; + + (fg0, fg1) := (fg, fg); + (bg0, bg1) := (bg, bg); + + if (attr & attrP) + (fg0, bg0, fg1, bg1) = (bg1, fg1, bg0, fg0); + + if (attr & attrF) { + fg0 = fg; + fg1 = bg; + } + + if ((attr & attrC) && !showC) + (fg0, fg1) = (bg0, bg1); + return (fg0, bg0, fg1, bg1); +} + +kill(pid : int) +{ + prog := "/prog/" + string pid + "/ctl"; + fd := sys->open(prog, Sys->OWRITE); + if (fd != nil) { + cmd := array of byte "kill"; + sys->write(fd, cmd, len cmd); + } +} + +timer(ms : int, pc, tick : chan of int) +{ + pc <- = sys->pctl(0, nil); + for (;;) { + sys->sleep(ms); + tick <- = 1; + } +} + +# Update() commands +Redraw, Pause, Continue, CursorSet, QuitUpdate : con iota; + +Update(cmd : chan of int) +{ + flashtick := chan of int; + cursortick := chan of int; + pc := chan of int; + spawn timer(1000, pc, flashtick); + flashpid := <- pc; + spawn timer(500, pc, cursortick); + cursorpid := <- pc; + + cursor : Point; + showcursor := 0; + cursoron := 0; + quit := 0; + nultick := chan of int; + flashchan := nultick; + pcount := 1; + fgframe := 0; + + for (;!quit ;) alt { + c := <- cmd => + case c { + Redraw => + frames[0].clipr = frames[0].r; + frames[1].clipr = frames[1].r; + r := modbbox.addpt(winoff); + window.draw(r.addpt(window.r.min), frames[fgframe], nil, modbbox.min); + if (showcursor && cursoron) + drawcursor(cursor, fgframe, 1); + modbbox = Rect((0,0),(0,0)); + + Pause => + if (pcount++ == 0) + flashchan = nultick; + + Continue => + pcount--; + if (pcount == 0) + flashchan = flashtick; + + QuitUpdate => + quit++; + + CursorSet => + frames[0].clipr = frames[0].r; + frames[1].clipr = frames[1].r; + if (showcursor && cursoron) + drawcursor(cursor, fgframe, 0); + cursoron = 0; + if (curpos.x < 0 || curpos.x >= ncols || curpos.y < 0 || curpos.y >= nrows) + showcursor = 0; + else { + cursor = curpos; + showcursor = 1; + drawcursor(cursor, fgframe, 1); + cursoron = 1; + } + } + + <- flashchan => + # flip displays... + fgframe = (fgframe + 1 ) % 2; + modbbox = Rect((0,0),(0,0)); + frames[0].clipr = frames[0].r; + frames[1].clipr = frames[1].r; + window.draw(window.r.addpt(winoff), frames[fgframe], nil, Point(0,0)); + if (showcursor && cursoron) + drawcursor(cursor, fgframe, 1); + + <- cursortick => + if (showcursor) { + cursoron = !cursoron; + drawcursor(cursor, fgframe, cursoron); + } + } + kill(flashpid); + kill(cursorpid); +} + + +drawstr(s : string, f : ref Font, clipr : Rect, drawpt : Point, attr : int) +{ + (fg0, bg0, fg1, bg1) := framecolours(attr); + frames[0].clipr = clipr; + frames[1].clipr = clipr; + frames[0].draw(clipr, bg0, nil, Point(0,0)); + frames[1].draw(clipr, bg1, nil, Point(0,0)); + ulrect : Rect; + ul := (attr & attrL) && ! (attr & attrD); + + if (f != nil) { + if (ul) + ulrect = Rect((drawpt.x, drawpt.y + f.height - ulheight), (drawpt.x + clipr.dx(), drawpt.y + f.height)); + if (fg0 != bg0) { + frames[0].text(drawpt, fg0, Point(0,0), f, s); + if (ul) + frames[0].draw(ulrect, fg0, nil, Point(0,0)); + } + if (fg1 != bg1) { + frames[1].text(drawpt, fg1, Point(0,0), f, s); + if (ul) + frames[1].draw(ulrect, fg1, nil, Point(0,0)); + } + } + if (modbbox.dx() == 0) + modbbox = clipr; + else + modbbox = boundingrect(modbbox, clipr); +} + +boundingrect(r1, r2 : Rect) : Rect +{ + if (r2.min.x < r1.min.x) + r1.min.x = r2.min.x; + if (r2.min.y < r1.min.y) + r1.min.y = r2.min.y; + if (r2.max.x > r1.max.x) + r1.max.x = r2.max.x; + if (r2.max.y > r1.max.y) + r1.max.y = r2.max.y; + return r1; +} + +drawcursor(pt : Point, srcix, show : int) +{ + col0 := pt.y * ncols; + c := cellmap[col0 + pt.x]; + s := ""; + + s[0] = c.ch; + (cx, cy) := c.clipmod; + cellpos := Point(pt.x * cellsize.x, pt.y * cellsize.y); + clipr := Rect(cellpos, cellpos.add(Point(cellsize.x, cellsize.y))); + clipr = clipr.addpt(winoff); + clipr = clipr.addpt(window.r.min); + + drawpt := cellpos.sub(Point(cx*cellsize.x, cy*cellsize.y)); + drawpt = drawpt.add(winoff); + drawpt = drawpt.add(window.r.min); + + if (!show) { + # copy from appropriate frame buffer + window.draw(clipr, frames[srcix], nil, cellpos); + return; + } + + # invert colours + attr := c.attr ^ (fgMask | bgMask); + + fg, bg : ref Image; + f := c.font; + if (srcix == 0) + (fg, bg, nil, nil) = framecolours(attr); + else + (nil, nil, fg, bg) = framecolours(attr); + + prevclipr := window.clipr; + window.clipr = clipr; + + window.draw(clipr, bg, nil, Point(0,0)); + ulrect : Rect; + ul := (attr & attrL) && ! (attr & attrD); + + if (f != nil) { + if (ul) + ulrect = Rect((drawpt.x, drawpt.y + f.height - ulheight), (drawpt.x + clipr.dx(), drawpt.y + f.height)); + if (fg != bg) { + window.text(drawpt, fg, Point(0,0), f, s); + if (ul) + window.draw(ulrect, fg, nil, Point(0,0)); + } + } + window.clipr = prevclipr; +} diff --git a/appl/wm/minitel/mdisplay.dis b/appl/wm/minitel/mdisplay.dis Binary files differnew file mode 100644 index 00000000..fd193994 --- /dev/null +++ b/appl/wm/minitel/mdisplay.dis diff --git a/appl/wm/minitel/mdisplay.m b/appl/wm/minitel/mdisplay.m new file mode 100644 index 00000000..24d7173f --- /dev/null +++ b/appl/wm/minitel/mdisplay.m @@ -0,0 +1,115 @@ +# +# Minitel display handling module +# +# © 1998 Vita Nuova Limited. All rights reserved. +# + +MDisplay: module +{ + + PATH: con "/dis/wm/minitel/mdisplay.dis"; + + # Available character sets + videotex, semigraphic, french, american : con iota; + + # Fill() attributes bit mask + # + # DL CFPH WBbb bfff + # + # D = Delimiter (set "serial" attributes for rest of line) + # L = Lining (underlined text & "separated" graphics) + # C = Concealing + # F = Flashing + # P = polarity (1 = "inverse") + # H = double height + # W = double width (set H+W for double size) + # B = bright (0: fgwhite=lt.grey, 1: fgwhite=white) + # bbb = background colour + # fff = foreground colour + + fgBase : con 8r001; + bgBase : con 8r010; + attrBase : con 8r100; + + fgMask : con 8r007; + bgMask : con 8r070; + attrMask : con ~0 ^ (fgMask | bgMask); + + fgBlack, fgBlue, fgRed, fgMagenta, + fgGreen, fgCyan, fgYellow, fgWhite : con iota * fgBase; + + bgBlack, bgBlue, bgRed, bgMagenta, + bgGreen, bgCyan, bgYellow, bgWhite : con iota * bgBase; + + attrB, attrW, attrH, attrP, attrF, attrC, attrL, attrD : con attrBase << iota; + + # + # Init (ctxt) : string + # performs general module initialisation + # creates the display window of size/position r using the + # given display context. + # spawns refresh thread + # returns reason for error, or nil on success + # + # Mode(rect, width, height, ulheight, delims, fontpath) : (string, ref Draw->Image) + # set/reset display to given rectangle and character grid size + # ulheight == underline height from bottom of character cell + # if delims != 0 then "field" attrs for Put() are derived from + # preceding delimiter otherwise Put() attrs are taken as is + # + # load fonts: + # <fontpath> videotex + # <fontpath>w videotex double width + # <fontpath>h videotex double height + # <fontpath>s videotex double size + # <fontpath>g1 videotex semigraphics + # <fontpath>fr french character set + # <fontpath>usa american character set + # Note: + # charset g2 is not directly supported, instead the symbols + # of g2 that do not appear in g0 (standard videotex charset) + # are available in videotex font using unicode char codes. + # Therefore controlling s/w must map g2 codes to unicode. + # + # Cursor(pt) + # move cursor to given position + # row number (y) is 0 based + # column number (x) is 1 based + # move cursor off-screen to hide + # + # Put(str, pt, charset, attr, insert) + # render string str at position pt in the given character set + # using specified attributes. + # if insert is non-zero, all characters from given position to end + # of line are moved right by len str positions. + # + # Scroll(topline, nlines) + # move the whole displayby nlines (+ve = scroll up). + # exposed lines of display are set to spaces rendered with + # the current mode attribute flags. + # scroll region is from topline to bottom of display + # + # Reveal(reveal) + # reveal/hide all chars affected by Concealing attribute. + # + # Refresh() + # force screen update + # + # GetWord(pt) : string + # returns on-screen word at given graphics co-ords + # returns nil if blank or semigraphic charset at location + # + # Quit() + # undo Init() + + + Init : fn (ctxt : ref Draw->Context) : string; + Mode : fn (r : Draw->Rect, width, height, ulh, attr : int, fontpath : string) : (string, ref Draw->Image); + Cursor : fn (pt : Draw->Point); + Put : fn (str : string, pt : Draw->Point, chset, attr, insert : int); + Scroll : fn (topline, nlines : int); + Reveal : fn (reveal : int); + Refresh : fn (); + GetWord : fn (gfxpt : Draw->Point) : string; + Quit : fn (); +}; diff --git a/appl/wm/minitel/mdisplay.sbl b/appl/wm/minitel/mdisplay.sbl new file mode 100644 index 00000000..c97ec8e7 --- /dev/null +++ b/appl/wm/minitel/mdisplay.sbl @@ -0,0 +1,1969 @@ +limbo .sbl 2.1 +MDisplay +4 +mdisplay.b +sys.m +draw.m +mdisplay.m +1460 +69.1,25 0 +70.1,28 1 +72.5,13 2 +17,33 2 +73.9,29 3 +2,29 3 +75.1,9 4 +76.1,21 5 +78.1,32 6 +10,14 6 +24,25 6 +27,28 6 +30,31 6 +1,32 6 +1,32 6 +79.1,34 7 +10,14 7 +24,25 7 +27,28 7 +30,33 7 +1,34 7 +1,34 7 +80.1,33 8 +9,13 8 +23,26 8 +28,29 8 +31,32 8 +1,33 8 +1,33 8 +81.1,38 9 +12,16 9 +26,29 9 +31,32 9 +34,37 9 +1,38 9 +1,38 9 +82.1,34 10 +10,14 10 +24,25 10 +27,30 10 +32,33 10 +1,34 10 +1,34 10 +83.1,36 11 +10,14 11 +24,25 11 +27,30 11 +32,35 11 +1,36 11 +1,36 11 +84.1,37 12 +11,15 12 +25,28 12 +30,33 12 +35,36 12 +1,37 12 +1,37 12 +85.1,38 13 +10,14 13 +24,27 13 +29,32 13 +34,37 13 +1,38 13 +1,38 13 +87.1,28 14 +11,15 14 +22,27 14 +1,28 14 +1,28 14 +88.1,27 15 +11,15 15 +22,26 15 +1,27 15 +1,27 15 +89.1,25 16 +10,14 16 +21,24 16 +1,25 16 +1,25 16 +90.1,32 17 +13,17 17 +24,31 17 +1,32 17 +1,32 17 +91.1,28 18 +11,15 18 +22,27 18 +1,28 18 +1,28 18 +92.1,26 19 +10,14 19 +21,25 19 +1,26 19 +1,26 19 +93.1,30 20 +12,16 20 +23,29 20 +1,30 20 +1,30 20 +94.1,28 21 +11,15 21 +22,27 21 +1,28 21 +1,28 21 +96.1,97.37 22 +96.25,31 22 +25,31 22 +33,38 22 +33,38 22 +40,44 22 +40,44 22 +46,54 22 +46,54 22 +97.6,12 22 +6,12 22 +14,19 22 +14,19 22 +21,28 22 +21,28 22 +30,36 22 +30,36 22 +98.21,49 23 +21,25 23 +35,38 23 +40,43 23 +45,48 23 +21,49 23 +21,49 23 +1,50 23 +10,14 23 +10,14 23 +1,50 23 +1,50 23 +100.1,21 24 +101.1,21 25 +14,20 25 +1,21 25 +102.1,15 26 +103.8,11 27 +1,11 27 +108.5,18 28 +109.2,24 29 +110.1,13 30 +111.1,13 31 +112.1,10 32 +1,16 32 +113.1,10 33 +1,16 33 +114.1,14 34 +115.1,14 35 +116.0,1 36 +120.5,19 37 +122.10,27 38 +29,32 38 +2,33 38 +124.16,18 39 +20,22 39 +125.5,18 40 +126.2,19 41 +128.1,14 42 +129.1,13 43 +130.14,20 44 +14,15 44 +14,20 44 +14,20 44 +22,28 44 +22,23 44 +22,28 44 +22,28 44 +131.5,12 45 +16,23 45 +132.10,13 46 +15,18 46 +2,19 46 +135.1,35 47 +10,17 47 +27,28 47 +30,31 47 +33,34 47 +1,35 47 +1,35 47 +136.1,58 48 +10,21 48 +32,33 48 +35,50 48 +52,57 48 +1,58 48 +1,58 48 +137.5,18 49 +138.10,32 50 +34,37 50 +2,38 50 +140.1,33 51 +1,7 51 +21,22 51 +23,24 51 +27,32 51 +1,33 51 +1,33 51 +141.15,16 52 +17,18 52 +21,29 52 +142.1,10 53 +1,59 53 +13,20 53 +30,34 53 +36,48 53 +50,51 53 +53,58 53 +1,59 53 +1,59 53 +143.1,10 54 +1,59 54 +13,20 54 +30,34 54 +36,48 54 +50,51 54 +53,58 54 +1,59 54 +1,59 54 +145.5,18 55 +22,31 55 +22,38 55 +42,51 55 +42,58 55 +146.2,14 56 +147.10,45 57 +47,50 57 +2,51 57 +150.1,10 58 +151.1,10 59 +152.1,15 60 +153.1,11 61 +154.1,10 62 +156.18,31 63 +1,44 63 +158.1,37 64 +19,26 64 +28,36 64 +1,37 64 +1,37 64 +159.1,43 65 +19,26 65 +28,42 65 +1,43 65 +1,43 65 +160.1,43 66 +19,26 66 +28,42 66 +1,43 66 +1,43 66 +161.1,44 67 +20,27 67 +29,43 67 +1,44 67 +1,44 67 +162.1,45 68 +20,27 68 +29,44 68 +1,45 68 +1,45 68 +163.1,45 69 +20,27 69 +29,44 69 +1,45 69 +1,45 69 +164.1,47 70 +21,28 70 +30,46 70 +1,47 70 +1,47 70 +166.5,16 71 +167.19,34 72 +19,23 72 +30,33 72 +19,34 72 +19,34 72 +36,47 72 +36,47 73 +169.19,27 74 +29,39 74 +171.18,38 75 +12,39 75 +1,43 75 +172.18,38 76 +12,39 76 +1,42 76 +173.5,17 77 +174.2,14 78 +175.5,17 79 +176.2,14 80 +178.1,82 81 +35,81 81 +35,81 81 +35,81 81 +44,48 81 +50,53 81 +55,73 81 +76,77 81 +78,79 81 +35,81 81 +35,81 81 +35,81 81 +179.6,12 82 +14,23 83 +180.2,19 84 +181.2,27 85 +179.25,28 86 +25,28 86 +189.1,21 87 +190.9,12 88 +14,20 88 +1,21 88 +195.5,18 89 +22,36 89 +197.2,8 90 +200.1,7 91 +202.1,12 92 +203.1,22 93 +204.0,1 94 +208.5,19 95 +23,33 95 +210.2,8 96 +213.1,7 97 +216.1,14 98 +218.6,13 99 +6,13 99 +6,13 99 +220.7,21 100 +7,21 100 +221.3,51 101 +222.6,18 102 +6,18 102 +22,34 102 +22,34 102 +223.3,16 103 +224.3,12 104 +3,12 105 +225.13,25 106 +13,25 106 +226.3,15 107 +227.3,12 108 +3,12 109 +228.13,25 110 +13,25 110 +229.3,15 111 +230.3,12 112 +3,12 113 +232.3,11 114 +3,11 99 +236.2,12 115 +237.6,18 116 +6,18 116 +239.3,15 117 +240.8,15 118 +22,29 119 +17,29 119 +241.17,24 120 +17,32 120 +4,32 120 +240.31,35 121 +31,35 121 +242.3,15 122 +3,15 123 +246.2,50 124 +2,50 99 +248.12,22 125 +12,22 99 +249.14,25 126 +14,25 99 +250.8,16 127 +8,16 99 +253.1,18 128 +255.17,25 129 +16,30 129 +1,30 129 +256.16,24 130 +6,28 130 +30,41 131 +257.2,20 132 +259.6,11 133 +6,11 134 +260.3,11 135 +261.6,16 136 +6,16 137 +262.3,8 138 +264.2,19 139 +265.2,25 140 +267.6,12 141 +17,31 141 +17,31 141 +270.3,24 142 +273.7,29 143 +274.4,16 144 +4,16 145 +276.4,34 146 +278.8,20 147 +22,29 148 +279.4,20 149 +280.8,20 150 +8,33 150 +8,33 150 +281.9,21 151 +9,36 151 +40,51 151 +283.6,20 152 +285.17,29 153 +5,34 153 +286.5,10 154 +278.31,35 155 +31,35 155 +289.18,23 156 +10,24 156 +27,45 156 +3,45 156 +292.7,29 157 +293.4,17 158 +296.16,23 159 +12,23 159 +26,33 159 +2,33 159 +297.2,33 160 +298.16,31 161 +33,45 161 +300.6,12 162 +302.7,20 163 +7,28 163 +303.9,26 164 +28,40 165 +304.5,27 166 +305.9,17 167 +306.6,11 168 +307.13,25 169 +5,26 169 +37,48 169 +29,49 169 +5,49 169 +303.42,49 170 +42,49 170 +311.18,35 171 +37,43 171 +312.18,24 172 +26,32 172 +36,54 172 +56,75 172 +314.4,13 173 +4,13 173 +22,31 173 +22,31 173 +4,33 173 +4,33 174 +4,33 175 +315.4,13 176 +4,13 176 +22,31 176 +22,31 176 +4,33 176 +4,33 177 +4,33 178 +316.4,46 179 +4,13 179 +4,13 179 +19,23 179 +25,34 179 +25,34 179 +36,39 179 +41,45 179 +4,46 179 +317.4,46 180 +4,13 180 +4,13 180 +19,23 180 +25,34 180 +25,34 180 +36,39 180 +41,45 180 +4,46 180 +318.8,20 181 +8,15 181 +8,20 181 +8,20 181 +8,25 181 +319.5,19 182 +5,19 183 +321.5,42 184 +28,35 184 +37,41 184 +5,42 184 +5,42 184 +326.2,11 185 +327.7,17 186 +19,28 187 +40,47 187 +32,47 187 +328.8,19 188 +34,46 189 +30,46 189 +21,46 189 +329.8,13 190 +330.5,13 191 +331.8,18 192 +332.5,10 193 +333.4,20 194 +334.4,17 195 +4,26 195 +335.4,17 196 +23,33 196 +336.4,17 197 +4,29 197 +337.4,17 198 +28,40 198 +28,48 198 +4,48 198 +328.63,66 199 +68,76 199 +49,50 199 +52,58 199 +52,58 199 +327.49,56 200 +49,56 200 +342.16,22 201 +24,41 201 +343.15,19 202 +22,39 202 +41,60 202 +344.19,37 203 +6,37 203 +345.3,34 204 +347.2,35 205 +10,13 205 +15,16 205 +18,22 205 +24,28 205 +30,34 205 +2,35 205 +356.2,15 206 +357.2,32 207 +358.2,9 208 +359.9,15 209 +19,28 209 +360.7,12 210 +7,12 211 +361.4,12 212 +362.22,30 213 +14,31 213 +3,36 213 +364.15,23 214 +7,24 214 +7,39 214 +366.12,29 215 +32,50 215 +4,50 215 +367.4,18 216 +4,18 217 +369.8,23 218 +8,23 218 +8,23 219 +370.5,10 220 +372.25,41 221 +7,21 221 +7,41 221 +7,41 222 +373.4,9 223 +374.24,29 224 +13,30 224 +33,47 224 +3,47 224 +375.11,19 225 +3,20 225 +3,35 225 +376.18,26 226 +10,27 226 +3,30 226 +377.23,31 227 +15,32 227 +15,40 227 +378.17,25 228 +9,26 228 +3,31 228 +380.20,34 229 +36,50 229 +381.17,24 230 +26,68 230 +26,33 230 +44,54 230 +56,66 230 +26,68 230 +26,68 230 +382.3,61 231 +13,20 231 +31,44 231 +46,59 231 +3,61 231 +3,61 231 +383.3,41 232 +11,12 232 +14,16 232 +18,23 232 +25,31 232 +33,40 232 +3,41 232 +3,41 233 +359.30,33 234 +30,33 234 +30,33 235 +256.43,51 236 +43,51 236 +386.1,21 237 +387.0,1 238 +391.5,19 239 +23,34 239 +392.2,8 240 +395.14,15 241 +16,36 241 +40,58 241 +60,78 241 +397.1,18 242 +399.1,10 243 +1,10 243 +1,22 243 +1,22 244 +400.1,10 245 +1,10 245 +1,22 245 +1,22 246 +401.1,49 247 +9,12 247 +25,26 247 +28,47 247 +1,49 247 +1,49 247 +403.1,58 248 +1,10 248 +1,10 248 +16,20 248 +22,31 248 +22,31 248 +33,36 248 +38,47 248 +38,47 248 +38,57 248 +38,57 249 +1,58 248 +404.1,58 250 +1,10 250 +1,10 250 +16,20 250 +22,31 250 +22,31 250 +33,36 250 +38,47 250 +38,47 250 +38,57 250 +38,57 251 +1,58 250 +406.5,15 252 +408.15,30 253 +6,30 253 +409.3,27 254 +410.7,28 255 +30,39 256 +411.3,23 257 +412.14,26 258 +3,34 258 +413.39,52 259 +23,30 259 +23,53 259 +3,53 259 +3,53 260 +410.41,44 261 +41,44 261 +415.7,25 262 +27,36 263 +416.3,20 264 +417.3,28 265 +415.38,41 266 +38,41 266 +419.22,23 267 +37,58 267 +25,58 267 +61,68 267 +61,68 268 +422.2,18 269 +423.15,30 270 +6,30 270 +424.3,27 271 +425.12,23 272 +7,32 272 +34,46 273 +426.3,23 274 +427.14,26 275 +3,34 275 +428.39,52 276 +23,30 276 +23,53 276 +3,53 276 +3,53 277 +425.48,51 278 +48,51 278 +430.7,18 279 +20,30 280 +431.3,20 281 +432.3,28 282 +430.32,35 283 +32,35 283 +434.16,23 284 +26,35 284 +49,70 284 +37,70 284 +436.1,52 285 +1,10 285 +1,10 285 +16,22 285 +24,34 285 +24,34 285 +36,39 285 +47,48 285 +49,50 285 +1,52 285 +437.1,52 286 +1,10 286 +1,10 286 +16,22 286 +24,34 286 +24,34 286 +36,39 286 +47,48 286 +49,50 286 +1,52 286 +438.5,17 287 +5,12 287 +5,17 287 +5,17 287 +5,23 287 +439.2,15 288 +2,15 289 +441.2,38 290 +25,32 290 +34,37 290 +2,38 290 +2,38 290 +442.1,21 291 +443.0,1 292 +447.1,13 293 +448.5,19 294 +449.2,8 295 +451.1,18 296 +452.6,12 297 +14,23 298 +453.2,19 299 +454.7,13 300 +15,24 301 +455.19,25 302 +11,26 302 +3,31 302 +456.8,22 303 +8,22 303 +8,22 304 +8,22 305 +457.4,12 306 +459.3,10 307 +460.18,26 308 +10,27 308 +3,30 308 +461.23,31 309 +15,32 309 +15,40 309 +462.16,24 310 +8,25 310 +3,30 310 +463.20,34 311 +36,50 311 +464.17,24 312 +26,68 312 +26,33 312 +44,54 312 +56,66 312 +26,68 312 +26,68 312 +465.3,61 313 +13,20 313 +31,44 313 +46,59 313 +3,61 313 +3,61 313 +467.3,37 314 +11,12 314 +14,15 314 +17,22 314 +24,30 314 +32,36 314 +3,37 314 +3,37 315 +3,37 316 +454.26,29 317 +26,29 317 +452.25,28 318 +25,28 318 +470.1,21 319 +471.0,1 320 +476.5,13 321 +17,30 321 +477.9,10 322 +2,10 322 +478.5,13 323 +17,30 323 +479.9,10 324 +2,10 324 +481.1,21 325 +482.14,25 326 +6,26 326 +1,26 326 +484.5,19 327 +5,19 327 +24,29 327 +486.9,10 328 +2,10 328 +488.5,21 329 +489.9,10 330 +2,10 330 +491.5,19 331 +5,19 331 +493.17,26 332 +3,7 332 +494.6,15 333 +496.7,16 334 +497.11,12 335 +4,12 335 +498.18,29 336 +18,31 336 +10,32 336 +3,32 336 +499.20,31 337 +4,10 337 +500.9,27 338 +9,27 338 +31,51 338 +55,71 338 +75,88 338 +501.11,12 339 +4,12 339 +4,12 340 +4,12 341 +504.15,24 342 +7,24 342 +505.11,12 343 +4,12 343 +506.18,29 344 +18,33 344 +10,34 344 +3,34 344 +507.20,31 345 +4,10 345 +508.9,27 346 +9,27 346 +31,51 346 +55,71 346 +75,88 346 +509.11,12 347 +4,12 347 +4,12 348 +512.5,18 349 +22,35 349 +514.9,10 350 +2,10 350 +515.5,18 351 +22,35 351 +517.9,10 352 +2,10 352 +518.5,18 353 +22,35 353 +520.9,10 354 +2,10 354 +521.5,16 355 +20,31 355 +522.9,10 356 +2,10 356 +523.8,9 357 +1,9 357 +528.5,19 358 +529.9,12 359 +2,12 359 +531.14,15 360 +16,17 360 +21,39 360 +41,59 360 +532.1,26 361 +9,14 361 +19,25 361 +1,26 361 +1,26 361 +534.6,19 362 +6,11 362 +15,18 362 +6,19 362 +6,19 362 +6,19 362 +535.9,12 363 +2,12 363 +537.1,26 364 +538.1,26 365 +539.1,18 366 +541.1,8 367 +544.6,13 368 +15,22 369 +545.7,29 370 +22,24 370 +26,27 370 +7,29 370 +7,29 370 +7,29 370 +546.3,8 371 +544.24,28 372 +24,28 372 +548.5,9 373 +5,9 373 +5,14 373 +549.9,12 374 +2,12 374 +552.8,18 375 +553.7,29 376 +22,24 376 +26,27 376 +7,29 376 +7,29 376 +7,29 376 +7,29 377 +554.3,8 378 +555.15,24 379 +7,25 379 +2,25 379 +556.4,9 380 +2,17 380 +557.6,20 381 +6,20 381 +558.3,7 382 +3,7 383 +552.20,24 384 +20,24 384 +560.8,9 385 +1,9 385 +565.5,18 386 +22,34 386 +22,29 386 +22,34 386 +22,34 386 +22,39 386 +566.2,8 387 +568.5,18 388 +569.2,20 389 +570.0,1 390 +575.1,23 391 +576.5,21 392 +25,37 392 +25,37 392 +577.2,13 393 +2,13 394 +579.7,30 395 +2,30 395 +582.1,23 396 +583.5,21 397 +25,37 397 +25,37 397 +584.2,13 398 +2,13 399 +586.15,29 400 +7,30 400 +2,30 400 +588.16,18 401 +20,22 401 +589.16,18 402 +20,22 402 +591.5,17 403 +5,17 403 +592.26,29 404 +31,34 404 +36,39 404 +41,44 404 +3,6 404 +8,11 404 +13,16 404 +18,21 404 +18,21 405 +18,21 406 +18,21 407 +18,21 408 +594.5,17 409 +5,17 409 +595.2,10 410 +596.2,10 411 +599.5,19 412 +5,19 412 +24,29 412 +600.15,25 413 +601.9,12 414 +14,17 414 +19,22 414 +24,27 414 +1,28 414 +606.20,30 415 +9,30 415 +1,39 415 +1,39 416 +607.1,35 417 +17,21 417 +23,34 417 +1,35 417 +1,35 417 +608.5,14 418 +609.2,29 419 +610.2,30 420 +13,15 420 +17,20 420 +22,29 420 +2,30 420 +2,30 420 +2,30 421 +612.0,1 422 +616.9,26 423 +19,20 423 +22,25 423 +9,26 423 +9,26 423 +1,26 423 +618.2,16 424 +13,15 424 +2,16 424 +2,16 424 +619.2,13 425 +2,13 425 +628.1,25 426 +629.1,26 427 +630.1,18 428 +631.1,33 429 +13,17 429 +19,21 429 +23,32 429 +1,33 429 +632.1,18 430 +633.1,33 431 +13,16 431 +18,20 431 +22,32 431 +1,33 431 +634.1,19 432 +637.1,16 433 +638.1,14 434 +639.1,11 435 +640.1,23 436 +641.1,21 437 +642.1,12 438 +643.1,13 439 +645.8,12 440 +646.9,12 441 +9,12 441 +685.4,13 441 +4,13 441 +695.4,14 441 +4,14 441 +645.16,22 441 +16,22 441 +16,22 441 +16,22 441 +647.7,8 442 +7,8 442 +7,8 442 +649.3,12 443 +3,12 443 +21,30 443 +21,30 443 +3,32 443 +3,32 444 +3,32 445 +650.3,12 446 +3,12 446 +21,30 446 +21,30 446 +3,32 446 +3,32 447 +3,32 448 +651.3,29 449 +8,15 449 +22,28 449 +3,29 449 +3,29 449 +652.15,36 450 +15,16 450 +23,35 450 +15,36 450 +15,36 450 +3,72 450 +3,9 450 +3,9 450 +38,53 450 +38,53 450 +55,58 450 +60,71 450 +3,72 450 +653.7,17 451 +21,29 451 +654.4,34 452 +15,21 452 +23,30 452 +32,33 452 +4,34 452 +655.19,20 453 +21,22 453 +25,26 453 +27,28 453 +27,28 442 +658.7,15 454 +7,15 454 +7,20 454 +659.4,23 455 +4,23 442 +662.3,11 456 +663.7,18 457 +664.4,25 458 +4,25 442 +667.3,9 459 +3,9 442 +670.3,12 460 +3,12 460 +21,30 460 +21,30 460 +3,32 460 +3,32 461 +3,32 462 +671.3,12 463 +3,12 463 +21,30 463 +21,30 463 +3,32 463 +3,32 464 +3,32 465 +672.7,17 466 +21,29 466 +673.4,34 467 +15,21 467 +23,30 467 +32,33 467 +4,34 467 +674.3,15 468 +675.7,19 469 +23,40 469 +44,56 469 +61,78 469 +676.4,18 470 +4,18 471 +678.4,19 472 +679.4,18 473 +680.4,34 474 +15,21 474 +23,30 474 +32,33 474 +4,34 474 +681.4,16 475 +4,16 442 +4,16 441 +687.12,26 476 +2,30 476 +688.18,19 477 +20,21 477 +24,25 477 +26,27 477 +689.2,11 478 +2,11 478 +20,29 478 +20,29 478 +2,31 478 +2,31 479 +2,31 480 +690.2,11 481 +2,11 481 +20,29 481 +20,29 481 +2,31 481 +2,31 482 +2,31 483 +691.14,36 484 +14,22 484 +29,35 484 +14,36 484 +14,36 484 +2,71 484 +2,8 484 +2,8 484 +38,53 484 +38,53 484 +55,58 484 +66,67 484 +68,69 484 +2,71 484 +692.6,16 485 +20,28 485 +693.3,33 486 +14,20 486 +22,29 486 +31,32 486 +3,33 486 +3,33 441 +696.6,16 487 +697.15,23 488 +3,23 488 +3,23 488 +3,23 488 +698.3,40 489 +14,20 489 +22,29 489 +31,39 489 +3,40 489 +3,40 441 +701.1,15 490 +6,14 490 +1,15 490 +702.1,16 491 +6,15 491 +1,16 491 +703.0,1 492 +708.25,43 493 +38,42 493 +25,43 493 +25,43 493 +709.1,10 494 +1,10 494 +1,24 494 +1,24 495 +710.1,10 496 +1,10 496 +1,24 496 +1,24 497 +711.1,44 498 +1,10 498 +1,10 498 +16,21 498 +23,26 498 +28,31 498 +39,40 498 +41,42 498 +1,44 498 +712.1,44 499 +1,10 499 +1,10 499 +16,21 499 +23,26 499 +28,31 499 +39,40 499 +41,42 499 +1,44 499 +714.7,21 500 +7,21 500 +27,41 500 +27,41 500 +1,41 500 +1,41 500 +1,41 500 +716.5,13 501 +717.6,8 502 +718.18,26 503 +28,47 503 +28,58 503 +73,83 503 +73,78 503 +73,83 503 +73,83 503 +62,83 503 +85,104 503 +719.6,16 504 +720.3,48 505 +3,12 505 +3,12 505 +18,24 505 +26,29 505 +37,38 505 +39,40 505 +43,44 505 +46,47 505 +3,48 505 +3,48 505 +721.7,9 506 +722.4,48 507 +4,13 507 +4,13 507 +19,25 507 +27,30 507 +32,35 507 +43,44 507 +45,46 507 +4,48 507 +724.6,16 508 +725.3,48 509 +3,12 509 +3,12 509 +18,24 509 +26,29 509 +37,38 509 +39,40 509 +43,44 509 +46,47 509 +3,48 509 +3,48 509 +726.7,9 510 +727.4,48 511 +4,13 511 +4,13 511 +19,25 511 +27,30 511 +32,35 511 +43,44 511 +45,46 511 +4,48 511 +730.5,17 512 +5,12 512 +5,17 512 +5,17 512 +5,22 512 +731.2,17 513 +734.0,1 514 +733.2,40 515 +25,32 515 +34,39 515 +2,40 515 +2,40 515 +734.0,1 514 +738.5,24 516 +739.2,21 517 +740.5,24 518 +741.2,21 519 +742.5,24 520 +743.2,21 521 +744.5,24 522 +745.2,21 523 +746.8,10 524 +1,10 524 +751.1,21 525 +752.14,25 526 +6,26 526 +1,26 526 +753.1,8 527 +755.1,12 528 +756.13,22 529 +757.18,35 530 +37,54 530 +758.15,22 531 +24,66 531 +24,31 531 +42,52 531 +54,64 531 +24,66 531 +24,66 531 +759.1,28 532 +9,14 532 +21,27 532 +1,28 532 +1,28 532 +760.1,34 533 +9,14 533 +21,33 533 +1,34 533 +1,34 533 +762.1,59 534 +11,18 534 +29,42 534 +44,57 534 +1,59 534 +1,59 534 +763.1,28 535 +10,16 535 +21,27 535 +1,28 535 +1,28 535 +764.1,34 536 +10,16 536 +21,33 536 +1,34 536 +1,34 536 +766.6,10 537 +768.2,49 538 +2,8 538 +14,19 538 +21,34 538 +21,34 538 +36,39 538 +41,48 538 +2,49 538 +769.2,8 539 +773.1,35 540 +776.1,12 541 +777.5,15 542 +778.23,41 543 +36,40 543 +23,41 543 +23,41 543 +3,5 543 +7,9 543 +7,9 544 +7,9 545 +7,9 546 +7,9 547 +7,9 548 +780.23,41 549 +36,40 549 +23,41 549 +23,41 549 +13,15 549 +17,19 549 +17,19 550 +17,19 551 +17,19 552 +17,19 553 +782.1,26 554 +783.1,21 555 +785.1,40 556 +1,7 556 +13,18 556 +20,22 556 +24,27 556 +35,36 556 +37,38 556 +1,40 556 +787.7,21 557 +7,21 557 +27,41 557 +27,41 557 +1,41 557 +1,41 557 +1,41 557 +789.5,13 558 +790.6,8 559 +791.18,26 560 +28,47 560 +28,58 560 +73,83 560 +73,78 560 +73,83 560 +73,83 560 +62,83 560 +85,104 560 +792.6,14 561 +793.3,44 562 +3,9 562 +15,21 562 +23,25 562 +33,34 562 +35,36 562 +39,40 562 +42,43 562 +3,44 562 +3,44 562 +794.7,9 563 +795.4,44 564 +4,10 564 +16,22 564 +24,26 564 +28,31 564 +39,40 564 +41,42 564 +4,44 564 +798.1,25 565 +799.0,1 566 +14 +aSys->Dir 1:26.1,39.2 64 +11 +0:name:28.2,6 s +4:uid:29.2,5 s +8:gid:30.2,5 s +12:muid:31.2,6 s +16:qid:32.2,5 @1 + +32:mode:33.2,6 i +36:atime:34.2,7 i +40:mtime:35.2,7 i +48:length:36.2,8 B +56:dtype:37.2,7 i +60:dev:38.2,5 i +aSys->Qid 11.1,16.2 16 +3 +0:path:13.2,6 B +8:vers:14.2,6 i +12:qtype:15.2,7 i +aDraw->Chans 2:70.1,82.2 4 +1 +0:desc:72.2,6 i +aDraw->Image 142.1,198.2 56 +8 +0:r:146.2,3 @4 + +16:clipr:147.2,7 @4 + +32:depth:148.2,7 i +36:chans:149.2,7 @2 + +40:repl:150.2,6 i +44:display:151.2,9 R@6 + +48:screen:152.2,8 R@7 + +52:iname:153.2,7 s +aDraw->Rect 116.1,139.2 16 +2 +0:min:118.2,5 @5 + +8:max:119.2,5 @5 + +aDraw->Point 99.1,113.2 8 +2 +0:x:101.2,3 i +4:y:102.2,3 i +aDraw->Display 201.1,230.2 20 +5 +0:image:203.2,7 R@3 + +4:white:204.2,7 R@3 + +8:black:205.2,7 R@3 + +12:opaque:206.2,8 R@3 + +16:transparent:207.2,13 R@3 + +aDraw->Screen 249.1,263.2 16 +4 +0:id:251.2,4 i +4:image:252.2,7 R@3 + +8:fill:253.2,6 R@3 + +12:display:254.2,9 R@6 + +aDraw->Context 274.1,279.2 12 +3 +0:display:276.2,9 R@6 + +4:screen:277.2,8 R@7 + +8:wm:278.2,4 Ct8.2 +0:t0:15,21 s +4:t1:15,21 Ct8.2 +0:t0:32,38 s +4:t1:32,38 R@9 + + + +aDraw->Wmcontext 282.1,291.2 28 +7 +0:kbd:284.2,5 Ci +4:ptr:285.2,5 CR@10 + +8:ctl:286.2,5 Cs +12:wctl:287.2,6 Cs +16:images:288.2,8 CR@3 + +20:connfd:289.2,8 R@11 + +24:ctxt:290.2,6 R@8 + +aDraw->Pointer 266.1,271.2 16 +3 +0:buttons:268.2,9 i +4:xy:269.2,4 @5 + +12:msec:270.2,6 i +aSys->FD 1:45.1,48.2 4 +1 +0:fd:47.2,4 i +aDraw->Font 2:233.1,246.2 16 +4 +0:name:235.2,6 s +4:height:236.2,8 i +8:ascent:237.2,8 i +12:display:238.2,9 R@6 + +aCellinfo 0:27.0,31.1 20 +4 +0:font:28.1,5 R@12 + +4:ch:29.1,3 i +8:attr:5,9 i +12:clipmod:30.1,8 t8.2 +0:t0:12,15 i +4:t1:12,15 i + +17 +0:Init +1 +32:c:67.5,6 R@8 + +17 +36:disp:76.1,5 R@6 + +40:black:78.1,6 i +44:blue:79.1,5 i +48:cyan:83.1,5 i +52:green:82.1,6 i +56:iblack:87.1,7 R@3 + +60:iblue:88.1,6 R@3 + +64:icyan:92.1,6 R@3 + +68:igreen:91.1,7 R@3 + +72:imagenta:90.1,9 R@3 + +76:ired:89.1,5 R@3 + +80:iwhite:94.1,7 R@3 + +84:iyellow:93.1,8 R@3 + +88:magenta:81.1,8 i +92:red:80.1,4 i +96:white:85.1,6 i +100:yellow:84.1,7 i +s140:Quit +0 +0 +n151:Mode +6 +32:r:118.5,6 @4 + +48:w:21,22 i +52:h:24,25 i +56:ulh:27,30 i +60:d:32,33 i +64:fontpath:41,49 s +6 +68:dx:130.2,4 i +72:dy:6,8 i +76:black:135.1,6 i +80:y:179.6,7 i +84:col0:180.2,6 i +96:winr:141.1,5 @4 + +t8.2 +0:t0:118.63,69 s +4:t1:63,69 R@3 + +313:Cursor +1 +32:pt:193.7,9 @5 + +0 +n320:Put +5 +32:str:206.4,7 s +36:pt:18,20 @5 + +44:charset:30,37 i +48:attr:39,43 i +52:insert:45,51 i +36 +56:col0:264.2,6 i +60:f:215.1,2 R@12 + +64:cell:216.1,5 AAt8.2 +0:t0:22.37,38 i +4:t1:40,41 i + +68:newattr:362.3,10 i +72:cellix:256.6,12 i +76:y:257.2,3 i +80:clipix:328.8,14 i +84:cmix:333.4,8 i +88:ix:240.8,10 i +92:mask:269.3,7 i +96:cix:279.4,7 i +100:ix:278.8,10 i +104:delimattr:270.3,12 i +108:newstr:239.3,9 s +112:s:358.2,3 s +116:cx:377.4,6 i +120:cy:8,10 i +124:f2:378.3,5 R@12 + +144:colbase:265.2,9 i +148:srco:298.2,6 @5 + +156:dstr:312.4,8 @4 + +172:cellpos:380.3,10 @5 + +180:drawpt:382.3,9 @5 + +188:dsto:311.4,8 @5 + +196:clipr:381.3,8 @4 + +104:x:326.2,3 i +80:mask:357.2,6 i +84:attr2:356.2,7 i +104:destx:303.9,14 i +100:strix:327.7,12 i +96:strlen:296.2,8 i +92:gfxwidth:297.2,10 i +100:srcx:304.5,9 i +88:txty:255.1,5 i +156:strr:343.2,6 @4 + +188:txto:342.2,6 @5 + +n609:Scroll +2 +32:topline:389.7,14 i +36:nlines:16,22 i +11 +40:y:410.7,8 i +44:srccol0:411.3,10 i +48:dstcol0:412.3,10 i +64:scr:395.1,4 @4 + +80:blankr:394.1,7 @4 + +96:dstr:401.1,5 @4 + +44:y:425.7,8 i +48:srccol0:426.3,10 i +44:col0:416.3,7 i +48:col0:431.3,7 i +40:dstcol0:427.3,10 i +n743:Reveal +1 +32:show:445.7,11 i +11 +36:x:454.7,8 i +40:col0:453.2,6 i +44:y:452.6,7 i +48:attr:455.3,7 i +52:s:459.3,4 s +56:cx:461.4,6 i +60:cy:8,10 i +64:f:462.3,4 R@12 + +72:cellpos:463.3,10 @5 + +80:drawpt:465.3,9 @5 + +88:clipr:464.3,8 @4 + +n800:wordchar +1 +32:pt:474.9,11 @5 + +7 +40:col0:481.1,5 i +44:lhmodx:499.4,10 i +48:modx:493.3,7 i +56:c:482.1,2 @13 + +76:lhc:498.3,6 @13 + +96:rhc:506.3,6 @13 + +44:rhmodx:507.4,10 i +i879:GetWord +1 +32:gfxpt:526.8,13 @5 + +7 +40:sx:544.6,8 i +44:s:541.1,2 s +48:y:538.1,2 i +52:x:537.1,2 i +56:col0:539.1,5 i +68:c:555.2,3 @13 + +88:scr:531.1,4 @4 + +s941:Refresh +0 +0 +n951:framecolours +1 +32:attr:572.13,17 i +8 +36:fg0:588.2,5 R@3 + +40:fg1:7,10 R@3 + +44:bg:581.1,3 R@3 + +48:bg0:589.2,5 R@3 + +52:bg1:7,10 R@3 + +56:fg:574.1,3 R@3 + +60:bgcol:582.1,6 i +64:fgcol:575.1,6 i +t16.4 +0:t0:572.28,37 R@3 + +4:t1:28,37 R@3 + +8:t2:28,37 R@3 + +12:t3:28,37 R@3 + +999:kill +1 +32:pid:604.5,8 i +3 +36:cmd:609.2,5 Ab +40:fd:607.1,3 R@11 + +44:prog:606.1,5 s +n1018:timer +3 +32:ms:614.6,8 i +36:pc:16,18 Ci +40:tick:20,24 Ci +0 +n1030:Update +1 +32:cmd:626.7,10 Ci +15 +36:fgframe:643.1,8 i +40:cursoron:638.1,9 i +44:showcursor:637.1,11 i +48:pc:630.1,3 Ci +52:flashchan:641.1,10 Ci +56:pcount:642.1,7 i +60:cursortick:629.1,11 Ci +64:flashtick:628.1,10 Ci +68:nultick:640.1,8 Ci +72:quit:639.1,5 i +76:c:646.1,2 i +80:cursorpid:634.1,10 i +84:flashpid:632.1,9 i +104:cursor:636.1,7 @5 + +128:r:651.3,4 @4 + +n1219:drawstr +5 +32:s:706.8,9 s +36:f:20,21 R@12 + +40:clipr:34,39 @4 + +56:drawpt:48,54 @5 + +64:attr:64,68 i +6 +68:fg0:708.2,5 R@3 + +72:bg0:7,10 R@3 + +76:fg1:12,15 R@3 + +80:bg1:17,20 R@3 + +84:ul:714.1,3 i +100:ulrect:713.1,7 @4 + +n1324:boundingrect +2 +32:r1:736.13,15 @4 + +48:r2:17,19 @4 + +0 +@4 +1334:drawcursor +3 +32:pt:749.11,13 @5 + +40:srcix:23,28 i +44:show:30,34 i +15 +48:attr:773.1,5 i +52:f:776.1,2 R@12 + +56:fg:775.1,3 R@3 + +60:bg:5,7 R@3 + +64:s:753.1,2 s +68:ul:787.1,3 i +72:col0:751.1,5 i +76:cx:756.2,4 i +80:cy:6,8 i +92:drawpt:762.1,7 @5 + +100:clipr:758.1,6 @4 + +116:cellpos:757.1,8 @5 + +124:c:752.1,2 @13 + +160:prevclipr:782.1,10 @4 + +176:ulrect:786.1,7 @4 + +n31 +88:blankrow:55.0,8 A@13 + +92:bright:41.0,6 R@3 + +104:cellH:24.0,5 AAt8.2 +0:t0:37,38 i +4:t1:40,41 i + +108:cellS:22.0,5 AAt8.2 +0:t0:37,38 i +4:t1:40,41 i + +112:cellW:23.0,5 AAt8.2 +0:t0:36,37 i +4:t1:39,40 i + +116:cellWH:25.0,6 AAt8.2 +0:t0:37,38 i +4:t1:40,41 i + +120:cellmap:44.0,7 A@13 + +124:cellsize:50.0,8 @5 + +132:colours:40.0,7 AR@3 + +136:ctxt:57.0,4 R@8 + +140:curpos:48.0,6 @5 + +148:delims:53.0,6 i +152:display:35.0,7 R@6 + +156:draw:13.0,4 mDraw +2:1.0,298.1 0 + +160:font:0:58.0,4 R@12 + +164:fontfr:63.0,6 R@12 + +168:fontg1:62.0,6 R@12 + +172:fonth:59.0,5 R@12 + +176:fonts:61.0,5 R@12 + +180:fontusa:64.0,7 R@12 + +184:fontw:60.0,5 R@12 + +192:frames:37.0,6 AR@3 + +208:modbbox:54.0,7 @4 + +224:ncols:46.0,5 i +240:nrows:45.0,5 i +248:showC:52.0,5 i +252:sys:12.0,3 mSys +1:4.0,160.1 0 + +256:ulheight:0:47.0,8 i +260:update:38.0,6 Ci +272:window:36.0,6 R@3 + +276:winoff:49.0,6 @5 + diff --git a/appl/wm/minitel/miniterm.b b/appl/wm/minitel/miniterm.b new file mode 100644 index 00000000..1c6ff759 --- /dev/null +++ b/appl/wm/minitel/miniterm.b @@ -0,0 +1,1187 @@ +# +# Copyright © 1998 Vita Nuova Limited. All rights reserved. +# + +implement Miniterm; + +include "sys.m"; + sys: Sys; + print, fprint, sprint, read: import sys; +include "draw.m"; + draw: Draw; +include "tk.m"; + tk: Tk; +include "tkclient.m"; + tkclient: Tkclient; + +include "miniterm.m"; + +Miniterm: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); + +}; + +pgrp: int = 0; +debug: array of int = array[256] of {* => 0}; +stderr: ref Sys->FD; + +# Minitel terminal identification request - reply sequence +TERMINALID1 := array [] of { + byte SOH, + byte 'S', byte 'X', byte '1', byte 'H', byte 'N', + byte EOT +}; +TERMINALID2 := array [] of { + byte SOH, + byte 'C', byte 'g', byte '1', + byte EOT +}; + +# Minitel module identifiers +Mscreen, Mmodem, Mkeyb, Msocket, Nmodule: con iota; +Pscreen, Pmodem, Pkeyb, Psocket: con (1 << iota); +Modname := array [Nmodule] of { + Mscreen => "S", + Mmodem => "M", + Mkeyb => "K", + Msocket => "C", + * => "?", +}; + +# attributes common to all modules +Module: adt { + path: int; # bitset to connected modules + disabled: int; +}; + +# A BufChan queues events from the terminal to the modules +BufChan: adt { + path: int; # id bit + ch: chan of ref Event; # set to `in' or `dummy' channel + ev: ref Event; # next event to send + in: chan of ref Event; # real channel for Events to the device + q: array of ref Event; # subsequent events to send +}; + +# holds state information for the minitel `protocol` (chapter 6) +PState: adt { + state: int; + arg: array of int; # up to 3 arguments: X,Y,Z + nargs: int; # expected number of arguments + n: int; # progress + skip: int; # transparency; bytes to skip +}; +PSstart, PSesc, PSarg: con iota; # states + +# Terminal display modes +Videotex, Mixed, Ascii, + +# Connection methods +Direct, Network, + +# Terminal connection states +Local, Connecting, Online, + +# Special features +Echo + : con (1 << iota); + +Terminal: adt { + in: chan of ref Event; + out: array of ref BufChan; # buffered output to the minitel modules + + mode: int; # display mode + state: int; # connection state + spec: int; # special features + connect: int; # Direct, or Network + toplevel: ref Tk->Toplevel; + cmd: chan of string; # from Tk + proto: array of ref PState; # minitel protocol state + netaddr: string; # network address to dial + buttonsleft: int; # display buttons on the LHS (40 cols) + terminalid: array of byte; # ENQROM response + kbctl: chan of string; # softkeyboard control + kbmode: string; # softkeyboard mode + + init: fn(t: self ref Terminal, toplevel: ref Tk->Toplevel, connect: int); + run: fn(t: self ref Terminal, done: chan of int); + reset: fn(t: self ref Terminal); + quit: fn(t: self ref Terminal); + layout: fn(t: self ref Terminal, cols: int); + setkbmode: fn(t: self ref Terminal, tmode: int); +}; + +include "arg.m"; +include "event.m"; +include "event.b"; + +include "keyb.b"; +include "modem.b"; +include "socket.b"; +include "screen.b"; + +K: ref Keyb; +M: ref Modem; +C: ref Socket; +S: ref Screen; +T: ref Terminal; +Modules: array of ref Module; + + +init(ctxt: ref Draw->Context, argv: list of string) +{ + s: string; + netaddr: string = nil; + + sys = load Sys Sys->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + tkclient->init(); + draw = load Draw Draw->PATH; + stderr = sys->fildes(2); + pgrp = sys->pctl(Sys->NEWPGRP|Sys->FORKNS, nil); + + arg := load Arg Arg->PATH; + arg->init(argv); + arg->setusage("miniterm [netaddr]"); + while((c := arg->opt()) != 0){ + case c { + 'D' => + s = arg->earg(); + for(i := 0; i < len s; i++){ + c = s[i]; + if(c < len debug) + debug[c] += 1; + } + * => + arg->usage(); + } + } + argv = arg->argv(); + if(len argv > 0) { + netaddr = hd argv; + argv = tl argv; + } + + if(argv != nil) + arg->usage(); + arg = nil; + + # usage: miniterm modem[!init[!number]] + # or miniterm tcp!a.b.c.d + connect: int; + initstr := dialstr := string nil; + if(netaddr == nil) + netaddr = "tcp!pdc.minitelfr.com!513"; # gateway + (nil, words) := sys->tokenize(netaddr, "!"); + if(len words == 0) { + connect = Direct; + words = "modem" :: nil; + } + if(hd words == "modem") { + connect = Direct; + words = tl words; + if(words != nil) { + initstr = hd words; + words = tl words; + if(words != nil) + dialstr = hd words; + } + if(initstr == "*") + initstr = nil; + if(dialstr == "*") + dialstr = nil; + } else { + connect = Network; + dialstr = netaddr; + } + + T = ref Terminal; + K = ref Keyb; + M = ref Modem; + C = ref Socket; + S = ref Screen; + Modules = array [Nmodule] of { + Mscreen => S.m, + Mmodem => M.m, + Mkeyb => K.m, + Msocket => C.m, + }; + + toplevel := tk->toplevel(ctxt.display, ""); + inittk(toplevel, connect); + + T.init(toplevel, connect); + K.init(toplevel); + M.init(connect, initstr, dialstr); + C.init(); + case connect { + Direct => + S.init(ctxt, Rect((0,0), (640,425)), Rect((0,0), (640,425))); + Network => + S.init(ctxt, Rect((0,0), (596,440)), Rect((0,50), (640,350))); + } + + done := chan of int; + spawn K.run(); + spawn M.run(); + spawn C.run(); + spawn S.run(); + spawn T.run(done); + <- done; + + # now tidy up + K.quit(); + M.quit(); + C.quit(); + S.quit(); + T.quit(); +} + +# the keyboard module handles keypresses and focus +BTN40x25: con "-height 24 -font {/fonts/lucidasans/unicode.6.font}"; +BTNCTL: con "-width 60 -height 20 -font {/fonts/lucidasans/unicode.7.font}"; +BTNMAIN: con "-width 80 -height 20 -font {/fonts/lucidasans/unicode.7.font}"; + +tkinitbs := array[] of { + "button .cxfin -text {Cx/Fin} -command {send keyb skey Connect}", + "button .done -text {Quitter} -command {send keyb skey Exit}", + "button .hup -text {Raccr.} -command {send term hangup}", + "button .somm -text {Somm.} -command {send keyb skey Index}", + "button .guide -text {Guide} -command {send keyb skey Guide}", + "button .annul -text {Annul.} -command {send keyb skey Cancel}", + "button .corr -text {Corr.} -command {send keyb skey Correct}", + "button .retour -text {Retour} -command {send keyb skey Previous}", + "button .suite -text {Suite} -command {send keyb skey Next}", + "button .repet -text {Répét.} -command {send keyb skey Repeat}", + "button .envoi -text {Envoi} -command {send keyb skey Send}", + "button .play -text {P} -command {send term play}", +# "button .db -text {D} -command {send term debug}" , + "button .kb -text {Clavier} -command {send term keyboard}", + "button .move -text {<-} -command {send term buttonsleft} " + BTN40x25, +}; + +tkinitdirect := array [] of { + ". configure -background black -height 480 -width 640", + + ".cxfin configure " + BTNCTL, + ".hup configure " + BTNCTL, + ".done configure " + BTNCTL, + ".somm configure " + BTNMAIN, + ".guide configure " + BTNMAIN, + ".annul configure " + BTNMAIN, + ".corr configure " + BTNMAIN, + ".retour configure " + BTNMAIN, + ".suite configure " + BTNMAIN, + ".repet configure " + BTNMAIN, + ".envoi configure " + BTNMAIN, +# ".play configure " + BTNCTL, +# ".db configure " + BTNCTL, + ".kb configure " + BTNCTL, + + "canvas .c -height 425 -width 640 -background black", + "bind .c <Configure> {send term resize}", + "bind .c <Key> {send keyb key %K}", + "bind .c <FocusIn> {send keyb focusin}", + "bind .c <FocusOut> {send keyb focusout}", + "bind .c <ButtonRelease> {focus .c; send keyb click %x %y}", + "frame .k -height 55 -width 640 -background black", + "pack propagate .k no", + "frame .klhs -background black", + "frame .krhs -background black", + "frame .krows -background black", + "frame .k1 -background black", + "frame .k2 -background black", + "pack .cxfin -in .klhs -anchor w -pady 4", + "pack .hup -in .klhs -anchor w", + "pack .somm .annul .retour .repet -in .k1 -side left -padx 2", + "pack .guide .corr .suite .envoi -in .k2 -side left -padx 2", + "pack .kb -in .krhs -anchor e -pady 4", + "pack .done -in .krhs -anchor e", + "pack .k1 -in .krows -pady 4", + "pack .k2 -in .krows", + "pack .klhs .krows .krhs -in .k -side left -expand 1 -fill x", + "pack .c .k", + "focus .c", + "update", +}; + +tkinitip := array [] of { + ". configure -background black -height 440 -width 640", + + # ip 40x25 mode support + "canvas .c40 -height 440 -width 596 -background black", + "bind .c40 <Configure> {send term resize}", + "bind .c40 <Key> {send keyb key %K}", + "bind .c40 <FocusIn> {send keyb focusin}", + "bind .c40 <FocusOut> {send keyb focusout}", + "bind .c40 <ButtonRelease> {focus .c40; send keyb click %x %y}", + "frame .k -height 427 -width 44 -background black", + "frame .gap1 -background black", + "frame .gap2 -background black", + "pack propagate .k no", + + # ip 80x25 mode support + "frame .padtop -height 50", + "canvas .c80 -height 300 -width 640 -background black", + "bind .c80 <Configure> {send term resize}", + "bind .c80 <Key> {send keyb key %K}", + "bind .c80 <FocusIn> {send keyb focusin}", + "bind .c80 <FocusOut> {send keyb focusout}", + "bind .c80 <ButtonRelease> {focus .c80; send keyb click %x %y}", + "frame .k80 -height 90 -width 640 -background black", + "pack propagate .k80 no", + "frame .klhs -background black", + "frame .krows -background black", + "frame .krow1 -background black", + "frame .krow2 -background black", + "frame .krhs -background black", + "pack .krow1 .krow2 -in .krows -pady 2", + "pack .klhs -in .k80 -side left", + "pack .krows -in .k80 -side left -expand 1", + "pack .krhs -in .k80 -side left", +}; + +tkip40x25show := array [] of { + ".cxfin configure " + BTN40x25, + ".hup configure " + BTN40x25, + ".done configure " + BTN40x25, + ".somm configure " + BTN40x25, + ".guide configure " + BTN40x25, + ".annul configure " + BTN40x25, + ".corr configure " + BTN40x25, + ".retour configure " + BTN40x25, + ".suite configure " + BTN40x25, + ".repet configure " + BTN40x25, + ".envoi configure " + BTN40x25, + ".play configure " + BTN40x25, +# ".db configure " + BTN40x25, + ".kb configure " + BTN40x25, + "pack .cxfin -in .k -side top -fill x", + "pack .gap1 -in .k -side top -expand 1", + "pack .guide .repet .somm .annul .corr .retour .suite .envoi -in .k -side top -fill x", + "pack .gap2 -in .k -side top -expand 1", + "pack .done .hup .kb .move -in .k -side bottom -pady 2 -fill x", +# "pack .db -in .k -side bottom", +}; + +tkip40x25lhs := array [] of { + ".move configure -text {->} -command {send term buttonsright}", + "pack .k .c40 -side left", + "focus .c40", + "update", +}; + +tkip40x25rhs := array [] of { + ".move configure -text {<-} -command {send term buttonsleft}", + "pack .c40 .k -side left", + "focus .c40", + "update", +}; + +tkip40x25hide := array [] of { + "pack forget .k .c40", +}; + +tkip80x25show := array [] of { + ".cxfin configure " + BTNCTL, + ".hup configure " + BTNCTL, + ".done configure " + BTNCTL, + ".somm configure " + BTNMAIN, + ".guide configure " + BTNMAIN, + ".annul configure " + BTNMAIN, + ".corr configure " + BTNMAIN, + ".retour configure " + BTNMAIN, + ".suite configure " + BTNMAIN, + ".repet configure " + BTNMAIN, + ".envoi configure " + BTNMAIN, +# ".play configure " + BTNCTL, +# ".db configure " + BTNCTL, + ".kb configure " + BTNCTL, + + "pack .cxfin .hup -in .klhs -anchor w -pady 2", + "pack .somm .annul .retour .repet -in .krow1 -side left -padx 2", + "pack .guide .corr .suite .envoi -in .krow2 -side left -padx 2", + "pack .done .kb -in .krhs -anchor e -pady 2", + "pack .padtop .c80 .k80 -side top", + "focus .c80", + "update", +}; + +tkip80x25hide := array [] of { + "pack forget .padtop .c80 .k80", +}; + +inittk(toplevel: ref Tk->Toplevel, connect: int) +{ + tkcmds(toplevel, tkinitbs); + if(connect == Direct) + tkcmds(toplevel, tkinitdirect); + else + tkcmds(toplevel, tkinitip); +} + +Terminal.layout(t: self ref Terminal, cols: int) +{ + if(t.connect == Direct) + return; + if(cols == 80) { + tkcmds(t.toplevel, tkip40x25hide); + tkcmds(t.toplevel, tkip80x25show); + } else { + tkcmds(t.toplevel, tkip80x25hide); + tkcmds(t.toplevel, tkip40x25show); + if (t.buttonsleft) + tkcmds(t.toplevel, tkip40x25lhs); + else + tkcmds(t.toplevel, tkip40x25rhs); + } +} + +Terminal.init(t: self ref Terminal, toplevel: ref Tk->Toplevel, connect: int) +{ + t.in = chan of ref Event; + t.proto = array [Nmodule] of { + Mscreen => ref PState(PSstart, array [] of {0,0,0}, 0, 0, 0), + Mmodem => ref PState(PSstart, array [] of {0,0,0}, 0, 0, 0), + Mkeyb => ref PState(PSstart, array [] of {0,0,0}, 0, 0, 0), + Msocket => ref PState(PSstart, array [] of {0,0,0}, 0, 0, 0), + }; + + t.toplevel = toplevel; + t.connect = connect; + if (t.connect == Direct) + t.spec = 0; + else + t.spec = Echo; + t.cmd = chan of string; + tk->namechan(t.toplevel, t.cmd, "term"); # Tk -> terminal + t.state = Local; + t.buttonsleft = 0; + t.kbctl = nil; + t.kbmode = "minitel"; + t.reset(); +} + +Terminal.reset(t: self ref Terminal) +{ + t.mode = Videotex; +} + +Terminal.run(t: self ref Terminal, done: chan of int) +{ + t.out = array [Nmodule] of { + Mscreen => ref BufChan(Pscreen, nil, nil, S.in, array [0] of ref Event), + Mmodem => ref BufChan(Pmodem, nil, nil, M.in, array [0] of ref Event), + Mkeyb => ref BufChan(Pkeyb, nil, nil, K.in, array [0] of ref Event), + Msocket => ref BufChan(Psocket, nil, nil, C.in, array [0] of ref Event), + }; + modcount := Nmodule; + if(debug['P']) + post(ref Event.Eproto(Pmodem, 0, Cplay, "play", 0,0,0)); +Evloop: + for(;;) { + ev: ref Event = nil; + post(nil); + alt { + # recv message from one of the modules + ev =<- t.in => + if(ev == nil) { # modules ack Equit with nil + if(--modcount == 0) + break Evloop; + continue; + } + pick e := ev { + Equit => # close modules down + post(ref Event.Equit(Pscreen|Pmodem|Pkeyb|Psocket,0)); + continue; + } + + eva := protocol(ev); + while(len eva > 0) { + post(eva[0]); + eva = eva[1:]; + } + + # send message to `plumbed' modules + t.out[Mscreen].ch <- = t.out[Mscreen].ev => + t.out[Mscreen].ev = nil; + t.out[Mmodem].ch <- = t.out[Mmodem].ev => + t.out[Mmodem].ev = nil; + t.out[Mkeyb].ch <- = t.out[Mkeyb].ev => + t.out[Mkeyb].ev = nil; + t.out[Msocket].ch <- = t.out[Msocket].ev => + t.out[Msocket].ev = nil; + + # recv message from Tk + cmd := <- t.cmd => + (n, word) := sys->tokenize(cmd, " "); + if(n >0) + case hd word { + "resize" => ; + "play" => # for testing only + post(ref Event.Eproto(Pmodem, Mmodem, Cplay, "play", 0,0,0)); + "keyboard" => + if (t.kbctl == nil) { + e: string; + (e, t.kbctl) = kb(t); + if (e != nil) + sys->print("cannot start keyboard: %s\n", e); + } else + t.kbctl <- = "click"; + "hangup" => + if(T.state == Online || T.state == Connecting) + post(ref Event.Eproto(Pmodem, 0, Cdisconnect, "",0,0,0)); + "buttonsleft" => + tkcmds(t.toplevel, tkip40x25lhs); + t.buttonsleft = 1; + if(S.image != nil) + draw->(S.image.origin)(Point(0,0), Point(44, 0)); + if (t.kbctl != nil) + t.kbctl <- = "fg"; + "buttonsright" => + tkcmds(t.toplevel, tkip40x25rhs); + t.buttonsleft = 0; + if(S.image != nil) + draw->(S.image.origin)(Point(0,0), Point(0, 0)); + if (t.kbctl != nil) + t.kbctl <- = "fg"; + "debug" => + debug['s'] ^= 1; + debug['m'] ^= 1; + } + } + + } + if (t.kbctl != nil) + t.kbctl <- = "quit"; + t.kbctl = nil; + done <-= 0; +} + +kb(t: ref Terminal): (string, chan of string) +{ + s := chan of string; + spawn dokb(t, s); + e := <- s; + if (e != nil) + return (e, nil); + return (nil, s); +} + +Terminal.setkbmode(t: self ref Terminal, tmode: int) +{ + case tmode { + Videotex => + t.kbmode = "minitel"; + Mixed or Ascii => + t.kbmode = "standard"; + } + if(t.kbctl != nil) { + t.kbctl <-= "mode"; + t.kbctl <-= "fg"; + } +} + +include "swkeyb.m"; +dokb(t: ref Terminal, c: chan of string) +{ + keyboard := load Keyboard Keyboard->PATH; + if (keyboard == nil) { + c <- = "cannot load keyboard"; + return; + } + + kbctl := chan of string; + (top, m) := tkclient->toplevel(S.ctxt, "", "Keyboard", 0); + tk->cmd(top, "pack .Wm_t -fill x"); + tk->cmd(top, "update"); + keyboard->chaninit(top, S.ctxt, ".keys", kbctl); + tk->cmd(top, "pack .keys"); + + kbctl <-= t.kbmode ; + + kbon := 1; + c <- = nil; # all ok, we are now ready to accept commands + + for (;;) alt { + mcmd := <- m => + if (mcmd == "exit") { + if (kbon) { + tk->cmd(top, ". unmap; update"); + kbon = 0; + } + } else + tkclient->wmctl(top, mcmd); + kbcmd := <- c => + case kbcmd { + "fg" => + if (kbon) + tk->cmd(top, "raise .;update"); + "click" => + if (kbon) { + tk->cmd(top, ". unmap; update"); + kbon = 0; + } else { + tk->cmd(top, ". map; raise ."); + kbon = 1; + } + "mode" => + kbctl <- = t.kbmode; + "quit" => + kbctl <- = "kill"; + top = nil; + # ensure tkclient not blocked on a send to us (probably overkill!) + alt { + <- m => ; + * => ; + } + return; + } + } +} + + +Terminal.quit(nil: self ref Terminal) +{ +} + +# a minitel module sends an event to the terminal for routing +send(e: ref Event) +{ + if(debug['e'] && e != nil) + fprint(stderr, "%s: -> %s\n", Modname[e.from], e.str()); + T.in <- = e; +} + +# post an event to one or more modules +post(e: ref Event) +{ + i,l: int; + for(i=0; i<Nmodule; i++) { + # `ev' is cleared once sent, reload it from the front of `q' + b: ref BufChan = T.out[i]; + l = len b.q; + if(b.ev == nil && l != 0) { + b.ev = b.q[0]; + na := array [l-1] of ref Event; + na[0:] = b.q[1:]; + b.q = na; + } + if (e != nil) { + if(e.path & b.path) { + if(debug['e'] > 0) { + pick de := e { + * => + fprint(stderr, "[%s<-%s] %s\n", Modname[i], Modname[e.from], e.str()); + } + } + if(b.ev == nil) # nothing queued + b.ev = e; + else { # enqueue it + l = len b.q; + na := array [l+1] of ref Event; + na[0:] = b.q[0:]; + na[l] = e; + b.q = na; + } + } + } + # set a dummy channel if nothing to send + if(b.ev == nil) + b.ch = chan of ref Event; + else + b.ch = b.in; + } +} + +# run the terminal protocol +protocol(ev: ref Event): array of ref Event +{ + # Introduced by the following sequences, the minitel protocol can be + # embedded in any normal data sequence + # ESC,0x39,X + # ESC,0x3a,X,Y + # ESC,0x3b,X,Y,Z + # ESC,0x61 - cursor position request + + ea := array [0] of ref Event; # resulting sequence of Events + changed := 0; # if set, results are found in `ea' + + pick e := ev { + Edata => + d0 := 0; # offset of start of last data sequence + p := T.proto[e.from]; + for(i:=0; i<len e.data; i++) { + ch := int e.data[i]; +# if(debug['p']) +# fprint(stderr, "protocol: [%s] %d %ux (%c)\n", Modname[e.from], p.state, ch, ch); + if(p.skip > 0) { # in transparency mode + if(ch == 0 && e.from == Mmodem) # 5.0 + continue; + p.skip--; + continue; + } + case p.state { + PSstart => + if(ch == ESC) { + p.state = PSesc; + changed = 1; + if(i > d0) + ea = eappend(ea, ref Event.Edata(e.path, e.from, e.data[d0:i])); + d0 = i+1; + } + PSesc => + p.state = PSarg; + p.n = 0; + d0 = i+1; + changed = 1; + if(ch >= 16r39 && ch <= 16r3b) #PRO1,2,3 + p.nargs = ch - 16r39 + 1; + else if(ch == 16r61) # cursor position request + p.nargs = 0; + else if(ch == ESC) { + ea = eappend(ea, ref Event.Edata(e.path, e.from, array [] of { byte ESC })); + p.state = PSesc; + } else { + # false alarm, restore as data + ea = eappend(ea, ref Event.Edata(e.path, e.from, array [] of { byte ESC, byte ch })); + p.state = PSstart; + } + PSarg => # expect `nargs' bytes + d0 = i+1; + changed =1; + if(p.n < p.nargs) + p.arg[p.n++] = ch; + if(p.n == p.nargs) { + # got complete protocol sequence + pe := proto(e.from, p); + if(pe != nil) + ea = eappend(ea, pe); + p.state = PSstart; + } + } + } + if(changed) { # some interpretation, results in `ea' + if(i > d0) + ea = eappend(ea, ref Event.Edata(e.path, e.from, e.data[d0:i])); + return ea; + } + ev = e; + return array [] of {ev}; + } + return array [] of {ev}; +} + +# append to an Event array +eappend(ea: array of ref Event, e: ref Event): array of ref Event +{ + l := len ea; + na := array [l+1] of ref Event; + na[0:] = ea[0:]; + na[l] = e; + return na; +} + +# act on a received protocol sequence +# some sequences are handled here by the terminal and result in a posted reply +# others are returned `inline' as Eproto events with the normal data stream. +proto(from: int, p: ref PState): ref Event +{ + if(debug['p']) { + fprint(stderr, "PRO%d: %ux", p.nargs, p.arg[0]); + if(p.nargs > 1) + fprint(stderr, " %ux", p.arg[1]); + if(p.nargs > 2) + fprint(stderr, " %ux", p.arg[2]); + fprint(stderr, " (%s)\n", Modname[from]); + } + case p.nargs { + 0 => # cursor position request ESC 0x61 + reply := array [] of { byte US, byte S.pos.y, byte S.pos.x }; + post(ref Event.Edata(Pmodem, from, reply)); + 1 => + case p.arg[0] { + PROTOCOLSTATUS => ; + ENQROM => # identification request + post(ref Event.Edata(Pmodem, from, T.terminalid)); + if(T.terminalid == TERMINALID1) + T.terminalid = TERMINALID2; + SETRAM1 or SETRAM2 => ; + FUNCTIONINGSTATUS => # 11.3 + PRO2(Pmodem, from, REPFUNCTIONINGSTATUS, osb()); + CONNECT => ; + DISCONNECT => + return ref Event.Eproto(Pscreen, from, Cscreenoff, "",0,0,0); + RESET => # reset the minitel terminal + all := Pscreen|Pmodem|Pkeyb|Psocket; + post(ref Event.Eproto(all, from, Creset, "",0,0,0)); # check + T.reset(); + reply := array [] of { byte SEP, byte 16r5E }; + post(ref Event.Edata(Pmodem, from, reply)); + } + 2 => + case p.arg[0] { + TO => # request for module status + PRO3(Pmodem, from, FROM, p.arg[1], psb(p.arg[1])); + NOBROADCAST => ; + BROADCAST => ; + TRANSPARENCY => # transparency mode - skip bytes + p.skip = p.arg[1]; + if(p.skip < 1 || p.skip > 127) # 5.0 + p.skip = 0; + else { + reply := array [] of { byte SEP, byte 16r57 }; + post(ref Event.Edata(Pmodem, from, reply)); + } + KEYBOARDSTATUS => + if(p.arg[1] == RxKeyb) + PRO3(Pmodem, from, REPKEYBOARDSTATUS, RxKeyb, kosb()); + START => + x := osb(); + if(p.arg[1] == PROCEDURE) + x |= 16r04; + if(p.arg[1] == SCROLLING) + x |= 16r02; + PRO2(Pmodem, from, REPFUNCTIONINGSTATUS, x); + case p.arg[1] { + PROCEDURE => # activate error correction procedure + sys->print("activate error correction\n"); + return ref Event.Eproto(Pmodem, from, Cstartecp, "",0,0,0); + SCROLLING => # set screen to scroll + return ref Event.Eproto(Pscreen, from, Cproto, "",START,SCROLLING,0); + LOWERCASE => # set keyb to invert case + return ref Event.Eproto(Pkeyb, from, Cproto, "",START,LOWERCASE,0); + } + STOP => + x := osb(); + if(p.arg[1] == SCROLLING) + x &= ~16r02; + PRO2(Pmodem, from, REPFUNCTIONINGSTATUS, osb()); + case p.arg[1] { + PROCEDURE => # deactivate error correction procedure + sys->print("deactivate error correction\n"); + return ref Event.Eproto(Pmodem, from, Cstopecp, "",0,0,0); + SCROLLING => # set screen to no scroll + return ref Event.Eproto(Pscreen, from, Cproto, "",STOP,SCROLLING,0); + LOWERCASE => # set keyb to not invert case + return ref Event.Eproto(Pkeyb, from, Cproto, "",STOP,LOWERCASE,0); + } + COPY => # copy screen to socket + # not implemented + ; + MIXED => # change video mode (12.1) + case p.arg[1] { + MIXED1 => # videotex -> mixed + reply := array [] of { byte SEP, byte 16r70 }; + return ref Event.Eproto(Pscreen, from, Cproto, "",MIXED,MIXED1,0); + MIXED2 => # mixed -> videotex + reply := array [] of { byte SEP, byte 16r71 }; + return ref Event.Eproto(Pscreen, from, Cproto, "",MIXED,MIXED2,0); + } + ASCII => # change video mode (12.2) + # TODO + ; + } + 3 => + case p.arg[0] { + OFF or ON => # link, unlink, enable, disable + modcmd(p.arg[0], p.arg[1], p.arg[2]); + PRO3(Pmodem, from, FROM, p.arg[1], psb(TxCode(p.arg[1]))); + START => + case p.arg[1] { + RxKeyb => # keyboard mode + case p.arg[2] { + ETEN => # extended keyboard + K.spec |= Extend; + C0 => # cursor control key coding from col 0 + K.spec |= C0keys; + } + PRO3(Pmodem, from, REPKEYBOARDSTATUS, RxKeyb, kosb()); + } + STOP => # keyboard mode + case p.arg[1] { + RxKeyb => # keyboard mode + case p.arg[2] { + ETEN => # extended keyboard + K.spec &= ~Extend; + C0 => # cursor control key coding from col 0 + K.spec &= ~C0keys; + } + PRO3(Pmodem, from, REPKEYBOARDSTATUS, RxKeyb, kosb()); + } + } + } + return nil; +} + +# post a PRO3 sequence to all modules on `path' +PRO3(path, from, x, y, z: int) +{ + data := array [] of { byte ESC, byte 16r3b, byte x, byte y, byte z}; + post(ref Event.Edata(path, from, data)); +} + +# post a PRO2 sequence to all modules on `path' +PRO2(path, from, x, y: int) +{ + data := array [] of { byte ESC, byte 16r3a, byte x, byte y}; + post(ref Event.Edata(path, from, data)); +} + +# post a PRO1 sequence to all modules on `path' +PRO1(path, from, x: int) +{ + data := array [] of { byte ESC, byte 16r39, byte x}; + post(ref Event.Edata(path, from, data)); +} + +# make or break links between modules, or enable and disable +modcmd(cmd, from, targ: int) +{ + from = RxTx(from); + targ = RxTx(targ); + if(from == targ) # enable or disable module + if(cmd == ON) + Modules[from].disabled = 0; + else + Modules[from].disabled = 1; + else # modify path + if(cmd == ON) + Modules[from].path |= (1<<targ); + else + Modules[from].path &= ~(1<<targ); +} + +# determine the path status byte (3.4) +# if bit 3 of `code' is set then a receive path status byte is returned +# otherwise a transmit path status byte +psb(code: int): int +{ + this := RxTx(code); + b := 16r40; # bit 6 always set + if(code == RxCode(code)) { # want a receive path status byte + mask := (1<<this); + if(Modules[Mscreen].path & mask) + b |= 16r01; + if(Modules[Mkeyb].path & mask) + b |= 16r02; + if(Modules[Mmodem].path & mask) + b |= 16r04; + if(Modules[Msocket].path & mask) + b |= 16r08; + } else { + mod := Modules[this]; + if(mod.path & Mscreen) + b |= 16r01; + if(mod.path & Mkeyb) + b |= 16r02; + if(mod.path & Mmodem) + b |= 16r04; + if(mod.path & Msocket) + b |= 16r08; + } +# if(parity(b)) +# b ^= 16r80; + return b; +} + +# convert `code' to a receive code by setting bit 3 +RxCode(code: int): int +{ + return (code | 16r08)&16rff; +} + +# covert `code' to a send code by clearing bit 3 +TxCode(code: int): int +{ + return (code & ~16r08)&16rff; +} + +# return 0 on even parity, 1 otherwise +# only the bottom 8 bits are considered +parity(b: int): int +{ + bits := 8; + p := 0; + while(bits-- > 0) { + if(b&1) + p ^= 1; + b >>= 1; + } + return p; +} + +# convert Rx or Tx code to a module code +RxTx(code: int): int +{ + rv := 0; + case code { + TxScreen or RxScreen => rv = Mscreen; + TxKeyb or RxKeyb => rv = Mkeyb; + TxModem or RxModem => rv = Mmodem; + TxSocket or RxSocket => rv = Msocket; + * => + fatal("invalid module code"); + } + return rv; +} + +# generate an operating status byte (11.2) +osb(): int +{ + b := 16r40; + if(S.cols == 80) + b |= 16r01; + if(S.spec & Scroll) + b |= 16r02; + if(M.spec & Ecp) + b |= 16r04; + if(K.spec & Invert) + b |= 16r08; +# if(parity(b)) +# b ^= 16r80; + return b; +} + +# generate a keyboard operating status byte (9.1.2) +kosb(): int +{ + b := 16r40; + if(K.spec & Extend) + b |= 16r01; + if(K.spec & C0keys) + b |= 16r04; +# if(parity(b)) +# b ^= 16r80; + return b; +} + +hex(v, n: int): string +{ + return sprint("%.*ux", n, v); +} + +tostr(ch: int): string +{ + str := ""; + str[0] = ch; + return str; +} + +toint(s: string, base: int): (int, string) +{ + if(base < 0 || base > 36) + return (0, s); + + c := 0; + for(i := 0; i < len s; i++) { + c = s[i]; + if(c != ' ' && c != '\t' && c != '\n') + break; + } + + neg := 0; + if(c == '+' || c == '-') { + if(c == '-') + neg = 1; + i++; + } + + ok := 0; + n := 0; + for(; i < len s; i++) { + c = s[i]; + v := base; + case c { + 'a' to 'z' => + v = c - 'a' + 10; + 'A' to 'Z' => + v = c - 'A' + 10; + '0' to '9' => + v = c - '0'; + } + if(v >= base) + break; + ok = 1; + n = n * base + v; + } + + if(!ok) + return (0, s); + if(neg) + n = -n; + return (n, s[i:]); +} + +tolower(s: string): string +{ + r := s; + for(i := 0; i < len r; i++) { + c := r[i]; + if(c >= int 'A' && c <= int 'Z') + r[i] = r[i] + (int 'a' - int 'A'); + } + return r; +} + +# duplicate `ch' exactly `n' times +dup(ch, n: int): string +{ + str := ""; + for(i:=0; i<n; i++) + str[i] = ch; + return str; +} + +fatal(msg: string) +{ + fprint(stderr, "fatal: %s\n", msg); + exits(msg); +} + +exits(s: string) +{ + if(s==nil); +# raise "fail: miniterm " + s; + fd := sys->open("#p/" + string pgrp + "/ctl", sys->OWRITE); + if(fd != nil) + sys->fprint(fd, "killgrp"); + exit; +} + +# Minitel byte MSB and LSB classification (p.87) +MSB(ch: int): int +{ + return (ch&16r70)>>4; +} +LSB(ch: int): int +{ + return (ch&16r0f); +} + +# Minitel character set classification (p.92) +ISC0(ch: int): int +{ + msb := (ch&16r70)>>4; + return msb == 0 || msb == 1; +} + +ISC1(ch: int): int +{ + return ch >= 16r40 && ch <= 16r5f; +} + +ISG0(ch: int): int +{ + # 0x20 (space) and 0x7f (DEL) are not in G0 + return ch > 16r20 && ch < 16r7f; +} + +tkcmds(t: ref Tk->Toplevel, cmds: array of string) +{ + n := len cmds; + for (ix := 0; ix < n; ix++) + tk->cmd(t, cmds[ix]); +} diff --git a/appl/wm/minitel/miniterm.dis b/appl/wm/minitel/miniterm.dis Binary files differnew file mode 100644 index 00000000..39c6ba5e --- /dev/null +++ b/appl/wm/minitel/miniterm.dis diff --git a/appl/wm/minitel/miniterm.m b/appl/wm/minitel/miniterm.m new file mode 100644 index 00000000..e0345f81 --- /dev/null +++ b/appl/wm/minitel/miniterm.m @@ -0,0 +1,120 @@ +# +# Copyright © 1998 Vita Nuova Limited. All rights reserved. +# + +# Common control bytes +NUL: con 16r00; +SOH: con 16r01; +EOT: con 16r04; +ENQ: con 16r05; +BEL: con 16r07; +BS: con 16r08; +HT: con 16r09; +LF: con 16r0a; +VT: con 16r0b; +FF: con 16r0c; +CR: con 16r0d; +SO: con 16r0e; +SI: con 16r0f; +DLE: con 16r10; +CON: con 16r11; +XON: con 16r11; +REP: con 16r12; +SEP: con 16r13; +XOFF: con 16r13; +COFF: con 16r14; +NACK: con 16r15; +SYN: con 16r16; +CAN: con 16r18; +SS2: con 16r19; +SUB: con 16r1a; +ESC: con 16r1b; +SS3: con 16r1d; +RS: con 16r1e; +US: con 16r1f; + +SP: con 16r20; +DEL: con 16r7f; + +# Minitel Protocol - some are duplicated (chapter 6) +ASCII: con 16r31; +MIXED: con 16r32; +ETEN: con 16r41; +C0: con 16r43; +SCROLLING: con 16r43; +PROCEDURE: con 16r44; +LOWERCASE: con 16r45; +OFF: con 16r60; +ON: con 16r61; +TO: con 16r62; +FROM: con 16r63; +NOBROADCAST: con 16r64; +BROADCAST: con 16r65; +NONRETURN: con 16r64; +RETURN: con 16r65; +TRANSPARENCY: con 16r66; +DISCONNECT: con 16r67; +CONNECT: con 16r68; +START: con 16r69; +STOP: con 16r6a; +KEYBOARDSTATUS: con 16r72; +REPKEYBOARDSTATUS: con 16r73; +FUNCTIONINGSTATUS: con 16r72; +REPFUNCTIONINGSTATUS: con 16r73; +EXCHANGERATESTATUS: con 16r74; +REPEXCHANGERATESTATUS: con 16r75; +PROTOCOLSTATUS: con 16r76; +REPPROTOCOLSTATUS: con 16r77; +SETRAM1: con 16r78; +SETRAM2: con 16r79; +ENQROM: con 16r7b; +COPY: con 16r7c; +ASCII1: con 16r7d; +MIXED1: con 16r7d; +MIXED2: con 16r7e; +RESET: con 16r7f; + +# Module send and receive codes (chapter 6) +TxScreen: con 16r50; +TxKeyb: con 16r51; +TxModem: con 16r52; +TxSocket: con 16r53; +RxScreen: con 16r58; +RxKeyb: con 16r59; +RxModem: con 16r5a; +RxSocket: con 16r5b; + +# Internal Event.Eproto command constants +Cplay, # for testing +Cconnect, # e.s contains the address to dial +Cdisconnect, # +Crequestecp, # ask server to start ecp +Creset, # reset module +Cstartecp, # start error correction +Cstopecp, # stop error correction +Cproto, # minitel protocol +Ccursor, # update screen cursor +Cindicators, # update row 0 indicators + +# softmodem bug: Cscreenoff, Cscreenon +Cscreenoff, # screen: ignore data +Cscreenon, # screen: don't ignore data + +Clast + : con iota; + +# Special keys - hardware returned byte +KupPC: con 16r0203; # pc emu +KdownPC: con 16r0204; # pc emu +Kup: con 16rE012; +Kdown: con 16rE013; +Kenter: con 16r000a; +Kback: con 16r0008; +Kesc: con 16r001b; +KF1: con 16rE041; +KF2: con 16rE042; +KF3: con 16rE043; +KF4: con 16rE044; +KF13: con 16rE04D; + + diff --git a/appl/wm/minitel/miniterm.sbl b/appl/wm/minitel/miniterm.sbl new file mode 100644 index 00000000..a34fa8b1 --- /dev/null +++ b/appl/wm/minitel/miniterm.sbl @@ -0,0 +1,6810 @@ +limbo .sbl 2.1 +Miniterm +15 +miniterm.b +sys.m +draw.m +tk.m +tkclient.m +miniterm.m +arg.m +event.m +event.b +keyb.b +modem.b +socket.b +screen.b +mdisplay.m +swkeyb.m +5725 +8:7.1,9 0 +8.6,13 1 +6,13 1 +10.3,40 2 +14,27 2 +29,39 2 +3,40 2 +3,40 2 +11.7,11 3 +15,25 4 +13,25 4 +12.9,30 5 +9,30 5 +28,29 5 +17,26 5 +13,26 5 +9,30 5 +9,30 5 +9,36 5 +4,36 5 +4,36 6 +11.27,30 7 +27,30 7 +14.3,14 8 +3,14 1 +16.3,44 9 +14,31 9 +33,38 9 +40,43 9 +3,44 9 +3,44 9 +3,44 1 +18.8,9 10 +1,9 10 +9:27.1,25 11 +28.1,23 12 +29.1,38 13 +14,22 13 +24,29 13 +31,37 13 +1,38 13 +1,38 13 +1,38 14 +30.1,10 15 +1,2 15 +1,10 15 +31.0,1 16 +35.1,36 17 +18,32 17 +34,35 17 +1,36 17 +1,36 18 +36.0,1 19 +42.1,18 20 +43.1,20 21 +1,2 21 +11,19 21 +1,20 21 +46.1,24 22 +47.1,14 23 +1,2 23 +7,13 23 +1,14 23 +51.20,30 24 +2,30 24 +53.6,12 25 +54.3,10 26 +3,10 27 +56.3,8 28 +57.2,28 29 +2,3 29 +17,27 29 +17,23 29 +17,27 29 +8,27 29 +8,27 30 +2,28 29 +58.2,14 31 +59.6,17 32 +60.3,9 33 +62.2,23 34 +18,22 34 +2,23 34 +2,23 34 +64.7,11 35 +66.3,8 36 +68.6,16 37 +6,20 37 +69.23,33 38 +23,36 38 +4,37 38 +4,37 35 +71.3,14 39 +72.3,8 40 +73.2,21 35 +76.3,17 41 +3,17 35 +80.1,11 42 +1,2 42 +7,10 42 +1,11 42 +82.1,4 43 +1,4 43 +84.11,13 43 +11,13 43 +81.10,16 43 +10,16 43 +10,16 43 +10,16 43 +83.2,8 44 +85.6,17 45 +86.3,9 46 +92.1,26 47 +93.1,19 48 +94.1,26 49 +98.11,15 50 +11,15 50 +122.12,17 50 +12,17 50 +215.15,22 50 +15,22 50 +97.2,8 50 +2,8 50 +2,8 50 +2,8 50 +99.8,15 51 +8,15 51 +8,15 52 +101.4,9 53 +103.9,14 54 +9,14 54 +9,14 54 +9,14 54 +9,14 54 +105.5,14 55 +5,6 55 +5,14 55 +5,14 54 +107.10,14 56 +109.11,15 57 +111.7,23 58 +7,23 57 +7,23 56 +114.11,15 59 +116.7,24 60 +7,24 59 +7,24 56 +7,24 54 +119.9,14 61 +9,14 62 +9,14 50 +123.6,16 63 +6,20 63 +124.4,34 64 +11,17 64 +19,28 64 +30,33 64 +4,34 64 +4,34 64 +126.16,39 65 +30,33 65 +35,38 65 +16,39 65 +16,39 65 +4,5 65 +7,11 65 +7,11 66 +127.6,10 67 +128.9,16 68 +9,16 68 +130.19,40 69 +28,35 69 +25,35 69 +25,35 70 +37,39 69 +19,40 69 +19,40 69 +6,9 69 +6,9 71 +131.8,26 72 +132.6,23 73 +19,22 73 +6,23 73 +6,23 73 +133.10,18 74 +134.7,17 75 +135.6,19 76 +6,19 77 +6,19 78 +136.6,11 79 +138.5,23 80 +13,14 80 +19,22 80 +5,23 80 +5,23 80 +139.8,19 81 +140.6,50 82 +11,49 82 +15,49 82 +27,30 82 +27,35 82 +27,35 83 +37,42 82 +44,48 82 +11,49 82 +11,49 84 +6,50 82 +6,50 85 +6,50 68 +143.11,18 86 +8,18 86 +8,28 86 +8,28 87 +144.9,27 88 +145.7,24 89 +146.7,22 90 +148.9,26 91 +30,51 91 +149.7,31 92 +21,30 92 +7,31 92 +7,31 92 +150.10,20 93 +151.8,51 94 +13,50 94 +17,50 94 +29,32 94 +29,37 94 +29,37 95 +39,44 94 +46,49 94 +13,50 94 +13,50 96 +8,51 94 +152.8,51 97 +13,50 97 +17,50 97 +29,32 97 +29,37 97 +29,37 98 +39,44 97 +46,49 97 +13,50 97 +13,50 99 +8,51 97 +154.7,68 100 +12,67 100 +16,67 100 +29,35 100 +37,42 100 +44,55 100 +57,59 100 +61,62 100 +63,64 100 +65,66 100 +12,67 100 +12,67 101 +7,68 100 +7,68 102 +156.6,33 103 +11,32 103 +15,32 103 +27,28 103 +30,31 103 +11,32 103 +11,32 104 +6,33 103 +157.6,11 105 +159.8,26 106 +160.21,28 107 +18,28 107 +6,28 107 +6,28 108 +161.6,11 109 +163.13,20 110 +10,20 110 +10,20 110 +165.11,18 111 +167.10,30 112 +168.8,66 113 +13,65 113 +17,65 113 +30,36 113 +38,43 113 +45,53 113 +55,57 113 +59,60 113 +61,62 113 +63,64 113 +13,65 113 +13,65 114 +8,66 113 +8,66 115 +170.8,32 116 +171.8,35 117 +18,25 117 +27,34 117 +8,35 117 +8,35 111 +174.7,68 118 +12,67 118 +16,67 118 +29,35 118 +37,42 118 +44,55 118 +57,59 118 +61,62 118 +63,64 118 +65,66 118 +12,67 118 +12,67 119 +7,68 118 +7,68 111 +176.7,31 120 +21,30 120 +7,31 120 +7,31 120 +177.10,20 121 +178.8,51 122 +13,50 122 +17,50 122 +29,32 122 +29,37 122 +29,37 123 +39,44 122 +46,49 122 +13,50 122 +13,50 124 +8,51 122 +8,51 125 +8,51 111 +8,51 110 +181.6,31 126 +23,30 126 +20,30 126 +20,30 127 +6,31 126 +6,31 126 +182.9,19 128 +183.7,50 129 +12,49 129 +16,49 129 +28,31 129 +28,36 129 +28,36 130 +38,43 129 +45,48 129 +12,49 129 +12,49 131 +7,50 129 +7,50 132 +7,50 110 +186.17,24 133 +14,24 133 +5,24 133 +5,24 134 +187.20,27 135 +17,27 135 +14,27 135 +5,27 135 +5,27 136 +188.5,39 137 +33,34 137 +36,37 137 +5,39 137 +5,39 137 +189.8,19 138 +190.10,28 139 +191.7,24 140 +7,24 141 +7,24 142 +192.7,12 143 +194.10,26 144 +195.11,25 145 +20,24 145 +11,25 145 +11,25 145 +11,38 145 +11,38 146 +196.11,31 147 +197.9,67 148 +14,66 148 +18,66 148 +31,37 148 +39,44 148 +46,54 148 +56,58 148 +60,61 148 +62,63 148 +64,65 148 +14,66 148 +14,66 149 +9,67 148 +9,67 150 +199.9,33 151 +200.9,36 152 +19,26 152 +28,35 152 +9,36 152 +9,36 153 +9,36 154 +202.8,13 155 +205.6,25 156 +20,24 156 +6,25 156 +6,25 156 +206.9,19 157 +207.7,50 158 +12,49 158 +16,49 158 +28,31 158 +28,36 158 +28,36 159 +38,43 158 +45,48 158 +12,49 158 +12,49 160 +7,50 158 +7,50 161 +209.7,66 162 +12,65 162 +16,65 162 +28,31 162 +28,36 162 +28,36 163 +38,43 162 +45,63 162 +12,65 162 +12,65 164 +7,66 162 +210.12,60 165 +16,60 165 +28,31 165 +28,36 165 +28,36 166 +38,43 165 +45,59 165 +52,58 165 +45,59 165 +45,59 165 +7,61 165 +7,61 165 +7,61 167 +7,61 165 +7,61 168 +7,61 169 +7,61 68 +7,61 170 +7,61 171 +7,61 50 +216.3,20 172 +217.6,20 173 +218.4,23 174 +219.4,62 175 +9,61 175 +13,61 175 +26,32 175 +34,39 175 +41,49 175 +51,53 175 +55,56 175 +57,58 175 +59,60 175 +9,61 175 +9,61 176 +4,62 175 +4,62 177 +4,62 50 +223.1,10 178 +6,9 178 +1,10 178 +224.0,1 179 +232.1,20 180 +16,19 180 +1,20 180 +1,20 180 +233.5,15 181 +234.2,20 182 +16,19 182 +2,20 182 +2,20 182 +235.5,15 183 +236.10,13 184 +3,13 184 +3,13 185 +240.6,12 186 +6,12 186 +6,12 186 +6,12 186 +6,12 186 +242.5,15 187 +19,29 187 +243.10,49 188 +24,48 188 +36,47 188 +29,48 188 +24,48 188 +3,49 188 +244.5,15 189 +19,29 189 +245.10,48 190 +23,47 190 +35,46 190 +28,47 190 +23,47 190 +3,48 190 +247.5,15 191 +19,29 191 +33,43 191 +47,57 191 +248.10,32 192 +23,31 192 +23,31 192 +3,32 192 +252.4,14 193 +18,28 193 +253.9,31 194 +22,30 194 +22,30 194 +2,31 194 +256.4,16 195 +20,31 195 +257.7,13 196 +7,13 196 +7,13 196 +7,13 196 +7,13 196 +259.10,13 197 +3,13 197 +261.10,32 198 +23,31 198 +23,31 198 +3,32 198 +265.6,9 199 +266.1,6 199 +267.17,55 200 +31,41 200 +31,41 200 +43,53 200 +43,53 200 +10,55 200 +268.1,11 199 +274.5,17 201 +5,22 201 +26,43 201 +47,66 201 +275.0,34 202 +7,13 202 +15,33 202 +0,34 202 +0,34 202 +276.10,46 203 +24,32 203 +24,32 203 +34,44 203 +34,44 203 +3,46 203 +278.9,12 204 +2,12 204 +279.14,17 205 +7,17 205 +281.8,30 206 +21,29 206 +21,29 206 +1,30 206 +286.4,10 207 +287.0,1 208 +291.1,15 209 +13,14 209 +1,15 209 +1,15 209 +292.6,7 210 +294.33,42 211 +26,42 211 +295.31,37 212 +24,37 212 +296.36,44 213 +29,44 213 +298.16,23 214 +9,23 214 +299.22,29 215 +15,29 215 +300.37,46 216 +30,46 216 +302.16,24 217 +9,24 217 +303.30,36 218 +23,36 218 +305.16,26 219 +9,26 219 +307.8,9 220 +1,9 220 +313.1,8 221 +314.1,14 222 +315.4,15 223 +19,27 223 +19,32 223 +316.9,12 224 +2,12 224 +317.4,11 225 +4,18 225 +318.2,14 226 +319.2,17 227 +2,17 227 +321.1,22 228 +17,21 228 +1,22 228 +1,22 228 +322.6,10 229 +323.15,25 230 +15,25 229 +324.14,24 231 +14,24 229 +325.14,24 232 +14,24 229 +326.13,23 233 +13,23 229 +327.13,23 234 +13,23 229 +328.15,25 235 +15,25 229 +329.14,24 236 +14,24 229 +330.12,22 237 +12,22 229 +331.16,26 238 +16,26 229 +333.4,6 239 +334.5,13 240 +335.10,52 241 +24,32 241 +24,32 241 +34,42 241 +34,42 241 +44,51 241 +44,51 241 +3,52 241 +337.10,42 242 +24,32 242 +24,32 242 +34,41 242 +34,41 242 +3,42 242 +339.9,12 243 +2,12 243 +345.6,9 244 +347.8,18 245 +2,18 245 +349.9,15 246 +2,15 246 +351.9,15 247 +2,15 247 +353.9,18 248 +2,18 248 +355.9,17 249 +2,17 249 +357.9,16 250 +2,16 250 +359.9,18 251 +2,18 251 +361.9,17 252 +2,17 252 +363.9,16 253 +2,16 253 +365.9,12 254 +2,12 254 +10:51.1,28 255 +18,24 255 +26,27 255 +1,28 255 +1,28 255 +52.5,11 256 +13,18 257 +53.7,37 258 +19,26 258 +32,36 258 +28,36 258 +7,37 258 +7,37 258 +2,37 258 +2,37 259 +52.20,23 260 +20,23 260 +54.8,9 261 +1,9 261 +59.1,28 262 +60.5,11 263 +13,20 264 +61.5,14 265 +12,13 265 +5,14 265 +5,14 265 +5,14 265 +62.3,12 266 +20,31 266 +3,31 266 +3,31 267 +64.3,12 268 +3,21 268 +60.22,25 269 +22,25 269 +65.1,25 270 +66.1,20 271 +67.1,18 272 +68.1,20 273 +69.1,20 274 +70.1,10 275 +71.1,11 276 +72.1,10 277 +73.1,14 278 +74.1,15 279 +75.1,16 280 +76.1,27 281 +77.1,29 282 +78.1,10 283 +1,2 283 +1,10 283 +79.0,1 284 +83.1,29 285 +18,25 285 +27,28 285 +1,29 285 +1,29 286 +84.0,1 287 +88.4,20 288 +89.2,61 289 +7,60 289 +11,60 289 +24,30 289 +32,38 289 +40,48 289 +50,52 289 +54,55 289 +56,57 289 +58,59 289 +7,60 289 +7,60 290 +2,61 289 +93.11,15 291 +11,15 291 +192.10,14 291 +10,14 291 +92.2,8 291 +2,8 291 +2,8 291 +2,8 291 +94.8,15 292 +8,15 292 +8,15 293 +96.4,9 294 +98.7,17 295 +7,21 295 +99.37,44 296 +37,38 296 +37,44 296 +37,44 296 +5,45 296 +12,18 296 +20,35 296 +20,35 296 +20,35 297 +5,45 296 +5,45 296 +100.4,19 298 +4,5 298 +12,18 298 +4,19 298 +4,19 298 +101.7,23 299 +27,40 299 +27,40 299 +102.8,23 300 +103.6,64 301 +11,63 301 +15,63 301 +28,35 301 +37,42 301 +44,51 301 +53,55 301 +57,58 301 +59,60 301 +61,62 301 +11,63 301 +11,63 302 +6,64 301 +104.6,51 303 +11,50 303 +15,50 303 +27,34 303 +36,41 303 +43,49 303 +11,50 303 +11,50 304 +6,51 303 +6,51 292 +108.9,14 305 +9,14 305 +9,14 305 +110.5,14 306 +5,6 306 +5,14 306 +5,14 305 +112.8,18 307 +113.6,11 308 +114.5,20 309 +115.5,25 310 +116.5,67 311 +10,66 311 +14,66 311 +27,34 311 +36,42 311 +44,55 311 +57,59 311 +60,61 311 +62,63 311 +64,65 311 +10,66 311 +10,66 312 +5,67 311 +118.10,19 313 +120.6,38 314 +6,7 314 +12,30 314 +12,37 314 +12,37 315 +6,38 314 +121.6,25 316 +122.9,26 317 +19,20 317 +22,25 317 +9,26 317 +9,26 317 +9,30 317 +123.7,30 318 +7,8 318 +13,29 318 +7,30 318 +124.7,22 319 +125.7,69 320 +12,68 320 +16,68 320 +29,36 320 +38,44 320 +46,57 320 +59,61 320 +62,63 320 +64,65 320 +66,67 320 +12,68 320 +12,68 321 +7,69 320 +7,69 322 +126.7,12 323 +128.6,25 324 +129.6,18 325 +130.6,16 326 +14,15 326 +6,16 326 +131.6,32 327 +6,32 328 +6,32 313 +133.6,39 329 +6,7 329 +12,38 329 +6,39 329 +134.9,19 330 +9,23 330 +27,37 330 +27,41 330 +135.7,42 331 +18,30 331 +32,41 331 +7,42 331 +7,42 331 +136.18,42 332 +28,37 332 +39,41 332 +18,42 332 +18,42 332 +137.10,18 333 +138.7,37 334 +7,8 334 +13,36 334 +7,37 334 +139.7,22 335 +140.7,69 336 +12,68 336 +16,68 336 +29,36 336 +38,44 336 +46,57 336 +59,61 336 +62,63 336 +64,65 336 +66,67 336 +12,68 336 +12,68 337 +7,69 336 +141.10,20 338 +10,24 338 +142.8,52 339 +19,40 339 +42,51 339 +8,52 339 +8,52 339 +8,52 340 +8,52 341 +8,52 342 +8,52 343 +8,52 344 +8,52 345 +143.7,12 346 +145.6,52 347 +23,39 347 +41,51 347 +6,52 347 +6,52 347 +146.6,20 348 +147.9,22 349 +9,27 349 +31,40 349 +31,45 349 +31,54 349 +31,54 350 +148.7,22 351 +149.9,20 352 +150.7,17 353 +7,8 353 +13,16 353 +7,17 353 +151.7,28 354 +152.7,23 355 +153.7,69 356 +12,68 356 +16,68 356 +29,36 356 +38,44 356 +46,57 356 +59,61 356 +62,63 356 +64,65 356 +66,67 356 +12,68 356 +12,68 357 +7,69 356 +155.6,32 358 +6,32 359 +6,32 360 +6,32 361 +6,32 362 +6,32 363 +6,32 364 +6,32 313 +157.8,19 365 +158.6,25 366 +159.6,26 367 +12,13 367 +21,25 367 +6,26 367 +160.6,20 368 +6,20 369 +6,20 305 +163.8,18 370 +164.6,30 371 +6,7 371 +12,29 371 +6,30 371 +165.6,31 372 +167.8,27 373 +168.6,15 374 +13,14 374 +6,15 374 +6,15 375 +170.6,18 376 +16,17 376 +6,18 376 +6,18 305 +172.10,13 377 +174.6,15 378 +13,14 378 +6,15 378 +6,15 377 +6,15 305 +177.8,20 379 +8,20 379 +178.6,20 380 +179.6,11 381 +181.5,47 382 +5,6 382 +13,46 382 +25,33 382 +25,33 382 +35,45 382 +35,45 382 +5,47 382 +5,47 382 +182.0,39 383 +11,38 383 +0,39 383 +0,39 383 +0,39 305 +184.5,18 384 +185.5,14 385 +186.5,18 386 +5,18 305 +188.5,19 387 +5,19 305 +189.9,14 388 +9,14 389 +9,14 291 +193.6,16 390 +6,20 390 +194.36,49 391 +41,42 391 +43,48 391 +36,49 391 +36,49 391 +4,50 391 +11,17 391 +19,34 391 +19,34 391 +19,34 392 +4,50 391 +4,50 391 +196.6,14 393 +197.4,13 394 +198.9,16 395 +200.5,25 396 +5,6 396 +11,24 396 +5,25 396 +5,25 395 +202.5,15 397 +5,6 397 +11,14 397 +5,15 397 +5,15 395 +204.4,21 398 +205.4,19 399 +206.4,64 400 +9,63 400 +13,63 400 +26,33 400 +35,41 400 +43,52 400 +54,56 400 +57,58 400 +59,60 400 +61,62 400 +9,63 400 +9,63 401 +4,64 400 +207.4,66 402 +9,65 402 +13,65 402 +26,33 402 +35,41 402 +43,54 402 +56,58 402 +59,60 402 +61,62 402 +63,64 402 +9,65 402 +9,65 403 +4,66 402 +4,66 404 +208.4,9 405 +210.3,16 406 +3,4 406 +14,15 406 +3,16 406 +3,16 407 +3,16 291 +213.4,14 408 +214.2,13 409 +7,12 409 +2,13 409 +215.1,10 410 +6,9 410 +1,10 410 +216.0,1 411 +220.0,1 412 +224.4,14 413 +4,16 413 +225.42,62 414 +47,51 414 +53,61 414 +42,62 414 +42,62 414 +2,63 414 +13,31 414 +33,40 414 +33,40 414 +33,40 415 +2,63 414 +2,63 414 +226.6,13 416 +6,13 416 +6,13 416 +6,13 416 +6,13 416 +256.0,1 417 +229.6,10 418 +14,22 419 +12,22 419 +230.13,20 420 +3,20 420 +231.6,16 421 +20,30 421 +232.12,23 422 +4,29 422 +4,29 423 +233.4,12 424 +235.18,36 425 +28,35 425 +18,36 425 +18,36 425 +236.8,12 426 +8,12 426 +8,12 426 +237.3,14 426 +239.4,14 427 +4,5 427 +10,13 427 +4,14 427 +240.4,25 428 +241.4,20 429 +242.4,66 430 +9,65 430 +13,65 430 +26,33 430 +35,41 430 +43,54 430 +56,58 430 +59,60 430 +61,62 430 +63,64 430 +9,65 430 +9,65 431 +4,66 430 +4,66 426 +244.4,13 432 +11,12 432 +4,13 432 +245.4,14 433 +4,5 433 +10,13 433 +4,14 433 +246.4,21 434 +247.4,19 435 +248.4,66 436 +9,65 436 +13,65 436 +26,33 436 +35,41 436 +43,54 436 +56,58 436 +59,60 436 +61,62 436 +63,64 436 +9,65 436 +9,65 437 +4,66 436 +4,66 426 +250.3,15 438 +3,15 439 +3,15 440 +229.24,27 441 +24,27 441 +253.2,47 442 +7,46 442 +11,46 442 +23,26 442 +23,31 442 +23,31 443 +33,39 442 +41,45 442 +7,46 442 +7,46 444 +2,47 442 +256.0,1 417 +0,1 417 +0,1 417 +260.4,15 445 +261.9,11 446 +2,11 446 +262.4,12 447 +4,17 447 +263.9,10 448 +2,10 448 +264.4,15 449 +266.14,22 450 +2,31 450 +267.6,12 451 +16,24 452 +14,24 452 +268.3,8 453 +22,29 453 +18,29 453 +18,37 453 +11,38 453 +3,38 453 +267.26,29 454 +26,29 454 +269.2,11 455 +2,11 456 +271.4,14 457 +4,16 457 +272.27,47 458 +32,36 458 +38,46 458 +27,47 458 +27,47 458 +2,48 458 +13,25 458 +13,25 458 +13,25 459 +2,48 458 +2,48 458 +273.8,40 460 +19,23 460 +25,29 460 +31,39 460 +8,40 460 +8,40 460 +1,40 460 +292.1,27 461 +293.5,11 462 +13,20 463 +294.2,8 464 +295.2,10 465 +296.6,12 466 +14,19 467 +297.3,12 468 +298.6,13 469 +6,21 469 +6,21 469 +299.4,18 470 +300.3,10 471 +296.21,24 472 +21,24 472 +302.2,11 473 +14,22 473 +2,30 473 +293.22,25 474 +22,25 474 +304.0,1 475 +310.7,12 476 +311.5,9 477 +5,21 477 +312.3,8 478 +310.14,17 479 +14,17 479 +313.8,9 480 +1,9 480 +320.4,14 481 +4,16 481 +321.29,44 482 +34,35 482 +37,43 482 +29,44 482 +29,44 482 +2,45 482 +13,27 482 +13,27 482 +13,27 483 +2,45 482 +2,45 482 +322.1,12 484 +323.15,26 485 +1,26 485 +324.1,9 486 +325.1,8 487 +326.1,9 488 +327.5,9 489 +11,21 490 +328.11,15 491 +2,15 491 +329.2,17 492 +330.12,22 493 +8,22 493 +5,23 493 +5,31 493 +5,31 493 +331.3,11 494 +332.15,22 495 +8,23 495 +2,23 495 +334.6,9 496 +335.6,15 497 +19,22 497 +19,33 497 +336.4,11 498 +337.4,12 499 +339.6,15 500 +340.4,12 501 +342.2,9 502 +343.4,8 503 +4,8 503 +2,9 503 +2,19 503 +327.23,26 504 +23,26 504 +345.4,10 505 +346.5,15 506 +5,19 506 +347.3,29 507 +14,28 507 +3,29 507 +3,29 507 +348.9,12 508 +2,12 508 +350.7,15 509 +1,21 509 +351.8,19 510 +4,19 510 +4,29 510 +352.5,15 511 +5,19 511 +353.3,67 512 +14,41 512 +43,49 512 +55,66 512 +51,66 512 +3,67 512 +3,67 512 +354.9,12 513 +2,12 513 +356.1,23 514 +357.9,10 515 +9,16 515 +1,16 515 +1,16 516 +358.4,14 517 +4,18 517 +359.37,47 518 +42,43 518 +44,46 518 +37,47 518 +37,47 518 +2,61 518 +13,35 518 +13,35 518 +13,35 519 +49,52 518 +54,60 518 +2,61 518 +2,61 518 +360.8,9 520 +1,9 520 +365.10,27 521 +20,21 521 +23,26 521 +10,27 521 +10,27 521 +1,27 521 +366.4,17 522 +367.2,10 523 +2,10 523 +368.1,34 524 +369.1,11 525 +370.7,18 526 +371.8,49 527 +19,23 527 +25,34 527 +25,26 527 +25,34 527 +36,41 527 +36,47 527 +8,49 527 +8,49 527 +8,49 527 +8,53 527 +372.3,13 528 +373.3,12 529 +374.6,20 530 +6,25 530 +375.4,25 531 +376.8,14 532 +16,19 533 +377.5,9 534 +22,26 534 +18,26 534 +17,35 534 +5,35 534 +376.21,24 535 +21,24 535 +378.4,14 536 +4,14 537 +4,14 538 +381.4,10 539 +382.7,16 540 +383.5,43 541 +16,35 541 +37,42 541 +5,43 541 +5,43 541 +384.5,30 542 +385.5,15 543 +386.11,19 544 +387.9,13 545 +9,20 545 +26,29 545 +24,30 545 +24,37 545 +48,51 545 +46,52 545 +42,52 545 +41,59 545 +63,68 545 +41,68 545 +388.7,13 546 +389.7,20 547 +390.7,51 548 +18,38 548 +40,45 548 +47,50 548 +7,51 548 +7,51 548 +391.7,12 549 +386.21,24 550 +21,24 550 +394.4,19 551 +395.10,34 552 +25,26 552 +28,29 552 +31,32 552 +10,34 552 +10,34 552 +10,34 552 +10,41 552 +396.5,23 553 +17,22 553 +17,18 553 +17,22 553 +5,23 553 +5,23 553 +397.8,18 554 +22,30 554 +398.6,20 555 +399.6,26 556 +400.6,10 557 +6,22 557 +401.6,10 558 +18,33 558 +6,33 558 +402.6,37 559 +17,29 559 +31,36 559 +6,37 559 +6,37 559 +403.6,16 560 +6,7 560 +14,15 560 +6,16 560 +6,16 560 +404.6,19 561 +405.6,11 562 +6,11 563 +406.6,11 564 +408.13,22 565 +5,29 565 +409.5,15 566 +5,15 567 +395.43,54 568 +43,54 568 +411.7,12 569 +412.13,14 570 +13,19 570 +5,19 570 +5,19 571 +413.5,16 572 +5,16 572 +417.5,11 573 +418.3,8 574 +421.1,13 575 +422.0,1 576 +429.1,27 577 +431.1,7 578 +432.1,26 579 +433.1,13 580 +434.4,17 581 +435.2,47 582 +21,34 582 +36,46 582 +2,47 582 +2,47 582 +436.4,17 583 +437.2,8 584 +438.1,8 585 +439.1,13 586 +440.1,17 587 +441.1,9 588 +442.1,11 589 +445.2,38 590 +17,23 590 +25,28 590 +30,37 590 +2,38 590 +2,38 590 +446.5,11 591 +447.3,8 592 +448.6,10 593 +12,15 594 +449.13,19 595 +3,19 595 +450.6,8 596 +451.9,11 597 +452.11,22 598 +11,22 597 +453.11,22 599 +454.9,14 600 +455.7,23 601 +18,22 601 +7,23 601 +7,23 601 +7,23 597 +456.12,21 602 +12,21 597 +457.12,17 603 +459.6,16 604 +460.4,10 605 +4,10 606 +462.4,10 607 +463.6,13 608 +464.4,12 609 +465.7,12 610 +466.4,12 611 +467.6,22 612 +28,37 612 +41,50 612 +56,65 612 +69,78 612 +468.7,13 613 +4,19 613 +4,19 614 +469.11,20 615 +470.4,19 616 +471.16,29 617 +22,24 617 +26,28 617 +16,29 617 +16,29 617 +5,6 617 +5,6 618 +472.7,10 619 +7,10 619 +4,11 619 +4,20 619 +473.7,16 620 +474.5,48 621 +10,47 621 +14,47 621 +26,29 621 +26,34 621 +26,34 622 +36,42 621 +44,46 621 +10,47 621 +10,47 623 +5,48 621 +475.5,10 624 +476.5,29 625 +477.5,19 626 +16,18 626 +5,19 626 +5,19 626 +479.4,11 627 +4,11 628 +480.12,21 629 +481.4,19 630 +448.17,20 631 +17,20 631 +484.1,13 632 +486.0,1 633 +490.17,27 634 +9,27 634 +1,36 634 +1,36 635 +491.1,35 636 +17,21 636 +23,34 636 +1,35 636 +1,35 636 +492.5,14 637 +493.2,29 638 +494.2,30 639 +13,15 639 +17,20 639 +22,29 639 +2,30 639 +2,30 639 +2,30 640 +496.0,1 641 +526.1,21 642 +527.8,34 643 +19,23 643 +25,26 643 +28,33 643 +8,34 643 +8,34 643 +1,34 643 +535.1,10 644 +536.5,11 645 +17,22 646 +13,22 646 +537.7,11 647 +538.6,13 648 +2,18 648 +539.5,14 649 +24,29 649 +23,33 649 +18,33 649 +540.6,15 650 +541.8,15 651 +4,23 651 +542.6,19 652 +12,13 652 +15,18 652 +6,19 652 +6,19 652 +6,23 652 +543.11,18 653 +4,18 653 +544.3,11 654 +536.24,27 655 +24,27 655 +547.8,10 656 +1,10 656 +552.1,34 657 +18,21 657 +23,33 657 +1,34 657 +1,34 657 +553.1,41 658 +19,28 658 +30,40 658 +1,41 658 +1,41 658 +554.4,15 659 +19,31 659 +555.9,11 660 +2,11 660 +561.8,9 661 +1,9 661 +566.1,17 662 +12,16 662 +1,17 662 +1,17 662 +567.1,16 663 +7,8 663 +10,15 663 +1,16 663 +1,16 663 +568.1,17 664 +12,16 664 +1,17 664 +1,17 664 +569.1,17 665 +7,8 665 +10,16 665 +1,17 665 +1,17 665 +570.1,11 666 +572.1,40 667 +12,17 667 +19,36 667 +38,39 667 +1,40 667 +1,40 667 +573.1,12 668 +575.1,37 669 +11,23 669 +25,36 669 +1,37 669 +1,37 669 +1,37 670 +576.0,1 671 +580.1,11 672 +581.1,45 673 +12,17 673 +19,41 673 +43,44 673 +1,45 673 +1,45 673 +582.1,12 674 +583.0,1 675 +591.5,11 676 +17,25 677 +13,25 677 +592.18,25 678 +14,30 678 +5,10 678 +5,30 678 +59,66 678 +59,71 678 +42,49 678 +38,54 678 +34,35 678 +34,55 678 +34,71 678 +34,71 679 +34,71 680 +593.11,18 681 +11,23 681 +25,32 681 +25,38 681 +3,39 681 +591.27,30 682 +27,30 682 +595.9,14 683 +16,17 683 +1,18 683 +613.4,20 684 +614.2,26 685 +8,9 685 +11,25 685 +2,26 685 +2,26 685 +615.4,20 686 +616.2,27 687 +8,9 687 +11,26 687 +2,27 687 +2,27 687 +617.2,27 688 +618.2,17 689 +620.0,1 690 +11:17.1,25 691 +18.1,10 692 +1,2 692 +1,10 692 +19.0,1 693 +23.1,29 694 +18,25 694 +27,28 694 +1,29 694 +1,29 695 +24.0,1 696 +30.2,15 697 +31.7,14 698 +7,14 698 +7,14 699 +33.3,8 700 +35.8,13 701 +37.4,13 702 +4,5 702 +4,13 702 +4,13 701 +38.8,13 703 +40.2,7 698 +2,7 704 +2,7 704 +43.1,10 705 +6,9 705 +1,10 705 +44.0,1 706 +48.4,10 707 +49.0,1 708 +12:88.1,37 709 +89.4,15 710 +90.2,57 711 +8,56 711 +2,57 711 +92.1,23 712 +18,19 712 +21,22 712 +1,23 712 +1,23 713 +93.1,14 714 +94.1,16 715 +95.1,16 716 +96.1,16 717 +97.1,25 718 +98.1,19 719 +12,18 719 +1,19 719 +1,19 719 +1,19 720 +99.1,10 721 +1,2 721 +1,10 721 +100.15,16 722 +18,19 722 +1,20 722 +101.1,13 723 +102.1,13 724 +103.1,17 725 +104.1,19 726 +105.0,1 727 +109.1,18 728 +1,2 728 +11,17 728 +1,18 728 +110.1,14 729 +12,13 729 +1,14 729 +111.1,17 730 +112.0,1 731 +118.10,14 732 +10,14 732 +117.9,15 732 +9,15 732 +9,15 732 +9,15 732 +119.7,14 733 +7,14 733 +7,14 734 +121.3,8 735 +123.8,13 736 +8,13 736 +8,13 736 +8,13 736 +8,13 736 +125.4,13 737 +4,5 737 +4,13 737 +4,13 736 +127.9,13 738 +129.10,14 739 +131.6,22 740 +6,22 739 +6,22 738 +134.10,14 741 +136.6,23 742 +6,23 741 +6,23 738 +139.10,14 743 +141.9,24 744 +142.7,23 745 +7,8 745 +17,22 745 +7,23 745 +143.6,20 746 +6,20 743 +145.9,27 747 +146.7,26 748 +7,8 748 +17,25 748 +7,26 748 +147.6,23 749 +6,23 743 +6,23 738 +6,23 736 +151.4,16 750 +4,16 736 +153.4,17 751 +15,16 751 +4,17 751 +4,17 736 +155.4,20 752 +156.4,20 753 +4,20 736 +158.4,20 754 +4,20 736 +159.8,13 755 +162.6,18 756 +6,18 757 +6,18 758 +163.4,12 759 +164.3,18 760 +165.3,20 761 +166.3,26 762 +16,17 762 +19,25 762 +3,26 762 +3,26 762 +167.9,15 763 +9,19 763 +168.4,21 764 +4,5 764 +15,20 764 +15,20 764 +4,21 764 +169.4,15 765 +4,15 765 +4,15 765 +172.6,25 766 +29,48 766 +52,67 766 +68,84 766 +52,84 766 +52,84 766 +173.4,16 767 +174.6,14 768 +175.7,22 769 +7,22 769 +176.5,24 770 +18,23 770 +5,24 770 +5,24 771 +178.5,31 772 +24,26 772 +27,29 772 +5,31 772 +179.4,16 773 +180.4,13 774 +4,13 774 +4,13 775 +181.13,28 776 +182.4,13 777 +4,13 777 +4,13 778 +4,13 733 +4,13 779 +4,13 732 +185.1,10 780 +6,9 780 +1,10 780 +186.0,1 781 +194.1,24 782 +195.6,13 783 +197.2,10 784 +2,10 783 +199.2,10 785 +200.2,15 786 +2,15 783 +202.2,10 787 +2,10 783 +204.4,16 788 +205.2,10 789 +206.2,15 790 +2,15 791 +208.2,10 792 +209.1,48 793 +11,13 793 +21,24 793 +26,27 793 +30,38 793 +40,44 793 +46,47 793 +1,48 793 +210.0,1 794 +217.1,12 795 +218.1,11 796 +219.10,28 797 +1,28 797 +220.1,14 798 +221.27,29 799 +31,33 799 +9,34 799 +1,34 799 +222.1,13 800 +223.6,11 801 +6,11 801 +6,11 801 +6,11 801 +225.2,19 802 +226.2,13 803 +227.2,19 804 +228.2,12 805 +229.2,14 806 +230.16,17 807 +18,19 807 +2,20 807 +231.2,19 808 +2,19 801 +234.2,20 809 +235.2,13 810 +236.2,19 811 +237.2,12 812 +238.2,14 813 +239.2,18 814 +240.16,17 815 +19,20 815 +2,21 815 +2,21 801 +242.2,17 816 +243.2,13 817 +244.2,19 818 +245.2,12 819 +246.2,14 820 +2,14 801 +248.4,23 821 +249.19,61 822 +32,33 822 +34,35 822 +38,39 822 +40,41 822 +45,46 822 +48,49 822 +51,52 822 +54,55 822 +57,60 822 +19,61 822 +19,61 822 +8,15 822 +8,15 823 +8,15 824 +250.2,18 825 +2,3 825 +11,17 825 +2,18 825 +251.2,61 826 +21,44 826 +46,52 826 +54,60 826 +2,61 826 +2,61 826 +252.19,80 827 +30,35 827 +37,43 827 +45,51 827 +53,61 827 +63,69 827 +71,79 827 +19,80 827 +19,80 827 +8,15 827 +8,15 828 +8,15 829 +253.2,20 830 +2,3 830 +14,19 830 +2,20 830 +2,20 831 +255.1,16 832 +14,15 832 +1,16 832 +256.1,27 833 +20,22 833 +23,25 833 +1,27 833 +257.1,19 834 +258.0,1 835 +262.1,13 836 +1,13 836 +263.0,1 837 +267.7,15 838 +7,19 838 +268.7,13 839 +7,13 839 +7,13 839 +7,13 839 +7,13 839 +270.3,25 840 +17,18 840 +20,24 840 +3,25 840 +3,25 840 +3,25 839 +272.3,25 841 +17,18 841 +20,24 841 +3,25 841 +3,25 841 +3,25 839 +274.3,25 842 +3,25 839 +3,25 839 +276.0,1 843 +281.6,8 844 +6,8 844 +6,8 844 +286.2,16 845 +381.0,1 846 +288.2,16 847 +381.0,1 846 +290.2,16 848 +381.0,1 846 +292.2,22 849 +293.2,32 850 +294.2,18 851 +381.0,1 846 +296.2,19 852 +297.2,18 853 +298.2,32 854 +381.0,1 846 +300.2,17 855 +381.0,1 846 +302.5,17 856 +303.6,18 857 +381.0,1 846 +305.6,18 858 +306.14,20 859 +4,24 859 +4,24 860 +308.4,16 861 +309.3,19 862 +381.0,1 846 +311.3,15 863 +381.0,1 846 +313.16,22 864 +5,22 864 +314.6,18 865 +381.0,1 846 +316.17,23 866 +17,27 866 +6,13 866 +6,27 866 +317.4,15 867 +4,15 868 +319.4,16 869 +320.3,14 870 +381.0,1 846 +322.3,15 871 +381.0,1 846 +324.16,22 872 +16,26 872 +5,12 872 +5,26 872 +325.6,19 873 +6,19 873 +326.4,16 874 +11,12 874 +14,15 874 +4,16 874 +381.0,1 846 +328.4,15 875 +381.0,1 846 +329.10,22 876 +330.3,20 877 +331.3,22 878 +381.0,1 846 +333.3,15 879 +381.0,1 846 +335.5,17 880 +336.6,19 881 +6,19 881 +337.4,17 882 +11,12 882 +14,16 882 +4,17 882 +381.0,1 846 +339.14,20 883 +4,24 883 +381.0,1 846 +340.10,22 884 +381.0,1 846 +343.3,15 885 +381.0,1 846 +345.2,13 886 +381.0,1 846 +347.10,16 887 +10,26 887 +2,30 887 +348.12,26 888 +16,19 888 +21,25 888 +12,26 888 +12,26 888 +2,70 888 +2,70 888 +2,70 889 +34,41 888 +42,49 888 +52,58 888 +60,66 888 +68,69 888 +2,70 888 +381.0,1 846 +351.2,16 890 +381.0,1 846 +353.2,19 891 +354.2,16 892 +355.16,17 893 +18,19 893 +2,20 893 +356.2,19 894 +357.2,14 895 +358.2,10 896 +8,9 896 +2,10 896 +381.0,1 846 +360.2,19 897 +361.2,16 898 +362.16,17 899 +18,19 899 +2,20 899 +363.2,19 900 +364.2,14 901 +381.0,1 846 +366.2,18 902 +367.2,14 903 +381.0,1 846 +369.2,19 904 +370.2,14 905 +381.0,1 846 +373.2,19 906 +381.0,1 846 +0,1 846 +0,1 846 +0,1 846 +386.4,12 907 +9,11 907 +4,12 907 +4,12 907 +4,12 907 +387.2,18 908 +388.2,12 909 +6,7 909 +9,11 909 +2,12 909 +389.2,8 910 +391.4,15 911 +19,30 911 +392.5,16 912 +393.3,25 913 +3,25 914 +394.10,21 915 +395.3,21 916 +3,21 917 +397.3,21 918 +398.9,17 919 +2,17 919 +399.2,8 920 +402.7,15 921 +1,15 921 +403.6,8 922 +6,8 922 +6,8 922 +6,8 922 +407.2,17 923 +408.2,8 924 +411.9,17 925 +2,17 925 +412.5,16 926 +413.3,18 927 +414.2,8 928 +417.10,22 929 +10,22 922 +418.10,20 930 +10,20 922 +419.10,22 931 +10,22 922 +420.10,23 932 +10,23 922 +421.10,21 933 +10,21 922 +422.10,24 934 +10,24 922 +423.10,21 935 +10,21 922 +424.10,22 936 +10,22 922 +427.10,22 937 +10,22 922 +428.10,20 938 +10,20 922 +429.10,22 939 +10,22 922 +430.10,23 940 +10,23 922 +431.10,21 941 +10,21 922 +432.10,24 942 +10,24 922 +433.10,21 943 +10,21 922 +434.10,22 944 +10,22 922 +437.10,25 945 +10,25 922 +438.10,26 946 +10,26 922 +441.10,25 947 +442.3,16 948 +3,16 922 +443.10,26 949 +444.3,16 950 +3,16 922 +447.10,25 951 +448.3,16 952 +3,16 922 +449.10,26 953 +450.3,16 954 +3,16 922 +453.10,25 955 +10,25 922 +454.10,26 956 +10,26 922 +458.2,26 957 +2,26 922 +462.5,16 958 +463.3,8 959 +464.2,26 960 +465.2,17 961 +2,17 922 +469.5,16 962 +470.3,8 963 +471.2,26 964 +472.2,17 965 +2,17 922 +476.5,16 966 +477.3,8 967 +478.2,25 968 +2,25 922 +480.4,11 969 +481.2,19 970 +482.2,14 971 +484.4,11 972 +485.2,19 973 +486.2,14 974 +487.2,15 975 +489.1,17 976 +490.0,1 977 +496.4,12 978 +9,11 978 +4,12 978 +4,12 978 +4,12 978 +497.2,18 979 +498.2,12 980 +6,7 980 +9,11 980 +2,12 980 +499.2,8 981 +501.6,8 982 +507.2,11 983 +508.2,19 984 +509.2,8 985 +510.10,18 986 +10,18 982 +511.10,18 987 +10,18 982 +512.10,18 988 +10,18 982 +513.10,18 989 +10,18 982 +514.10,20 990 +10,20 982 +515.10,20 991 +10,20 982 +516.10,20 992 +10,20 982 +517.10,20 993 +10,20 982 +518.10,18 994 +10,18 982 +519.10,18 995 +10,18 982 +520.10,18 996 +10,18 982 +521.10,18 997 +10,18 982 +522.10,18 998 +10,18 982 +523.10,18 999 +10,18 982 +524.10,18 1000 +10,18 982 +525.10,18 1001 +10,18 982 +526.10,18 1002 +10,18 982 +528.7,16 1003 +13,15 1003 +7,16 1003 +7,16 1003 +1,17 1003 +1,2 1003 +1,2 1003 +1,2 1004 +1,17 1003 +529.1,14 1005 +530.1,17 1006 +531.0,1 1007 +536.6,13 1008 +538.7,9 1009 +541.3,18 1010 +542.6,17 1011 +543.4,15 1012 +4,15 1009 +547.3,18 1013 +548.17,23 1014 +6,23 1014 +549.14,20 1015 +4,24 1015 +4,24 1009 +553.3,18 1016 +554.16,22 1017 +6,22 1017 +555.4,20 1018 +4,20 1009 +559.3,18 1019 +560.6,17 1020 +561.4,15 1021 +4,15 1009 +565.3,18 1022 +566.3,9 1023 +569.8,12 1024 +8,12 1024 +8,12 1024 +572.4,38 1025 +13,20 1025 +22,29 1025 +31,37 1025 +4,38 1025 +573.8,20 1026 +22,30 1027 +574.5,27 1028 +14,15 1028 +17,18 1028 +20,26 1028 +5,27 1028 +573.32,35 1029 +32,35 1029 +577.8,12 1030 +14,23 1031 +578.5,27 1032 +14,15 1032 +17,18 1032 +20,26 1032 +5,27 1032 +577.25,28 1033 +25,28 1033 +579.4,33 1034 +13,20 1034 +22,23 1034 +25,32 1034 +4,33 1034 +4,33 1024 +582.4,12 1035 +10,11 1035 +4,12 1035 +4,12 1024 +4,12 1009 +586.8,12 1036 +8,12 1036 +8,12 1036 +588.8,42 1037 +17,24 1037 +26,33 1037 +35,41 1037 +8,42 1037 +8,42 1036 +591.8,37 1038 +17,24 1038 +26,27 1038 +29,36 1038 +8,37 1038 +8,37 1036 +594.8,36 1039 +17,24 1039 +26,27 1039 +29,35 1039 +8,36 1039 +8,36 1036 +8,36 1009 +599.3,45 1040 +12,19 1040 +21,28 1040 +30,37 1040 +30,42 1040 +30,44 1040 +3,45 1040 +3,45 1009 +603.13,27 1041 +17,20 1041 +22,26 1041 +13,27 1041 +13,27 1041 +3,71 1041 +3,71 1041 +3,71 1042 +35,42 1041 +43,50 1041 +53,59 1041 +61,67 1041 +69,70 1041 +3,71 1041 +3,71 1009 +607.6,15 1043 +608.4,20 1044 +4,20 1009 +611.6,15 1045 +612.4,21 1046 +4,21 1009 +616.3,24 1047 +10,17 1047 +19,23 1047 +3,24 1047 +3,24 1009 +620.3,27 1048 +10,17 1048 +19,26 1048 +3,27 1048 +3,27 1009 +622.2,18 1049 +632.0,1 1050 +624.7,9 1051 +627.6,14 1052 +25,31 1052 +18,31 1052 +35,43 1052 +55,61 1052 +47,61 1052 +628.18,22 1053 +24,28 1053 +4,29 1053 +4,29 1051 +630.2,18 1054 +632.0,1 1050 +0,1 1050 +638.5,10 1055 +16,24 1056 +12,24 1056 +639.12,19 1057 +2,19 1057 +641.5,15 1058 +5,15 1058 +642.3,9 1059 +643.6,22 1060 +24,32 1061 +24,32 1062 +39,45 1063 +644.3,120 1064 +10,16 1064 +18,64 1064 +66,73 1064 +75,77 1064 +79,81 1064 +83,89 1064 +91,97 1064 +99,101 1064 +103,110 1064 +112,119 1064 +3,120 1064 +3,120 1064 +3,120 1065 +646.7,14 1066 +7,14 1066 +7,14 1066 +648.6,14 1067 +6,14 1067 +18,26 1067 +649.4,10 1068 +650.4,13 1069 +651.14,22 1070 +10,22 1070 +652.14,21 1071 +5,21 1071 +653.8,16 1072 +8,16 1072 +20,28 1072 +654.10,13 1073 +10,13 1073 +26,29 1073 +26,29 1073 +21,30 1073 +17,30 1073 +6,30 1073 +6,30 1074 +656.6,9 1075 +657.6,11 1076 +660.7,12 1077 +661.8,18 1078 +8,18 1078 +662.6,46 1079 +13,19 1079 +21,40 1079 +42,45 1079 +6,46 1079 +6,46 1079 +663.5,15 1080 +5,6 1080 +11,14 1080 +5,15 1080 +664.20,23 1081 +16,24 1081 +16,24 1082 +16,24 1083 +666.13,21 1084 +18,20 1084 +13,21 1084 +13,21 1084 +13,21 1084 +667.4,14 1085 +8,9 1085 +11,13 1085 +4,14 1085 +4,14 1086 +668.11,20 1087 +669.7,28 1088 +670.5,15 1089 +671.10,19 1090 +16,18 1090 +10,19 1090 +10,19 1090 +4,20 1090 +4,5 1090 +4,5 1090 +4,5 1091 +4,20 1090 +672.4,17 1092 +4,17 1066 +675.6,15 1093 +676.4,12 1094 +677.6,27 1095 +678.4,12 1096 +679.3,14 1097 +8,9 1097 +11,13 1097 +3,14 1097 +3,14 1066 +681.6,15 1098 +682.4,12 1099 +683.3,13 1100 +7,8 1100 +10,12 1100 +3,13 1100 +3,13 1066 +687.6,15 1101 +688.4,12 1102 +689.6,14 1103 +11,13 1103 +6,14 1103 +6,14 1103 +6,14 1103 +690.4,20 1104 +691.4,14 1105 +8,9 1105 +11,13 1105 +4,14 1105 +692.4,9 1106 +694.6,17 1107 +21,32 1107 +695.10,35 1108 +14,22 1108 +24,34 1108 +10,35 1108 +10,35 1108 +4,36 1108 +4,5 1108 +4,5 1108 +4,5 1109 +4,36 1108 +696.3,19 1110 +3,19 1066 +698.8,12 1111 +8,12 1111 +8,12 1111 +8,12 1111 +8,12 1111 +700.9,11 1112 +701.11,19 1113 +11,19 1112 +702.11,19 1114 +11,19 1112 +703.11,19 1115 +11,19 1112 +11,19 1111 +706.9,11 1116 +707.11,19 1117 +11,19 1116 +11,19 1111 +710.9,11 1118 +711.11,19 1119 +11,19 1118 +712.11,19 1120 +11,19 1118 +713.12,20 1121 +12,20 1118 +714.11,19 1122 +11,19 1118 +715.11,19 1123 +11,19 1118 +11,19 1111 +718.9,11 1124 +719.11,19 1125 +11,19 1124 +720.11,19 1126 +11,19 1124 +721.12,20 1127 +12,20 1124 +722.11,19 1128 +11,19 1124 +723.11,19 1129 +11,19 1124 +11,19 1111 +726.9,11 1130 +727.11,19 1131 +11,19 1130 +11,19 1111 +730.9,18 1132 +15,17 1132 +9,18 1132 +9,18 1132 +3,19 1132 +3,4 1132 +3,4 1132 +3,4 1133 +3,19 1132 +731.3,16 1134 +732.3,19 1135 +3,19 1066 +734.6,17 1136 +21,32 1136 +735.4,14 1137 +736.12,24 1138 +4,24 1138 +4,24 1139 +737.14,25 1140 +29,40 1140 +46,57 1140 +61,72 1140 +738.4,12 1141 +739.4,22 1142 +4,22 1143 +741.4,15 1144 +9,10 1144 +12,14 1144 +4,15 1144 +4,15 1066 +743.6,17 1145 +21,32 1145 +744.4,14 1146 +745.12,24 1147 +4,24 1147 +4,24 1148 +747.4,15 1149 +9,10 1149 +12,14 1149 +4,15 1149 +4,15 1066 +749.6,17 1150 +750.4,19 1151 +751.4,12 1152 +752.4,9 1153 +754.6,17 1154 +21,31 1154 +755.4,23 1155 +4,23 1156 +756.11,22 1157 +26,37 1157 +757.4,23 1158 +4,23 1159 +759.4,13 1160 +760.3,17 1161 +3,17 1066 +762.6,17 1162 +21,31 1162 +763.4,23 1163 +4,23 1164 +764.11,22 1165 +26,37 1165 +765.4,23 1166 +766.11,18 1167 +21,25 1167 +4,25 1167 +767.4,12 1168 +4,12 1169 +769.4,13 1170 +771.6,15 1171 +26,32 1171 +19,32 1171 +36,44 1171 +56,62 1171 +48,62 1171 +772.7,16 1172 +20,31 1172 +773.5,22 1173 +774.5,24 1174 +776.18,22 1175 +24,28 1175 +4,29 1175 +777.4,17 1176 +778.4,18 1177 +779.4,21 1178 +781.3,19 1179 +3,19 1066 +784.3,19 1180 +785.6,14 1181 +11,13 1181 +6,14 1181 +6,14 1181 +6,14 1181 +786.4,14 1182 +8,9 1182 +11,13 1182 +4,14 1182 +4,14 1066 +789.6,16 1183 +790.4,20 1184 +4,20 1066 +795.6,15 1185 +796.4,12 1186 +797.6,14 1187 +11,13 1187 +6,14 1187 +6,14 1187 +6,14 1187 +798.4,20 1188 +799.4,14 1189 +8,9 1189 +11,13 1189 +4,14 1189 +800.4,9 1190 +802.3,9 1191 +803.6,15 1192 +804.7,18 1193 +22,33 1193 +805.5,10 1194 +807.7,16 1195 +20,31 1195 +35,46 1195 +808.5,21 1196 +809.5,10 1197 +811.3,19 1198 +812.9,19 1199 +15,18 1199 +9,19 1199 +9,19 1199 +3,20 1199 +3,4 1199 +3,4 1199 +3,4 1200 +3,20 1199 +3,20 1066 +817.6,14 1201 +11,13 1201 +6,14 1201 +6,14 1201 +6,14 1201 +818.4,20 1202 +819.4,14 1203 +8,9 1203 +11,13 1203 +4,14 1203 +820.4,9 1204 +822.6,17 1205 +21,32 1205 +823.5,10 1206 +824.6,17 1207 +21,32 1207 +825.4,20 1208 +826.4,9 1209 +828.3,19 1210 +829.9,19 1211 +15,18 1211 +9,19 1211 +9,19 1211 +3,20 1211 +3,4 1211 +3,4 1211 +3,4 1212 +3,20 1211 +3,20 1066 +834.3,837.4 1213 +835.5,38 1213 +5,38 1213 +19,22 1213 +19,22 1213 +24,29 1213 +24,29 1213 +31,36 1213 +31,36 1213 +836.5,38 1213 +5,38 1213 +19,22 1213 +19,22 1213 +24,29 1213 +24,29 1213 +31,36 1213 +31,36 1213 +838.6,15 1214 +839.11,19 1215 +4,19 1215 +4,19 1216 +840.4,9 1217 +842.12,20 1218 +12,26 1218 +6,26 1218 +843.4,10 1219 +4,10 1220 +845.4,12 1221 +846.12,20 1222 +12,26 1222 +6,26 1222 +847.4,10 1223 +4,10 1224 +849.4,12 1225 +850.6,15 1226 +19,28 1226 +851.4,20 1227 +4,20 1228 +4,20 1066 +853.6,14 1229 +854.4,10 1230 +855.7,16 1231 +856.5,21 1232 +857.5,10 1233 +859.13,21 1234 +860.4,18 1235 +4,18 1236 +861.11,22 1237 +862.4,12 1238 +4,12 1066 +869.8,12 1239 +871.7,18 1240 +22,33 1240 +872.5,14 1241 +5,14 1239 +874.9,11 1242 +876.5,20 1243 +18,19 1243 +5,20 1243 +877.5,20 1244 +5,20 1244 +5,20 1242 +879.5,20 1245 +18,19 1245 +5,20 1245 +880.5,20 1246 +5,20 1246 +5,20 1242 +882.4,20 1247 +4,20 1239 +884.4,20 1248 +4,20 1239 +4,20 1066 +638.26,29 1249 +26,29 1249 +888.9,17 1250 +5,17 1250 +889.9,17 1251 +9,13 1251 +9,17 1251 +2,17 1251 +891.9,12 1252 +2,12 1252 +899.5,10 1253 +16,24 1254 +12,24 1254 +900.12,19 1255 +2,19 1255 +902.5,15 1256 +5,15 1256 +903.3,9 1257 +904.6,22 1258 +24,32 1259 +24,32 1260 +39,45 1261 +905.3,122 1262 +10,16 1262 +18,64 1262 +66,73 1262 +75,77 1262 +79,81 1262 +83,89 1262 +91,99 1262 +101,103 1262 +105,112 1262 +114,121 1262 +3,122 1262 +3,122 1262 +3,122 1263 +907.7,14 1264 +7,14 1264 +7,14 1264 +909.6,14 1265 +6,14 1265 +18,26 1265 +910.4,10 1266 +911.4,13 1267 +912.14,22 1268 +10,22 1268 +913.14,21 1269 +5,21 1269 +914.8,16 1270 +8,16 1270 +20,28 1270 +915.10,13 1271 +10,13 1271 +26,29 1271 +26,29 1271 +21,30 1271 +17,30 1271 +6,30 1271 +6,30 1272 +917.6,9 1273 +918.6,11 1274 +921.7,12 1275 +922.8,18 1276 +8,18 1276 +923.6,46 1277 +13,19 1277 +21,40 1277 +42,45 1277 +6,46 1277 +6,46 1277 +924.5,15 1278 +5,6 1278 +11,14 1278 +5,15 1278 +925.20,23 1279 +16,24 1279 +16,24 1280 +16,24 1281 +927.13,21 1282 +18,20 1282 +13,21 1282 +13,21 1282 +13,21 1282 +928.4,14 1283 +8,9 1283 +11,13 1283 +4,14 1283 +4,14 1284 +929.11,20 1285 +930.7,28 1286 +931.5,15 1287 +932.10,19 1288 +16,18 1288 +10,19 1288 +10,19 1288 +4,20 1288 +4,5 1288 +4,5 1288 +4,5 1289 +4,20 1288 +933.4,17 1290 +4,17 1264 +936.6,15 1291 +937.4,12 1292 +938.3,13 1293 +7,8 1293 +10,12 1293 +3,13 1293 +3,13 1264 +940.6,17 1294 +21,32 1294 +941.4,14 1295 +942.12,24 1296 +4,24 1296 +4,24 1297 +943.13,22 1298 +944.4,14 1299 +4,14 1300 +946.4,15 1301 +9,10 1301 +12,14 1301 +4,15 1301 +947.6,21 1302 +948.4,9 1303 +950.6,17 1304 +21,32 1304 +951.4,14 1305 +952.12,24 1306 +4,24 1306 +4,24 1307 +954.4,15 1308 +9,10 1308 +12,14 1308 +4,15 1308 +4,15 1264 +956.6,17 1309 +21,31 1309 +957.4,23 1310 +4,23 1311 +958.11,22 1312 +26,37 1312 +959.4,23 1313 +4,23 1314 +961.4,13 1315 +962.3,17 1316 +3,17 1264 +964.6,17 1317 +21,31 1317 +965.4,23 1318 +4,23 1319 +966.11,22 1320 +26,37 1320 +967.4,23 1321 +968.11,18 1322 +21,25 1322 +4,25 1322 +969.4,12 1323 +4,12 1324 +971.4,13 1325 +973.6,15 1326 +26,32 1326 +19,32 1326 +36,44 1326 +56,62 1326 +48,62 1326 +974.7,16 1327 +20,31 1327 +975.5,22 1328 +976.5,24 1329 +978.18,22 1330 +24,28 1330 +4,29 1330 +979.4,17 1331 +980.4,18 1332 +981.4,21 1333 +983.3,19 1334 +3,19 1264 +988.6,14 1335 +11,13 1335 +6,14 1335 +6,14 1335 +6,14 1335 +989.4,20 1336 +990.4,14 1337 +8,9 1337 +11,13 1337 +4,14 1337 +991.4,9 1338 +993.6,17 1339 +21,32 1339 +994.5,10 1340 +995.6,17 1341 +21,32 1341 +996.4,20 1342 +997.4,9 1343 +999.3,19 1344 +1000.9,19 1345 +15,18 1345 +9,19 1345 +9,19 1345 +3,20 1345 +3,4 1345 +3,4 1345 +3,4 1346 +3,20 1345 +3,20 1264 +899.26,29 1347 +26,29 1347 +1003.9,17 1348 +5,17 1348 +1004.9,17 1349 +9,13 1349 +9,17 1349 +2,17 1349 +1006.9,12 1350 +2,12 1350 +1013.6,8 1351 +6,8 1351 +6,8 1351 +1015.2,16 1352 +1060.0,1 1353 +0,1 1353 +0,1 1353 +1023.5,16 1354 +1024.3,15 1355 +1060.0,1 1353 +1026.2,14 1356 +1027.15,21 1357 +5,21 1357 +1028.3,19 1358 +1060.0,1 1353 +1030.16,22 1359 +16,26 1359 +5,12 1359 +5,26 1359 +1031.6,19 1360 +6,19 1360 +1032.4,16 1361 +11,12 1361 +14,15 1361 +4,16 1361 +1060.0,1 1353 +1034.4,15 1362 +1060.0,1 1353 +1035.10,22 1363 +1036.6,14 1364 +1037.4,21 1365 +1038.4,23 1366 +1060.0,1 1353 +1041.3,15 1367 +1060.0,1 1353 +1043.2,13 1368 +1060.0,1 1353 +1045.12,25 1369 +16,21 1369 +23,24 1369 +12,25 1369 +12,25 1369 +2,69 1369 +2,69 1369 +2,69 1370 +33,40 1369 +41,48 1369 +51,57 1369 +59,65 1369 +67,68 1369 +2,69 1369 +1060.0,1 1353 +0,1 1353 +0,1 1353 +0,1 1353 +0,1 1353 +1058.2,16 1371 +1060.0,1 1353 +0,1 1353 +1065.4,12 1372 +9,11 1372 +4,12 1372 +4,12 1372 +4,12 1372 +1066.2,18 1373 +1067.2,12 1374 +6,7 1374 +9,11 1374 +2,12 1374 +1068.2,8 1375 +1070.6,8 1376 +1072.9,17 1377 +2,17 1377 +1073.5,16 1378 +1074.3,18 1379 +1075.2,8 1380 +1079.5,16 1381 +1080.3,14 1382 +1081.16,22 1383 +16,26 1383 +5,12 1383 +5,26 1383 +1082.6,19 1384 +6,19 1384 +1083.4,16 1385 +11,12 1385 +14,15 1385 +4,16 1385 +4,16 1386 +1085.4,15 1387 +4,15 1388 +1086.10,22 1389 +1087.3,20 1390 +1088.3,22 1391 +3,22 1392 +1090.3,15 1393 +3,15 1376 +1092.5,17 1394 +1093.6,19 1395 +6,19 1395 +1094.4,17 1396 +11,12 1396 +14,16 1396 +4,17 1396 +4,17 1397 +1096.14,20 1398 +4,24 1398 +4,24 1399 +1097.10,22 1400 +1098.3,8 1401 +1100.3,15 1402 +3,15 1376 +1102.1,17 1403 +1103.0,1 1404 +1109.6,13 1405 +1111.7,9 1406 +1114.6,15 1407 +1115.4,12 1408 +1116.3,18 1409 +1117.6,17 1410 +1118.4,15 1411 +4,15 1406 +1122.6,15 1412 +1123.4,12 1413 +1124.3,18 1414 +1125.17,23 1415 +6,23 1415 +1126.14,20 1416 +4,24 1416 +4,24 1406 +1130.6,15 1417 +1131.4,12 1418 +1132.3,18 1419 +1133.16,22 1420 +6,22 1420 +1134.4,20 1421 +4,20 1406 +1138.6,15 1422 +1139.4,12 1423 +1140.3,18 1424 +1141.6,17 1425 +1142.4,15 1426 +4,15 1406 +1146.3,18 1427 +1147.3,9 1428 +1150.8,12 1429 +8,12 1429 +8,12 1429 +1153.4,38 1430 +13,20 1430 +22,29 1430 +31,37 1430 +4,38 1430 +1154.8,20 1431 +22,30 1432 +1155.5,27 1433 +14,15 1433 +17,18 1433 +20,26 1433 +5,27 1433 +1154.32,35 1434 +32,35 1434 +1158.8,12 1435 +14,23 1436 +1159.5,27 1437 +14,15 1437 +17,18 1437 +20,26 1437 +5,27 1437 +1158.25,28 1438 +25,28 1438 +1160.4,33 1439 +13,20 1439 +22,23 1439 +25,32 1439 +4,33 1439 +4,33 1429 +1163.4,12 1440 +10,11 1440 +4,12 1440 +4,12 1429 +4,12 1406 +1167.8,12 1441 +8,12 1441 +8,12 1441 +1169.8,42 1442 +17,24 1442 +26,33 1442 +35,41 1442 +8,42 1442 +8,42 1441 +1172.8,37 1443 +17,24 1443 +26,27 1443 +29,36 1443 +8,37 1443 +8,37 1441 +1175.8,36 1444 +17,24 1444 +26,27 1444 +29,35 1444 +8,36 1444 +8,36 1441 +8,36 1406 +1180.13,27 1445 +17,20 1445 +22,26 1445 +13,27 1445 +13,27 1445 +3,71 1445 +3,71 1445 +3,71 1446 +35,42 1445 +43,50 1445 +53,59 1445 +61,67 1445 +69,70 1445 +3,71 1445 +3,71 1406 +1184.6,15 1447 +1185.4,20 1448 +4,20 1406 +1188.6,15 1449 +1189.4,21 1450 +4,21 1406 +1193.3,27 1451 +10,17 1451 +19,26 1451 +3,27 1451 +1194.3,14 1452 +3,14 1406 +1198.3,24 1453 +10,17 1453 +19,23 1453 +3,24 1453 +1199.3,14 1454 +3,14 1406 +1203.3,45 1455 +12,19 1455 +21,28 1455 +30,37 1455 +30,42 1455 +30,44 1455 +3,45 1455 +3,45 1406 +1207.6,17 1456 +1208.4,21 1457 +1209.4,21 1458 +4,5 1458 +14,20 1458 +4,21 1458 +4,21 1406 +1214.8,12 1459 +1215.9,45 1460 +9,45 1459 +1216.9,24 1461 +9,24 1459 +1217.9,24 1462 +9,24 1459 +1218.9,24 1463 +9,24 1459 +1219.9,24 1464 +9,24 1459 +1220.9,25 1465 +9,25 1459 +1221.9,25 1466 +9,25 1459 +1222.9,25 1467 +9,25 1459 +1223.9,25 1468 +9,25 1459 +9,25 1406 +1227.6,15 1469 +1228.4,12 1470 +1229.6,15 1471 +1230.4,12 1472 +1231.6,14 1473 +25,31 1473 +18,31 1473 +35,43 1473 +55,61 1473 +47,61 1473 +1232.18,22 1474 +24,28 1474 +4,29 1474 +4,29 1406 +1234.2,18 1475 +1248.0,1 1476 +1236.7,9 1477 +1239.6,15 1478 +1240.4,12 1479 +1241.6,15 1480 +1242.4,12 1481 +1243.6,14 1482 +25,31 1482 +18,31 1482 +35,43 1482 +55,61 1482 +47,61 1482 +1244.18,22 1483 +24,28 1483 +4,29 1483 +4,29 1477 +1246.2,18 1484 +1248.0,1 1476 +0,1 1476 +1260.7,21 1485 +7,21 1485 +7,25 1485 +1261.7,13 1486 +7,23 1486 +2,27 1486 +1262.5,19 1487 +5,19 1487 +1263.6,11 1488 +1264.4,10 1489 +1266.5,10 1490 +1267.3,8 1491 +1268.5,14 1492 +1269.7,11 1493 +13,16 1494 +1270.7,13 1495 +7,20 1495 +1271.5,10 1496 +1269.18,21 1497 +18,21 1497 +1272.6,11 1498 +1273.4,61 1499 +14,17 1499 +14,22 1499 +24,29 1499 +31,37 1499 +39,45 1499 +47,60 1499 +4,61 1499 +1274.4,16 1500 +11,12 1500 +14,15 1500 +4,16 1500 +1276.6,11 1501 +1277.7,17 1502 +7,17 1502 +1278.5,11 1503 +1279.8,24 1504 +26,34 1505 +26,34 1506 +41,47 1507 +1280.5,51 1508 +12,18 1508 +20,32 1508 +34,46 1508 +48,50 1508 +5,51 1508 +5,51 1508 +5,51 1509 +1282.14,27 1510 +20,26 1510 +14,27 1510 +14,27 1510 +4,72 1510 +4,72 1510 +4,72 1511 +29,34 1510 +36,42 1510 +44,56 1510 +58,71 1510 +4,72 1510 +1283.4,16 1512 +11,12 1512 +14,15 1512 +4,16 1512 +1284.4,17 1513 +1287.9,15 1514 +1289.5,29 1515 +5,29 1514 +1291.5,23 1516 +5,23 1514 +1294.10,13 1517 +6,13 1517 +1295.4,63 1518 +18,21 1518 +14,17 1518 +14,24 1518 +26,31 1518 +33,39 1518 +41,47 1518 +49,62 1518 +4,63 1518 +1296.4,22 1519 +11,12 1519 +16,21 1519 +14,21 1519 +4,22 1519 +4,22 1520 +1299.3,60 1521 +13,16 1521 +13,21 1521 +23,28 1521 +30,36 1521 +38,44 1521 +46,59 1521 +3,60 1521 +1300.3,15 1522 +10,11 1522 +13,14 1522 +3,15 1522 +1302.9,16 1523 +5,16 1523 +1303.3,16 1524 +3,16 1524 +3,16 1525 +1305.3,12 1526 +3,12 1526 +1309.0,1 1527 +1315.4,18 1528 +4,18 1528 +1316.13,16 1529 +2,16 1529 +2,16 1530 +1318.2,14 1531 +1319.14,20 1532 +4,20 1532 +1320.5,17 1533 +1321.3,19 1534 +1340.0,1 1535 +1323.3,14 1536 +1324.17,23 1537 +17,27 1537 +6,13 1537 +6,27 1537 +31,44 1537 +31,44 1537 +1325.7,21 1538 +7,21 1538 +1326.5,17 1539 +12,13 1539 +15,16 1539 +5,17 1539 +1340.0,1 1535 +1328.5,17 1540 +12,13 1540 +15,16 1540 +5,17 1540 +1329.5,33 1541 +14,21 1541 +23,24 1541 +26,32 1541 +5,33 1541 +1340.0,1 1535 +1332.7,21 1542 +7,21 1542 +1333.5,17 1543 +5,17 1544 +1335.5,17 1545 +1336.18,24 1546 +7,24 1546 +1337.17,23 1547 +16,26 1547 +5,26 1547 +1340.0,1 1535 +1346.11,35 1548 +15,20 1548 +22,32 1548 +22,34 1548 +11,35 1548 +11,35 1548 +1,77 1548 +1,77 1548 +1,77 1549 +43,48 1548 +49,50 1548 +53,64 1548 +66,73 1548 +75,76 1548 +1,77 1548 +1348.0,1 1550 +1352.5,9 1551 +11,19 1552 +1353.2,24 1553 +11,12 1553 +14,15 1553 +17,23 1553 +2,24 1553 +1352.21,24 1554 +21,24 1554 +1354.0,1 1555 +1359.1,16 1556 +1,16 1556 +1360.0,1 1557 +1365.1,30 1558 +14,21 1558 +23,29 1558 +1,30 1558 +1366.1,16 1559 +1,16 1559 +1367.0,1 1560 +1373.6,12 1561 +6,12 1561 +6,12 1561 +6,12 1561 +6,12 1561 +1375.9,25 1562 +17,18 1562 +20,24 1562 +9,25 1562 +9,25 1562 +2,25 1562 +1377.9,25 1563 +17,18 1563 +20,24 1563 +9,25 1563 +9,25 1563 +2,25 1563 +1379.9,25 1564 +9,25 1564 +20,24 1564 +2,25 1564 +1381.8,11 1565 +1,11 1565 +1387.1,33 1566 +1388.1,13 1567 +1390.1,8 1568 +1391.5,9 1569 +13,21 1570 +11,21 1570 +1392.12,19 1571 +2,19 1571 +1393.7,15 1572 +7,15 1572 +7,15 1572 +1395.6,15 1573 +1396.4,20 1574 +1397.4,15 1575 +1398.7,13 1576 +1399.5,33 1577 +18,20 1577 +22,26 1577 +22,32 1577 +5,33 1577 +5,33 1577 +1400.4,12 1578 +4,12 1572 +1403.3,11 1579 +1404.3,14 1580 +1405.6,15 1581 +1406.4,21 1582 +1407.4,32 1583 +1408.4,14 1584 +1412.4,14 1585 +4,14 1586 +1413.13,22 1587 +1414.4,46 1588 +17,19 1588 +21,45 1588 +35,43 1588 +35,43 1588 +4,46 1588 +4,46 1588 +1415.4,20 1589 +4,20 1590 +1418.4,55 1591 +17,19 1591 +21,54 1591 +35,43 1591 +35,43 1591 +45,52 1591 +45,52 1591 +4,55 1591 +4,55 1591 +1419.4,22 1592 +4,22 1572 +1422.3,11 1593 +1423.3,14 1594 +1424.6,17 1595 +21,32 1595 +1425.8,18 1596 +22,32 1596 +37,46 1596 +1426.5,15 1597 +1427.16,28 1598 +16,32 1598 +4,41 1598 +1428.12,24 1599 +12,20 1599 +12,24 1599 +4,24 1599 +4,24 1600 +1429.6,11 1601 +6,15 1601 +4,16 1601 +4,26 1601 +1430.4,16 1602 +4,16 1603 +4,16 1604 +1432.4,14 1605 +1433.10,12 1606 +1434.4,7 1606 +1435.4,7 1606 +1436.4,7 1606 +1437.4,7 1606 +1438.4,7 1606 +1439.4,7 1606 +1440.4,7 1606 +1441.4,7 1606 +1442.4,7 1606 +1443.4,7 1606 +1444.4,7 1606 +1445.4,7 1606 +1446.4,7 1606 +1448.5,14 1607 +5,14 1606 +1450.7,13 1608 +1451.5,14 1609 +1452.7,17 1610 +7,17 1610 +1453.5,69 1611 +12,18 1611 +20,40 1611 +42,47 1611 +49,64 1611 +66,68 1611 +5,69 1611 +5,69 1611 +1454.7,12 1612 +1455.5,57 1613 +18,20 1613 +22,56 1613 +36,44 1613 +36,44 1613 +46,54 1613 +46,54 1613 +5,57 1613 +5,57 1613 +1456.5,31 1614 +18,20 1614 +22,30 1614 +5,31 1614 +5,31 1614 +1457.5,47 1615 +18,20 1615 +22,45 1615 +36,43 1615 +36,43 1615 +5,47 1615 +5,47 1615 +1459.4,22 1616 +4,22 1572 +1461.2,8 1572 +1391.23,26 1617 +23,26 1617 +1464.4,11 1618 +1465.5,11 1619 +1466.3,31 1620 +16,18 1620 +20,24 1620 +20,30 1620 +3,31 1620 +3,31 1620 +1467.9,11 1621 +2,11 1621 +1469.8,28 1622 +22,26 1622 +22,26 1622 +1,28 1622 +1475.1,33 1623 +1476.1,13 1624 +1478.1,8 1625 +1479.5,9 1626 +13,21 1627 +11,21 1627 +1480.12,19 1628 +2,19 1628 +1481.7,15 1629 +7,15 1629 +7,15 1629 +1483.8,10 1630 +1485.4,20 1631 +1486.4,15 1632 +1487.7,13 1633 +1488.5,33 1634 +18,20 1634 +22,26 1634 +22,32 1634 +5,33 1634 +5,33 1634 +1489.4,12 1635 +4,12 1630 +1491.4,20 1636 +1492.4,15 1637 +1493.7,13 1638 +1494.5,33 1639 +18,20 1639 +22,26 1639 +22,32 1639 +5,33 1639 +5,33 1639 +1495.4,12 1640 +4,12 1630 +4,12 1629 +1498.3,11 1641 +1499.3,14 1642 +1500.6,15 1643 +1501.4,21 1644 +1502.4,32 1645 +1503.4,14 1646 +4,14 1647 +1504.13,22 1648 +1505.4,46 1649 +17,19 1649 +21,45 1649 +35,43 1649 +35,43 1649 +4,46 1649 +4,46 1649 +1506.4,20 1650 +4,20 1651 +1509.4,55 1652 +17,19 1652 +21,54 1652 +35,43 1652 +35,43 1652 +45,52 1652 +45,52 1652 +4,55 1652 +4,55 1652 +1510.4,22 1653 +4,22 1629 +1513.3,11 1654 +1514.3,14 1655 +1515.6,15 1656 +1516.4,46 1657 +17,19 1657 +21,45 1657 +35,43 1657 +35,43 1657 +4,46 1657 +4,46 1657 +1517.4,20 1658 +4,20 1659 +1518.13,22 1660 +1519.4,46 1661 +17,19 1661 +21,45 1661 +35,43 1661 +35,43 1661 +4,46 1661 +4,46 1661 +1520.4,20 1662 +4,20 1663 +1522.7,18 1664 +22,33 1664 +1523.5,57 1665 +18,20 1665 +22,56 1665 +36,44 1665 +36,44 1665 +47,54 1665 +47,54 1665 +5,57 1665 +5,57 1665 +1525.4,22 1666 +4,22 1629 +1528.3,11 1667 +1529.3,14 1668 +1530.6,17 1669 +21,32 1669 +1531.8,18 1670 +22,32 1670 +37,46 1670 +50,59 1670 +1532.5,15 1671 +1533.16,28 1672 +16,32 1672 +4,41 1672 +1534.12,24 1673 +12,20 1673 +12,24 1673 +4,24 1673 +4,24 1674 +1535.6,11 1675 +6,15 1675 +4,16 1675 +4,26 1675 +1536.4,16 1676 +4,16 1677 +4,16 1678 +1538.4,14 1679 +1539.10,12 1680 +1540.4,7 1680 +1541.4,7 1680 +1542.4,7 1680 +1543.4,7 1680 +1544.4,7 1680 +1545.4,7 1680 +1546.4,7 1680 +1547.4,7 1680 +1548.4,7 1680 +1549.4,7 1680 +1550.4,7 1680 +1551.4,7 1680 +1552.4,7 1680 +1553.4,7 1680 +1555.5,22 1681 +1556.8,14 1682 +27,30 1682 +18,31 1682 +18,43 1682 +1557.6,16 1683 +6,16 1680 +1559.5,14 1684 +5,14 1680 +1561.7,13 1685 +1562.5,14 1686 +1563.7,17 1687 +7,17 1687 +1564.5,69 1688 +12,18 1688 +20,40 1688 +42,47 1688 +49,64 1688 +66,68 1688 +5,69 1688 +5,69 1688 +1565.7,12 1689 +1566.5,57 1690 +18,20 1690 +22,56 1690 +36,44 1690 +36,44 1690 +46,54 1690 +46,54 1690 +5,57 1690 +5,57 1690 +1567.5,31 1691 +18,20 1691 +22,30 1691 +5,31 1691 +5,31 1691 +1568.5,47 1692 +18,20 1692 +22,45 1692 +36,43 1692 +36,43 1692 +5,47 1692 +5,47 1692 +1570.4,22 1693 +4,22 1629 +1572.2,8 1629 +1479.23,26 1694 +23,26 1694 +1575.4,11 1695 +1576.5,11 1696 +1577.3,31 1697 +16,18 1697 +20,24 1697 +20,30 1697 +3,31 1697 +3,31 1697 +1578.9,11 1698 +2,11 1698 +1580.8,28 1699 +22,26 1699 +22,26 1699 +1,28 1699 +1592.1,12 1700 +1593.14,17 1701 +1,35 1701 +1594.10,16 1702 +10,12 1702 +10,16 1702 +1,16 1702 +1,16 1703 +1595.1,6 1704 +1,10 1704 +1596.8,10 1705 +1,10 1705 +1602.24,30 1706 +24,33 1706 +17,53 1706 +44,52 1706 +44,52 1706 +44,52 1706 +44,52 1706 +44,52 1706 +44,52 1706 +1,53 1706 +1,53 1707 +1603.1,13 1708 +1604.8,14 1709 +8,18 1709 +4,18 1709 +1605.6,12 1710 +2,16 1710 +1606.1,46 1711 +11,16 1711 +24,25 1711 +27,28 1711 +31,39 1711 +41,42 1711 +44,45 1711 +1,46 1711 +1607.4,14 1712 +1608.2,62 1713 +12,15 1713 +12,20 1713 +28,29 1713 +31,32 1713 +35,43 1713 +45,58 1713 +60,61 1713 +2,62 1713 +1609.1,16 1714 +1,16 1714 +1610.0,1 1715 +0:135.19,22 1716 +137.1,25 1717 +138.1,22 1718 +139.1,40 1719 +140.1,17 1720 +1,17 1720 +141.1,28 1721 +142.1,24 1722 +22,23 1722 +1,24 1722 +1,24 1722 +143.1,48 1723 +18,42 1723 +44,47 1723 +1,48 1723 +1,48 1723 +145.1,26 1724 +146.1,16 1725 +11,15 1725 +1,16 1725 +147.1,36 1726 +15,35 1726 +1,36 1726 +148.7,24 1727 +7,24 1727 +7,24 1727 +7,24 1727 +7,29 1727 +149.7,8 1728 +151.3,18 1729 +3,18 1729 +3,18 1729 +152.7,13 1730 +19,24 1731 +15,24 1731 +153.8,12 1732 +154.11,20 1733 +7,20 1733 +155.5,13 1734 +5,18 1734 +152.26,29 1735 +26,29 1735 +158.3,15 1736 +3,15 1736 +3,15 1728 +161.1,19 1737 +1,19 1737 +1,19 1737 +162.4,12 1738 +4,16 1738 +163.2,19 1739 +164.2,16 1740 +167.4,15 1741 +168.2,14 1742 +2,14 1742 +169.1,10 1743 +174.12,33 1744 +1,33 1744 +175.4,18 1745 +176.2,39 1746 +177.17,44 1747 +31,38 1747 +40,43 1747 +17,44 1747 +17,44 1747 +7,12 1747 +7,12 1748 +178.4,13 1749 +4,18 1749 +179.2,18 1750 +180.21,24 1751 +2,24 1751 +2,24 1751 +2,24 1752 +182.4,12 1753 +4,23 1753 +4,23 1754 +183.2,18 1755 +184.2,18 1756 +185.5,17 1757 +186.3,21 1758 +187.3,19 1759 +188.6,18 1760 +189.4,22 1761 +191.5,19 1762 +192.3,16 1763 +193.5,19 1764 +194.3,16 1765 +3,16 1766 +196.2,19 1767 +197.2,19 1768 +200.1,17 1769 +201.1,13 1770 +202.1,14 1771 +203.1,15 1772 +204.1,15 1773 +205.1,210.2 1774 +206.2,9 1774 +13,16 1774 +207.2,8 1774 +12,15 1774 +208.2,7 1774 +12,15 1774 +209.2,9 1774 +13,16 1774 +212.1,43 1775 +26,38 1775 +40,42 1775 +1,43 1775 +1,43 1775 +213.1,26 1776 +8,16 1776 +18,25 1776 +1,26 1776 +215.1,26 1777 +1,2 1777 +8,16 1777 +18,25 1777 +1,26 1777 +216.1,17 1778 +1,2 1778 +8,16 1778 +1,17 1778 +217.1,34 1779 +1,2 1779 +8,15 1779 +17,24 1779 +26,33 1779 +1,34 1779 +218.1,9 1780 +1,2 1780 +1,9 1780 +219.6,13 1781 +221.2,62 1782 +2,3 1782 +9,13 1782 +21,22 1782 +23,24 1782 +28,31 1782 +32,35 1782 +45,46 1782 +47,48 1782 +52,55 1782 +56,59 1782 +2,62 1782 +2,62 1781 +223.2,63 1783 +2,3 1783 +9,13 1783 +21,22 1783 +23,24 1783 +28,31 1783 +32,35 1783 +45,46 1783 +47,49 1783 +53,56 1783 +57,60 1783 +2,63 1783 +2,63 1781 +226.1,20 1784 +227.1,14 1785 +7,8 1785 +1,14 1785 +228.1,14 1786 +7,8 1786 +1,14 1786 +229.1,14 1787 +7,8 1787 +1,14 1787 +230.1,14 1788 +7,8 1788 +1,14 1788 +231.1,18 1789 +7,8 1789 +13,17 1789 +1,18 1789 +232.1,8 1790 +235.1,9 1791 +1,2 1791 +1,9 1791 +236.1,9 1792 +1,2 1792 +1,9 1792 +237.1,9 1793 +1,2 1793 +1,9 1793 +238.1,9 1794 +1,2 1794 +1,9 1794 +239.1,9 1795 +1,2 1795 +1,9 1795 +240.0,1 1796 +418.1,27 1797 +8,16 1797 +18,26 1797 +1,27 1797 +419.4,21 1798 +420.2,32 1799 +9,17 1799 +19,31 1799 +2,32 1799 +423.0,1 1800 +422.2,28 1801 +9,17 1801 +19,27 1801 +2,28 1801 +423.0,1 1800 +427.4,23 1802 +428.2,8 1803 +429.4,14 1804 +430.2,35 1805 +9,19 1805 +21,34 1805 +2,35 1805 +431.2,35 1806 +9,19 1806 +21,34 1806 +2,35 1806 +440.0,1 1807 +433.2,35 1808 +9,19 1808 +21,34 1808 +2,35 1808 +434.2,35 1809 +9,19 1809 +21,34 1809 +2,35 1809 +435.6,19 1810 +436.3,35 1811 +10,20 1811 +22,34 1811 +3,35 1811 +440.0,1 1807 +438.3,35 1812 +10,20 1812 +22,34 1812 +3,35 1812 +440.0,1 1807 +444.1,25 1813 +445.1,450.2 1814 +446.2,9 1814 +13,62 1814 +24,31 1814 +33,52 1814 +46,47 1814 +46,47 1814 +48,49 1814 +48,49 1814 +50,51 1814 +50,51 1814 +54,55 1814 +57,58 1814 +60,61 1814 +13,62 1814 +13,62 1815 +447.2,8 1814 +12,61 1814 +23,30 1814 +32,51 1814 +45,46 1814 +45,46 1814 +47,48 1814 +47,48 1814 +49,50 1814 +49,50 1814 +53,54 1814 +56,57 1814 +59,60 1814 +12,61 1814 +12,61 1816 +448.2,7 1814 +11,60 1814 +22,29 1814 +31,50 1814 +44,45 1814 +44,45 1814 +46,47 1814 +46,47 1814 +48,49 1814 +48,49 1814 +52,53 1814 +55,56 1814 +58,59 1814 +11,60 1814 +11,60 1817 +449.2,9 1814 +13,62 1814 +24,31 1814 +33,52 1814 +46,47 1814 +46,47 1814 +48,49 1814 +48,49 1814 +50,51 1814 +50,51 1814 +54,55 1814 +57,58 1814 +60,61 1814 +13,62 1814 +13,62 1818 +452.1,22 1819 +453.1,20 1820 +454.5,24 1821 +455.2,12 1822 +2,12 1823 +457.2,15 1824 +458.1,23 1825 +459.1,40 1826 +14,24 1826 +26,31 1826 +33,39 1826 +1,40 1826 +1,40 1826 +1,40 1827 +460.1,16 1828 +461.1,18 1829 +462.1,14 1830 +463.1,21 1831 +464.1,10 1832 +1,2 1832 +1,10 1832 +465.0,1 1833 +469.1,18 1834 +470.0,1 1835 +474.1,479.2 1836 +475.2,9 1836 +13,73 1836 +25,32 1836 +34,37 1836 +39,42 1836 +44,48 1836 +50,72 1836 +13,73 1836 +13,73 1837 +476.2,8 1836 +12,71 1836 +24,30 1836 +32,35 1836 +37,40 1836 +42,46 1836 +48,70 1836 +12,71 1836 +12,71 1838 +477.2,7 1836 +12,70 1836 +24,29 1836 +31,34 1836 +36,39 1836 +41,45 1836 +47,69 1836 +12,70 1836 +12,70 1839 +478.2,9 1836 +13,73 1836 +25,32 1836 +34,37 1836 +39,42 1836 +44,48 1836 +50,72 1836 +13,73 1836 +13,73 1840 +480.1,20 1841 +481.4,14 1842 +4,14 1842 +482.2,57 1843 +7,56 1843 +11,56 1843 +24,30 1843 +32,33 1843 +35,40 1843 +42,48 1843 +50,51 1843 +52,53 1843 +54,55 1843 +7,56 1843 +7,56 1844 +2,57 1843 +485.18,21 1845 +486.2,11 1846 +7,10 1846 +2,11 1846 +489.9,13 1847 +9,13 1847 +508.2,16 1847 +2,16 1847 +2,19 1847 +2,19 1848 +25,39 1847 +25,39 1847 +25,42 1847 +2,19 1847 +510.2,15 1847 +2,15 1847 +2,18 1847 +2,18 1849 +24,37 1847 +24,37 1847 +24,40 1847 +2,18 1847 +512.2,14 1847 +2,14 1847 +2,17 1847 +2,17 1850 +24,36 1847 +24,36 1847 +24,39 1847 +2,17 1847 +514.2,16 1847 +2,16 1847 +2,19 1847 +2,19 1851 +25,39 1847 +25,39 1847 +25,42 1847 +2,19 1847 +518.12,17 1847 +12,17 1847 +487.2,8 1847 +2,8 1847 +2,8 1847 +2,8 1847 +490.6,15 1852 +491.7,17 1853 +7,17 1853 +7,22 1853 +7,22 1854 +7,22 1855 +492.5,10 1856 +5,10 1857 +5,10 1858 +493.4,12 1859 +495.8,15 1860 +8,15 1860 +497.4,57 1861 +9,56 1861 +13,56 1861 +25,53 1861 +54,55 1861 +9,56 1861 +9,56 1862 +4,57 1861 +4,57 1863 +4,57 1864 +498.4,12 1865 +501.3,22 1866 +19,21 1866 +3,22 1866 +3,22 1866 +502.9,16 1867 +9,20 1867 +503.4,16 1868 +9,15 1868 +9,15 1868 +4,16 1868 +504.4,17 1869 +4,17 1869 +4,17 1869 +4,17 1870 +4,17 1847 +4,17 1871 +509.3,17 1872 +3,17 1872 +3,26 1872 +3,26 1873 +3,26 1847 +3,26 1874 +511.3,16 1875 +3,16 1875 +3,25 1875 +3,25 1876 +3,25 1847 +3,25 1877 +513.3,15 1878 +3,15 1878 +3,24 1878 +3,24 1879 +3,24 1847 +3,24 1880 +515.3,17 1881 +3,17 1881 +3,26 1881 +3,26 1882 +3,26 1847 +519.16,39 1883 +30,33 1883 +35,38 1883 +16,39 1883 +16,39 1883 +4,5 1883 +7,11 1883 +7,11 1884 +520.6,10 1885 +521.9,16 1886 +9,16 1886 +522.4,12 1886 +524.5,65 1887 +10,64 1887 +14,64 1887 +27,33 1887 +35,41 1887 +43,48 1887 +50,56 1887 +58,59 1887 +60,61 1887 +62,63 1887 +10,64 1887 +10,64 1888 +5,65 1887 +5,65 1886 +526.9,23 1889 +528.21,26 1890 +24,25 1890 +21,26 1890 +21,26 1890 +7,8 1890 +10,17 1890 +10,17 1891 +10,17 1892 +529.10,18 1893 +530.7,51 1894 +18,47 1894 +49,50 1894 +7,51 1894 +7,51 1894 +7,51 1895 +7,51 1896 +532.6,26 1897 +6,26 1886 +534.8,25 1898 +29,50 1898 +535.6,62 1899 +11,61 1899 +15,61 1899 +28,34 1899 +36,37 1899 +39,50 1899 +52,54 1899 +55,56 1899 +57,58 1899 +59,60 1899 +11,61 1899 +11,61 1900 +6,62 1899 +6,62 1886 +537.5,37 1901 +12,22 1901 +24,36 1901 +5,37 1901 +538.5,22 1902 +539.8,22 1903 +540.6,54 1904 +13,20 1904 +35,36 1904 +37,38 1904 +47,49 1904 +51,52 1904 +6,54 1904 +6,54 1904 +541.9,23 1905 +542.6,23 1906 +6,23 1886 +544.5,37 1907 +12,22 1907 +24,36 1907 +5,37 1907 +545.5,22 1908 +546.8,22 1909 +547.6,53 1910 +13,20 1910 +35,36 1910 +37,38 1910 +47,48 1910 +50,51 1910 +6,53 1910 +6,53 1910 +548.9,23 1911 +549.6,23 1912 +6,23 1886 +551.5,15 1913 +5,20 1913 +552.5,15 1914 +5,20 1914 +5,20 1886 +5,20 1915 +5,20 1916 +5,20 1847 +5,20 1917 +5,20 1917 +557.5,19 1918 +558.2,21 1919 +559.1,14 1920 +560.1,11 1921 +561.0,1 1922 +565.1,20 1923 +566.1,17 1924 +12,13 1924 +15,16 1924 +1,17 1924 +567.1,10 1925 +568.5,13 1926 +569.10,11 1927 +13,16 1927 +2,17 1927 +570.9,12 1928 +14,15 1928 +1,16 1928 +575.6,11 1929 +6,11 1929 +6,11 1929 +6,11 1929 +577.2,22 1930 +2,22 1929 +579.2,23 1931 +2,23 1929 +581.4,18 1932 +582.2,20 1933 +583.2,18 1934 +585.0,1 1935 +590.1,41 1936 +591.5,20 1937 +592.2,31 1938 +593.2,8 1939 +596.1,24 1940 +597.13,58 1941 +32,38 1941 +40,42 1941 +44,54 1941 +56,57 1941 +13,58 1941 +13,58 1941 +598.1,35 1942 +9,12 1942 +14,34 1942 +1,35 1942 +1,35 1942 +1,35 1943 +599.1,23 1944 +9,12 1944 +14,22 1944 +1,23 1944 +1,23 1944 +1,23 1945 +600.1,48 1946 +20,23 1946 +25,31 1946 +33,40 1946 +42,47 1946 +1,48 1946 +1,48 1946 +1,48 1947 +601.1,27 1948 +9,12 1948 +14,26 1948 +1,27 1948 +1,27 1948 +1,27 1949 +603.1,19 1950 +605.1,10 1951 +606.1,11 1952 +609.12,13 1953 +12,13 1953 +617.13,14 1953 +13,14 1953 +608.10,16 1953 +10,16 1953 +10,16 1953 +10,16 1953 +610.6,20 1954 +611.7,11 1955 +612.4,35 1956 +12,15 1956 +17,34 1956 +4,35 1956 +4,35 1956 +4,35 1957 +613.4,12 1958 +4,12 1959 +616.3,29 1960 +19,22 1960 +24,28 1960 +3,29 1960 +3,29 1960 +3,29 1961 +3,29 1962 +3,29 1953 +618.7,12 1963 +620.7,11 1964 +621.4,34 1965 +12,15 1965 +17,33 1965 +4,34 1965 +4,34 1965 +4,34 1966 +4,34 1963 +623.7,11 1967 +624.4,35 1968 +12,15 1968 +17,34 1968 +4,35 1968 +4,35 1968 +4,35 1969 +625.4,12 1970 +4,12 1971 +627.4,34 1972 +12,15 1972 +17,33 1972 +4,34 1972 +4,34 1972 +4,34 1973 +628.4,12 1974 +4,12 1963 +631.3,22 1975 +3,22 1963 +633.3,20 1976 +634.3,12 1977 +637.7,8 1978 +7,8 1978 +636.3,9 1978 +3,9 1978 +3,9 1978 +3,9 1978 +3,9 1979 +640.3,9 1980 +3,9 1980 +3,9 1981 +3,9 1953 +648.0,1 1982 +653.4,14 1983 +4,14 1983 +18,26 1983 +654.49,56 1984 +49,50 1984 +49,56 1984 +49,56 1984 +2,57 1984 +9,15 1984 +17,30 1984 +32,47 1984 +32,47 1984 +32,47 1984 +32,47 1985 +2,57 1984 +2,57 1984 +655.1,12 1986 +656.0,1 1987 +662.5,8 1988 +10,19 1989 +664.19,27 1990 +19,27 1990 +665.2,13 1991 +666.5,16 1992 +20,26 1992 +667.10,16 1993 +3,16 1993 +668.16,19 1994 +3,33 1994 +669.12,19 1995 +12,15 1995 +12,19 1995 +3,19 1995 +3,19 1996 +670.3,11 1997 +3,11 1998 +672.6,14 1999 +673.6,12 2000 +6,21 2000 +6,21 2000 +674.7,17 2001 +7,21 2001 +675.10,17 2002 +10,17 2002 +677.67,74 2003 +67,68 2003 +67,74 2003 +67,74 2003 +6,75 2003 +13,19 2003 +21,36 2003 +38,48 2003 +38,48 2003 +50,65 2003 +50,65 2003 +50,65 2003 +50,65 2004 +6,75 2003 +6,75 2003 +6,75 2002 +680.7,18 2005 +681.5,13 2006 +5,13 2007 +683.5,16 2008 +684.18,21 2009 +5,35 2009 +685.14,21 2010 +14,17 2010 +14,21 2010 +5,21 2010 +5,21 2011 +686.5,10 2012 +5,14 2012 +687.5,13 2013 +5,13 2014 +692.5,16 2015 +693.3,27 2016 +3,27 2017 +695.3,14 2018 +3,14 2019 +662.21,24 2020 +21,24 2020 +697.0,1 2021 +709.1,29 2022 +710.1,13 2023 +712.6,13 2024 +6,13 2024 +714.2,9 2025 +715.7,22 2026 +2,22 2026 +716.6,10 2027 +14,24 2028 +12,24 2028 +717.13,22 2029 +3,22 2029 +720.6,16 2030 +721.7,14 2031 +18,34 2031 +722.5,13 2032 +723.4,12 2033 +724.4,12 2034 +726.8,15 2035 +8,15 2035 +8,15 2035 +728.7,16 2036 +729.5,20 2037 +730.5,16 2038 +731.8,14 2039 +732.6,69 2040 +19,21 2040 +23,68 2040 +27,68 2040 +39,45 2040 +47,53 2040 +55,61 2040 +55,67 2040 +23,68 2040 +23,68 2041 +6,69 2040 +6,69 2040 +733.5,13 2042 +5,13 2035 +736.4,19 2043 +737.4,11 2044 +738.4,12 2045 +739.4,15 2046 +740.7,18 2047 +22,33 2047 +741.15,25 2048 +5,29 2048 +5,29 2049 +742.12,23 2050 +743.5,16 2051 +5,16 2052 +744.12,21 2053 +745.5,80 2054 +18,20 2054 +22,79 2054 +26,79 2054 +38,44 2054 +46,52 2054 +54,78 2054 +68,76 2054 +68,76 2054 +22,79 2054 +22,79 2055 +5,80 2054 +5,80 2054 +746.5,20 2056 +5,20 2057 +749.5,89 2058 +18,20 2058 +22,88 2058 +26,88 2058 +38,44 2058 +46,52 2058 +54,87 2058 +68,76 2058 +68,76 2058 +78,85 2058 +78,85 2058 +22,88 2058 +22,88 2059 +5,89 2058 +5,89 2058 +750.5,22 2060 +5,22 2035 +753.4,12 2061 +754.4,14 2062 +755.13,20 2063 +7,20 2063 +756.11,16 2064 +11,16 2064 +5,17 2064 +5,22 2064 +757.14,21 2065 +7,21 2065 +759.5,27 2066 +17,23 2066 +25,26 2066 +5,27 2066 +5,27 2066 +760.8,17 2067 +761.6,26 2068 +19,21 2068 +23,25 2068 +6,26 2068 +6,26 2068 +762.5,22 2069 +5,22 2070 +5,22 2035 +716.26,29 2071 +26,29 2071 +766.5,12 2072 +767.6,12 2073 +768.4,67 2074 +17,19 2074 +21,66 2074 +25,66 2074 +37,43 2074 +45,51 2074 +53,59 2074 +53,65 2074 +21,66 2074 +21,66 2075 +4,67 2074 +4,67 2074 +769.10,12 2076 +3,12 2076 +771.2,8 2077 +772.9,25 2078 +22,24 2078 +22,24 2078 +2,25 2078 +774.8,24 2079 +21,23 2079 +21,23 2079 +1,24 2079 +780.1,12 2080 +781.14,17 2081 +1,31 2081 +782.10,16 2082 +10,12 2082 +10,16 2082 +1,16 2082 +1,16 2083 +783.1,6 2084 +1,10 2084 +784.8,10 2085 +1,10 2085 +792.4,14 2086 +4,14 2086 +793.2,49 2087 +9,15 2087 +17,29 2087 +31,38 2087 +40,48 2087 +40,48 2087 +2,49 2087 +2,49 2087 +794.5,16 2088 +795.3,35 2089 +10,16 2089 +18,24 2089 +26,34 2089 +26,34 2089 +3,35 2089 +3,35 2089 +796.5,16 2090 +797.3,35 2091 +10,16 2091 +18,24 2091 +26,34 2091 +26,34 2091 +3,35 2091 +3,35 2091 +798.2,42 2092 +9,15 2092 +17,26 2092 +28,41 2092 +28,41 2092 +2,42 2092 +2,42 2092 +800.6,13 2093 +6,13 2093 +6,13 2093 +802.2,62 2094 +25,32 2094 +25,32 2094 +34,46 2094 +34,46 2094 +48,60 2094 +48,60 2094 +803.2,44 2095 +7,43 2095 +11,43 2095 +23,29 2095 +31,35 2095 +37,42 2095 +7,43 2095 +7,43 2096 +2,44 2095 +2,44 2097 +2,44 2093 +805.7,15 2098 +7,15 2098 +806.2,16 2098 +808.3,52 2099 +8,51 2099 +12,51 2099 +24,30 2099 +32,36 2099 +38,50 2099 +8,51 2099 +8,51 2100 +3,52 2099 +809.6,33 2101 +810.4,30 2102 +4,30 2098 +811.2,20 2098 +813.44,49 2103 +44,49 2103 +44,49 2103 +3,50 2103 +8,14 2103 +16,20 2103 +22,42 2103 +22,42 2103 +3,50 2103 +3,50 2098 +814.2,9 2098 +816.10,63 2104 +14,63 2104 +27,34 2104 +36,40 2104 +42,52 2104 +54,56 2104 +57,58 2104 +59,60 2104 +61,62 2104 +10,63 2104 +10,63 2105 +3,63 2104 +818.3,38 2106 +819.3,54 2107 +8,53 2107 +12,53 2107 +25,28 2107 +30,34 2107 +36,42 2107 +44,46 2107 +47,48 2107 +49,50 2107 +51,52 2107 +8,53 2107 +8,53 2108 +3,54 2107 +820.3,12 2109 +3,4 2109 +3,12 2109 +821.3,48 2110 +26,34 2110 +26,34 2110 +36,46 2110 +36,46 2110 +822.3,45 2111 +8,44 2111 +12,44 2111 +24,30 2111 +32,36 2111 +38,43 2111 +8,44 2111 +8,44 2112 +3,45 2111 +3,45 2113 +3,45 2098 +3,45 2093 +825.7,15 2114 +7,15 2114 +827.38,51 2115 +42,50 2115 +42,50 2115 +38,51 2115 +38,51 2115 +3,52 2115 +8,14 2115 +16,20 2115 +22,26 2115 +28,36 2115 +28,36 2115 +28,36 2115 +3,52 2115 +3,52 2114 +828.2,13 2114 +829.2,11 2114 +831.12,20 2116 +3,20 2116 +832.6,16 2117 +20,32 2117 +833.4,14 2118 +4,14 2119 +835.4,49 2120 +27,35 2120 +27,35 2120 +37,47 2120 +37,47 2120 +836.4,46 2121 +9,45 2121 +13,45 2121 +25,31 2121 +33,37 2121 +39,44 2121 +9,45 2121 +9,45 2122 +4,46 2121 +4,46 2123 +4,46 2114 +839.6,14 2124 +6,24 2124 +840.50,56 2125 +50,56 2125 +50,56 2125 +4,57 2125 +9,15 2125 +17,21 2125 +23,40 2125 +42,48 2125 +42,48 2125 +4,57 2125 +4,57 2114 +842.3,13 2126 +3,13 2126 +3,13 2126 +843.6,14 2127 +6,27 2127 +844.4,14 2128 +845.6,14 2129 +6,27 2129 +846.4,14 2130 +847.3,46 2131 +8,14 2131 +16,20 2131 +22,42 2131 +44,45 2131 +3,46 2131 +848.8,16 2132 +8,16 2132 +8,16 2132 +8,16 2132 +8,16 2132 +8,16 2132 +850.4,45 2133 +15,44 2133 +4,45 2133 +4,45 2133 +851.11,62 2134 +15,62 2134 +28,34 2134 +36,40 2134 +42,51 2134 +53,55 2134 +56,57 2134 +58,59 2134 +60,61 2134 +11,62 2134 +11,62 2135 +4,62 2134 +853.11,72 2136 +15,72 2136 +28,35 2136 +37,41 2136 +43,49 2136 +51,53 2136 +54,59 2136 +60,69 2136 +70,71 2136 +11,72 2136 +11,72 2137 +4,72 2136 +855.11,70 2138 +15,70 2138 +28,33 2138 +35,39 2138 +41,47 2138 +49,51 2138 +52,57 2138 +58,67 2138 +68,69 2138 +11,70 2138 +11,70 2139 +4,70 2138 +4,70 2114 +858.3,13 2140 +3,13 2140 +3,13 2140 +859.6,14 2141 +6,27 2141 +860.4,15 2142 +861.44,49 2143 +44,49 2143 +44,49 2143 +3,50 2143 +8,14 2143 +16,20 2143 +22,42 2143 +22,42 2143 +3,50 2143 +862.8,16 2144 +8,16 2144 +8,16 2144 +8,16 2144 +8,16 2144 +8,16 2144 +864.4,47 2145 +15,46 2145 +4,47 2145 +4,47 2145 +865.11,61 2146 +15,61 2146 +28,34 2146 +36,40 2146 +42,50 2146 +52,54 2146 +55,56 2146 +57,58 2146 +59,60 2146 +11,61 2146 +11,61 2147 +4,61 2146 +867.11,71 2148 +15,71 2148 +28,35 2148 +37,41 2148 +43,49 2148 +51,53 2148 +54,58 2148 +59,68 2148 +69,70 2148 +11,71 2148 +11,71 2149 +4,71 2148 +869.11,69 2150 +15,69 2150 +28,33 2150 +35,39 2150 +41,47 2150 +49,51 2150 +52,56 2150 +57,66 2150 +67,68 2150 +11,69 2150 +11,69 2151 +4,69 2150 +4,69 2114 +871.2,6 2114 +875.8,16 2152 +8,16 2152 +877.4,49 2153 +27,35 2153 +27,35 2153 +37,47 2153 +37,47 2153 +878.11,69 2154 +15,69 2154 +28,35 2154 +37,41 2154 +43,49 2154 +51,53 2154 +54,59 2154 +60,66 2154 +67,68 2154 +11,69 2154 +11,69 2155 +4,69 2154 +880.4,49 2156 +27,35 2156 +27,35 2156 +37,47 2156 +37,47 2156 +881.11,69 2157 +15,69 2157 +28,35 2157 +37,41 2157 +43,49 2157 +51,53 2157 +54,59 2157 +60,66 2157 +67,68 2157 +11,69 2157 +11,69 2158 +4,69 2157 +4,69 2114 +883.2,7 2114 +2,7 2093 +888.7,15 2159 +7,15 2159 +890.3,39 2160 +10,18 2160 +10,18 2160 +20,28 2160 +20,28 2160 +30,38 2160 +30,38 2160 +3,39 2160 +891.38,59 2161 +49,57 2161 +42,58 2161 +38,59 2161 +38,59 2161 +3,60 2161 +8,14 2161 +16,20 2161 +22,26 2161 +28,36 2161 +28,36 2161 +28,36 2161 +3,60 2161 +3,60 2159 +893.8,16 2162 +8,16 2162 +895.9,17 2163 +9,17 2163 +9,17 2163 +9,17 2163 +9,17 2163 +9,17 2163 +897.5,21 2164 +5,21 2163 +899.5,21 2165 +5,21 2163 +901.50,56 2166 +50,56 2166 +50,56 2166 +4,57 2166 +9,15 2166 +17,21 2166 +23,40 2166 +42,48 2166 +42,48 2166 +4,57 2166 +4,57 2162 +4,57 2159 +904.8,16 2167 +8,16 2167 +906.9,17 2168 +9,17 2168 +9,17 2168 +9,17 2168 +9,17 2168 +9,17 2168 +908.5,22 2169 +5,22 2168 +910.5,22 2170 +5,22 2168 +912.50,56 2171 +50,56 2171 +50,56 2171 +4,57 2171 +9,15 2171 +17,21 2171 +23,40 2171 +42,48 2171 +42,48 2171 +4,57 2171 +4,57 2167 +4,57 2159 +4,57 2093 +916.8,11 2172 +1,11 2172 +922.1,68 2173 +23,31 2173 +23,31 2173 +33,43 2173 +33,43 2173 +45,51 2173 +45,51 2173 +53,59 2173 +53,59 2173 +61,67 2173 +61,67 2173 +923.1,40 2174 +6,39 2174 +10,39 2174 +22,26 2174 +28,32 2174 +34,38 2174 +6,39 2174 +6,39 2175 +1,40 2174 +924.0,1 2176 +929.1,60 2177 +23,31 2177 +23,31 2177 +33,43 2177 +33,43 2177 +45,51 2177 +45,51 2177 +53,59 2177 +53,59 2177 +930.1,40 2178 +6,39 2178 +10,39 2178 +22,26 2178 +28,32 2178 +34,38 2178 +6,39 2178 +6,39 2179 +1,40 2178 +931.0,1 2180 +943.1,18 2181 +13,17 2181 +1,18 2181 +1,18 2181 +944.1,18 2182 +13,17 2182 +1,18 2182 +1,18 2182 +945.4,16 2183 +946.5,14 2184 +947.3,16 2185 +3,16 2185 +3,29 2185 +3,29 2186 +955.0,1 2187 +949.3,16 2188 +3,16 2188 +3,29 2188 +3,29 2189 +955.0,1 2187 +951.5,14 2190 +952.3,16 2191 +3,16 2191 +25,34 2191 +3,34 2191 +3,34 2192 +955.0,1 2187 +954.3,16 2193 +3,16 2193 +26,35 2193 +25,35 2193 +3,35 2193 +3,35 2194 +955.0,1 2187 +962.1,19 2195 +14,18 2195 +1,19 2195 +1,19 2195 +963.1,11 2196 +964.12,24 2197 +12,24 2197 +4,24 2197 +965.2,19 2198 +966.5,21 2199 +5,21 2199 +5,33 2199 +5,33 2200 +5,33 2199 +967.3,13 2201 +968.5,19 2202 +5,19 2202 +5,31 2202 +5,31 2203 +5,31 2202 +969.3,13 2204 +970.5,20 2205 +5,20 2205 +5,32 2205 +5,32 2206 +5,32 2205 +971.3,13 2207 +972.5,21 2208 +5,21 2208 +5,33 2208 +5,33 2209 +5,33 2208 +973.3,13 2210 +3,13 2211 +975.9,22 2212 +2,22 2212 +976.5,23 2213 +5,23 2213 +977.3,13 2214 +978.5,21 2215 +5,21 2215 +979.3,13 2216 +980.5,22 2217 +5,22 2217 +981.3,13 2218 +982.5,23 2219 +5,23 2219 +983.3,13 2220 +3,13 2221 +987.8,9 2222 +1,9 2222 +1006.1,10 2223 +1007.1,7 2224 +1008.7,13 2225 +7,13 2225 +7,17 2225 +1009.5,8 2226 +5,8 2226 +1010.3,9 2227 +1011.2,9 2228 +2,9 2228 +1013.8,9 2229 +1,9 2229 +1019.1,8 2230 +1020.6,10 2231 +6,10 2231 +6,10 2231 +6,10 2231 +1021.25,37 2232 +25,37 2231 +1022.22,32 2233 +22,32 2231 +1023.23,34 2234 +23,34 2231 +1024.25,37 2235 +25,37 2231 +1026.2,30 2236 +8,29 2236 +2,30 2236 +2,30 2231 +1028.8,10 2237 +1,10 2237 +1034.1,11 2238 +1035.4,16 2239 +1036.2,12 2240 +1037.4,19 2241 +4,19 2241 +1038.2,12 2242 +1039.4,16 2243 +4,16 2243 +1040.2,12 2244 +1041.4,19 2245 +4,19 2245 +1042.2,12 2246 +1045.8,9 2247 +1,9 2247 +1051.1,11 2248 +1052.4,19 2249 +4,19 2249 +1053.2,12 2250 +1054.4,19 2251 +4,19 2251 +1055.2,12 2252 +1058.8,9 2253 +1,9 2253 +1068.1,10 2254 +1069.1,12 2255 +1070.8,11 2256 +1,11 2256 +1075.4,12 2257 +16,25 2257 +1076.10,11 2258 +13,14 2258 +2,15 2258 +1078.1,7 2259 +1079.5,11 2260 +17,22 2261 +13,22 2261 +1080.6,10 2262 +1081.5,13 2263 +17,26 2263 +30,39 2263 +1082.3,8 2264 +1079.24,27 2265 +24,27 2265 +1085.1,9 2266 +1086.4,12 2267 +16,24 2267 +1087.5,13 2268 +1088.3,10 2269 +1089.2,5 2270 +1092.1,8 2271 +1093.1,7 2272 +1094.11,16 2273 +7,16 2273 +1095.6,10 2274 +1096.2,11 2275 +1097.7,8 2276 +1099.7,14 2277 +3,19 2277 +3,19 2276 +1101.7,14 2278 +3,19 2278 +3,19 2276 +1103.3,14 2279 +3,14 2276 +1105.5,14 2280 +1106.3,8 2281 +1107.2,8 2282 +1108.6,14 2283 +2,18 2283 +1094.18,21 2284 +18,21 2284 +1111.5,7 2285 +1112.10,11 2286 +13,14 2286 +2,15 2286 +1113.4,7 2287 +1114.2,8 2288 +1115.9,10 2289 +12,17 2289 +12,13 2289 +12,17 2289 +1,18 2289 +1120.1,7 2290 +1121.5,11 2291 +17,22 2292 +13,22 2292 +1122.7,11 2293 +1123.5,17 2294 +21,33 2294 +1124.10,14 2295 +10,36 2295 +3,36 2295 +1121.24,27 2296 +24,27 2296 +1126.8,9 2297 +1,9 2297 +1132.1,10 2298 +1133.5,9 2299 +11,14 2300 +1134.2,13 2301 +1133.16,19 2302 +16,19 2302 +1135.8,11 2303 +1,11 2303 +1140.1,35 2304 +8,14 2304 +16,29 2304 +31,34 2304 +1,35 2304 +1,35 2304 +1141.1,11 2305 +7,10 2305 +1,11 2305 +1142.0,1 2306 +1146.4,10 2307 +1148.1,59 2308 +25,36 2308 +17,36 2308 +17,45 2308 +17,45 2309 +47,58 2308 +1,59 2308 +1,59 2308 +1149.4,13 2310 +1150.2,28 2311 +14,16 2311 +18,27 2311 +2,28 2311 +2,28 2311 +1151.1,5 2312 +1167.8,18 2313 +1,21 2313 +1168.8,16 2314 +20,28 2314 +8,28 2314 +1,28 2314 +8,28 2314 +1,28 2314 +1184.1,14 2315 +1185.6,13 2316 +15,21 2317 +1186.2,22 2318 +10,11 2318 +13,21 2318 +13,21 2318 +2,22 2318 +2,22 2318 +2,22 2319 +1185.23,27 2320 +23,27 2320 +1187.0,1 2321 +24 +aSys->Dir 1:26.1,39.2 64 +11 +0:name:28.2,6 s +4:uid:29.2,5 s +8:gid:30.2,5 s +12:muid:31.2,6 s +16:qid:32.2,5 @1 + +32:mode:33.2,6 i +36:atime:34.2,7 i +40:mtime:35.2,7 i +48:length:36.2,8 B +56:dtype:37.2,7 i +60:dev:38.2,5 i +aSys->Qid 11.1,16.2 16 +3 +0:path:13.2,6 B +8:vers:14.2,6 i +12:qtype:15.2,7 i +aDraw->Chans 2:70.1,82.2 4 +1 +0:desc:72.2,6 i +pEvent 7:5.0,19.1 0 +2 +4:path:6.1,5 i +8:from:7.1,5 i +3 +Edata:9.2,7 16 +1 +12:data:10.3,7 Ab +Eproto:11.2,8 32 +5 +12:cmd:12.3,6 i +16:s:13.3,4 s +20:a0:14.3,5 i +24:a1:7,9 i +28:a2:11,13 i +Equit:15.2,7 12 +0 +aKeyb 9:11.0,23.1 16 +4 +0:m:12.1,2 R@5 + +4:in:13.1,3 CR@3 + +8:cmd:15.1,4 Cs +12:spec:16.1,5 i +aModule 0:53.0,56.1 8 +2 +0:path:54.1,5 i +4:disabled:55.1,9 i +aTk->Toplevel 3:5.1,12.2 32 +5 +0:display:7.2,9 R@7 + +4:wreq:8.2,6 Cs +8:image:9.2,7 R@8 + +12:ctxt:10.2,6 R@12 + +16:screenr:11.2,9 @9 + +aDraw->Display 2:201.1,230.2 20 +5 +0:image:203.2,7 R@8 + +4:white:204.2,7 R@8 + +8:black:205.2,7 R@8 + +12:opaque:206.2,8 R@8 + +16:transparent:207.2,13 R@8 + +aDraw->Image 142.1,198.2 56 +8 +0:r:146.2,3 @9 + +16:clipr:147.2,7 @9 + +32:depth:148.2,7 i +36:chans:149.2,7 @2 + +40:repl:150.2,6 i +44:display:151.2,9 R@7 + +48:screen:152.2,8 R@11 + +52:iname:153.2,7 s +aDraw->Rect 116.1,139.2 16 +2 +0:min:118.2,5 @10 + +8:max:119.2,5 @10 + +aDraw->Point 99.1,113.2 8 +2 +0:x:101.2,3 i +4:y:102.2,3 i +aDraw->Screen 249.1,263.2 16 +4 +0:id:251.2,4 i +4:image:252.2,7 R@8 + +8:fill:253.2,6 R@8 + +12:display:254.2,9 R@7 + +aDraw->Wmcontext 282.1,291.2 28 +7 +0:kbd:284.2,5 Ci +4:ptr:285.2,5 CR@13 + +8:ctl:286.2,5 Cs +12:wctl:287.2,6 Cs +16:images:288.2,8 CR@8 + +20:connfd:289.2,8 R@14 + +24:ctxt:290.2,6 R@15 + +aDraw->Pointer 266.1,271.2 16 +3 +0:buttons:268.2,9 i +4:xy:269.2,4 @10 + +12:msec:270.2,6 i +aSys->FD 1:45.1,48.2 4 +1 +0:fd:47.2,4 i +aDraw->Context 2:274.1,279.2 12 +3 +0:display:276.2,9 R@7 + +4:screen:277.2,8 R@11 + +8:wm:278.2,4 Ct8.2 +0:t0:15,21 s +4:t1:15,21 Ct8.2 +0:t0:32,38 s +4:t1:32,38 R@12 + + + +aModem 10:14.0,45.1 76 +19 +0:m:15.1,2 R@5 + +4:in:16.1,3 CR@3 + +8:connect:18.1,8 i +12:state:19.1,6 i +16:saved:20.1,6 s +20:initstr:21.1,8 s +24:dialstr:22.1,8 s +28:lastdialstr:23.1,12 s +32:spec:25.1,5 i +36:fd:26.1,3 R@14 + +40:cfd:27.1,4 R@14 + +44:devpath:28.1,8 s +48:avail:29.1,6 Ab +52:rd:30.1,3 CAb +56:pid:31.1,4 i +60:seq:33.1,4 i +64:waitsyn:34.1,8 i +68:errforce:35.1,9 i +72:addparity:36.1,10 i +aMsg 508.0,512.1 12 +3 +0:text:509.1,5 s +4:trans:510.1,6 s +8:code:511.1,5 i +aSocket 11:5.0,13.1 8 +2 +0:m:6.1,2 R@5 + +4:in:7.1,3 CR@3 + +aScreen 12:45.0,84.1 132 +25 +0:m:46.1,2 R@5 + +4:ctxt:47.1,5 R@15 + +8:in:48.1,3 CR@3 + +12:image:50.1,6 R@8 + +16:dispr40:51.1,8 @9 + +32:dispr80:10,17 @9 + +48:oldtmode:52.1,9 i +52:rows:53.1,5 i +56:cols:54.1,5 i +60:cset:55.1,5 i +64:pos:57.1,4 @10 + +72:attr:58.1,5 i +76:spec:59.1,5 i +80:savepos:60.1,8 @10 + +88:saveattr:61.1,9 i +92:savech:62.1,7 i +96:delimit:63.1,8 i +100:cursor:64.1,7 i +104:state:66.1,6 i +108:a0:67.1,3 i +112:a1:68.1,3 i +116:fstate:70.1,7 i +120:fsaved:71.1,7 Ab +124:badp:72.1,5 i +128:ignoredata:74.1,11 i +aTerminal 0:90.0,113.1 56 +14 +0:in:91.1,3 CR@3 + +4:out:92.1,4 AR@21 + +8:mode:94.1,5 i +12:state:95.1,6 i +16:spec:96.1,5 i +20:connect:97.1,8 i +24:toplevel:98.1,9 R@6 + +28:cmd:99.1,4 Cs +32:proto:100.1,6 AR@22 + +36:netaddr:101.1,8 s +40:buttonsleft:102.1,12 i +44:terminalid:103.1,11 Ab +48:kbctl:104.1,6 Cs +52:kbmode:105.1,7 s +aBufChan 59.0,65.1 20 +5 +0:path:60.1,5 i +4:ch:61.1,3 CR@3 + +8:ev:62.1,3 R@3 + +12:in:63.1,3 CR@3 + +16:q:64.1,2 AR@3 + +aPState 68.0,74.1 20 +5 +0:state:69.1,6 i +4:arg:70.1,4 Ai +8:nargs:71.1,6 i +12:n:72.1,2 i +16:skip:73.1,5 i +aSys->Connection 1:52.1,57.2 12 +3 +0:dfd:54.2,5 R@14 + +4:cfd:55.2,5 R@14 + +8:dir:56.2,5 s +92 +0:Event.str +1 +32:ev:8:5.10,12 R@3 + +3 +36:e:8.6,7 R@3 + +40:s:7.1,2 s +44:i:11.7,8 i +s34:Keyb.init +2 +32:k:9:25.10,11 R@4 + +36:toplevel:28,36 R@6 + +0 +n47:Keyb.reset +1 +32:k:33.11,12 R@4 + +0 +n53:ask +2 +32:in:38.4,6 Cs +36:out:24,27 Cs +3 +40:number:46.1,7 s +44:keys:40.1,5 s +48:n:51.2,3 i +n112:Keyb.run +1 +32:k:90.9,10 R@4 + +19 +36:askchan:93.1,8 Cs +40:dontask:92.1,8 Cs +44:askkeys:94.1,8 Cs +48:e:99.8,9 R@3 + +52:word:188.5,9 s +56:key:130.6,9 i +60:s:132.6,7 s +64:seq:149.7,10 Ab +68:cmd:122.2,5 s +72:dialstr:215.2,9 s +76:keys:138.5,9 Ab +80:seq:176.7,10 Ab +84:seq:181.6,9 Ab +88:seq:205.6,9 Ab +92:ev:98.2,4 R@3 + +96:n:126.4,5 i +100:args:7,11 Ls +104:y:187.5,6 i +56:x:186.5,6 i +n472:Keyb.map +1 +36:key:229.29,32 i +2 +40:cmd:232.1,4 s +44:seq:234.2,5 Ab +Ab565:Keyb.quit +1 +32:k:284.10,11 R@4 + +0 +n567:canoncmd +1 +32:s:289.9,10 s +0 +s592:keyseq +1 +32:skey:311.7,11 s +2 +36:b2:313.1,3 i +40:asterisk:314.1,9 i +Ab645:minikey +1 +32:key:343.8,11 i +0 +s666:dump +2 +32:a:10:49.5,6 Ab +36:n:23,24 i +2 +40:i:52.5,6 i +44:s:51.1,2 s +s685:Modem.init +4 +32:m:57.11,12 R@16 + +36:connect:30,37 i +40:initstr:44,51 s +44:dialstr:53,60 s +1 +48:c:60.5,6 i +n718:Modem.reset +1 +32:m:81.12,13 R@16 + +0 +n724:Modem.run +1 +32:m:86.10,11 R@16 + +7 +36:e:94.8,9 R@3 + +40:b:192.2,3 Ab +44:pidc:158.6,10 Ci +48:dev:121.6,9 s +52:ev:93.2,4 R@3 + +56:ok:136.7,9 i +60:cx:11,13 @23 + +n1081:Modem.quit +0 +0 +n1082:Modem.runstate +2 +32:m:222.15,16 R@16 + +36:data:34,38 Ab +4 +40:ch:230.3,5 i +44:i:229.6,7 i +48:code:235.4,8 i +52:str:10,13 s +n1183:Modem.write +2 +32:m:258.12,13 R@16 + +36:data:31,35 Ab +2 +40:i:267.6,7 i +44:pa:266.2,4 Ab +i1226:mktabs +0 +4 +32:c:293.5,6 i +36:crc:295.2,5 i +40:i:296.6,7 i +44:v:294.2,3 i +n1247:nextblock +3 +32:a:308.10,11 Ab +36:i:28,29 i +40:n:36,37 i +0 +i1255:decode +1 +32:a:318.7,8 Ab +9 +36:crc:324.1,4 i +40:i:327.5,6 i +44:nc:329.2,4 i +48:op:325.1,3 i +52:b:356.1,2 Ab +56:c:328.2,3 i +60:dle:326.1,4 i +64:oldcrc:323.1,7 i +68:badpar:322.1,7 i +Ab1350:Modem.reader +2 +32:m:363.13,14 R@16 + +36:pidc:32,36 Ci +10 +40:syn:384.5,8 b +44:n:371.9,10 i +48:a:368.1,2 Ab +52:b:396.5,6 Ab +56:inbuf:369.1,6 i +60:i:376.8,9 i +64:b:375.4,5 Ab +68:lim:385.5,8 i +60:i:381.4,5 i +68:lim:394.4,7 i +n1477:replay +1 +32:m:427.7,8 R@16 + +12 +36:ch:449.3,5 i +40:hs:441.1,3 s +44:buf:429.1,4 Ab +48:d:431.1,2 i +52:da:432.1,3 Ab +56:discard:439.1,8 i +60:i:448.6,7 i +64:nl:438.1,3 i +68:start:442.1,6 i +72:state:440.1,6 i +76:n:445.2,3 i +80:v:471.5,6 i +n1575:kill +1 +32:pid:488.5,8 i +3 +36:cmd:493.2,5 Ab +40:fd:491.1,3 R@14 + +44:prog:490.1,5 s +n1594:msend +2 +32:m:524.6,7 R@16 + +36:x:20,21 s +1 +40:a:526.1,2 Ab +i1602:apply +2 +32:m:533.6,7 R@16 + +36:s:20,21 s +3 +40:buf:535.1,4 s +44:i:536.5,6 i +48:c:537.2,3 i +i1629:openmodem +2 +32:m:550.10,11 R@16 + +36:dev:24,27 s +0 +i1645:hangup +1 +32:m:564.7,8 R@16 + +0 +n1678:nethangup +1 +32:m:578.10,11 R@16 + +0 +n1687:seenreply +1 +32:s:589.10,11 s +1 +36:k:591.5,6 i +t8.2 +0:t0:589.23,26 i +4:t1:23,26 s +1713:dialout +1 +32:m:611.8,9 R@16 + +0 +n1728:Socket.init +1 +32:c:11:15.12,13 R@18 + +0 +n1733:Socket.reset +1 +32:c:21.13,14 R@18 + +0 +n1739:Socket.run +1 +32:c:26.11,12 R@18 + +2 +36:e:31.7,8 R@3 + +40:ev:30.2,4 R@3 + +n1757:Socket.quit +1 +32:c:46.12,13 R@18 + +0 +n1759:Screen.init +4 +32:s:12:86.12,13 R@19 + +36:ctxt:32,36 R@15 + +40:r40:57,60 @9 + +56:r80:62,65 @9 + +0 +n1790:Screen.reset +1 +32:s:107.13,14 R@19 + +0 +n1799:Screen.run +1 +32:s:114.11,12 R@19 + +5 +36:e:119.7,8 R@3 + +40:da:166.3,5 AAb +44:ev:118.1,3 R@3 + +48:oldspec:165.3,10 i +60:oldpos:164.3,9 @10 + +n1910:indicators +1 +32:s:189.11,12 R@19 + +3 +36:attr:194.1,5 i +40:ch:192.1,3 s +44:col:191.1,4 i +n1933:Screen.setmode +2 +32:s:212.15,16 R@19 + +36:tmode:35,40 i +4 +40:delims:215.1,7 i +44:ulheight:216.1,9 i +48:fontpath:251.2,10 s +56:dispr:214.1,6 @9 + +n2025:Screen.quit +0 +0 +n2028:Screen.runstate +2 +32:s:265.16,17 R@19 + +36:data:36,40 Ab +0 +n2051:vc0 +2 +32:s:279.4,5 R@19 + +36:ch:19,21 i +1 +40:cols:347.2,6 i +n2183:vc1 +2 +32:s:384.4,5 R@19 + +36:ch:19,21 i +2 +40:bg:402.7,9 i +44:fg:1,3 i +n2296:vss2 +2 +32:s:494.5,6 R@19 + +36:ch:20,22 i +0 +n2357:vcsi +2 +32:s:534.5,6 R@19 + +36:ch:20,22 i +2 +40:r:573.8,9 i +40:r:577.8,9 i +n2494:vstate +2 +32:s:635.7,8 R@19 + +36:data:22,26 Ab +6 +40:ch:639.2,4 i +44:i:637.1,2 i +48:str:650.4,7 s +52:cs:642.3,5 s +56:n:649.4,5 i +60:match:834.3,8 AAi +Ab2921:mstate +2 +32:s:895.7,8 R@19 + +36:data:22,26 Ab +5 +40:ch:900.2,4 i +44:i:897.1,2 i +48:str:911.4,7 s +52:cs:903.3,5 s +56:n:910.4,5 i +Ab3127:mc0 +2 +32:s:1011.4,5 R@19 + +36:ch:19,21 i +0 +n3186:mc1 +2 +32:s:1063.4,5 R@19 + +36:ch:19,21 i +0 +n3241:mcsi +2 +32:s:1107.5,6 R@19 + +36:ch:20,22 i +2 +40:r:1154.8,9 i +40:r:1158.8,9 i +n3433:Screen.put +2 +32:s:1258.11,12 R@19 + +36:str:31,34 s +4 +40:n:1261.2,3 i +44:i:1269.7,8 i +48:cs:1278.5,7 s +52:l:1260.8,9 i +n3541:incpos +2 +32:s:1313.7,8 R@19 + +36:n:22,23 i +0 +n3587:rowclear +3 +32:r:1343.9,10 i +36:first:12,17 i +40:last:19,23 i +0 +n3603:clear +1 +32:s:1350.6,7 R@19 + +1 +36:r:1352.5,6 i +n3613:refresh +0 +0 +n3616:scroll +2 +32:topline:1363.7,14 i +36:nlines:16,22 i +0 +n3623:filter +2 +32:s:1371.7,8 R@19 + +36:data:22,26 Ab +0 +AAb3646:vfilter +2 +32:s:1385.8,9 R@19 + +36:data:23,27 Ab +7 +40:ba:1387.1,3 AAb +44:ch:1392.2,4 i +48:i:1391.5,6 i +52:d0:1390.1,3 i +56:a:1427.4,5 Ab +60:changed:1388.1,8 i +64:valid:1432.4,9 i +AAb3791:mfilter +2 +32:s:1473.8,9 R@19 + +36:data:23,27 Ab +8 +40:ba:1475.1,3 AAb +44:ch:1480.2,4 i +48:i:1479.5,6 i +52:d0:1478.1,3 i +56:changed:1476.1,8 i +60:a:1533.4,5 Ab +64:valid:1538.4,9 i +68:n:1555.5,6 i +AAb3991:dappend +2 +32:ba:1590.8,10 AAb +36:b:36,37 Ab +2 +40:na:1593.1,3 AAb +44:l:1592.1,2 i +AAb4003:Screen.msg +2 +32:s:1600.11,12 R@19 + +36:str:31,34 s +2 +40:n:1603.1,2 i +44:blank:1602.1,6 s +n4041:init +2 +32:ctxt:0:132.5,9 R@15 + +36:argv:30,34 Ls +11 +40:words:177.7,12 Ls +44:arg:145.1,4 mArg +6:1.0,14.1 0 + +48:dialstr:0:174.12,19 s +52:netaddr:135.1,8 s +56:c:148.8,9 i +60:initstr:174.1,8 s +64:i:152.7,8 i +68:toplevel:212.1,9 R@6 + +72:done:226.1,5 Ci +76:s:134.1,2 s +64:connect:173.1,8 i +n4234:inittk +2 +32:toplevel:416.7,15 R@6 + +36:connect:35,42 i +0 +n4249:Terminal.layout +2 +32:t:425.16,17 R@20 + +36:cols:38,42 i +0 +n4280:Terminal.init +3 +32:t:442.14,15 R@20 + +36:toplevel:36,44 R@6 + +40:connect:64,71 i +0 +n4364:Terminal.reset +1 +32:t:467.15,16 R@20 + +0 +n4366:Terminal.run +2 +32:t:472.13,14 R@20 + +36:done:35,39 Ci +8 +40:ev:485.2,4 R@3 + +44:eva:501.3,6 AR@3 + +48:e:527.6,7 s +52:cmd:518.2,5 s +56:e:495.8,9 R@3 + +60:modcount:480.1,9 i +64:n:519.4,5 i +68:word:7,11 Ls +n4635:kb +1 +32:t:563.3,4 R@20 + +2 +36:s:565.1,2 Cs +40:e:567.1,2 s +t8.2 +0:t0:563.22,28 s +4:t1:22,28 Cs +4648:Terminal.setkbmode +2 +32:t:573.19,20 R@20 + +36:tmode:41,46 i +0 +n4660:dokb +2 +32:t:588.5,6 R@20 + +36:c:22,23 Cs +7 +40:top:597.2,5 R@6 + +44:m:7,8 Cs +48:kbon:605.1,5 i +52:kbctl:596.1,6 Cs +56:keyboard:590.1,9 mKeyboard +14:13.0,21.1 0 + +60:mcmd:0:609.1,5 s +64:kbcmd:617.1,6 s +n4768:Terminal.quit +0 +0 +n4769:send +1 +32:e:651.5,6 R@3 + +0 +n4787:post +1 +32:e:659.5,6 R@3 + +6 +36:b:664.2,3 R@21 + +40:l:661.3,4 i +44:i:1,2 i +48:na:684.5,7 AR@3 + +52:na:668.3,5 AR@3 + +56:de:675.10,12 R@3 + +n4852:protocol +1 +32:ev:700.9,11 R@3 + +8 +36:p:715.2,3 R@22 + +40:e:712.6,7 R@3 + +44:ea:709.1,3 AR@3 + +48:i:716.6,7 i +52:ch:717.3,5 i +56:d0:714.2,4 i +60:changed:710.1,8 i +64:pe:759.5,7 R@3 + +AR@3 +4987:eappend +2 +32:ea:778.8,10 AR@3 + +36:e:32,33 R@3 + +2 +40:na:781.1,3 AR@3 + +44:l:780.1,2 i +AR@3 +4999:proto +2 +32:from:790.6,10 i +36:p:17,18 R@22 + +8 +40:all:818.3,6 i +44:reply:802.2,7 Ab +48:reply:821.3,8 Ab +52:reply:835.4,9 Ab +76:reply:877.4,9 Ab +80:reply:880.4,9 Ab +40:x:842.3,4 i +40:x:858.3,4 i +R@3 +5418:PRO3 +5 +32:path:920.5,9 i +36:from:11,15 i +40:x:17,18 i +44:y:20,21 i +48:z:23,24 i +1 +52:data:922.1,5 Ab +n5439:PRO2 +4 +32:path:927.5,9 i +36:from:11,15 i +40:x:17,18 i +44:y:20,21 i +1 +48:data:929.1,5 Ab +n5458:modcmd +3 +32:cmd:941.7,10 i +36:from:12,16 i +40:targ:18,22 i +0 +n5492:psb +1 +32:code:960.4,8 i +4 +36:b:963.1,2 i +40:mask:965.2,6 i +44:mod:975.2,5 R@5 + +48:this:962.1,5 i +i5543:parity +1 +32:b:1004.7,8 i +2 +36:p:1007.1,2 i +40:bits:1006.1,5 i +i5555:RxTx +1 +32:code:1017.5,9 i +1 +36:rv:1019.1,3 i +i5574:osb +0 +1 +32:b:1034.1,2 i +i5588:kosb +0 +1 +32:b:1051.1,2 i +i5597:tostr +1 +32:ch:1066.6,8 i +1 +36:str:1068.1,4 s +s5601:toint +2 +32:s:1073.6,7 s +36:base:17,21 i +6 +40:c:1078.1,2 i +44:i:1079.5,6 i +48:n:1093.1,2 i +52:v:1096.2,3 i +56:neg:1085.1,4 i +60:ok:1092.1,3 i +t8.2 +0:t0:1073.30,33 i +4:t1:30,33 s +5656:tolower +1 +32:s:1118.8,9 s +3 +36:i:1121.5,6 i +40:r:1120.1,2 s +44:c:1122.2,3 i +s5670:dup +2 +32:ch:1130.4,6 i +36:n:8,9 i +2 +40:i:1133.5,6 i +44:str:1132.1,4 s +s5678:fatal +1 +32:msg:1138.6,9 s +0 +n5688:exits +1 +32:s:1144.6,7 s +1 +36:fd:1148.1,3 R@14 + +n5704:ISC0 +1 +32:ch:1165.5,7 i +1 +36:msb:1167.1,4 i +i5712:tkcmds +2 +32:t:1182.7,8 R@6 + +36:cmds:28,32 As +2 +40:ix:1185.6,8 i +44:n:1184.1,2 i +n30 +5144:C:126.0,1 R@18 + +5196:K:124.0,1 R@4 + +5204:M:125.0,1 R@16 + +5220:Modname:44.0,7 As +5224:Modules:129.0,7 AR@5 + +5244:S:127.0,1 R@19 + +5248:T:128.0,1 R@20 + +5252:TERMINALID1:30.0,11 Ab +5256:TERMINALID2:35.0,11 Ab +5320:crctab:10:285.0,6 Ai +5332:debug:0:26.0,5 Ai +5340:disp:12:10.0,4 mMDisplay +13:7.0,115.1 0 + +5344:draw:0:11.1,5 mDraw +2:1.0,298.1 0 + +5420:msgs:10:514.0,4 A@17 + +5448:partab:47.0,6 Ab +5452:pgrp:0:25.0,4 i +5460:playfd:10:424.0,6 R@14 + +5508:stderr:0:27.0,6 R@14 + +5512:sys:8.1,4 mSys +1:4.0,160.1 0 + +5528:tk:0:13.1,3 mTk +3:1.0,25.1 0 + +5532:tkclient:0:15.1,9 mTkclient +4:1.0,26.1 0 + +5536:tkinitbs:0:247.0,8 As +5540:tkinitdirect:265.0,12 As +5544:tkinitip:310.0,8 As +5548:tkip40x25hide:383.0,13 As +5552:tkip40x25lhs:369.0,12 As +5556:tkip40x25rhs:376.0,12 As +5560:tkip40x25show:346.0,13 As +5564:tkip80x25hide:412.0,13 As +5568:tkip80x25show:387.0,13 As diff --git a/appl/wm/minitel/mkfile b/appl/wm/minitel/mkfile new file mode 100644 index 00000000..16f816a6 --- /dev/null +++ b/appl/wm/minitel/mkfile @@ -0,0 +1,24 @@ +<../../../mkconfig + +TARG=\ + mdisplay.dis\ + miniterm.dis\ + swkeyb.dis\ + +MODULES=\ + mdisplay.m\ + miniterm.m\ + event.m\ + swkeyb.m\ + +SYSMODULES=\ + arg.m\ + sys.m\ + debug.m\ + draw.m\ + tk.m\ + wmlib.m\ + +DISBIN=$ROOT/dis/wm/minitel + +<$ROOT/mkfiles/mkdis diff --git a/appl/wm/minitel/modem.b b/appl/wm/minitel/modem.b new file mode 100644 index 00000000..b7a21c1d --- /dev/null +++ b/appl/wm/minitel/modem.b @@ -0,0 +1,620 @@ +# +# Copyright © 1998 Vita Nuova Limited. All rights reserved. +# + +#modem states for direct connection +MSstart, MSdialing, MSconnected, MSdisconnecting, + +# special features +Ecp # error correction + : con (1 << iota); + +Ecplen: con 17; # error correction block length: data[15], crc, validation (=0) + +Modem: adt { + m: ref Module; # common attributes + in: chan of ref Event; + + connect: int; # None, Direct, Network + state: int; # modem dialing state + saved: string; # response, so far (direct dial) + initstr: string; # softmodem init string (direct dial) + dialstr: string; # softmodem dial string (direct dial) + lastdialstr: string; + + spec: int; # special features + fd: ref Sys->FD; # modem data file, if != nil + cfd: ref Sys->FD; # modem ctl file, if != nil (direct dial only) + devpath: string; # path to the modem; + avail: array of byte; # already read + rd: chan of array of byte; # reader -> rd + pid: int; # reader pid if != 0 + + seq: int; # ECP block sequence number + waitsyn: int; # awaiting restart SYN SYN ... sequence + errforce: int; + addparity: int; # must add parity to outgoing data + + init: fn(m: self ref Modem, connect: int, initstr, dialstr: string); + reset: fn(m: self ref Modem); + run: fn(m: self ref Modem); + quit: fn(m: self ref Modem); + runstate: fn(m: self ref Modem, data: array of byte); + write: fn(m: self ref Modem, data: array of byte):int; # to network + reader: fn(m: self ref Modem, pidc: chan of int); +}; + +partab: array of byte; + +dump(a: array of byte, n: int): string +{ + s := sys->sprint("[%d]", n); + for(i := 0; i < n; i++) + s += sys->sprint(" %.2x", int a[i]); + return s; +} + +Modem.init(m: self ref Modem, connect: int, initstr, dialstr: string) +{ + partab = array[128] of byte; + for(c := 0; c < 128; c++) + if(parity(c)) + partab[c] = byte (c | 16r80); + else + partab[c] = byte c; + m.in = chan of ref Event; + m.connect = connect; + m.state = MSstart; + m.initstr = initstr; + m.dialstr = dialstr; + m.pid = 0; + m.spec = 0; + m.seq = 0; + m.waitsyn = 0; + m.errforce = 0; + m.addparity = 0; + m.avail = array[0] of byte; + m.rd = chan of array of byte; + m.reset(); +} + +Modem.reset(m: self ref Modem) +{ + m.m = ref Module(Pscreen, 0); +} + +Modem.run(m: self ref Modem) +{ + if(m.dialstr != nil) + send(ref Event.Eproto(Pmodem, Mmodem, Cconnect, "", 0,0,0)); +Runloop: + for(;;){ + alt { + ev := <- m.in => + pick e := ev { + Equit => + break Runloop; + Edata => + if(debug['m'] > 0) + fprint(stderr, "Modem <- %s\n", e.str()); + m.write(e.data); + if(T.state == Local || T.spec & Echo) { # loopback + if(e.from == Mkeyb) { + send(ref Event.Eproto(Pscreen, Mkeyb, Ccursor, "", 0,0,0)); + send(ref Event.Edata(Pscreen, Mkeyb, e.data)); + } + } + Eproto => + case e.cmd { + Creset => + m.reset(); + Cconnect => + if(m.pid != 0) + break; + m.addparity = 1; + T.state = Connecting; + send(ref Event.Eproto(Pscreen, Mmodem, Cindicators, "",0,0,0)); + + case m.connect { + Direct => + S.msg("Appel "+m.dialstr+" ..."); + dev := "/dev/modem"; + if(openmodem(m, dev) < 0) { + S.msg("Modem non prêt"); + T.state = Local; + send(ref Event.Eproto(Pscreen, Mmodem, Cindicators, "",0,0,0)); + break; + } + m.state = MSdialing; + m.saved = ""; + dialout(m); + T.terminalid = TERMINALID2; + Network => + S.msg("Connexion au serveur ..."); + if(debug['m'] > 0 || debug['M'] > 0) + sys->print("dial(%s)\n", m.dialstr); + (ok, cx) := sys->dial(m.dialstr, ""); + if (ok == -1){ + S.msg("Echec de la connexion"); + T.state = Local; + send(ref Event.Eproto(Pscreen, Mmodem, Cindicators, "",0,0,0)); + if(debug['m'] > 0) + sys->print("can't dial %s: %r\n", m.dialstr); + break; + } + m.fd = sys->open(cx.dir + "/data", Sys->ORDWR); + m.cfd = cx.cfd; + if(len m.dialstr >= 3 && m.dialstr[0:3] == "tcp") + m.addparity = 0; # Internet gateway apparently doesn't require parity + if(m.fd != nil) { + S.msg(nil); + m.state = MSconnected; + T.state = Online; + send(ref Event.Eproto(Pscreen, Mmodem, Cindicators, "",0,0,0)); + } + T.terminalid = TERMINALID1; + } + if(m.fd != nil) { + pidc := chan of int; + spawn m.reader(pidc); + m.pid = <-pidc; + } + Cdisconnect => + if(m.pid != 0) { + S.msg("Déconnexion ..."); + m.state = MSdisconnecting; + } + if(m.connect == Direct) + hangup(m); + else + nethangup(m); + Cplay => # for testing + case e.s { + "play" => + replay(m); + } + Crequestecp => + if(m.spec & Ecp){ # for testing: if already active, force an error + m.errforce = 1; + break; + } + m.write(array[] of {byte SEP, byte 16r4A}); +sys->print("sending request for ecp\n"); + Cstartecp => + m.spec |= Ecp; + m.seq = 0; # not in spec + m.waitsyn = 0; # not in spec + Cstopecp => + m.spec &= ~Ecp; + * => break; + } + } + b := <- m.rd => + if(debug['m'] > 0){ + fprint(stderr, "Modem -> %s\n", dump(b,len b)); + } + if(b == nil) { + m.pid = 0; + case m.state { + MSdialing => + S.msg("Echec appel"); + MSdisconnecting => + S.msg(nil); + } + m.state = MSstart; + T.state = Local; + send(ref Event.Eproto(Pscreen, Mmodem, Cscreenon, "",0,0,0)); + send(ref Event.Eproto(Pscreen, Mmodem, Cindicators, "",0,0,0)); + break; + } + m.runstate(b); + } + } + if(m.pid != 0) + kill(m.pid); + send(nil); +} + +Modem.quit(nil: self ref Modem) +{ +} + +Modem.runstate(m: self ref Modem, data: array of byte) +{ + if(debug['m']>0) + sys->print("runstate %d %s\n", m.state, dump(data, len data)); + case m.state { + MSstart => ; + MSdialing => + for(i:=0; i<len data; i++) { + ch := int data[i]; + if(ch != '\n' && ch != '\r') { + m.saved[len m.saved] = ch; + continue; + } + (code, str) := seenreply(m.saved); + case code { + Noise or Ok => ; + Success => + S.msg(nil); + m.state = MSconnected; + T.state = Online; + send(ref Event.Eproto(Pscreen, Mmodem, Cindicators, "",0,0,0)); + Failure => + hangup(m); + S.msg(str); + m.state = MSstart; + T.state = Local; + send(ref Event.Eproto(Pscreen, Mmodem, Cindicators, "",0,0,0)); + } + m.saved = ""; + } + MSconnected => + send(ref Event.Edata(m.m.path, Mmodem, data)); + MSdisconnecting => ; + } +} + +Modem.write(m: self ref Modem, data: array of byte): int +{ + if(m.fd == nil) + return -1; + if(len data == 0) + return 0; + if(m.addparity){ + # unfortunately must copy data to add parity for direct modem connection + pa := array[len data] of byte; + for(i := 0; i<len data; i++) + pa[i] = partab[int data[i] & 16r7F]; + data = pa; + } + if(debug['m']>0) + sys->print("WRITE %s\n", dump(data, len data)); + return sys->write(m.fd, data, len data); +} + +# +# minitel error correction protocol +# +# SYN, SYN, block number start of retransmission +# NUL ignored +# DLE escapes {DLE, SYN, NACK, NUL} +# NACK, block restart request +# + +crctab: array of int; +Crcpoly: con 16r9; # crc7 = x^7+x^3+1 + +# precalculate the CRC7 remainder for all bytes + +mktabs() +{ + crctab = array[256] of int; + for(c := 0; c < 256; c++){ + v := c; + crc := 0; + for(i := 0; i < 8; i++){ + crc <<= 1; # align remainder's MSB with value's + if((v^crc) & 16r80) + crc ^= Crcpoly; + v <<= 1; + } + crctab[c] = (crc<<1) & 16rFE; # pre-align the result to save <<1 later + } +} + +# return the index of the first non-NUL character (the start of a block) + +nextblock(a: array of byte, i: int, n: int): int +{ + for(; i < n; i++) + if(a[i] != byte NUL) + break; + return i; +} + +# return the data in the ecp block in a[0:Ecplen] (return nil for bad format) + +decode(a: array of byte): array of byte +{ + if(debug['M']>0) + sys->print("DECODE: %s\n", dump(a, Ecplen)); + badpar := 0; + oldcrc := int a[Ecplen-2]; + crc := 0; + op := 0; + dle := 0; + for(i:=0; i<Ecplen-2; i++){ # first byte is high-order byte of polynomial (MSB first) + c := int a[i]; + nc := c & 16r7F; # strip parity + if((c^int partab[nc]) & 16r80) + badpar++; + crc = crctab[crc ^ c]; + # collapse DLE sequences + if(!dle){ + if(nc == DLE && i+1 < Ecplen-2){ + dle = 1; + continue; + } + if(nc == NUL) + continue; # strip non-escaped NULs + } + dle = 0; + a[op++] = byte nc; + } + if(badpar){ + if(debug['E'] > 0) + sys->print("bad parity\n"); + return nil; + } + crc = (crc>>1)&16r7F; + if(int partab[crc] != oldcrc){ + if(debug['E'] > 0) + sys->print("bad crc: in %ux got %ux\n", oldcrc, int partab[crc]); + return nil; + } + b := array[op] of byte; + b[0:] = a[0:op]; + if(debug['M'] > 0) + sys->print("OUT: %s [%x :: %x]\n", dump(b,op), crc, oldcrc); + return b; +} + +Modem.reader(m: self ref Modem, pidc: chan of int) +{ + pidc <-= sys->pctl(0, nil); + if(crctab == nil) + mktabs(); + a := array[Sys->ATOMICIO] of byte; + inbuf := 0; + while(m.fd != nil) { + while((n := read(m.fd, a[inbuf:], len a-inbuf)) > 0){ + n += inbuf; + inbuf = 0; + if((m.spec & Ecp) == 0){ + b := array[n] of byte; + for(i := 0; i<n; i++) + b[i] = byte (int a[i] & 16r7F); # strip parity + m.rd <-= b; + }else{ + #sys->print("IN: %s\n", dump(a,n)); + i := 0; + if(m.waitsyn){ + sys->print("seeking SYN #%x\n", m.seq); + syn := byte (SYN | 16r80); + lim := n-3; + for(; i <= lim; i++) + if(a[i] == syn && a[i+1] == syn && (int a[i+2]&16r0F) == m.seq){ + i += 3; + m.waitsyn = 0; + sys->print("found SYN #%x@%d\n", m.seq, i-3); + break; + } + } + lim := n-Ecplen; + for(; (i = nextblock(a, i, n)) <= lim; i += Ecplen){ + b := decode(a[i:]); + if(m.errforce || b == nil){ + m.errforce = 0; + b = array[2] of byte; + b[0] = byte NACK; + b[1] = byte (m.seq | 16r40); + sys->print("NACK #%x\n", m.seq); + m.write(b); + m.waitsyn = 1; + i = n; # discard rest of block + break; + } + m.seq = (m.seq+1) & 16rF; # mod 16 counter + m.rd <-= b; + } + if(i < n){ + a[0:] = a[i:n]; + inbuf = n-i; + } + } + } + if(n <= 0) + break; + } +# m.fd = nil; + m.rd <-= nil; +} + +playfd: ref Sys->FD; +in_code, in_char: con iota; + +replay(m: ref Modem) +{ + buf := array[8192] of byte; + DMAX: con 10; + d := 0; + da := array[DMAX] of byte; + playfd = nil; + if(playfd == nil) + playfd = sys->open("minitel.txt", Sys->OREAD); + if(playfd == nil) + return; + nl := 1; + discard := 1; + state := in_code; + hs := ""; + start := 0; +mainloop: + for(;;) { + n := sys->read(playfd, buf, len buf); + if(n <= 0) + break; + for(i:=0; i<n; i++) { + ch := int buf[i]; + if(nl) + case ch { + '>' => discard = 0; + '<' => discard = 1; + if(start) + sys->sleep(1000); + '{' => start = 1; + '}' => break mainloop; + } + if(ch == '\n') + nl = 1; + else + nl = 0; + if(discard) + continue; + if(!start) + continue; + if(state == in_code && ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z'))) + hs[len hs] = ch; + else if(ch == '(') { + state = in_char; + (v, nil) := toint(hs, 16); + da[d++] = byte v; + if(d == DMAX) { + send(ref Event.Edata(m.m.path, Mmodem, da)); + d = 0; + da = array[DMAX] of byte; + sys->sleep(50); + } + hs = ""; + }else if(ch == ')') + state = in_code; + } + } + playfd = nil; + +} + +kill(pid : int) +{ + prog := "#p/" + string pid + "/ctl"; + fd := sys->open(prog, Sys->OWRITE); + if (fd != nil) { + cmd := array of byte "kill"; + sys->write(fd, cmd, len cmd); + } +} + + +# Modem stuff + + +# modem return codes +Ok, Success, Failure, Noise, Found: con iota; + +# +# modem return messages +# +Msg: adt { + text: string; + trans: string; + code: int; +}; + +msgs: array of Msg = array [] of { + ("OK", "Ok", Ok), + ("NO CARRIER", "No carrier", Failure), + ("ERROR", "Bad modem command", Failure), + ("NO DIALTONE", "No dial tone", Failure), + ("BUSY", "Busy tone", Failure), + ("NO ANSWER", "No answer", Failure), + ("CONNECT", "", Success), +}; + +msend(m: ref Modem, x: string): int +{ + a := array of byte x; + return sys->write(m.fd, a, len a); +} + +# +# apply a string of commands to modem +# +apply(m: ref Modem, s: string): int +{ + buf := ""; + for(i := 0; i < len s; i++){ + c := s[i]; + buf[len buf] = c; # assume no Unicode + if(c == '\r' || i == (len s -1)){ + if(c != '\r') + buf[len buf] = '\r'; + if(msend(m, buf) < 0) + return Failure; + buf = ""; + } + } + return Ok; +} + +openmodem(m: ref Modem, dev: string): int +{ + m.fd = sys->open(dev, Sys->ORDWR); + m.cfd = sys->open(dev+"ctl", Sys->ORDWR); + if(m.fd == nil || m.cfd == nil) + return -1; +# hangup(m); +# m.fd = sys->open(dev, Sys->ORDWR); +# m.cfd = sys->open(dev+"ctl", Sys->ORDWR); +# if(m.fd == nil || m.cfd == nil) +# return -1; + return 0; +} + +hangup(m: ref Modem) +{ + sys->sleep(1020); + msend(m, "+++"); + sys->sleep(1020); + apply(m, "ATH0"); + m.fd = nil; +# sys->write(m.cfd, array of byte "f", 1); + sys->write(m.cfd, array of byte "h", 1); + m.cfd = nil; + # HACK: shannon softmodem "off-hook" bug fix + sys->open("/dev/modem", Sys->OWRITE); +} + +nethangup(m: ref Modem) +{ + m.fd = nil; + sys->write(m.cfd, array of byte "hangup", 6); + m.cfd = nil; +} + + +# +# check `s' for a known reply or `substr' +# +seenreply(s: string): (int, string) +{ + for(k := 0; k < len msgs; k++) + if(len s >= len msgs[k].text && s[0:len msgs[k].text] == msgs[k].text) { + return (msgs[k].code, msgs[k].trans); + } + return (Noise, s); +} + +contains(s, t: string): int +{ + if(t == nil) + return 1; + if(s == nil) + return 0; + n := len t; + for(i := 0; i+n <= len s; i++) + if(s[i:i+n] == t) + return 1; + return 0; +} + +dialout(m: ref Modem) +{ + if(m.initstr != nil) + apply(m, "AT"+m.initstr); + if(m.dialstr != nil) { + apply(m, "ATD"+m.dialstr); + m.lastdialstr = m.dialstr; + m.dialstr = nil; + } +} diff --git a/appl/wm/minitel/screen.b b/appl/wm/minitel/screen.b new file mode 100644 index 00000000..4313d48d --- /dev/null +++ b/appl/wm/minitel/screen.b @@ -0,0 +1,1610 @@ +# +# Occasional references are made to sections and tables in the +# France Telecom Minitel specification +# +# Copyright © 1998 Vita Nuova Limited. All rights reserved. +# + +include "mdisplay.m"; + +disp: MDisplay; + +Rect, Point : import Draw; + +# display character sets +videotex, semigraphic, french, american :import MDisplay; + +# display foreground colour attributes +fgBlack, fgBlue, fgRed, fgMagenta, +fgGreen, fgCyan, fgYellow, fgWhite :import MDisplay; + +# display background colour attributes +bgBlack, bgBlue, bgRed, bgMagenta, +bgGreen, bgCyan, bgYellow, bgWhite :import MDisplay; + +fgMask, bgMask : import MDisplay; + +# display formatting attributes +attrB, attrW, attrH, attrP, attrF, attrC, attrL, attrD :import MDisplay; + +# Initial attributes - white on black +ATTR0: con fgWhite|bgBlack&~(attrB|attrW|attrH|attrP|attrF|attrC|attrL|attrD); + +# special features +Cursor, Scroll, Insert + : con (1 << iota); + +# Screen states +Sstart, Sss2, Sesc, Srepeat, Saccent, Scsi0, Scsi1, Sus0, Sus1, Sskip, +Siso2022, Siso6429, Stransparent, Sdrcs, Sconceal, Swaitfor + : con iota; + +# Filter states +FSstart, FSesc, FSsep, FS6429, FS2022: con iota; + +Screen: adt { + m: ref Module; # common attributes + ctxt: ref Draw->Context; + in: chan of ref Event; # from the terminal + + image: ref Draw->Image; # Mdisplay image + dispr40, dispr80: Rect; # 40 and 80 column display region + oldtmode: int; # old terminal mode + rows: int; # number of screen rows (25 for minitel) + cols: int; # number of screen cols (40 or 80) + cset: int; # current display charset + + pos: Point; # current writing position (x:1, y:0) + attr: int; # display attribute set + spec: int; # special features + savepos: Point; # `pos' before moving to row zero + saveattr: int; # `attr' before moving to row zero + savech: int; # last character `Put' + delimit: int; # attr changed, make next space a delimiter + cursor: int; # update cursor soon + + state: int; # recogniser state + a0: int; # recogniser arg 0 + a1: int; # recogniser arg 1 + + fstate: int; # filter state + fsaved: array of byte; # filter `chars so far' + badp: int; # filter because of bad parameter + + ignoredata: int; # ignore data from + + init: fn(s: self ref Screen, ctxt: ref Draw->Context, r40, r80: Rect); + reset: fn(s: self ref Screen); + run: fn(s: self ref Screen); + quit: fn(s: self ref Screen); + setmode: fn(s: self ref Screen, tmode: int); + runstate: fn(s: self ref Screen, data: array of byte); + put: fn(s: self ref Screen, str: string); + msg: fn(s: self ref Screen, str: string); +}; + +Screen.init(s: self ref Screen, ctxt: ref Draw->Context, r40, r80: Rect) +{ + disp = load MDisplay MDisplay->PATH; + if(disp == nil) + fatal("can't load the display module: "+MDisplay->PATH); + + s.m = ref Module(0, 0); + s.ctxt = ctxt; + s.dispr40 = r40; + s.dispr80 = r80; + s.oldtmode = -1; + s.in = chan of ref Event; + disp->Init(s.ctxt); + s.reset(); + s.pos = Point(1, 1); + s.savech = 0; + s.cursor = 1; + s.ignoredata = 0; + s.fstate = FSstart; +} + +Screen.reset(s: self ref Screen) +{ + s.setmode(T.mode); + indicators(s); + s.state = Sstart; +} + +Screen.run(s: self ref Screen) +{ +Runloop: + for(;;) alt { + ev := <- s.in => + pick e := ev { + Equit => + break Runloop; + Eproto => + case e.cmd { + Creset => + s.reset(); + Cproto => + case e.a0 { + START => + case e.a1 { + SCROLLING => + s.spec |= Scroll; + } + STOP => + case e.a1 { + SCROLLING => + s.spec &= ~Scroll; + } + MIXED => + case e.a1 { + MIXED1 => # videotex -> mixed + if(T.mode != Mixed) + s.setmode(Mixed); + T.mode = Mixed; + MIXED2 => # mixed -> videotex + if(T.mode != Videotex) + s.setmode(Videotex); + T.mode = Videotex; + } + } + Ccursor => # update the cursor soon + s.cursor = 1; + Cindicators => + indicators(s); + Cscreenoff => + s.ignoredata = 1; + s.state = Sstart; + Cscreenon => + s.ignoredata = 0; + * => break; + } + Edata => + if(s.ignoredata) + continue; + oldpos := s.pos; + oldspec := s.spec; + da := filter(s, e.data); + while(len da > 0) { + s.runstate(da[0]); + da = da[1:]; + } + + if(s.pos.x != oldpos.x || s.pos.y != oldpos.y || (s.spec&Cursor)^(oldspec&Cursor)) + s.cursor = 1; + if(s.cursor) { + if(s.spec & Cursor) + disp->Cursor(s.pos); + else + disp->Cursor(Point(-1,-1)); + s.cursor = 0; + refresh(); + } else if(e.from == Mkeyb) + refresh(); + } + } + send(nil); +} + +# row0 indicators (1.2.2) +indicators(s: ref Screen) +{ + col: int; + ch: string; + + attr := fgWhite|bgBlack; + case T.state { + Local => + ch = "F"; + Connecting => + ch = "C"; + attr |= attrF; + Online => + ch = "C"; + } + if(s.cols == 40) { + col = 39; + attr |= attrP; + } else + col = 77; + disp->Put(ch, Point(col, 0), videotex, attr, 0); +} + +Screen.setmode(s: self ref Screen, tmode: int) +{ + dispr: Rect; + delims: int; + ulheight: int; + s.rows = 25; + s.spec = 0; + s.attr = s.saveattr = ATTR0; + s.delimit = 0; + s.pos = s.savepos = Point(-1, -1); + s.cursor = 1; + case tmode { + Videotex => + s.cset = videotex; + s.cols = 40; + dispr = s.dispr40; + delims = 1; + ulheight = 2; + s.pos = Point(1,1); + s.spec &= ~Cursor; + Mixed => +# s.cset = french; + s.cset = videotex; + s.cols = 80; + dispr = s.dispr80; + delims = 0; + ulheight = 1; + s.spec |= Scroll; + s.pos = Point(1, 1); + Ascii => + s.cset = french; + s.cols = 80; + dispr = s.dispr80; + delims = 0; + ulheight = 1; + }; + if(tmode != s.oldtmode) { + (nil, s.image) = disp->Mode(((0,0),(0,0)), 0, 0, 0, 0, nil); + T.layout(s.cols); + fontpath := sprint("/fonts/minitel/f%dx%d", s.cols, s.rows); + (nil, s.image) = disp->Mode(dispr, s.cols, s.rows, ulheight, delims, fontpath); + T.setkbmode(tmode); + } + disp->Reveal(0); # concealing enabled (1.2.2) + disp->Cursor(Point(-1,-1)); + s.oldtmode = tmode; +} + +Screen.quit(nil: self ref Screen) +{ + disp->Quit(); +} + +Screen.runstate(s: self ref Screen, data: array of byte) +{ + while(len data > 0) + case T.mode { + Videotex => + data = vstate(s, data); + Mixed => + data = mstate(s, data); + Ascii => + data = astate(s, data); + }; +} + +# process a byte from set C0 +vc0(s: ref Screen, ch: int) +{ + case ch { +# SOH => # not in spec, wait for 16r04 +# s.a0 = 16r04; +# s.state = Swaitfor; + SS2 => + s.state = Sss2; + SYN => + s.state = Sss2; # not in the spec, but acts like SS2 + ESC => + s.state = Sesc; + SO => + s.cset = semigraphic; + s.attr &= ~(attrH|attrW|attrP); # 1.2.4.2 + s.attr &= ~attrL; # 1.2.4.3 + SI => + s.cset = videotex; + s.attr &= ~attrL; # 1.2.4.3 + s.attr &= ~(attrH|attrW|attrP); # some servers seem to assume this too + SEP or SS3 => # 1.2.7 + s.state = Sskip; + BS => + if(s.pos.x == 1) { + if(s.pos.y == 0) + break; + if(s.pos.y == 1) + s.pos.y = s.rows - 1; + else + s.pos.y -= 1; + s.pos.x = s.cols; + } else + s.pos.x -= 1; + HT => + if(s.pos.x == s.cols) { + if(s.pos.y == 0) + break; + if(s.pos.y == s.rows - 1) + s.pos.y = 1; + else + s.pos.y += 1; + s.pos.x = 1; + } else + s.pos.x += 1; + LF => + if(s.pos.y == s.rows - 1) + if(s.spec&Scroll) + scroll(1, 1); + else + s.pos.y = 1; + else if(s.pos.y == 0) { # restore attributes on leaving row zero + s.pos = s.savepos; + s.attr = s.saveattr; + } else + s.pos.y += 1; + VT => + if(s.pos.y == 1) + if(s.spec&Scroll) + scroll(1, -1); + else + s.pos.y = s.rows - 1; + else if(s.pos.y == 0) + break; + else + s.pos.y -= 1; + CR => + s.pos.x = 1; + CAN => + cols := s.cols - s.pos.x + 1; + disp->Put(dup(' ', cols), Point(s.pos.x,s.pos.y), s.cset, s.attr, 0); + US => + # expect US row, col + s.state = Sus0; + FF => + s.cset = videotex; + s.attr = ATTR0; + s.pos = Point(1,1); + s.spec &= ~Cursor; + s.cursor = 1; + clear(s); + RS => + s.cset = videotex; + s.attr = ATTR0; + s.pos = Point(1,1); + s.spec &= ~Cursor; + s.cursor = 1; + CON => + s.spec |= Cursor; + s.cursor = 1; + COFF => + s.spec &= ~Cursor; + s.cursor = 1; + REP => + # repeat + s.state = Srepeat; + NUL => + # padding character - ignore, but may appear anywhere + ; + BEL => + # ah ... + ; + } +} + +# process a byte from the set c1 - introduced by the ESC character +vc1(s: ref Screen, ch: int) +{ + if(ISC0(ch)) { + s.state = Sstart; + vc0(s, ch); + return; + } + if(ch >= 16r20 && ch <= 16r2f) { + if(ch == 16r25) + s.state = Stransparent; + else if(ch == 16r23) + s.state = Sconceal; + else + s.state = Siso2022; + s.a0 = s.a1 = 0; + return; + } + + fg := bg := -1; + case ch { + 16r35 or + 16r36 or + 16r37 => + s.state = Sskip; # skip next char unless C0 + return; + + 16r5b => # CSI sequence + s.a0 = s.a1 = 0; + if(s.pos.y > 0) # 1.2.5.2 + s.state = Scsi0; + return; + + # foreground colour + 16r40 => fg = fgBlack; + 16r41 => fg = fgRed; + 16r42 => fg = fgGreen; + 16r43 => fg = fgYellow; + 16r44 => fg = fgBlue; + 16r45 => fg = fgMagenta; + 16r46 => fg = fgCyan; + 16r47 => fg = fgWhite; + + # background colour + 16r50 => bg = bgBlack; + 16r51 => bg = bgRed; + 16r52 => bg = bgGreen; + 16r53 => bg = bgYellow; + 16r54 => bg = bgBlue; + 16r55 => bg = bgMagenta; + 16r56 => bg = bgCyan; + 16r57 => bg = bgWhite; + + # flashing + 16r48 => s.attr |= attrF; + 16r49 => s.attr &= ~attrF; + + # conceal (serial attribute) + 16r58 => s.attr |= attrC; + s.delimit = 1; + 16r5f => s.attr &= ~attrC; + s.delimit = 1; + + # start lining (+separated graphics) (serial attribute) + 16r5a => s.attr |= attrL; + s.delimit = 1; + 16r59 => s.attr &= ~attrL; + s.delimit = 1; + + # reverse polarity + 16r5d => s.attr |= attrP; + 16r5c => s.attr &= ~attrP; + + # normal size + 16r4c => + s.attr &= ~(attrW|attrH); + + # double height + 16r4d => + if(s.pos.y < 2) + break; + s.attr &= ~(attrW|attrH); + s.attr |= attrH; + + # double width + 16r4e => + if(s.pos.y < 1) + break; + s.attr &= ~(attrW|attrH); + s.attr |= attrW; + + # double size + 16r4f => + if(s.pos.y < 2) + break; + s.attr |= (attrW|attrH); + } + if(fg >= 0) { + s.attr &= ~fgMask; + s.attr |= fg; + } + if(bg >= 0) { + s.attr &= ~bgMask; + s.attr |= bg; + s.delimit = 1; + } + s.state = Sstart; +} + + +# process a SS2 character +vss2(s: ref Screen, ch: int) +{ + if(ISC0(ch)) { + s.state = Sstart; + vc0(s, ch); + return; + } + case ch { + 16r41 or # grave # 5.1.2 + 16r42 or # acute + 16r43 or # circumflex + 16r48 or # umlaut + 16r4b => # cedilla + s.a0 = ch; + s.state = Saccent; + return; + 16r23 => ch = '£'; # Figure 2.8 + 16r24 => ch = '$'; + 16r26 => ch = '#'; + 16r27 => ch = '§'; + 16r2c => ch = 16rc3; # '←'; + 16r2d => ch = 16rc0; # '↑'; + 16r2e => ch = 16rc4; # '→'; + 16r2f => ch = 16rc5; # '↓'; + 16r30 => ch = '°'; + 16r31 => ch = '±'; + 16r38 => ch = '÷'; + 16r3c => ch = '¼'; + 16r3d => ch = '½'; + 16r3e => ch = '¾'; + 16r7a => ch = 'œ'; + 16r6a => ch = 'Œ'; + 16r7b => ch = 'ß'; + } + s.put(tostr(ch)); + s.savech = ch; + s.state = Sstart; +} + +# process CSI functions +vcsi(s: ref Screen, ch: int) +{ + case s.state { + Scsi0 => + case ch { + # move cursor up n rows, stop at top of screen + 'A' => + s.pos.y -= s.a0; + if(s.pos.y < 1) + s.pos.y = 1; + + # move cursor down n rows, stop at bottom of screen + 'B' => + s.pos.y += s.a0; + if(s.pos.y >= s.rows) + s.pos.y = s.rows - 1; + + # move cursor n columns right, stop at edge of screen + 'C' => + s.pos.x += s.a0; + if(s.pos.x > s.cols) + s.pos.x = s.cols; + + # move cursor n columns left, stop at edge of screen + 'D' => + s.pos.x -= s.a0; + if(s.pos.x < 1) + s.pos.x = 1; + + # direct cursor addressing + ';' => + s.state = Scsi1; + return; + + 'J' => + case s.a0 { + # clears from the cursor to the end of the screen inclusive + 0 => + rowclear(s.pos.y, s.pos.x, s.cols); + for(r:=s.pos.y+1; r<s.rows; r++) + rowclear(r, 1, s.cols); + # clears from the beginning of the screen to the cursor inclusive + 1 => + for(r:=1; r<s.pos.y; r++) + rowclear(r, 1, s.cols); + rowclear(s.pos.y, 1, s.pos.x); + # clears the entire screen + 2 => + clear(s); + } + + 'K' => + case s.a0 { + # clears from the cursor to the end of the row + 0 => rowclear(s.pos.y, s.pos.x, s.cols); + + # clears from the start of the row to the cursor + 1 => rowclear(s.pos.y, 1, s.pos.x); + + # clears the entire row in which the cursor is positioned + 2 => rowclear(s.pos.y, 1, s.cols); + } + + # deletes n characters from cursor position + 'P' => + rowclear(s.pos.y, s.pos.x, s.pos.x+s.a0-1); + + # inserts n characters from cursor position + '@' => + disp->Put(dup(' ', s.a0), Point(s.pos.x,s.pos.y), s.cset, s.attr, 1); + + # starts cursor insert mode + 'h' => + if(s.a0 == 4) + s.spec |= Insert; + + 'l' => # ends cursor insert mode + if(s.a0 == 4) + s.spec &= ~Insert; + + # deletes n rows from cursor row + 'M' => + scroll(s.pos.y, s.a0); + + # inserts n rows from cursor row + 'L' => + scroll(s.pos.y, -1*s.a0); + } + s.state = Sstart; + Scsi1 => + case ch { + # direct cursor addressing + 'H' => + if(s.a0 > 0 && s.a0 < s.rows && s.a1 > 0 && s.a1 <= s.cols) + s.pos = Point(s.a1, s.a0); + } + s.state = Sstart; + } +} + +# Screen state - Videotex mode +vstate(s: ref Screen, data: array of byte): array of byte +{ + i: int; + for(i = 0; i < len data; i++) { + ch := int data[i]; + + if(debug['s']) { + cs:=""; + if(s.cset==videotex) cs = "v"; else cs="s"; + fprint(stderr, "vstate %d, %ux (%c) %.4ux %.4ux %s (%d,%d)\n", s.state, ch, ch, s.attr, s.spec, cs, s.pos.y, s.pos.x); + } + case s.state { + Sstart => + if(ISG0(ch) || ch == SP) { + n := 0; + str := ""; + while(i < len data) { + ch = int data[i]; + if(ISG0(ch) || ch == SP) + str[n++] = int data[i++]; + else { + i--; + break; + } + } + if(n > 0) { + if(debug['s']) + fprint(stderr, "vstate puts(%s)\n", str); + s.put(str); + s.savech = str[n-1]; + } + } else if(ISC0(ch)) + vc0(s, ch); + else if(ch == DEL) { + if(s.cset == semigraphic) + ch = 16r5f; + s.put(tostr(ch)); + s.savech = ch; + } + Sss2 => + if(ch == NUL) # 1.2.6.1 + continue; + if(s.cset == semigraphic) # 1.2.3.4 + continue; + vss2(s, ch); + Sesc => + if(ch == NUL) + continue; + vc1(s, ch); + Srepeat => + # byte from `columns' 4 to 7 gives repeat count on 6 bits + # of the last `Put' character + if(ch == NUL) + continue; + if(ISC0(ch)) { + s.state = Sstart; + vc0(s, ch); + break; + } + if(ch >= 16r40 && ch <= 16r7f) + s.put(dup(s.savech, (ch-16r40))); + s.state = Sstart; + Saccent => + case s.a0 { + 16r41 => # grave + case ch { + 'a' => ch = 'à'; + 'e' => ch = 'è'; + 'u' => ch = 'ù'; + } + 16r42 => # acute + case ch { + 'e' => ch = 'é'; + } + 16r43 => # circumflex + case ch { + 'a' => ch = 'â'; + 'e' => ch = 'ê'; + 'i' => ch = 'î'; + 'o' => ch = 'ô'; + 'u' => ch = 'û'; + } + 16r48 => # umlaut + case ch { + 'a' => ch = 'ä'; + 'e' => ch = 'ë'; + 'i' => ch = 'ï'; + 'o' => ch = 'ö'; + 'u' => ch = 'ü'; + } + 16r4b => # cedilla + case ch { + 'c' => ch = 'ç'; + } + } + s.put(tostr(ch)); + s.savech = ch; + s.state = Sstart; + Scsi0 => + if(ch >= 16r30 && ch <= 16r39) { + s.a0 *= 10; + s.a0 += (ch - 16r30); + } else if((ch >= 16r20 && ch <= 16r29) || (ch >= 16r3a && ch <= 16r3f)) { # 1.2.7 + s.a0 = 0; + s.state = Siso6429; + } else + vcsi(s, ch); + Scsi1 => + if(ch >= 16r30 && ch <= 16r39) { + s.a1 *= 10; + s.a1 += (ch - 16r30); + } else + vcsi(s, ch); + Sus0 => + if(ch == 16r23) { # start DRCS definition + s.state = Sdrcs; + s.a0 = 0; + break; + } + if(ch >= 16r40 && ch < 16r80) + s.a0 = (ch - 16r40); + else if(ch >= 16r30 && ch <= 16r32) + s.a0 = (ch - 16r30); + else + s.a0 = -1; + s.state = Sus1; + Sus1 => + if(ch >= 16r40 && ch < 16r80) + s.a1 = (ch - 16r40); + else if(ch >= 16r30 && ch <= 16r39) { + s.a1 = (ch - 16r30); + s.a0 = s.a0*10 + s.a1; # shouldn't be used any more + s.a1 = 1; + } else + s.a1 = -1; + # US row, col : this is how you get to row zero + if(s.a0 >= 0 && s.a0 < s.rows && s.a1 > 0 && s.a1 <= s.cols) { + if(s.a0 == 0 && s.pos.y > 0) { + s.savepos = s.pos; + s.saveattr = s.attr; + } + s.pos = Point(s.a1, s.a0); + s.delimit = 0; # 1.2.5.3, don't reset serial attributes + s.attr = ATTR0; + s.cset = videotex; + } + s.state = Sstart; + Sskip => + # swallow the next character unless from C0 + s.state = Sstart; + if(ISC0(ch)) + vc0(s, ch); + Swaitfor => + # ignore characters until the character in a0 inclusive + if(ch == s.a0) + s.state = Sstart; + Siso2022 => + # 1.2.7 + # swallow (upto) 3 characters from column 2, + # then 1 character from columns 3 to 7 + if(ch == NUL) + continue; + if(ISC0(ch)) { + s.state = Sstart; + vc0(s, ch); + break; + } + s.a0++; + if(s.a0 <= 3) { + if(ch >= 16r20 && ch <= 16r2f) + break; + } + if (s.a0 <= 4 && ch >= 16r30 && ch <= 16r7f) { + s.state = Sstart; + break; + } + s.state = Sstart; + s.put(tostr(DEL)); + Siso6429 => + # 1.2.7 + # swallow characters from column 3, + # or column 2, then 1 from column 4 to 7 + if(ISC0(ch)) { + s.state = Sstart; + vc0(s, ch); + break; + } + if(ch >= 16r20 && ch <= 16r3f) + break; + if(ch >= 16r40 && ch <= 16r7f) { + s.state = Sstart; + break; + } + s.state = Sstart; + s.put(tostr(DEL)); + Stransparent => + # 1.2.7 + # ignore all codes until ESC, 25, 40 or ESC, 2F, 3F + # progress in s.a0 and s.a1 + match := array [] of { + array [] of { ESC, 16r25, 16r40 }, + array [] of { ESC, 16r2f, 16r3f }, + }; + if(ch == ESC) { + s.a0 = s.a1 = 1; + break; + } + if(ch == match[0][s.a0]) + s.a0++; + else + s.a0 = 0; + if(ch == match[1][s.a1]) + s.a1++; + else + s.a1 = 0; + if(s.a0 == 3 || s.a1 == 3) + s.state = Sstart; + Sdrcs => + if(s.a0 > 0) { # fixed number of bytes to skip in a0 + s.a0--; + if(s.a0 == 0) { + s.state = Sstart; + break; + } + } else if(ch == US) # US XX YY - end of DRCS + s.state = Sus0; + else if(ch == 16r20) # US 23 20 20 20 4[23] 49 + s.a0 = 4; + Sconceal => + # 1.2.4.4 + # ESC 23 20 58 - Conceal fields + # ESC 23 20 5F - Reveal fields + # ESC 23 21 XX - Filter + # progress in s.a0 + case s.a0 { + 0 => + if(ch == 16r20 || ch == 16r21) + s.a0 = ch; + 16r20 => + case ch { + 16r58 => + disp->Reveal(0); + disp->Refresh(); + 16r5f => + disp->Reveal(1); + disp->Refresh(); + } + s.state = Sstart; + 16r21 => + s.state = Sstart; + } + } + } + if (i < len data) + return data[i:]; + else + return nil; +} + +# Screen state - Mixed mode +mstate(s: ref Screen, data: array of byte): array of byte +{ + i: int; +Stateloop: + for(i = 0; i < len data; i++) { + ch := int data[i]; + + if(debug['s']) { + cs:=""; + if(s.cset==videotex) cs = "v"; else cs="s"; + fprint(stderr, "mstate %d, %ux (%c) %.4ux %.4ux %s (%d,%d)\n", s.state, ch, ch, s.attr, s.fstate, cs, s.pos.y, s.pos.x); + } + case s.state { + Sstart => + if(ISG0(ch) || ch == SP) { + n := 0; + str := ""; + while(i < len data) { + ch = int data[i]; + if(ISG0(ch) || ch == SP) + str[n++] = int data[i++]; + else { + i--; + break; + } + } + if(n > 0) { + if(debug['s']) + fprint(stderr, "mstate puts(%s)\n", str); + s.put(str); + s.savech = str[n-1]; + } + } else if(ISC0(ch)) + mc0(s, ch); + else if(ch == DEL) { + if(s.cset == semigraphic) + ch = 16r5f; + s.put(tostr(ch)); + s.savech = ch; + } + Sesc => + if(ch == NUL) + continue; + mc1(s, ch); + Scsi0 => + if(ch >= 16r30 && ch <= 16r39) { + s.a0 *= 10; + s.a0 += (ch - 16r30); + } else if(ch == '?') { + s.a0 = '?'; + } else + mcsi(s, ch); + if(T.mode != Mixed) # CSI ? { changes to Videotex mode + break Stateloop; + Scsi1 => + if(ch >= 16r30 && ch <= 16r39) { + s.a1 *= 10; + s.a1 += (ch - 16r30); + } else + mcsi(s, ch); + Sus0 => + if(ch >= 16r40 && ch < 16r80) + s.a0 = (ch - 16r40); + else if(ch >= 16r30 && ch <= 16r32) + s.a0 = (ch - 16r30); + else + s.a0 = -1; + s.state = Sus1; + Sus1 => + if(ch >= 16r40 && ch < 16r80) + s.a1 = (ch - 16r40); + else if(ch >= 16r30 && ch <= 16r39) { + s.a1 = (ch - 16r30); + s.a0 = s.a0*10 + s.a1; # shouldn't be used any more + s.a1 = 1; + } else + s.a1 = -1; + # US row, col : this is how you get to row zero + if(s.a0 >= 0 && s.a0 < s.rows && s.a1 > 0 && s.a1 <= s.cols) { + if(s.a0 == 0 && s.pos.y > 0) { + s.savepos = s.pos; + s.saveattr = s.attr; + } + s.pos = Point(s.a1, s.a0); + s.delimit = 0; # 1.2.5.3, don't reset serial attributes + s.attr = ATTR0; + s.cset = videotex; + } + s.state = Sstart; + Siso6429 => + # 1.2.7 + # swallow characters from column 3, + # or column 2, then 1 from column 4 to 7 + if(ISC0(ch)) { + s.state = Sstart; + mc0(s, ch); + break; + } + if(ch >= 16r20 && ch <= 16r3f) + break; + if(ch >= 16r40 && ch <= 16r7f) { + s.state = Sstart; + break; + } + s.state = Sstart; + s.put(tostr(DEL)); + } + } + if (i < len data) + return data[i:]; + else + return nil; + return nil; +} + +# process a byte from set C0 - Mixed mode +mc0(s: ref Screen, ch: int) +{ + case ch { + ESC => + s.state = Sesc; + SO => +# s.cset = french; + ; + SI => +# s.cset = american; + ; + BS => + if(s.pos.x > 1) + s.pos.x -= 1; + HT => + s.pos.x += 8; + if(s.pos.x > s.cols) + s.pos.x = s.cols; + LF or VT or FF => + if(s.pos.y == s.rows - 1) + if(s.spec&Scroll) + scroll(1, 1); + else + s.pos.y = 1; + else if(s.pos.y == 0) { # restore attributes on leaving row zero + if(ch == LF) { # 4.5 + s.pos = s.savepos; + s.attr = s.saveattr; + } + } else + s.pos.y += 1; + CR => + s.pos.x = 1; + CAN or SUB => # displays the error symbol - filled in rectangle + disp->Put(dup(16r5f, 1), Point(s.pos.x,s.pos.y), s.cset, s.attr, 0); + NUL => + # padding character - ignore, but may appear anywhere + ; + BEL => + # ah ... + ; + XON => # screen copying + ; + XOFF => # screen copying + ; + US => + # expect US row, col + s.state = Sus0; + } +} + +# process a byte from the set c1 - introduced by the ESC character - Mixed mode +mc1(s: ref Screen, ch: int) +{ + if(ISC0(ch)) { + s.state = Sstart; + mc0(s, ch); + return; + } + case ch { + 16r5b => # CSI sequence + s.a0 = s.a1 = 0; + if(s.pos.y > 0) # 1.2.5.2 + s.state = Scsi0; + return; + + 16r44 or # IND like LF + 16r45 => # NEL like CR LF + if(ch == 16r45) + s.pos.x = 1; + if(s.pos.y == s.rows - 1) + if(s.spec&Scroll) + scroll(1, 1); + else + s.pos.y = 1; + else if(s.pos.y == 0) { # restore attributes on leaving row zero + s.pos = s.savepos; + s.attr = s.saveattr; + } else + s.pos.y += 1; + 16r4d => # RI + if(s.pos.y == 1) + if(s.spec&Scroll) + scroll(1, -1); + else + s.pos.y = s.rows - 1; + else if(s.pos.y == 0) + break; + else + s.pos.y -= 1; + } + s.state = Sstart; +} + + +# process CSI functions - Mixed mode +mcsi(s: ref Screen, ch: int) +{ + case s.state { + Scsi0 => + case ch { + # move cursor up n rows, stop at top of screen + 'A' => + if(s.a0 == 0) + s.a0 = 1; + s.pos.y -= s.a0; + if(s.pos.y < 1) + s.pos.y = 1; + + # move cursor down n rows, stop at bottom of screen + 'B' => + if(s.a0 == 0) + s.a0 = 1; + s.pos.y += s.a0; + if(s.pos.y >= s.rows) + s.pos.y = s.rows - 1; + + # move cursor n columns right, stop at edge of screen + 'C' => + if(s.a0 == 0) + s.a0 = 1; + s.pos.x += s.a0; + if(s.pos.x > s.cols) + s.pos.x = s.cols; + + # move cursor n columns left, stop at edge of screen + 'D' => + if(s.a0 == 0) + s.a0 = 1; + s.pos.x -= s.a0; + if(s.pos.x < 1) + s.pos.x = 1; + + # second parameter + ';' => + s.state = Scsi1; + return; + + 'J' => + case s.a0 { + # clears from the cursor to the end of the screen inclusive + 0 => + rowclear(s.pos.y, s.pos.x, s.cols); + for(r:=s.pos.y+1; r<s.rows; r++) + rowclear(r, 1, s.cols); + # clears from the beginning of the screen to the cursor inclusive + 1 => + for(r:=1; r<s.pos.y; r++) + rowclear(r, 1, s.cols); + rowclear(s.pos.y, 1, s.pos.x); + # clears the entire screen + 2 => + clear(s); + } + + 'K' => + case s.a0 { + # clears from the cursor to the end of the row + 0 => rowclear(s.pos.y, s.pos.x, s.cols); + + # clears from the start of the row to the cursor + 1 => rowclear(s.pos.y, 1, s.pos.x); + + # clears the entire row in which the cursor is positioned + 2 => rowclear(s.pos.y, 1, s.cols); + } + + # inserts n characters from cursor position + '@' => + disp->Put(dup(' ', s.a0), Point(s.pos.x,s.pos.y), s.cset, s.attr, 1); + + # starts cursor insert mode + 'h' => + if(s.a0 == 4) + s.spec |= Insert; + + 'l' => # ends cursor insert mode + if(s.a0 == 4) + s.spec &= ~Insert; + + # inserts n rows from cursor row + 'L' => + scroll(s.pos.y, -1*s.a0); + s.pos.x = 1; + + # deletes n rows from cursor row + 'M' => + scroll(s.pos.y, s.a0); + s.pos.x = 1; + + # deletes n characters from cursor position + 'P' => + rowclear(s.pos.y, s.pos.x, s.pos.x+s.a0-1); + + # select Videotex mode + '{' => + if(s.a0 == '?') { + T.mode = Videotex; + s.setmode(T.mode); + } + + # display attributes + 'm' => + case s.a0 { + 0 => s.attr &= ~(attrL|attrF|attrP|attrB); + 1 => s.attr |= attrB; + 4 => s.attr |= attrL; + 5 => s.attr |= attrF; + 7 => s.attr |= attrP; + 22 => s.attr &= ~attrB; + 24 => s.attr &= ~attrL; + 25 => s.attr &= ~attrF; + 27 => s.attr &= ~attrP; + } + # direct cursor addressing + 'H' => + if(s.a0 == 0) + s.a0 = 1; + if(s.a1 == 0) + s.a1 = 1; + if(s.a0 > 0 && s.a0 < s.rows && s.a1 > 0 && s.a1 <= s.cols) + s.pos = Point(s.a1, s.a0); + } + s.state = Sstart; + Scsi1 => + case ch { + # direct cursor addressing + 'H' => + if(s.a0 == 0) + s.a0 = 1; + if(s.a1 == 0) + s.a1 = 1; + if(s.a0 > 0 && s.a0 < s.rows && s.a1 > 0 && s.a1 <= s.cols) + s.pos = Point(s.a1, s.a0); + } + s.state = Sstart; + } +} + + +# Screen state - ASCII mode +astate(nil: ref Screen, nil: array of byte): array of byte +{ + return nil; +} + +# Put a string in the current attributes to the current writing position +Screen.put(s: self ref Screen, str: string) +{ + while((l := len str) > 0) { + n := s.cols - s.pos.x + 1; # characters that will fit on this row + if(s.attr & attrW) { + if(n > 1) # fit normal width character in last column + n /= 2; + } + if(n > l) + n = l; + if(s.delimit) { # set delimiter bit on 1st space (if any) + for(i:=0; i<n; i++) + if(str[i] == ' ') + break; + if(i > 0) { + disp->Put(str[0:i], s.pos, s.cset, s.attr, s.spec&Insert); + incpos(s, i); + } + if(i < n) { + if(debug['s']) { + cs:=""; + if(s.cset==videotex) cs = "v"; else cs="s"; + fprint(stderr, "D %ux %s\n", s.attr|attrD, cs); + } + disp->Put(tostr(str[i]), s.pos, s.cset, s.attr|attrD, s.spec&Insert); + incpos(s, 1); + s.delimit = 0; + # clear serial attributes once used + # hang onto background attribute - needed for semigraphics + case s.cset { + videotex => + s.attr &= ~(attrL|attrC); + semigraphic => + s.attr &= ~(attrC); + } + } + if(i < n-1) { + disp->Put(str[i+1:n], s.pos, s.cset, s.attr, s.spec&Insert); + incpos(s, n-(i+1)); + } + } else { + disp->Put(str[0:n], s.pos, s.cset, s.attr, s.spec&Insert); + incpos(s, n); + } + if(n < len str) + str = str[n:]; + else + str = nil; + } +# if(T.state == Local || T.spec&Echo) +# refresh(); +} + +# increment the current writing position by `n' cells. +# caller must ensure that `n' characters can fit +incpos(s: ref Screen, n: int) +{ + if(s.attr & attrW) + s.pos.x += 2*n; + else + s.pos.x += n; + if(s.pos.x > s.cols) + if(s.pos.y == 0) # no wraparound from row zero + s.pos.x = s.cols; + else { + s.pos.x = 1; + if(s.pos.y == s.rows - 1 && s.spec&Scroll) { + if(s.attr & attrH) { + scroll(1, 2); + } else { + scroll(1, 1); + rowclear(s.pos.y, 1, s.cols); + } + } else { + if(s.attr & attrH) + s.pos.y += 2; + else + s.pos.y += 1; + if(s.pos.y >= s.rows) + s.pos.y -= (s.rows-1); + } + } +} + +# clear row `r' from `first' to `last' column inclusive +rowclear(r, first, last: int) +{ + # 16r5f is the semi-graphic black rectangle + disp->Put(dup(16r5f, last-first+1), Point(first,r), semigraphic, fgBlack, 0); +# disp->Put(dup(' ', last-first+1), Point(first,r), S.cset, fgBlack, 0); +} + +clear(s: ref Screen) +{ + for(r:=1; r<s.rows; r++) + rowclear(r, 1, s.cols); +} + +# called to suggest a display update +refresh() +{ + disp->Refresh(); +} + +# scroll the screen +scroll(topline, nlines: int) +{ + disp->Scroll(topline, nlines); + disp->Refresh(); +} + +# filter the specified ISO6429 and ISO2022 codes from the screen input +# TODO: filter some ISO2022 sequences +filter(s: ref Screen, data: array of byte): array of array of byte +{ + case T.mode { + Videotex => + return vfilter(s, data); + Mixed => + return mfilter(s, data); + Ascii => + return afilter(s, data); + } + return nil; +} + +# filter the specified ISO6429 and ISO2022 codes from the screen input +vfilter(s: ref Screen, data: array of byte): array of array of byte +{ + ba := array [0] of array of byte; + changed := 0; + + d0 := 0; + for(i:=0; i<len data; i++) { + ch := int data[i]; + case s.fstate { + FSstart => + if(ch == ESC) { + s.fstate = FSesc; + changed = 1; + if(i > d0) + ba = dappend(ba, data[d0:i]); + d0 = i+1; + } + FSesc => + d0 = i+1; + changed = 1; + if(ch == '[') { + s.fstate = FS6429; + s.fsaved = array [0] of byte; + s.badp = 0; +# } else if(ch == 16r20) { +# s.fstate = FS2022; +# s.fsaved = array [0] of byte; + s.badp = 0; + } else if(ch == ESC) { + ba = dappend(ba, array [] of { byte ESC }); + s.fstate = FSesc; + } else { + # false alarm - don't filter + ba = dappend(ba, array [] of { byte ESC, byte ch }); + s.fstate = FSstart; + } + FS6429 => # filter out invalid CSI sequences + d0 = i+1; + changed = 1; + if(ch >= 16r20 && ch <= 16r3f) { + if((ch < 16r30 || ch > 16r39) && ch != ';') + s.badp = 1; + a := array [len s.fsaved + 1] of byte; + a[0:] = s.fsaved[0:]; + a[len a - 1] = byte ch; + s.fsaved = a; + } else { + valid := 1; + case ch { + 'A' => ; + 'B' => ; + 'C' => ; + 'D' => ; + 'H' => ; + 'J' => ; + 'K' => ; + 'P' => ; + '@' => ; + 'h' => ; + 'l' => ; + 'M' => ; + 'L' => ; + * => + valid = 0; + } + if(s.badp) + valid = 0; + if(debug['f']) + fprint(stderr, "vfilter %d: %s%c\n", valid, string s.fsaved, ch); + if(valid) { # false alarm - don't filter + ba = dappend(ba, array [] of { byte ESC, byte '[' }); + ba = dappend(ba, s.fsaved); + ba = dappend(ba, array [] of { byte ch } ); + } + s.fstate = FSstart; + } + FS2022 => ; + } + } + if(changed) { + if(i > d0) + ba = dappend(ba, data[d0:i]); + return ba; + } + return array [] of { data }; +} + +# filter the specified ISO6429 and ISO2022 codes from the screen input - Videotex +mfilter(s: ref Screen, data: array of byte): array of array of byte +{ + ba := array [0] of array of byte; + changed := 0; + + d0 := 0; + for(i:=0; i<len data; i++) { + ch := int data[i]; + case s.fstate { + FSstart => + case ch { + ESC => + s.fstate = FSesc; + changed = 1; + if(i > d0) + ba = dappend(ba, data[d0:i]); + d0 = i+1; + SEP => + s.fstate = FSsep; + changed = 1; + if(i > d0) + ba = dappend(ba, data[d0:i]); + d0 = i+1; + } + FSesc => + d0 = i+1; + changed = 1; + if(ch == '[') { + s.fstate = FS6429; + s.fsaved = array [0] of byte; + s.badp = 0; + } else if(ch == ESC) { + ba = dappend(ba, array [] of { byte ESC }); + s.fstate = FSesc; + } else { + # false alarm - don't filter + ba = dappend(ba, array [] of { byte ESC, byte ch }); + s.fstate = FSstart; + } + FSsep => + d0 = i+1; + changed = 1; + if(ch == ESC) { + ba = dappend(ba, array [] of { byte SEP }); + s.fstate = FSesc; + } else if(ch == SEP) { + ba = dappend(ba, array [] of { byte SEP }); + s.fstate = FSsep; + } else { + if(ch >= 16r00 && ch <= 16r1f) + ba = dappend(ba, array [] of { byte SEP , byte ch }); + # consume the character + s.fstate = FSstart; + } + FS6429 => # filter out invalid CSI sequences + d0 = i+1; + changed = 1; + if(ch >= 16r20 && ch <= 16r3f) { + if((ch < 16r30 || ch > 16r39) && ch != ';' && ch != '?') + s.badp = 1; + a := array [len s.fsaved + 1] of byte; + a[0:] = s.fsaved[0:]; + a[len a - 1] = byte ch; + s.fsaved = a; + } else { + valid := 1; + case ch { + 'm' => ; + 'A' => ; + 'B' => ; + 'C' => ; + 'D' => ; + 'H' => ; + 'J' => ; + 'K' => ; + '@' => ; + 'h' => ; + 'l' => ; + 'L' => ; + 'M' => ; + 'P' => ; + '{' => # allow CSI ? { + n := len s.fsaved; + if(n == 0 || s.fsaved[n-1] != byte '?') + s.badp = 1; + * => + valid = 0; + } + if(s.badp) # only decimal params + valid = 0; + if(debug['f']) + fprint(stderr, "mfilter %d: %s%c\n", valid, string s.fsaved, ch); + if(valid) { # false alarm - don't filter + ba = dappend(ba, array [] of { byte ESC, byte '[' }); + ba = dappend(ba, s.fsaved); + ba = dappend(ba, array [] of { byte ch } ); + } + s.fstate = FSstart; + } + FS2022 => ; + } + } + if(changed) { + if(i > d0) + ba = dappend(ba, data[d0:i]); + return ba; + } + return array [] of { data }; +} + +# filter the specified ISO6429 and ISO2022 codes from the screen input - Videotex +afilter(nil: ref Screen, data: array of byte): array of array of byte +{ + return array [] of { data }; +} + +# append to an array of array of byte +dappend(ba: array of array of byte, b: array of byte): array of array of byte +{ + l := len ba; + na := array [l+1] of array of byte; + na[0:] = ba[0:]; + na[l] = b; + return na; +} + +# Put a diagnostic string to row 0 +Screen.msg(s: self ref Screen, str: string) +{ + blank := string array [s.cols -4] of {* => byte ' '}; + n := len str; + if(n > s.cols - 4) + n = s.cols - 4; + disp->Put(blank, Point(1, 0), videotex, 0, 0); + if(str != nil) + disp->Put(str[0:n], Point(1, 0), videotex, fgWhite|attrB, 0); + disp->Refresh(); +}
\ No newline at end of file diff --git a/appl/wm/minitel/socket.b b/appl/wm/minitel/socket.b new file mode 100644 index 00000000..b3ce7fcf --- /dev/null +++ b/appl/wm/minitel/socket.b @@ -0,0 +1,49 @@ +# +# Copyright © 1998 Vita Nuova Limited. All rights reserved. +# + +Socket: adt { + m: ref Module; # common attributes + in: chan of ref Event; + + init: fn(c: self ref Socket); + reset: fn(c: self ref Socket); + run: fn(c: self ref Socket); + quit: fn(c: self ref Socket); +}; + +Socket.init(c: self ref Socket) +{ + c.in = chan of ref Event; + c.reset(); +} + +Socket.reset(c: self ref Socket) +{ + c.m = ref Module(Pscreen, 0); +} + +Socket.run(c: self ref Socket) +{ +Runloop: + for(;;){ + ev := <- c.in; + pick e := ev { + Equit => + break Runloop; + Eproto => + case e.cmd { + Creset => + c.reset(); + * => break; + } + Edata => + } + } + send(nil); +} + +Socket.quit(c: self ref Socket) +{ + if(c==nil); +} diff --git a/appl/wm/minitel/swkeyb.b b/appl/wm/minitel/swkeyb.b new file mode 100644 index 00000000..50cb238f --- /dev/null +++ b/appl/wm/minitel/swkeyb.b @@ -0,0 +1,370 @@ +### +### This data and information is not to be used as the basis of manufacture, +### or be reproduced or copied, or be distributed to another party, in whole +### or in part, without the prior written consent of Lucent Technologies. +### +### (C) Copyright 1997 Lucent Technologies +### +### Written by N. W. Knauft +### +# +# Revisions Copyright © 1998 Vita Nuova Limited. + +implement Keyboard; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + +include "tk.m"; + tk: Tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "swkeyb.m"; + +#Icon path +ICPATH: con "keybd/"; + +#Font +FONT: con "/fonts/lucidasans/latin1.7.font"; +SPECFONT: con "/fonts/lucidasans/latin1.6.font"; + +# Dimension constants +KBDWIDTH: con 360; +KBDHEIGHT: con 120; +KEYSIZE: con "19"; +KEYSPACE: con 5; +KEYBORDER: con 1; +KEYGAP: con KEYSPACE - (2 * KEYBORDER); +ENDGAP: con 2 - KEYBORDER; + +# Row size constants (cumulative) +ROW1: con 14; +ROW2: con 28; +ROW3: con 41; +ROW4: con 53; +NKEYS: con 63; + +#Special key number constants +DELKEY: con 13; +TABKEY: con 14; +BACKSLASHKEY: con 27; +CAPSLOCKKEY: con 28 ; +RETURNKEY: con 40; +LSHIFTKEY: con 41; +RSHIFTKEY: con 52; +ESCKEY: con 53; +CTRLKEY: con 54; +METAKEY: con 55; +ALTKEY: con 56; +SPACEKEY: con 57; +ENTERKEY: con 58; +LEFTKEY: con 59; +RIGHTKEY: con 60; +DOWNKEY: con 61; +UPKEY: con 62; + +#Special key code constants +CAPSLOCK: con -1 ; +SHIFT: con -2; +CTRL: con -3; +ALT: con -4; +META: con -5; +MAGIC_PREFIX: con 256; +ARROW_OFFSET: con 57344; +ARROW_PREFIX: con ARROW_OFFSET + 18; + +#Special key width constants +DELSIZE: con 44; +TABSIZE: con 32; +BACKSLASHSIZE: con 31; +CAPSLOCKSIZE: con 44; +RETURNSIZE: con 43; +LSHIFTSIZE: con 56; +RSHIFTSIZE: con 55; +ESCSIZE: con 21; +CTRLSIZE: con 23; +METASIZE: con 38; +ALTSIZE: con 22; +SPACESIZE: con 100; +ENTERSIZE: con 31; + +#Arrow key code constants +UP: con ARROW_PREFIX; +DOWN: con ARROW_PREFIX + 1; +LEFT: con ARROW_PREFIX + 2; +RIGHT: con ARROW_PREFIX + 3; + +direction:= array[] of {"up", "down", "left", "right"}; +row_dimensions:= array[] of {0, ROW1, ROW2, ROW3, ROW4, NKEYS}; + +special_keys:= array[] of { + (DELKEY, DELSIZE), + (TABKEY, TABSIZE), + (BACKSLASHKEY, BACKSLASHSIZE), + (CAPSLOCKKEY, CAPSLOCKSIZE), + (RETURNKEY, RETURNSIZE), + (LSHIFTKEY, LSHIFTSIZE), + (RSHIFTKEY, RSHIFTSIZE), + (ESCKEY, ESCSIZE), + (CTRLKEY, CTRLSIZE), + (METAKEY, METASIZE), + (ALTKEY, ALTSIZE), + (SPACEKEY, SPACESIZE), + (ENTERKEY, ENTERSIZE), +}; + +keys:= array[] of { + # Unshifted + "`", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "Delete", + "Tab", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\\\\", + "CapLoc", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "\'", "Return", + "Shift", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "Shift", + "Esc", "Ctrl", " ", "Alt", " ", "Enter", "<-", "->", "v", "^", + # Shifted + "~", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "Delete", + "Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "\\{", "\\}", "|", + "CapLoc", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":", "\"", "Return", + "Shift", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", "Shift", + "Esc", "Ctrl", " ", "Alt", " ", "Enter", "<-", "->", "v", "^", +}; + +keyvals:= array[] of { + # Unshifted + '`', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', + '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\\', + CAPSLOCK, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '\n', + SHIFT, 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', SHIFT, + 27, CTRL, META, ALT, 32, '\n', LEFT, RIGHT, DOWN, UP, + # Shifted + '~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b', + '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '|', + CAPSLOCK, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '\n', + SHIFT, 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', SHIFT, + 27, CTRL, META, ALT, 32, '\n', LEFT, RIGHT, DOWN, UP, +}; + +rowlayout := array[] of { + "frame .f1", + "frame .f2", + "frame .f3", + "frame .f4", + "frame .f5", + "frame .dummy0 -height " + string (ENDGAP), + "frame .dummy1 -height " + string KEYGAP, + "frame .dummy2 -height " + string KEYGAP, + "frame .dummy3 -height " + string KEYGAP, + "frame .dummy4 -height " + string KEYGAP, + "frame .dummy5 -height " + string (ENDGAP + 1), +}; + +# Move key flags +move_key_enabled := 0; +meta_active := 0; + +# Create keyboard widget, spawn keystroke handler +initialize(t: ref Tk->Toplevel, ctxt : ref Draw->Context, dot: string): chan of string +{ + return chaninit(t, ctxt, dot, chan of string); +} + +chaninit(t: ref Tk->Toplevel, ctxt : ref Draw->Context, dot: string, rc: chan of string): chan of string +{ + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + + tkclient->init(); + + tk->cmd(t, "frame " + dot + " -bd 2 -relief raised -width " + string KBDWIDTH + + " -height " + string KBDHEIGHT); + tkcmds(t, rowlayout); + + for(i := 0; i < NKEYS; i++) { + tk->cmd(t, "button .b" + string i + " -font " + FONT + " -width " + KEYSIZE + + " -height " + KEYSIZE + " -bd " + string KEYBORDER); + + tk->cmd(t, ".b" + string i + " configure -text {" + keys[i] + + "} -command 'send keypress " + string keyvals[i]); + } + + for(i = 0; i < len special_keys; i++) { + (keynum, keysize) := special_keys[i]; + tk->cmd(t, ".b" + string keynum + " configure -font " + SPECFONT + " -width " + string keysize); + } + + tk->cmd(t, "image create bitmap Capslock_on -file " + ICPATH + "capson.bit -maskfile " + ICPATH + "capson.bit"); + tk->cmd(t, "image create bitmap Capslock_off -file " + ICPATH + "capsoff.bit -maskfile " + ICPATH + "capsoff.bit"); + tk->cmd(t, "image create bitmap Left_arrow -file " + ICPATH + "larrow.bit -maskfile " + ICPATH + "larrow.bit"); + tk->cmd(t, "image create bitmap Right_arrow -file " + ICPATH + "rarrow.bit -maskfile " + ICPATH + "rarrow.bit"); + tk->cmd(t, "image create bitmap Down_arrow -file " + ICPATH + "darrow.bit -maskfile " + ICPATH + "darrow.bit"); + tk->cmd(t, "image create bitmap Up_arrow -file " + ICPATH + "uarrow.bit -maskfile " + ICPATH + "uarrow.bit"); + tk->cmd(t, "image create bitmap Move_on -file " + ICPATH + "moveon.bit -maskfile " + ICPATH + "moveon.bit"); + tk->cmd(t, "image create bitmap Move_off -file " + ICPATH + "moveoff.bit -maskfile " + ICPATH + "moveoff.bit"); + tk->cmd(t, "image create bitmap None -file " + ICPATH + "none.bit -maskfile " + ICPATH + "none.bit"); + tk->cmd(t, ".b" + string CAPSLOCKKEY + " configure -image Capslock_off"); + tk->cmd(t, ".b" + string LEFTKEY + " configure -image Left_arrow"); + tk->cmd(t, ".b" + string RIGHTKEY + " configure -image Right_arrow"); + tk->cmd(t, ".b" + string DOWNKEY + " configure -image Down_arrow"); + tk->cmd(t, ".b" + string UPKEY + " configure -image Up_arrow"); + + for(j:=1; j < len row_dimensions; j++) { + rowstart := row_dimensions[j-1]; + rowend := row_dimensions[j]; + for(i=rowstart; i<rowend; i++) { + if (i == rowstart) { + tk->cmd(t, "frame .f" + string j + ".dummy -width " + string ENDGAP); + tk->cmd(t, "pack .f" + string j + ".dummy -side left"); + } + tk->cmd(t, "pack .b" + string i + " -in .f" + string j + " -side left"); + if (i == rowend-1) + tk->cmd(t, "frame .f" + string j + ".dummy" + string i + " -width " + string ENDGAP); + else + tk->cmd(t, "frame .f" + string j + ".dummy" + string i + " -width " + string KEYGAP); + tk->cmd(t, "pack .f" + string j + ".dummy" + string i + " -side left"); + } + } + + tk->cmd(t, "pack .dummy0 .f1 .dummy1 .f2 .dummy2 .f3 .dummy3 .f4 .dummy4 .f5 .dummy5 -in " + dot); + tk->cmd(t,"update"); + + key := chan of string; + spawn handle_keyclicks(t, ctxt, key, rc); + return key; +} + +tkcmds(t: ref Tk->Toplevel, cmds: array of string) +{ + for(i := 0; i < len cmds; i++) + tk->cmd(t, cmds[i]); +} + +# Process key clicks and hand keycodes off to Tk +handle_keyclicks(t: ref Tk->Toplevel, ctxt : ref Draw->Context, sc, rc: chan of string) +{ + keypress := chan of string; + tk->namechan(t, keypress, "keypress"); + + minitel := 0; + caps_locked := 0; + shifted := 0; + ctrl_active := 0; + alt_active := 0; + +Work: + for(;;){ + alt { + k := <-keypress => + (n, cmdstr) := sys->tokenize(k, " \t\n"); + keycode := int hd cmdstr; + case keycode { + CAPSLOCK => + redisplay_keyboard(t, minitel, caps_locked ^= 1, caps_locked); + shifted = 0; + ctrl_active = 0; + alt_active = 0; + SHIFT => + redisplay_keyboard(t, minitel, (shifted ^= 1) ^ caps_locked, caps_locked); + CTRL => + ctrl_active ^= 1; + if (shifted) { + redisplay_keyboard(t, minitel, caps_locked, caps_locked); + shifted = 0; + } + alt_active = 0; + ALT => + alt_active ^= 1; + if (shifted) { + redisplay_keyboard(t, minitel, caps_locked, caps_locked); + shifted = 0; + } + ctrl_active = 0; + META => + if (move_key_enabled) { + if (meta_active ^= 1) + tk->cmd(t, ".b" + string METAKEY + " configure -image Move_on"); + else + tk->cmd(t, ".b" + string METAKEY + " configure -image Move_off"); + } + redisplay_keyboard(t, minitel, caps_locked, caps_locked); + shifted = 0; + ctrl_active = 0; + alt_active = 0; + * => + if (ctrl_active) { + keycode &= 16r1F; + ctrl_active = 0; + } else if (alt_active) { + keycode += MAGIC_PREFIX; + alt_active = 0; + } + if (meta_active && UP <= keycode && keycode <= RIGHT) { + spawn send_move_msg(direction[keycode - ARROW_PREFIX], sc); + } else + tk->keyboard(t, keycode); + if (shifted) { + redisplay_keyboard(t, minitel, caps_locked, caps_locked); + shifted = 0; + } + } + s := <-rc => + case s { + "kill" => + break Work; + "minitel" => + if (!minitel) { + minitel = 1; + redisplay_keyboard(t, minitel, shifted, caps_locked); + } + "standard" => + if (minitel) { + minitel = 0; + redisplay_keyboard(t, minitel, shifted, caps_locked); + } + } + } + } +} + +send_move_msg(dir: string, ch: chan of string) +{ + ch <-= dir; +} + + +# Redisplay keyboard to reflect current state (shifted or unshifted) +redisplay_keyboard(t: ref Tk->Toplevel, minitel, shifted, caps_locked: int) +{ + base: int; + + if (shifted) + base = NKEYS; + else + base = 0; + + for(i:=0; i<NKEYS; i++) { + n := base + i; + val := keyvals[n]; + key := keys[n]; + if (minitel) { + if (val >= int 'A' && val <= int 'Z') { + key = keys[n-NKEYS]; + } else if (val >= int 'a' && val <= int 'z') { + key = keys[n+NKEYS]; + } + } + + tk->cmd(t, ".b" + string i + " configure -text {" + key + + "} -command 'send keypress " + string val); + } + if (caps_locked) + tk->cmd(t, ".b" + string CAPSLOCKKEY + " configure -image Capslock_on"); + else + tk->cmd(t, ".b" + string CAPSLOCKKEY + " configure -image Capslock_off"); + tk->cmd(t, "update"); +} diff --git a/appl/wm/minitel/swkeyb.dis b/appl/wm/minitel/swkeyb.dis Binary files differnew file mode 100644 index 00000000..2928c713 --- /dev/null +++ b/appl/wm/minitel/swkeyb.dis diff --git a/appl/wm/minitel/swkeyb.m b/appl/wm/minitel/swkeyb.m new file mode 100644 index 00000000..52206801 --- /dev/null +++ b/appl/wm/minitel/swkeyb.m @@ -0,0 +1,21 @@ +### +### This data and information is not to be used as the basis of manufacture, +### or be reproduced or copied, or be distributed to another party, in whole +### or in part, without the prior written consent of Lucent Technologies. +### +### (C) Copyright 1997 Lucent Technologies +### +### Written by N. W. Knauft +### + +# Revisions Copyright © 1998 Vita Nuova Limited. + +Keyboard: module +{ + PATH: con "/dis/wm/minitel/swkeyb.dis"; + + initialize: fn(t: ref Tk->Toplevel, ctxt : ref Draw->Context, + dot: string): chan of string; + chaninit: fn(t: ref Tk->Toplevel, ctxt : ref Draw->Context, + dot: string, rc: chan of string): chan of string; +}; diff --git a/appl/wm/minitel/swkeyb.sbl b/appl/wm/minitel/swkeyb.sbl new file mode 100644 index 00000000..f79889f8 --- /dev/null +++ b/appl/wm/minitel/swkeyb.sbl @@ -0,0 +1,724 @@ +limbo .sbl 2.1 +Keyboard +6 +swkeyb.b +sys.m +draw.m +tk.m +tkclient.m +swkeyb.m +504 +172.8,46 0 +17,18 0 +20,24 0 +26,29 0 +31,45 0 +8,46 0 +8,46 0 +1,46 0 +177.1,25 1 +178.1,28 2 +179.1,22 3 +180.1,40 4 +182.1,17 5 +1,17 5 +184.1,185.35 6 +184.9,10 6 +12,26 6 +12,60 6 +12,60 7 +1,185.35 6 +184.1,185.35 6 +184.1,185.35 8 +186.1,21 9 +8,9 9 +11,20 9 +1,21 9 +188.5,11 10 +13,22 11 +189.2,190.59 12 +189.10,11 12 +27,35 12 +13,35 12 +13,47 12 +13,47 13 +2,190.59 12 +189.2,190.59 12 +189.2,190.59 14 +192.2,193.54 15 +192.10,11 15 +20,28 15 +13,28 15 +13,51 15 +54,61 15 +13,61 15 +13,193.33 15 +43,53 15 +36,53 15 +192.13,193.53 15 +192.13,193.53 16 +192.13,193.53 17 +192.2,193.54 15 +192.2,193.54 15 +192.2,193.54 18 +188.24,27 19 +24,27 19 +196.5,10 20 +16,32 21 +12,32 21 +197.23,38 22 +23,38 22 +198.2,97 23 +10,11 23 +20,33 23 +13,33 23 +13,55 23 +82,96 23 +13,96 23 +13,96 24 +13,96 25 +2,97 23 +2,97 23 +2,97 26 +196.34,37 27 +34,37 27 +201.1,112 28 +9,10 28 +12,111 28 +1,112 28 +1,112 28 +1,112 29 +202.1,115 30 +9,10 30 +12,114 30 +1,115 30 +1,115 30 +1,115 31 +203.1,111 32 +9,10 32 +12,110 32 +1,111 32 +1,111 32 +1,111 33 +204.1,112 34 +9,10 34 +12,111 34 +1,112 34 +1,112 34 +1,112 35 +205.1,111 36 +9,10 36 +12,110 36 +1,111 36 +1,111 36 +1,111 37 +206.1,109 38 +9,10 38 +12,108 38 +1,109 38 +1,109 38 +1,109 39 +207.1,108 40 +9,10 40 +12,107 40 +1,108 40 +1,108 40 +1,108 41 +208.1,111 42 +9,10 42 +12,110 42 +1,111 42 +1,111 42 +1,111 43 +209.1,101 44 +9,10 44 +12,100 44 +1,101 44 +1,101 44 +1,101 45 +210.1,73 46 +9,10 46 +12,72 46 +1,73 46 +1,73 46 +1,73 47 +211.1,67 48 +9,10 48 +12,66 48 +1,67 48 +1,67 48 +1,67 49 +212.1,69 50 +9,10 50 +12,68 50 +1,69 50 +1,69 50 +1,69 51 +213.1,67 52 +9,10 52 +12,66 52 +1,67 52 +1,67 52 +1,67 53 +214.1,63 54 +9,10 54 +12,62 54 +1,63 54 +1,63 54 +1,63 55 +216.5,9 56 +15,33 57 +11,33 57 +217.29,32 58 +14,33 58 +2,33 58 +218.12,29 59 +2,29 59 +219.6,16 60 +18,26 61 +220.7,20 62 +221.4,72 63 +12,13 63 +28,36 63 +15,36 63 +15,55 63 +15,55 64 +4,72 63 +4,72 63 +4,72 65 +222.4,58 66 +12,13 66 +27,35 66 +15,35 66 +15,57 66 +15,57 67 +4,58 66 +4,58 66 +4,58 68 +224.3,74 69 +11,12 69 +26,34 69 +14,34 69 +14,46 69 +49,57 69 +14,57 69 +14,57 70 +14,73 69 +14,73 71 +3,74 69 +3,74 69 +3,74 72 +225.12,20 73 +7,20 73 +226.4,88 74 +12,13 74 +28,36 74 +15,36 74 +15,47 74 +50,58 74 +15,58 74 +15,58 75 +15,71 74 +15,71 76 +4,88 74 +4,88 74 +4,88 77 +4,88 78 +228.4,88 79 +12,13 79 +28,36 79 +15,36 79 +15,47 79 +50,58 79 +15,58 79 +15,58 80 +15,71 79 +15,71 81 +4,88 79 +4,88 79 +4,88 82 +229.3,73 83 +11,12 83 +26,34 83 +14,34 83 +14,45 83 +48,56 83 +14,56 83 +14,56 84 +14,72 83 +14,72 85 +3,73 83 +3,73 83 +3,73 86 +219.28,31 87 +28,31 87 +216.35,38 88 +35,38 88 +233.1,98 89 +9,10 89 +12,97 89 +1,98 89 +1,98 89 +1,98 90 +234.1,20 91 +9,10 91 +11,19 91 +1,20 91 +1,20 91 +1,20 92 +236.1,22 93 +237.1,41 94 +24,25 94 +27,31 94 +33,36 94 +38,40 94 +1,41 94 +238.8,11 95 +1,11 95 +243.5,11 96 +17,25 97 +13,25 97 +244.2,21 98 +10,11 98 +13,20 98 +13,20 98 +2,21 98 +2,21 98 +2,21 99 +243.27,30 100 +27,30 100 +245.0,1 101 +250.1,27 102 +251.1,38 103 +14,15 103 +17,25 103 +27,37 103 +1,38 103 +1,38 103 +1,38 104 +253.1,13 105 +254.1,17 106 +255.1,13 107 +256.1,17 108 +257.1,16 109 +262.9,17 110 +9,17 110 +315.9,11 110 +9,11 110 +261.2,8 110 +2,8 110 +2,8 110 +2,8 110 +263.18,43 111 +32,33 111 +35,42 111 +18,43 111 +18,43 111 +4,5 111 +7,13 111 +7,13 112 +264.18,27 113 +3,27 113 +3,27 114 +265.8,15 115 +8,15 115 +8,15 115 +8,15 115 +267.4,65 116 +23,24 116 +26,33 116 +35,51 116 +35,51 116 +53,64 116 +4,65 116 +268.4,15 117 +269.4,19 118 +270.4,18 119 +4,18 115 +272.4,77 120 +23,24 120 +26,33 120 +35,49 120 +35,49 120 +35,63 120 +65,76 120 +4,77 120 +4,77 115 +274.4,20 121 +275.8,15 122 +276.5,61 123 +24,25 123 +27,34 123 +36,47 123 +49,60 123 +5,61 123 +277.5,16 124 +279.4,18 125 +4,18 115 +281.4,19 126 +282.8,15 127 +283.5,61 128 +24,25 128 +27,34 128 +36,47 128 +49,60 128 +5,61 128 +284.5,16 129 +286.4,19 130 +4,19 115 +288.8,24 131 +289.9,25 132 +9,25 132 +9,25 132 +290.6,69 133 +14,15 133 +17,68 133 +6,69 133 +6,69 133 +6,69 134 +6,69 135 +292.6,70 136 +14,15 136 +17,69 136 +6,70 136 +6,70 136 +6,70 137 +294.4,60 138 +23,24 138 +26,33 138 +35,46 138 +48,59 138 +4,60 138 +295.4,15 139 +296.4,19 140 +297.4,18 141 +4,18 115 +299.8,19 142 +300.5,21 143 +301.5,20 144 +5,20 145 +302.15,25 146 +303.5,28 147 +304.5,19 148 +306.8,19 149 +23,36 149 +40,56 149 +307.5,63 150 +35,57 150 +25,58 150 +25,58 150 +60,62 150 +5,63 150 +5,63 151 +309.5,29 152 +18,19 152 +21,28 152 +5,29 152 +310.8,15 153 +311.5,61 154 +24,25 154 +27,34 154 +36,47 154 +49,60 154 +5,61 154 +312.5,16 155 +5,16 115 +5,16 156 +5,16 157 +5,16 110 +316.8,9 158 +8,9 159 +332.0,1 160 +320.9,16 161 +321.5,16 162 +322.5,57 163 +24,25 163 +27,34 163 +36,43 163 +45,56 163 +5,57 163 +5,57 158 +325.8,15 164 +326.5,16 165 +327.5,57 166 +24,25 166 +27,34 166 +36,43 166 +45,56 166 +5,57 166 +5,57 158 +5,57 167 +5,57 110 +336.1,11 168 +337.0,1 169 +345.5,12 170 +346.2,14 171 +2,14 172 +348.2,10 173 +350.5,9 174 +11,18 175 +351.2,15 176 +352.9,19 177 +2,19 177 +353.9,16 178 +2,16 178 +354.6,13 179 +355.7,21 180 +25,39 180 +356.15,22 181 +10,23 181 +4,23 181 +4,23 182 +357.14,28 183 +32,46 183 +358.15,22 184 +10,23 184 +4,23 184 +362.2,363.59 185 +362.10,11 185 +20,28 185 +13,28 185 +13,51 185 +13,57 185 +13,363.45 185 +48,58 185 +362.13,363.58 185 +362.13,363.58 186 +362.13,363.58 187 +362.2,363.59 185 +362.2,363.59 185 +362.2,363.59 188 +362.2,363.59 189 +350.20,23 190 +20,23 190 +365.5,16 191 +366.2,73 192 +10,11 192 +13,72 192 +2,73 192 +2,73 192 +2,73 193 +2,73 194 +368.2,74 195 +10,11 195 +13,73 195 +2,74 195 +2,74 195 +2,74 196 +369.1,21 197 +9,10 197 +12,20 197 +1,21 197 +1,21 197 +1,21 198 +370.0,1 199 +13 +aSys->Dir 1:26.1,39.2 64 +11 +0:name:28.2,6 s +4:uid:29.2,5 s +8:gid:30.2,5 s +12:muid:31.2,6 s +16:qid:32.2,5 @1 + +32:mode:33.2,6 i +36:atime:34.2,7 i +40:mtime:35.2,7 i +48:length:36.2,8 B +56:dtype:37.2,7 i +60:dev:38.2,5 i +aSys->Qid 11.1,16.2 16 +3 +0:path:13.2,6 B +8:vers:14.2,6 i +12:qtype:15.2,7 i +aDraw->Chans 2:70.1,82.2 4 +1 +0:desc:72.2,6 i +aTk->Toplevel 3:5.1,12.2 32 +5 +0:display:7.2,9 R@4 + +4:wreq:8.2,6 Cs +8:image:9.2,7 R@5 + +12:ctxt:10.2,6 R@9 + +16:screenr:11.2,9 @6 + +aDraw->Display 2:201.1,230.2 20 +5 +0:image:203.2,7 R@5 + +4:white:204.2,7 R@5 + +8:black:205.2,7 R@5 + +12:opaque:206.2,8 R@5 + +16:transparent:207.2,13 R@5 + +aDraw->Image 142.1,198.2 56 +8 +0:r:146.2,3 @6 + +16:clipr:147.2,7 @6 + +32:depth:148.2,7 i +36:chans:149.2,7 @2 + +40:repl:150.2,6 i +44:display:151.2,9 R@4 + +48:screen:152.2,8 R@8 + +52:iname:153.2,7 s +aDraw->Rect 116.1,139.2 16 +2 +0:min:118.2,5 @7 + +8:max:119.2,5 @7 + +aDraw->Point 99.1,113.2 8 +2 +0:x:101.2,3 i +4:y:102.2,3 i +aDraw->Screen 249.1,263.2 16 +4 +0:id:251.2,4 i +4:image:252.2,7 R@5 + +8:fill:253.2,6 R@5 + +12:display:254.2,9 R@4 + +aDraw->Wmcontext 282.1,291.2 28 +7 +0:kbd:284.2,5 Ci +4:ptr:285.2,5 CR@10 + +8:ctl:286.2,5 Cs +12:wctl:287.2,6 Cs +16:images:288.2,8 CR@5 + +20:connfd:289.2,8 R@11 + +24:ctxt:290.2,6 R@12 + +aDraw->Pointer 266.1,271.2 16 +3 +0:buttons:268.2,9 i +4:xy:269.2,4 @7 + +12:msec:270.2,6 i +aSys->FD 1:45.1,48.2 4 +1 +0:fd:47.2,4 i +aDraw->Context 2:274.1,279.2 12 +3 +0:display:276.2,9 R@4 + +4:screen:277.2,8 R@8 + +8:wm:278.2,4 Ct8.2 +0:t0:15,21 s +4:t1:15,21 Ct8.2 +0:t0:32,38 s +4:t1:32,38 R@9 + + + +6 +0:initialize +3 +32:t:0:170.11,12 R@3 + +36:ctxt:32,36 R@12 + +40:dot:58,61 s +0 +Cs8:chaninit +4 +32:t:175.9,10 R@3 + +36:ctxt:30,34 R@12 + +40:dot:56,59 s +44:rc:69,71 Cs +7 +48:i:188.5,6 i +52:j:216.5,6 i +56:key:236.1,4 Cs +60:rowend:218.2,8 i +64:rowstart:217.2,10 i +68:keynum:197.3,9 i +72:keysize:11,18 i +Cs267:tkcmds +2 +32:t:241.7,8 R@3 + +36:cmds:28,32 As +1 +40:i:243.5,6 i +n280:handle_keyclicks +4 +32:t:248.17,18 R@3 + +36:ctxt:38,42 R@12 + +40:sc:64,66 Cs +44:rc:68,70 Cs +11 +48:caps_locked:254.1,12 i +52:minitel:253.1,8 i +56:shifted:255.1,8 i +60:keycode:264.3,10 i +64:alt_active:257.1,11 i +68:ctrl_active:256.1,12 i +72:keypress:250.1,9 Cs +76:k:262.2,3 s +80:s:315.2,3 s +96:n:263.4,5 i +100:cmdstr:7,13 Ls +n441:send_move_msg +2 +32:dir:334.14,17 s +36:ch:27,29 Cs +0 +n443:redisplay_keyboard +4 +32:t:341.19,20 R@3 + +36:minitel:40,47 i +40:shifted:49,56 i +44:caps_locked:58,69 i +5 +48:val:352.2,5 i +52:i:350.5,6 i +56:n:351.2,3 i +60:key:353.2,5 s +64:base:343.1,5 i +n12 +188:direction:102.0,9 As +192:draw:19.8,12 mDraw +2:1.0,298.1 0 + +244:keys:0:121.0,4 As +248:keyvals:136.0,7 Ai +252:meta_active:167.0,11 i +256:move_key_enabled:166.0,16 i +276:row_dimensions:103.0,14 Ai +280:rowlayout:151.0,9 As +284:special_keys:105.0,12 At8.2 +0:t0:106.2,8 i +4:t1:10,17 i + +288:sys:16.8,11 mSys +1:4.0,160.1 0 + +292:tk:0:22.8,10 mTk +3:1.0,25.1 0 + +296:tkclient:0:8,16 mTkclient +4:1.0,26.1 0 + diff --git a/appl/wm/mkfile b/appl/wm/mkfile new file mode 100644 index 00000000..fd9f2157 --- /dev/null +++ b/appl/wm/mkfile @@ -0,0 +1,103 @@ +<../../mkconfig + +DIRS=\ + brutus\ + camera\ +# diary\ + drawmux\ + ftree\ + mailtool\ + mpeg\ +# minitel\ + +TARG=\ + about.dis\ + avi.dis\ + bounce.dis\ + brutus.dis\ + c4.dis\ + calendar.dis\ + clock.dis\ + coffee.dis\ + collide.dis\ + colors.dis\ + cprof.dis\ + date.dis\ + deb.dis\ + debdata.dis\ + debsrc.dis\ + dir.dis\ + edit.dis\ + filename.dis\ + getauthinfo.dis\ + keyboard.dis\ + logon.dis\ + logwindow.dis\ + man.dis\ + mand.dis\ + mash.dis\ + memory.dis\ +## mpeg.dis\ + mprof.dis\ + pen.dis\ + polyhedra.dis\ + prof.dis\ +## qt.dis\ + readmail.dis\ + remotelogon.dis\ + reversi.dis\ + rmtdir.dis\ + rt.dis\ + sendmail.dis\ + sh.dis\ + smenu.dis\ + snake.dis\ + stopwatch.dis\ + sweeper.dis\ + task.dis\ + telnet.dis\ + tetris.dis\ + toolbar.dis\ + unibrowse.dis\ + view.dis\ + vt.dis\ + wish.dis\ + wm.dis\ + wmplay.dis\ + +MODULES=\ + wmdeb.m\ + +SYSMODULES=\ + bufio.m\ + cci.m\ + daytime.m\ + debug.m\ + draw.m\ + filepat.m\ + html.m\ + keyring.m\ + man.m\ + mpeg.m\ + newns.m\ + plumbmsg.m\ + quicktime.m\ + rand.m\ + readdir.m\ + riff.m\ + security.m\ + sh.m\ + string.m\ + sys.m\ + tk.m\ + tkclient.m\ + url.m\ + webget.m\ + wmclient.m\ + wmsrv.m\ + workdir.m\ + +DISBIN=$ROOT/dis/wm + +<$ROOT/mkfiles/mkdis +<$ROOT/mkfiles/mksubdirs diff --git a/appl/wm/mpeg.b b/appl/wm/mpeg.b new file mode 100644 index 00000000..619aa338 --- /dev/null +++ b/appl/wm/mpeg.b @@ -0,0 +1,185 @@ +implement WmMpeg; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + Rect, Display, Image: import draw; + ctxt: ref Draw->Context; + +include "tk.m"; + tk: Tk; + Toplevel: import tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "mpeg.m"; + mpeg: Mpeg; + +WmMpeg: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +Stopped, Playing: con iota; + +dx, dy: int; +dw, dh: int; +adjust: int; + +task_cfg := array[] of { + "canvas .c -background =5", + "frame .b", + "button .b.File -text File -command {send cmd file}", + "button .b.Stop -text Stop -command {send cmd stop}", + "button .b.Pause -text Pause -command {send cmd pause}", + "button .b.Play -text Play -command {send cmd play}", + "button .b.Picture -text Picture -command {send cmd pict}", + "frame .f", + "label .f.file -text {File:}", + "label .f.name", + "pack .f.file .f.name -side left", + "pack .b.File .b.Stop .b.Pause .b.Play .b.Picture -side left", + "pack .f -fill x", + "pack .b -anchor w", + "pack .c -side bottom -fill both -expand 1", + "pack propagate . 0", +}; + +init(xctxt: ref Draw->Context, nil: list of string) +{ + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + mpeg = load Mpeg Mpeg->PATH; + + ctxt = xctxt; + + tkclient->init(); + + (t, menubut) := tkclient->toplevel(ctxt.screen, "", "Mpeg Player", Tkclient->Appl); + + cmd := chan of string; + tk->namechan(t, cmd, "cmd"); + + tkclient->tkcmds(t, task_cfg); + + tk->cmd(t, "bind . <Configure> {send cmd resize}"); + tk->cmd(t, "update"); + + fname := ""; + ctl := chan of string; + state := Stopped; + + for(;;) alt { + menu := <-menubut => + if(menu == "exit") { + if(state == Playing) { + mpeg->ctl("stop"); + <-ctl; + } + return; + } + tkclient->wmctl(t, menu); + press := <-cmd => + case press { + "file" => + pat := list of { + "*.mpg (MPEG movie file)" + }; + fname = tkclient->filename(ctxt.screen, t, "Locate MPEG clip", pat, ""); + if(fname != nil) { + tk->cmd(t, ".f.name configure -text {"+fname+"}"); + tk->cmd(t, "update"); + } + "play" => + s := mpeg->play(ctxt.display, nil, 0, canvsize(t), fname, ctl); + if(s != nil) { + tkclient->dialog(t, "error -fg red", "Play MPEG", + "Media player error:\n"+s, + 0, "Stop Play"::nil); + break; + } + state = Playing; + "resize" => + if(state != Playing) + break; + r := canvsize(t); + s := sys->sprint("window %d %d %d %d", + r.min.x, r.min.y, r.max.x, r.max.y); + mpeg->ctl(s); + "pict" => + if(adjust) + break; + adjust = 1; + spawn pict(t); + * => + # Stop & Pause + mpeg->ctl(press); + } + done := <-ctl => + state = Stopped; + } +} + +canvsize(t: ref Toplevel): Rect +{ + r: Rect; + + r.min.x = int tk->cmd(t, ".c cget -actx") + dx; + r.min.y = int tk->cmd(t, ".c cget -acty") + dy; + r.max.x = r.min.x + int tk->cmd(t, ".c cget -width") + dw; + r.max.y = r.min.y + int tk->cmd(t, ".c cget -height") + dh; + + return r; +} + +pict_cfg := array[] of { + "scale .dx -orient horizontal -from -5 -to 5 -label {Origin X}"+ + " -command { send c dx}", + "scale .dy -orient horizontal -from -5 -to 5 -label {Origin Y}"+ + " -command { send c dy}", + "scale .dw -orient horizontal -from -5 -to 5 -label {Width}"+ + " -command {send c dw}", + "scale .dh -orient horizontal -from -5 -to 5 -label {Height}"+ + " -command {send c dh}", + "pack .Wm_t -fill x", + "pack .dx .dy .dw .dh -fill x", + "pack propagate . 0", + "update", +}; + +pict(parent: ref Toplevel) +{ + targ := +" -borderwidth 2 -relief raised"; + + (t, menubut) := tkclient->toplevel(ctxt.screen, tkclient->geom(parent), "Mpeg Picture", 0); + + pchan := chan of string; + tk->namechan(t, pchan, "c"); + + tkclient->tkcmds(t, pict_cfg); + + for(;;) alt { + menu := <-menubut => + if(menu == "exit") { + adjust = 0; + return; + } + tkclient->wmctl(t, menu); + tcip := <-pchan => + case tcip { + "dx" => dx = int tk->cmd(t, ".dx get"); + "dy" => dy = int tk->cmd(t, ".dy get"); + "dw" => dw = int tk->cmd(t, ".dw get"); + "dh" => dh = int tk->cmd(t, ".dh get"); + } + r := canvsize(parent); + s := sys->sprint("window %d %d %d %d", + r.min.x, r.min.y, r.max.x, r.max.y); + mpeg->ctl(s); + } +} diff --git a/appl/wm/mpeg/c0.tab b/appl/wm/mpeg/c0.tab new file mode 100644 index 00000000..3949d009 --- /dev/null +++ b/appl/wm/mpeg/c0.tab @@ -0,0 +1,261 @@ +# vlc -uUNDEF,UNDEF c0 +c0_size: con 256; +c0_bits: con 8; +c0_table:= array[] of { + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (0, UNDEF,UNDEF), + (8, 18,1), + (8, -18,1), + (8, 17,1), + (8, -17,1), + (8, 16,1), + (8, -16,1), + (8, 15,1), + (8, -15,1), + (8, 3,6), + (8, -3,6), + (8, 2,16), + (8, -2,16), + (8, 2,15), + (8, -2,15), + (8, 2,14), + (8, -2,14), + (8, 2,13), + (8, -2,13), + (8, 2,12), + (8, -2,12), + (8, 2,11), + (8, -2,11), + (8, 1,31), + (8, -1,31), + (8, 1,30), + (8, -1,30), + (8, 1,29), + (8, -1,29), + (8, 1,28), + (8, -1,28), + (8, 1,27), + (8, -1,27), + (7, 40,0), + (7, 40,0), + (7, -40,0), + (7, -40,0), + (7, 39,0), + (7, 39,0), + (7, -39,0), + (7, -39,0), + (7, 38,0), + (7, 38,0), + (7, -38,0), + (7, -38,0), + (7, 37,0), + (7, 37,0), + (7, -37,0), + (7, -37,0), + (7, 36,0), + (7, 36,0), + (7, -36,0), + (7, -36,0), + (7, 35,0), + (7, 35,0), + (7, -35,0), + (7, -35,0), + (7, 34,0), + (7, 34,0), + (7, -34,0), + (7, -34,0), + (7, 33,0), + (7, 33,0), + (7, -33,0), + (7, -33,0), + (7, 32,0), + (7, 32,0), + (7, -32,0), + (7, -32,0), + (7, 14,1), + (7, 14,1), + (7, -14,1), + (7, -14,1), + (7, 13,1), + (7, 13,1), + (7, -13,1), + (7, -13,1), + (7, 12,1), + (7, 12,1), + (7, -12,1), + (7, -12,1), + (7, 11,1), + (7, 11,1), + (7, -11,1), + (7, -11,1), + (7, 10,1), + (7, 10,1), + (7, -10,1), + (7, -10,1), + (7, 9,1), + (7, 9,1), + (7, -9,1), + (7, -9,1), + (7, 8,1), + (7, 8,1), + (7, -8,1), + (7, -8,1), + (6, 31,0), + (6, 31,0), + (6, 31,0), + (6, 31,0), + (6, -31,0), + (6, -31,0), + (6, -31,0), + (6, -31,0), + (6, 30,0), + (6, 30,0), + (6, 30,0), + (6, 30,0), + (6, -30,0), + (6, -30,0), + (6, -30,0), + (6, -30,0), + (6, 29,0), + (6, 29,0), + (6, 29,0), + (6, 29,0), + (6, -29,0), + (6, -29,0), + (6, -29,0), + (6, -29,0), + (6, 28,0), + (6, 28,0), + (6, 28,0), + (6, 28,0), + (6, -28,0), + (6, -28,0), + (6, -28,0), + (6, -28,0), + (6, 27,0), + (6, 27,0), + (6, 27,0), + (6, 27,0), + (6, -27,0), + (6, -27,0), + (6, -27,0), + (6, -27,0), + (6, 26,0), + (6, 26,0), + (6, 26,0), + (6, 26,0), + (6, -26,0), + (6, -26,0), + (6, -26,0), + (6, -26,0), + (6, 25,0), + (6, 25,0), + (6, 25,0), + (6, 25,0), + (6, -25,0), + (6, -25,0), + (6, -25,0), + (6, -25,0), + (6, 24,0), + (6, 24,0), + (6, 24,0), + (6, 24,0), + (6, -24,0), + (6, -24,0), + (6, -24,0), + (6, -24,0), + (6, 23,0), + (6, 23,0), + (6, 23,0), + (6, 23,0), + (6, -23,0), + (6, -23,0), + (6, -23,0), + (6, -23,0), + (6, 22,0), + (6, 22,0), + (6, 22,0), + (6, 22,0), + (6, -22,0), + (6, -22,0), + (6, -22,0), + (6, -22,0), + (6, 21,0), + (6, 21,0), + (6, 21,0), + (6, 21,0), + (6, -21,0), + (6, -21,0), + (6, -21,0), + (6, -21,0), + (6, 20,0), + (6, 20,0), + (6, 20,0), + (6, 20,0), + (6, -20,0), + (6, -20,0), + (6, -20,0), + (6, -20,0), + (6, 19,0), + (6, 19,0), + (6, 19,0), + (6, 19,0), + (6, -19,0), + (6, -19,0), + (6, -19,0), + (6, -19,0), + (6, 18,0), + (6, 18,0), + (6, 18,0), + (6, 18,0), + (6, -18,0), + (6, -18,0), + (6, -18,0), + (6, -18,0), + (6, 17,0), + (6, 17,0), + (6, 17,0), + (6, 17,0), + (6, -17,0), + (6, -17,0), + (6, -17,0), + (6, -17,0), + (6, 16,0), + (6, 16,0), + (6, 16,0), + (6, 16,0), + (6, -16,0), + (6, -16,0), + (6, -16,0), + (6, -16,0), +}; diff --git a/appl/wm/mpeg/c0.vlc b/appl/wm/mpeg/c0.vlc new file mode 100644 index 00000000..cc1af6e6 --- /dev/null +++ b/appl/wm/mpeg/c0.vlc @@ -0,0 +1,50 @@ +# Run/Level continuation 0 +# vlc -uUNDEF,UNDEF c0 < c0.vlc > c0.tab +11111s 16,0 +11110s 17,0 +11101s 18,0 +11100s 19,0 +11011s 20,0 +11010s 21,0 +11001s 22,0 +11000s 23,0 +10111s 24,0 +10110s 25,0 +10101s 26,0 +10100s 27,0 +10011s 28,0 +10010s 29,0 +10001s 30,0 +10000s 31,0 +011000s 32,0 +010111s 33,0 +010110s 34,0 +010101s 35,0 +010100s 36,0 +010011s 37,0 +010010s 38,0 +010001s 39,0 +010000s 40,0 +011111s 8,1 +011110s 9,1 +011101s 10,1 +011100s 11,1 +011011s 12,1 +011010s 13,1 +011001s 14,1 +0010011s 15,1 +0010010s 16,1 +0010001s 17,1 +0010000s 18,1 +0010100s 3,6 +0011010s 2,11 +0011001s 2,12 +0011000s 2,13 +0010111s 2,14 +0010110s 2,15 +0010101s 2,16 +0011111s 1,27 +0011110s 1,28 +0011101s 1,29 +0011100s 1,30 +0011011s 1,31 diff --git a/appl/wm/mpeg/c1.tab b/appl/wm/mpeg/c1.tab new file mode 100644 index 00000000..ff834508 --- /dev/null +++ b/appl/wm/mpeg/c1.tab @@ -0,0 +1,37 @@ +# vlc -cfp c1 +c1_size: con 32; +c1_bits: con 5; +c1_table:= array[] of { + (2,10), + (-2,10), + (2,9), + (-2,9), + (3,5), + (-3,5), + (4,3), + (-4,3), + (5,2), + (-5,2), + (7,1), + (-7,1), + (6,1), + (-6,1), + (15,0), + (-15,0), + (14,0), + (-14,0), + (13,0), + (-13,0), + (12,0), + (-12,0), + (1,26), + (-1,26), + (1,25), + (-1,25), + (1,24), + (-1,24), + (1,23), + (-1,23), + (1,22), + (-1,22), +}; diff --git a/appl/wm/mpeg/c1.vlc b/appl/wm/mpeg/c1.vlc new file mode 100644 index 00000000..1e18d599 --- /dev/null +++ b/appl/wm/mpeg/c1.vlc @@ -0,0 +1,18 @@ +# Run/Level continuation 1 +# vlc -cfp c1 < c1.vlc > c1.tab +1010s 12,0 +1001s 13,0 +1000s 14,0 +0111s 15,0 +0110s 6,1 +0101s 7,1 +0100s 5,2 +0011s 4,3 +0010s 3,5 +0001s 2,9 +0000s 2,10 +1111s 1,22 +1110s 1,23 +1101s 1,24 +1100s 1,25 +1011s 1,26 diff --git a/appl/wm/mpeg/c2.tab b/appl/wm/mpeg/c2.tab new file mode 100644 index 00000000..69c0ebf2 --- /dev/null +++ b/appl/wm/mpeg/c2.tab @@ -0,0 +1,21 @@ +# vlc -cfp c2 +c2_size: con 16; +c2_bits: con 4; +c2_table:= array[] of { + (11,0), + (-11,0), + (2,8), + (-2,8), + (3,4), + (-3,4), + (10,0), + (-10,0), + (4,2), + (-4,2), + (2,7), + (-2,7), + (1,21), + (-1,21), + (1,20), + (-1,20), +}; diff --git a/appl/wm/mpeg/c2.vlc b/appl/wm/mpeg/c2.vlc new file mode 100644 index 00000000..167d011a --- /dev/null +++ b/appl/wm/mpeg/c2.vlc @@ -0,0 +1,10 @@ +# Run/Level continuation 2 +# vlc -cfp c2 < c2.vlc > c2.tab +011s 10,0 +000s 11,0 +100s 4,2 +010s 3,4 +101s 2,7 +001s 2,8 +111s 1,20 +110s 1,21 diff --git a/appl/wm/mpeg/c3.tab b/appl/wm/mpeg/c3.tab new file mode 100644 index 00000000..06fd7cfb --- /dev/null +++ b/appl/wm/mpeg/c3.tab @@ -0,0 +1,21 @@ +# vlc -cfp c3 +c3_size: con 16; +c3_bits: con 4; +c3_table:= array[] of { + (9,0), + (-9,0), + (1,19), + (-1,19), + (1,18), + (-1,18), + (5,1), + (-5,1), + (3,3), + (-3,3), + (8,0), + (-8,0), + (2,6), + (-2,6), + (1,17), + (-1,17), +}; diff --git a/appl/wm/mpeg/c3.vlc b/appl/wm/mpeg/c3.vlc new file mode 100644 index 00000000..df6a8c46 --- /dev/null +++ b/appl/wm/mpeg/c3.vlc @@ -0,0 +1,10 @@ +# Run/Level continuation 3 +# vlc -cfp c3 < c3.vlc > c3.tab +101s 8,0 +000s 9,0 +011s 5,1 +100s 3,3 +110s 2,6 +111s 1,17 +010s 1,18 +001s 1,19 diff --git a/appl/wm/mpeg/c4.tab b/appl/wm/mpeg/c4.tab new file mode 100644 index 00000000..db1c3a56 --- /dev/null +++ b/appl/wm/mpeg/c4.tab @@ -0,0 +1,9 @@ +# vlc -cfp c4 +c4_size: con 4; +c4_bits: con 2; +c4_table:= array[] of { + (1,16), + (-1,16), + (2,5), + (-2,5), +}; diff --git a/appl/wm/mpeg/c4.vlc b/appl/wm/mpeg/c4.vlc new file mode 100644 index 00000000..cd6797d1 --- /dev/null +++ b/appl/wm/mpeg/c4.vlc @@ -0,0 +1,4 @@ +# Run/Level continuation 4 +# vlc -cfp c4 < c4.vlc > c4.tab +0s 1,16 +1s 2,5 diff --git a/appl/wm/mpeg/c5.tab b/appl/wm/mpeg/c5.tab new file mode 100644 index 00000000..98a1d285 --- /dev/null +++ b/appl/wm/mpeg/c5.tab @@ -0,0 +1,9 @@ +# vlc -cfp c5 +c5_size: con 4; +c5_bits: con 2; +c5_table:= array[] of { + (7,0), + (-7,0), + (3,2), + (-3,2), +}; diff --git a/appl/wm/mpeg/c5.vlc b/appl/wm/mpeg/c5.vlc new file mode 100644 index 00000000..ae0c10a8 --- /dev/null +++ b/appl/wm/mpeg/c5.vlc @@ -0,0 +1,4 @@ +# Run/Level continuation 5 +# vlc -cfp c5 < c5.vlc > c5.tab +0s 7,0 +1s 3,2 diff --git a/appl/wm/mpeg/c6.tab b/appl/wm/mpeg/c6.tab new file mode 100644 index 00000000..fe1c5c35 --- /dev/null +++ b/appl/wm/mpeg/c6.tab @@ -0,0 +1,9 @@ +# vlc -cfp c6 +c6_size: con 4; +c6_bits: con 2; +c6_table:= array[] of { + (4,1), + (-4,1), + (1,15), + (-1,15), +}; diff --git a/appl/wm/mpeg/c6.vlc b/appl/wm/mpeg/c6.vlc new file mode 100644 index 00000000..86165acf --- /dev/null +++ b/appl/wm/mpeg/c6.vlc @@ -0,0 +1,4 @@ +# Run/Level continuation 6 +# vlc -cfp c6 < c6.vlc > c6.tab +0s 4,1 +1s 1,15 diff --git a/appl/wm/mpeg/c7.tab b/appl/wm/mpeg/c7.tab new file mode 100644 index 00000000..a0385192 --- /dev/null +++ b/appl/wm/mpeg/c7.tab @@ -0,0 +1,9 @@ +# vlc -cfp c7 +c7_size: con 4; +c7_bits: con 2; +c7_table:= array[] of { + (1,14), + (-1,14), + (2,4), + (-2,4), +}; diff --git a/appl/wm/mpeg/c7.vlc b/appl/wm/mpeg/c7.vlc new file mode 100644 index 00000000..45986054 --- /dev/null +++ b/appl/wm/mpeg/c7.vlc @@ -0,0 +1,4 @@ +# Run/Level continuation 7 +# vlc -cfp c7 < c7.vlc > c7.tab +0s 1,14 +1s 2,4 diff --git a/appl/wm/mpeg/cbp.tab b/appl/wm/mpeg/cbp.tab new file mode 100644 index 00000000..ee97febb --- /dev/null +++ b/appl/wm/mpeg/cbp.tab @@ -0,0 +1,517 @@ +# vlc cbp +cbp_size: con 512; +cbp_bits: con 9; +cbp_table:= array[] of { + (0, UNDEF), + (0, UNDEF), + (9, 39), + (9, 27), + (9, 59), + (9, 55), + (9, 47), + (9, 31), + (8, 58), + (8, 58), + (8, 54), + (8, 54), + (8, 46), + (8, 46), + (8, 30), + (8, 30), + (8, 57), + (8, 57), + (8, 53), + (8, 53), + (8, 45), + (8, 45), + (8, 29), + (8, 29), + (8, 38), + (8, 38), + (8, 26), + (8, 26), + (8, 37), + (8, 37), + (8, 25), + (8, 25), + (8, 43), + (8, 43), + (8, 23), + (8, 23), + (8, 51), + (8, 51), + (8, 15), + (8, 15), + (8, 42), + (8, 42), + (8, 22), + (8, 22), + (8, 50), + (8, 50), + (8, 14), + (8, 14), + (8, 41), + (8, 41), + (8, 21), + (8, 21), + (8, 49), + (8, 49), + (8, 13), + (8, 13), + (8, 35), + (8, 35), + (8, 19), + (8, 19), + (8, 11), + (8, 11), + (8, 7), + (8, 7), + (7, 34), + (7, 34), + (7, 34), + (7, 34), + (7, 18), + (7, 18), + (7, 18), + (7, 18), + (7, 10), + (7, 10), + (7, 10), + (7, 10), + (7, 6), + (7, 6), + (7, 6), + (7, 6), + (7, 33), + (7, 33), + (7, 33), + (7, 33), + (7, 17), + (7, 17), + (7, 17), + (7, 17), + (7, 9), + (7, 9), + (7, 9), + (7, 9), + (7, 5), + (7, 5), + (7, 5), + (7, 5), + (6, 63), + (6, 63), + (6, 63), + (6, 63), + (6, 63), + (6, 63), + (6, 63), + (6, 63), + (6, 3), + (6, 3), + (6, 3), + (6, 3), + (6, 3), + (6, 3), + (6, 3), + (6, 3), + (6, 36), + (6, 36), + (6, 36), + (6, 36), + (6, 36), + (6, 36), + (6, 36), + (6, 36), + (6, 24), + (6, 24), + (6, 24), + (6, 24), + (6, 24), + (6, 24), + (6, 24), + (6, 24), + (5, 62), + (5, 62), + (5, 62), + (5, 62), + (5, 62), + (5, 62), + (5, 62), + (5, 62), + (5, 62), + (5, 62), + (5, 62), + (5, 62), + (5, 62), + (5, 62), + (5, 62), + (5, 62), + (5, 2), + (5, 2), + (5, 2), + (5, 2), + (5, 2), + (5, 2), + (5, 2), + (5, 2), + (5, 2), + (5, 2), + (5, 2), + (5, 2), + (5, 2), + (5, 2), + (5, 2), + (5, 2), + (5, 61), + (5, 61), + (5, 61), + (5, 61), + (5, 61), + (5, 61), + (5, 61), + (5, 61), + (5, 61), + (5, 61), + (5, 61), + (5, 61), + (5, 61), + (5, 61), + (5, 61), + (5, 61), + (5, 1), + (5, 1), + (5, 1), + (5, 1), + (5, 1), + (5, 1), + (5, 1), + (5, 1), + (5, 1), + (5, 1), + (5, 1), + (5, 1), + (5, 1), + (5, 1), + (5, 1), + (5, 1), + (5, 56), + (5, 56), + (5, 56), + (5, 56), + (5, 56), + (5, 56), + (5, 56), + (5, 56), + (5, 56), + (5, 56), + (5, 56), + (5, 56), + (5, 56), + (5, 56), + (5, 56), + (5, 56), + (5, 52), + (5, 52), + (5, 52), + (5, 52), + (5, 52), + (5, 52), + (5, 52), + (5, 52), + (5, 52), + (5, 52), + (5, 52), + (5, 52), + (5, 52), + (5, 52), + (5, 52), + (5, 52), + (5, 44), + (5, 44), + (5, 44), + (5, 44), + (5, 44), + (5, 44), + (5, 44), + (5, 44), + (5, 44), + (5, 44), + (5, 44), + (5, 44), + (5, 44), + (5, 44), + (5, 44), + (5, 44), + (5, 28), + (5, 28), + (5, 28), + (5, 28), + (5, 28), + (5, 28), + (5, 28), + (5, 28), + (5, 28), + (5, 28), + (5, 28), + (5, 28), + (5, 28), + (5, 28), + (5, 28), + (5, 28), + (5, 40), + (5, 40), + (5, 40), + (5, 40), + (5, 40), + (5, 40), + (5, 40), + (5, 40), + (5, 40), + (5, 40), + (5, 40), + (5, 40), + (5, 40), + (5, 40), + (5, 40), + (5, 40), + (5, 20), + (5, 20), + (5, 20), + (5, 20), + (5, 20), + (5, 20), + (5, 20), + (5, 20), + (5, 20), + (5, 20), + (5, 20), + (5, 20), + (5, 20), + (5, 20), + (5, 20), + (5, 20), + (5, 48), + (5, 48), + (5, 48), + (5, 48), + (5, 48), + (5, 48), + (5, 48), + (5, 48), + (5, 48), + (5, 48), + (5, 48), + (5, 48), + (5, 48), + (5, 48), + (5, 48), + (5, 48), + (5, 12), + (5, 12), + (5, 12), + (5, 12), + (5, 12), + (5, 12), + (5, 12), + (5, 12), + (5, 12), + (5, 12), + (5, 12), + (5, 12), + (5, 12), + (5, 12), + (5, 12), + (5, 12), + (4, 32), + (4, 32), + (4, 32), + (4, 32), + (4, 32), + (4, 32), + (4, 32), + (4, 32), + (4, 32), + (4, 32), + (4, 32), + (4, 32), + (4, 32), + (4, 32), + (4, 32), + (4, 32), + (4, 32), + (4, 32), + (4, 32), + (4, 32), + (4, 32), + (4, 32), + (4, 32), + (4, 32), + (4, 32), + (4, 32), + (4, 32), + (4, 32), + (4, 32), + (4, 32), + (4, 32), + (4, 32), + (4, 16), + (4, 16), + (4, 16), + (4, 16), + (4, 16), + (4, 16), + (4, 16), + (4, 16), + (4, 16), + (4, 16), + (4, 16), + (4, 16), + (4, 16), + (4, 16), + (4, 16), + (4, 16), + (4, 16), + (4, 16), + (4, 16), + (4, 16), + (4, 16), + (4, 16), + (4, 16), + (4, 16), + (4, 16), + (4, 16), + (4, 16), + (4, 16), + (4, 16), + (4, 16), + (4, 16), + (4, 16), + (4, 8), + (4, 8), + (4, 8), + (4, 8), + (4, 8), + (4, 8), + (4, 8), + (4, 8), + (4, 8), + (4, 8), + (4, 8), + (4, 8), + (4, 8), + (4, 8), + (4, 8), + (4, 8), + (4, 8), + (4, 8), + (4, 8), + (4, 8), + (4, 8), + (4, 8), + (4, 8), + (4, 8), + (4, 8), + (4, 8), + (4, 8), + (4, 8), + (4, 8), + (4, 8), + (4, 8), + (4, 8), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), + (3, 60), +}; diff --git a/appl/wm/mpeg/cbp.vlc b/appl/wm/mpeg/cbp.vlc new file mode 100644 index 00000000..c7c9438c --- /dev/null +++ b/appl/wm/mpeg/cbp.vlc @@ -0,0 +1,65 @@ +# Coded Block Pattern +# vlc cbp < cbp.vlc > cbp.tab +01011 1 +01001 2 +001101 3 +1101 4 +0010111 5 +0010011 6 +00011111 7 +1100 8 +0010110 9 +0010010 10 +00011110 11 +10011 12 +00011011 13 +00010111 14 +00010011 15 +1011 16 +0010101 17 +0010001 18 +00011101 19 +10001 20 +00011001 21 +00010101 22 +00010001 23 +001111 24 +00001111 25 +00001101 26 +000000011 27 +01111 28 +00001011 29 +00000111 30 +000000111 31 +1010 32 +0010100 33 +0010000 34 +00011100 35 +001110 36 +00001110 37 +00001100 38 +000000010 39 +10000 40 +00011000 41 +00010100 42 +00010000 43 +01110 44 +00001010 45 +00000110 46 +000000110 47 +10010 48 +00011010 49 +00010110 50 +00010010 51 +01101 52 +00001001 53 +00000101 54 +000000101 55 +01100 56 +00001000 57 +00000100 58 +000000100 59 +111 60 +01010 61 +01000 62 +001100 63 diff --git a/appl/wm/mpeg/cdc.tab b/appl/wm/mpeg/cdc.tab new file mode 100644 index 00000000..91ccb057 --- /dev/null +++ b/appl/wm/mpeg/cdc.tab @@ -0,0 +1,261 @@ +# vlc cdc +cdc_size: con 256; +cdc_bits: con 8; +cdc_table:= array[] of { + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 0), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (5, 5), + (5, 5), + (5, 5), + (5, 5), + (5, 5), + (5, 5), + (5, 5), + (5, 5), + (6, 6), + (6, 6), + (6, 6), + (6, 6), + (7, 7), + (7, 7), + (8, 8), + (0, UNDEF), +}; diff --git a/appl/wm/mpeg/cdc.vlc b/appl/wm/mpeg/cdc.vlc new file mode 100644 index 00000000..fadf199f --- /dev/null +++ b/appl/wm/mpeg/cdc.vlc @@ -0,0 +1,11 @@ +# Chrominance DC +# vlc cdc < cdc.vlc > cdc.tab +00 0 +01 1 +10 2 +110 3 +1110 4 +11110 5 +111110 6 +1111110 7 +11111110 8 diff --git a/appl/wm/mpeg/closest.m b/appl/wm/mpeg/closest.m new file mode 100644 index 00000000..eccd7dab --- /dev/null +++ b/appl/wm/mpeg/closest.m @@ -0,0 +1,514 @@ +closest := array[16*16*16] of { + byte 255,byte 255,byte 255,byte 254,byte 254,byte 237,byte 220,byte 203, + byte 253,byte 236,byte 219,byte 202,byte 252,byte 235,byte 218,byte 201, + byte 255,byte 255,byte 255,byte 254,byte 254,byte 237,byte 220,byte 203, + byte 253,byte 236,byte 219,byte 202,byte 252,byte 235,byte 218,byte 201, + byte 255,byte 255,byte 255,byte 250,byte 250,byte 250,byte 220,byte 249, + byte 249,byte 249,byte 232,byte 248,byte 248,byte 248,byte 231,byte 201, + byte 251,byte 251,byte 250,byte 250,byte 250,byte 250,byte 249,byte 249, + byte 249,byte 249,byte 232,byte 248,byte 248,byte 248,byte 231,byte 201, + byte 251,byte 251,byte 250,byte 250,byte 250,byte 233,byte 233,byte 249, + byte 249,byte 232,byte 215,byte 215,byte 248,byte 231,byte 214,byte 197, + byte 234,byte 234,byte 250,byte 250,byte 233,byte 233,byte 216,byte 216, + byte 249,byte 232,byte 215,byte 198,byte 198,byte 231,byte 214,byte 197, + byte 217,byte 217,byte 217,byte 246,byte 233,byte 216,byte 216,byte 199, + byte 199,byte 215,byte 215,byte 198,byte 198,byte 198,byte 214,byte 197, + byte 200,byte 200,byte 246,byte 246,byte 246,byte 216,byte 199,byte 199, + byte 245,byte 245,byte 198,byte 244,byte 244,byte 244,byte 227,byte 197, + byte 247,byte 247,byte 246,byte 246,byte 246,byte 246,byte 199,byte 245, + byte 245,byte 245,byte 228,byte 244,byte 244,byte 244,byte 227,byte 193, + byte 230,byte 230,byte 246,byte 246,byte 229,byte 229,byte 212,byte 245, + byte 245,byte 228,byte 228,byte 211,byte 244,byte 227,byte 210,byte 193, + byte 213,byte 213,byte 229,byte 229,byte 212,byte 212,byte 212,byte 195, + byte 228,byte 228,byte 211,byte 211,byte 194,byte 227,byte 210,byte 193, + byte 196,byte 196,byte 242,byte 242,byte 212,byte 195,byte 195,byte 241, + byte 241,byte 211,byte 211,byte 194,byte 194,byte 240,byte 210,byte 193, + byte 243,byte 243,byte 242,byte 242,byte 242,byte 195,byte 195,byte 241, + byte 241,byte 241,byte 194,byte 194,byte 240,byte 240,byte 239,byte 205, + byte 226,byte 226,byte 242,byte 242,byte 225,byte 225,byte 195,byte 241, + byte 241,byte 224,byte 224,byte 240,byte 240,byte 239,byte 239,byte 205, + byte 209,byte 209,byte 225,byte 225,byte 208,byte 208,byte 208,byte 224, + byte 224,byte 223,byte 223,byte 223,byte 239,byte 239,byte 222,byte 205, + byte 192,byte 192,byte 192,byte 192,byte 207,byte 207,byte 207,byte 207, + byte 206,byte 206,byte 206,byte 206,byte 205,byte 205,byte 205,byte 205, + byte 255,byte 255,byte 255,byte 254,byte 254,byte 237,byte 220,byte 203, + byte 253,byte 236,byte 219,byte 202,byte 252,byte 235,byte 218,byte 201, + byte 255,byte 238,byte 221,byte 221,byte 254,byte 237,byte 220,byte 203, + byte 253,byte 236,byte 219,byte 202,byte 252,byte 235,byte 218,byte 201, + byte 255,byte 221,byte 221,byte 221,byte 204,byte 250,byte 220,byte 249, + byte 249,byte 249,byte 232,byte 248,byte 248,byte 248,byte 231,byte 201, + byte 251,byte 221,byte 221,byte 204,byte 250,byte 250,byte 249,byte 249, + byte 249,byte 249,byte 232,byte 248,byte 248,byte 248,byte 231,byte 201, + byte 251,byte 251,byte 204,byte 250,byte 250,byte 233,byte 233,byte 249, + byte 249,byte 232,byte 215,byte 215,byte 248,byte 231,byte 214,byte 197, + byte 234,byte 234,byte 250,byte 250,byte 233,byte 233,byte 216,byte 216, + byte 249,byte 232,byte 215,byte 198,byte 198,byte 231,byte 214,byte 197, + byte 217,byte 217,byte 217,byte 246,byte 233,byte 216,byte 216,byte 199, + byte 199,byte 215,byte 215,byte 198,byte 198,byte 198,byte 214,byte 197, + byte 200,byte 200,byte 246,byte 246,byte 246,byte 216,byte 199,byte 199, + byte 245,byte 245,byte 198,byte 244,byte 244,byte 244,byte 227,byte 197, + byte 247,byte 247,byte 246,byte 246,byte 246,byte 246,byte 199,byte 245, + byte 245,byte 245,byte 228,byte 244,byte 244,byte 244,byte 227,byte 193, + byte 230,byte 230,byte 246,byte 246,byte 229,byte 229,byte 212,byte 245, + byte 245,byte 228,byte 228,byte 211,byte 244,byte 227,byte 210,byte 193, + byte 213,byte 213,byte 229,byte 229,byte 212,byte 212,byte 212,byte 195, + byte 228,byte 228,byte 211,byte 211,byte 194,byte 227,byte 210,byte 193, + byte 196,byte 196,byte 242,byte 242,byte 212,byte 195,byte 195,byte 241, + byte 241,byte 211,byte 211,byte 194,byte 194,byte 240,byte 210,byte 193, + byte 243,byte 243,byte 242,byte 242,byte 242,byte 195,byte 195,byte 241, + byte 241,byte 241,byte 194,byte 194,byte 240,byte 240,byte 239,byte 205, + byte 226,byte 226,byte 242,byte 242,byte 225,byte 225,byte 195,byte 241, + byte 241,byte 224,byte 224,byte 240,byte 240,byte 239,byte 239,byte 205, + byte 209,byte 209,byte 225,byte 225,byte 208,byte 208,byte 208,byte 224, + byte 224,byte 223,byte 223,byte 223,byte 239,byte 239,byte 222,byte 205, + byte 192,byte 192,byte 192,byte 192,byte 207,byte 207,byte 207,byte 207, + byte 206,byte 206,byte 206,byte 206,byte 205,byte 205,byte 205,byte 205, + byte 255,byte 255,byte 255,byte 191,byte 191,byte 191,byte 220,byte 190, + byte 190,byte 190,byte 173,byte 189,byte 189,byte 189,byte 172,byte 201, + byte 255,byte 221,byte 221,byte 221,byte 204,byte 191,byte 220,byte 190, + byte 190,byte 190,byte 173,byte 189,byte 189,byte 189,byte 172,byte 201, + byte 255,byte 221,byte 221,byte 204,byte 204,byte 204,byte 186,byte 186, + byte 186,byte 186,byte 186,byte 185,byte 185,byte 185,byte 168,byte 201, + byte 188,byte 221,byte 204,byte 204,byte 204,byte 187,byte 186,byte 186, + byte 186,byte 186,byte 232,byte 185,byte 185,byte 185,byte 168,byte 201, + byte 188,byte 204,byte 204,byte 204,byte 187,byte 187,byte 186,byte 186, + byte 186,byte 186,byte 169,byte 185,byte 185,byte 185,byte 168,byte 197, + byte 188,byte 188,byte 204,byte 187,byte 187,byte 233,byte 216,byte 186, + byte 186,byte 186,byte 215,byte 185,byte 185,byte 185,byte 168,byte 197, + byte 217,byte 217,byte 183,byte 183,byte 183,byte 216,byte 216,byte 199, + byte 182,byte 182,byte 215,byte 198,byte 198,byte 181,byte 214,byte 197, + byte 184,byte 184,byte 183,byte 183,byte 183,byte 183,byte 199,byte 182, + byte 182,byte 182,byte 182,byte 181,byte 181,byte 181,byte 181,byte 197, + byte 184,byte 184,byte 183,byte 183,byte 183,byte 183,byte 182,byte 182, + byte 182,byte 182,byte 182,byte 181,byte 181,byte 181,byte 164,byte 193, + byte 184,byte 184,byte 183,byte 183,byte 183,byte 183,byte 182,byte 182, + byte 182,byte 228,byte 165,byte 181,byte 181,byte 164,byte 164,byte 193, + byte 167,byte 167,byte 183,byte 229,byte 166,byte 212,byte 212,byte 182, + byte 182,byte 165,byte 211,byte 211,byte 181,byte 164,byte 210,byte 193, + byte 180,byte 180,byte 179,byte 179,byte 179,byte 179,byte 195,byte 178, + byte 178,byte 178,byte 211,byte 194,byte 177,byte 177,byte 177,byte 193, + byte 180,byte 180,byte 179,byte 179,byte 179,byte 179,byte 195,byte 178, + byte 178,byte 178,byte 178,byte 177,byte 177,byte 177,byte 177,byte 205, + byte 180,byte 180,byte 179,byte 179,byte 179,byte 179,byte 178,byte 178, + byte 178,byte 161,byte 161,byte 177,byte 177,byte 177,byte 160,byte 205, + byte 163,byte 163,byte 162,byte 162,byte 162,byte 162,byte 208,byte 178, + byte 161,byte 161,byte 223,byte 177,byte 177,byte 160,byte 160,byte 205, + byte 192,byte 192,byte 192,byte 192,byte 207,byte 207,byte 207,byte 207, + byte 206,byte 206,byte 206,byte 206,byte 205,byte 205,byte 205,byte 205, + byte 176,byte 176,byte 191,byte 191,byte 191,byte 191,byte 190,byte 190, + byte 190,byte 190,byte 173,byte 189,byte 189,byte 189,byte 172,byte 201, + byte 176,byte 221,byte 221,byte 204,byte 191,byte 191,byte 190,byte 190, + byte 190,byte 190,byte 173,byte 189,byte 189,byte 189,byte 172,byte 201, + byte 188,byte 221,byte 204,byte 204,byte 204,byte 187,byte 186,byte 186, + byte 186,byte 186,byte 173,byte 185,byte 185,byte 185,byte 168,byte 201, + byte 188,byte 204,byte 204,byte 204,byte 187,byte 187,byte 186,byte 186, + byte 186,byte 186,byte 169,byte 185,byte 185,byte 185,byte 168,byte 201, + byte 188,byte 188,byte 204,byte 187,byte 187,byte 187,byte 186,byte 186, + byte 186,byte 186,byte 169,byte 185,byte 185,byte 185,byte 168,byte 197, + byte 188,byte 188,byte 187,byte 187,byte 187,byte 170,byte 170,byte 186, + byte 186,byte 169,byte 169,byte 185,byte 185,byte 168,byte 168,byte 197, + byte 184,byte 184,byte 183,byte 183,byte 183,byte 170,byte 170,byte 182, + byte 182,byte 169,byte 152,byte 152,byte 181,byte 168,byte 151,byte 197, + byte 184,byte 184,byte 183,byte 183,byte 183,byte 183,byte 182,byte 182, + byte 182,byte 182,byte 182,byte 181,byte 181,byte 181,byte 164,byte 197, + byte 184,byte 184,byte 183,byte 183,byte 183,byte 183,byte 182,byte 182, + byte 182,byte 182,byte 165,byte 181,byte 181,byte 181,byte 164,byte 193, + byte 184,byte 184,byte 183,byte 183,byte 183,byte 166,byte 166,byte 182, + byte 182,byte 165,byte 165,byte 181,byte 181,byte 164,byte 164,byte 193, + byte 167,byte 167,byte 167,byte 166,byte 166,byte 166,byte 149,byte 182, + byte 165,byte 165,byte 165,byte 148,byte 181,byte 164,byte 147,byte 193, + byte 180,byte 180,byte 179,byte 179,byte 179,byte 179,byte 149,byte 178, + byte 178,byte 178,byte 148,byte 177,byte 177,byte 177,byte 147,byte 193, + byte 180,byte 180,byte 179,byte 179,byte 179,byte 179,byte 178,byte 178, + byte 178,byte 178,byte 178,byte 177,byte 177,byte 177,byte 160,byte 205, + byte 180,byte 180,byte 179,byte 179,byte 179,byte 162,byte 162,byte 178, + byte 178,byte 161,byte 161,byte 177,byte 177,byte 160,byte 160,byte 205, + byte 163,byte 163,byte 162,byte 162,byte 162,byte 162,byte 145,byte 161, + byte 161,byte 161,byte 144,byte 144,byte 160,byte 160,byte 160,byte 205, + byte 192,byte 192,byte 192,byte 192,byte 207,byte 207,byte 207,byte 207, + byte 206,byte 206,byte 206,byte 206,byte 205,byte 205,byte 205,byte 205, + byte 176,byte 176,byte 191,byte 191,byte 191,byte 174,byte 174,byte 190, + byte 190,byte 173,byte 156,byte 156,byte 189,byte 172,byte 155,byte 138, + byte 176,byte 176,byte 204,byte 191,byte 191,byte 174,byte 174,byte 190, + byte 190,byte 173,byte 156,byte 156,byte 189,byte 172,byte 155,byte 138, + byte 188,byte 204,byte 204,byte 204,byte 187,byte 187,byte 186,byte 186, + byte 186,byte 186,byte 169,byte 185,byte 185,byte 185,byte 168,byte 138, + byte 188,byte 188,byte 204,byte 187,byte 187,byte 187,byte 186,byte 186, + byte 186,byte 186,byte 169,byte 185,byte 185,byte 185,byte 168,byte 138, + byte 188,byte 188,byte 187,byte 187,byte 187,byte 170,byte 170,byte 186, + byte 186,byte 169,byte 169,byte 185,byte 185,byte 168,byte 151,byte 134, + byte 171,byte 171,byte 187,byte 187,byte 170,byte 170,byte 170,byte 186, + byte 186,byte 169,byte 152,byte 152,byte 185,byte 168,byte 151,byte 134, + byte 171,byte 171,byte 183,byte 183,byte 170,byte 170,byte 170,byte 153, + byte 182,byte 169,byte 152,byte 135,byte 135,byte 168,byte 151,byte 134, + byte 184,byte 184,byte 183,byte 183,byte 183,byte 183,byte 153,byte 182, + byte 182,byte 182,byte 182,byte 181,byte 181,byte 181,byte 164,byte 134, + byte 184,byte 184,byte 183,byte 183,byte 183,byte 183,byte 182,byte 182, + byte 182,byte 182,byte 165,byte 181,byte 181,byte 181,byte 164,byte 130, + byte 167,byte 167,byte 183,byte 183,byte 166,byte 166,byte 166,byte 182, + byte 182,byte 165,byte 165,byte 181,byte 181,byte 164,byte 147,byte 130, + byte 150,byte 150,byte 166,byte 166,byte 166,byte 149,byte 149,byte 182, + byte 165,byte 165,byte 148,byte 148,byte 164,byte 164,byte 147,byte 130, + byte 150,byte 150,byte 179,byte 179,byte 179,byte 149,byte 132,byte 178, + byte 178,byte 178,byte 148,byte 131,byte 177,byte 177,byte 147,byte 130, + byte 180,byte 180,byte 179,byte 179,byte 179,byte 179,byte 132,byte 178, + byte 178,byte 178,byte 161,byte 177,byte 177,byte 177,byte 160,byte 142, + byte 163,byte 163,byte 179,byte 179,byte 162,byte 162,byte 162,byte 178, + byte 178,byte 161,byte 161,byte 177,byte 177,byte 160,byte 160,byte 142, + byte 146,byte 146,byte 162,byte 162,byte 145,byte 145,byte 145,byte 161, + byte 161,byte 144,byte 144,byte 144,byte 160,byte 160,byte 159,byte 142, + byte 129,byte 129,byte 129,byte 129,byte 128,byte 128,byte 128,byte 128, + byte 143,byte 143,byte 143,byte 143,byte 142,byte 142,byte 142,byte 142, + byte 175,byte 175,byte 191,byte 191,byte 174,byte 174,byte 157,byte 157, + byte 190,byte 173,byte 156,byte 139,byte 139,byte 172,byte 155,byte 138, + byte 175,byte 175,byte 191,byte 191,byte 174,byte 174,byte 157,byte 157, + byte 190,byte 173,byte 156,byte 139,byte 139,byte 172,byte 155,byte 138, + byte 188,byte 188,byte 204,byte 187,byte 187,byte 187,byte 157,byte 186, + byte 186,byte 186,byte 156,byte 185,byte 185,byte 185,byte 168,byte 138, + byte 188,byte 188,byte 187,byte 187,byte 187,byte 170,byte 170,byte 186, + byte 186,byte 169,byte 169,byte 185,byte 185,byte 168,byte 168,byte 138, + byte 171,byte 171,byte 187,byte 187,byte 170,byte 170,byte 170,byte 186, + byte 186,byte 169,byte 152,byte 152,byte 185,byte 168,byte 151,byte 134, + byte 171,byte 171,byte 187,byte 170,byte 170,byte 170,byte 170,byte 153, + byte 169,byte 169,byte 152,byte 135,byte 135,byte 168,byte 151,byte 134, + byte 154,byte 154,byte 154,byte 170,byte 170,byte 170,byte 153,byte 153, + byte 169,byte 152,byte 152,byte 135,byte 135,byte 135,byte 151,byte 134, + byte 154,byte 154,byte 183,byte 183,byte 183,byte 153,byte 153,byte 153, + byte 182,byte 182,byte 135,byte 135,byte 181,byte 181,byte 164,byte 134, + byte 184,byte 184,byte 183,byte 183,byte 183,byte 166,byte 166,byte 182, + byte 182,byte 165,byte 165,byte 181,byte 181,byte 164,byte 164,byte 130, + byte 167,byte 167,byte 183,byte 166,byte 166,byte 166,byte 149,byte 182, + byte 165,byte 165,byte 165,byte 148,byte 181,byte 164,byte 147,byte 130, + byte 150,byte 150,byte 150,byte 166,byte 149,byte 149,byte 149,byte 132, + byte 165,byte 165,byte 148,byte 148,byte 131,byte 147,byte 147,byte 130, + byte 133,byte 133,byte 179,byte 179,byte 149,byte 132,byte 132,byte 132, + byte 178,byte 148,byte 148,byte 131,byte 131,byte 131,byte 130,byte 130, + byte 133,byte 133,byte 179,byte 179,byte 179,byte 132,byte 132,byte 178, + byte 178,byte 178,byte 131,byte 131,byte 131,byte 177,byte 160,byte 142, + byte 163,byte 163,byte 179,byte 162,byte 162,byte 162,byte 132,byte 178, + byte 161,byte 161,byte 144,byte 131,byte 177,byte 160,byte 160,byte 142, + byte 146,byte 146,byte 162,byte 162,byte 145,byte 145,byte 145,byte 161, + byte 161,byte 144,byte 144,byte 143,byte 160,byte 160,byte 159,byte 142, + byte 129,byte 129,byte 129,byte 129,byte 128,byte 128,byte 128,byte 128, + byte 143,byte 143,byte 143,byte 143,byte 142,byte 142,byte 142,byte 142, + byte 158,byte 158,byte 158,byte 112,byte 174,byte 157,byte 157,byte 140, + byte 140,byte 156,byte 156,byte 139,byte 139,byte 139,byte 155,byte 138, + byte 158,byte 158,byte 158,byte 112,byte 174,byte 157,byte 157,byte 140, + byte 140,byte 156,byte 156,byte 139,byte 139,byte 139,byte 155,byte 138, + byte 158,byte 158,byte 124,byte 124,byte 124,byte 157,byte 157,byte 140, + byte 123,byte 123,byte 156,byte 139,byte 139,byte 122,byte 155,byte 138, + byte 125,byte 125,byte 124,byte 124,byte 124,byte 170,byte 170,byte 123, + byte 123,byte 169,byte 152,byte 152,byte 122,byte 168,byte 151,byte 138, + byte 171,byte 171,byte 124,byte 124,byte 170,byte 170,byte 170,byte 153, + byte 123,byte 169,byte 152,byte 135,byte 135,byte 168,byte 151,byte 134, + byte 154,byte 154,byte 154,byte 170,byte 170,byte 170,byte 153,byte 153, + byte 169,byte 152,byte 152,byte 135,byte 135,byte 135,byte 151,byte 134, + byte 154,byte 154,byte 154,byte 170,byte 170,byte 153,byte 153,byte 153, + byte 136,byte 152,byte 135,byte 135,byte 135,byte 135,byte 134,byte 134, + byte 137,byte 137,byte 137,byte 120,byte 153,byte 153,byte 153,byte 136, + byte 136,byte 136,byte 135,byte 135,byte 135,byte 118,byte 164,byte 134, + byte 137,byte 137,byte 120,byte 120,byte 120,byte 166,byte 136,byte 136, + byte 136,byte 165,byte 165,byte 118,byte 118,byte 164,byte 147,byte 130, + byte 150,byte 150,byte 120,byte 166,byte 166,byte 149,byte 149,byte 136, + byte 165,byte 165,byte 148,byte 148,byte 118,byte 164,byte 147,byte 130, + byte 150,byte 150,byte 150,byte 149,byte 149,byte 149,byte 132,byte 132, + byte 165,byte 148,byte 148,byte 131,byte 131,byte 147,byte 147,byte 130, + byte 133,byte 133,byte 133,byte 149,byte 132,byte 132,byte 132,byte 132, + byte 115,byte 148,byte 131,byte 131,byte 131,byte 131,byte 130,byte 130, + byte 133,byte 133,byte 133,byte 116,byte 132,byte 132,byte 132,byte 132, + byte 115,byte 115,byte 131,byte 131,byte 131,byte 131,byte 160,byte 142, + byte 133,byte 133,byte 116,byte 162,byte 162,byte 132,byte 132,byte 115, + byte 161,byte 161,byte 144,byte 131,byte 131,byte 160,byte 160,byte 142, + byte 146,byte 146,byte 146,byte 145,byte 145,byte 145,byte 128,byte 161, + byte 144,byte 144,byte 144,byte 143,byte 160,byte 160,byte 159,byte 142, + byte 129,byte 129,byte 129,byte 129,byte 128,byte 128,byte 128,byte 128, + byte 143,byte 143,byte 143,byte 143,byte 142,byte 142,byte 142,byte 142, + byte 141,byte 141,byte 112,byte 112,byte 112,byte 157,byte 140,byte 140, + byte 140,byte 127,byte 139,byte 126,byte 126,byte 126,byte 109,byte 138, + byte 141,byte 141,byte 112,byte 112,byte 112,byte 157,byte 140,byte 140, + byte 140,byte 127,byte 139,byte 126,byte 126,byte 126,byte 109,byte 138, + byte 125,byte 125,byte 124,byte 124,byte 124,byte 124,byte 140,byte 123, + byte 123,byte 123,byte 123,byte 122,byte 122,byte 122,byte 122,byte 138, + byte 125,byte 125,byte 124,byte 124,byte 124,byte 124,byte 123,byte 123, + byte 123,byte 123,byte 123,byte 122,byte 122,byte 122,byte 105,byte 138, + byte 125,byte 125,byte 124,byte 124,byte 124,byte 124,byte 153,byte 123, + byte 123,byte 123,byte 152,byte 122,byte 122,byte 122,byte 105,byte 134, + byte 154,byte 154,byte 124,byte 124,byte 124,byte 153,byte 153,byte 153, + byte 123,byte 123,byte 135,byte 135,byte 122,byte 122,byte 105,byte 134, + byte 137,byte 137,byte 137,byte 120,byte 153,byte 153,byte 153,byte 136, + byte 136,byte 136,byte 135,byte 135,byte 135,byte 118,byte 105,byte 134, + byte 137,byte 137,byte 120,byte 120,byte 120,byte 153,byte 136,byte 136, + byte 136,byte 119,byte 119,byte 118,byte 118,byte 118,byte 118,byte 134, + byte 137,byte 137,byte 120,byte 120,byte 120,byte 120,byte 136,byte 136, + byte 119,byte 119,byte 119,byte 118,byte 118,byte 118,byte 101,byte 130, + byte 121,byte 121,byte 120,byte 120,byte 120,byte 120,byte 136,byte 119, + byte 119,byte 119,byte 102,byte 118,byte 118,byte 118,byte 101,byte 130, + byte 133,byte 133,byte 120,byte 120,byte 149,byte 132,byte 132,byte 119, + byte 119,byte 102,byte 148,byte 131,byte 131,byte 101,byte 101,byte 130, + byte 117,byte 117,byte 116,byte 116,byte 116,byte 132,byte 132,byte 115, + byte 115,byte 115,byte 131,byte 131,byte 114,byte 114,byte 114,byte 130, + byte 117,byte 117,byte 116,byte 116,byte 116,byte 116,byte 132,byte 115, + byte 115,byte 115,byte 131,byte 114,byte 114,byte 114,byte 114,byte 142, + byte 117,byte 117,byte 116,byte 116,byte 116,byte 116,byte 115,byte 115, + byte 115,byte 115,byte 98,byte 114,byte 114,byte 114,byte 97,byte 142, + byte 100,byte 100,byte 116,byte 99,byte 99,byte 99,byte 99,byte 115, + byte 98,byte 98,byte 98,byte 114,byte 114,byte 97,byte 97,byte 142, + byte 129,byte 129,byte 129,byte 129,byte 128,byte 128,byte 128,byte 128, + byte 143,byte 143,byte 143,byte 143,byte 142,byte 142,byte 142,byte 142, + byte 113,byte 113,byte 112,byte 112,byte 112,byte 112,byte 140,byte 140, + byte 127,byte 127,byte 110,byte 126,byte 126,byte 126,byte 109,byte 75, + byte 113,byte 113,byte 112,byte 112,byte 112,byte 112,byte 140,byte 140, + byte 127,byte 127,byte 110,byte 126,byte 126,byte 126,byte 109,byte 75, + byte 125,byte 125,byte 124,byte 124,byte 124,byte 124,byte 123,byte 123, + byte 123,byte 123,byte 123,byte 122,byte 122,byte 122,byte 105,byte 75, + byte 125,byte 125,byte 124,byte 124,byte 124,byte 124,byte 123,byte 123, + byte 123,byte 123,byte 106,byte 122,byte 122,byte 122,byte 105,byte 75, + byte 125,byte 125,byte 124,byte 124,byte 124,byte 124,byte 123,byte 123, + byte 123,byte 123,byte 106,byte 122,byte 122,byte 122,byte 105,byte 71, + byte 125,byte 125,byte 124,byte 124,byte 124,byte 107,byte 107,byte 123, + byte 123,byte 106,byte 106,byte 122,byte 122,byte 105,byte 105,byte 71, + byte 137,byte 137,byte 120,byte 120,byte 120,byte 107,byte 136,byte 136, + byte 136,byte 106,byte 106,byte 118,byte 118,byte 105,byte 88,byte 71, + byte 137,byte 137,byte 120,byte 120,byte 120,byte 120,byte 136,byte 136, + byte 119,byte 119,byte 119,byte 118,byte 118,byte 118,byte 101,byte 71, + byte 121,byte 121,byte 120,byte 120,byte 120,byte 120,byte 136,byte 119, + byte 119,byte 119,byte 102,byte 118,byte 118,byte 118,byte 101,byte 67, + byte 121,byte 121,byte 120,byte 120,byte 120,byte 103,byte 103,byte 119, + byte 119,byte 102,byte 102,byte 118,byte 118,byte 101,byte 101,byte 67, + byte 104,byte 104,byte 120,byte 103,byte 103,byte 103,byte 103,byte 119, + byte 102,byte 102,byte 102,byte 118,byte 118,byte 101,byte 84,byte 67, + byte 117,byte 117,byte 116,byte 116,byte 116,byte 116,byte 115,byte 115, + byte 115,byte 115,byte 115,byte 114,byte 114,byte 114,byte 114,byte 67, + byte 117,byte 117,byte 116,byte 116,byte 116,byte 116,byte 115,byte 115, + byte 115,byte 115,byte 115,byte 114,byte 114,byte 114,byte 97,byte 79, + byte 117,byte 117,byte 116,byte 116,byte 116,byte 99,byte 99,byte 115, + byte 115,byte 98,byte 98,byte 114,byte 114,byte 97,byte 97,byte 79, + byte 100,byte 100,byte 99,byte 99,byte 99,byte 99,byte 82,byte 98, + byte 98,byte 98,byte 81,byte 114,byte 97,byte 97,byte 97,byte 79, + byte 66,byte 66,byte 66,byte 66,byte 65,byte 65,byte 65,byte 65, + byte 64,byte 64,byte 64,byte 64,byte 79,byte 79,byte 79,byte 79, + byte 96,byte 96,byte 112,byte 112,byte 111,byte 111,byte 94,byte 127, + byte 127,byte 110,byte 110,byte 93,byte 126,byte 109,byte 92,byte 75, + byte 96,byte 96,byte 112,byte 112,byte 111,byte 111,byte 94,byte 127, + byte 127,byte 110,byte 110,byte 93,byte 126,byte 109,byte 92,byte 75, + byte 125,byte 125,byte 124,byte 124,byte 124,byte 124,byte 123,byte 123, + byte 123,byte 123,byte 106,byte 122,byte 122,byte 105,byte 105,byte 75, + byte 125,byte 125,byte 124,byte 124,byte 124,byte 107,byte 107,byte 123, + byte 123,byte 106,byte 106,byte 122,byte 122,byte 105,byte 105,byte 75, + byte 108,byte 108,byte 124,byte 124,byte 107,byte 107,byte 107,byte 123, + byte 123,byte 106,byte 106,byte 122,byte 122,byte 105,byte 88,byte 71, + byte 108,byte 108,byte 124,byte 107,byte 107,byte 107,byte 90,byte 123, + byte 106,byte 106,byte 106,byte 89,byte 122,byte 105,byte 88,byte 71, + byte 91,byte 91,byte 120,byte 107,byte 107,byte 90,byte 90,byte 136, + byte 106,byte 106,byte 89,byte 89,byte 118,byte 105,byte 88,byte 71, + byte 121,byte 121,byte 120,byte 120,byte 120,byte 120,byte 136,byte 119, + byte 119,byte 119,byte 102,byte 118,byte 118,byte 118,byte 101,byte 71, + byte 121,byte 121,byte 120,byte 120,byte 120,byte 103,byte 103,byte 119, + byte 119,byte 102,byte 102,byte 118,byte 118,byte 101,byte 101,byte 67, + byte 104,byte 104,byte 120,byte 103,byte 103,byte 103,byte 103,byte 119, + byte 102,byte 102,byte 102,byte 118,byte 118,byte 101,byte 84,byte 67, + byte 104,byte 104,byte 103,byte 103,byte 103,byte 103,byte 86,byte 102, + byte 102,byte 102,byte 85,byte 85,byte 101,byte 101,byte 84,byte 67, + byte 87,byte 87,byte 116,byte 116,byte 116,byte 86,byte 86,byte 115, + byte 115,byte 115,byte 85,byte 85,byte 114,byte 114,byte 84,byte 67, + byte 117,byte 117,byte 116,byte 116,byte 116,byte 116,byte 115,byte 115, + byte 115,byte 115,byte 98,byte 114,byte 114,byte 114,byte 97,byte 79, + byte 100,byte 100,byte 99,byte 99,byte 99,byte 99,byte 99,byte 115, + byte 98,byte 98,byte 98,byte 114,byte 114,byte 97,byte 97,byte 79, + byte 83,byte 83,byte 99,byte 99,byte 82,byte 82,byte 82,byte 98, + byte 98,byte 81,byte 81,byte 81,byte 97,byte 97,byte 80,byte 79, + byte 66,byte 66,byte 66,byte 66,byte 65,byte 65,byte 65,byte 65, + byte 64,byte 64,byte 64,byte 64,byte 79,byte 79,byte 79,byte 79, + byte 95,byte 95,byte 111,byte 111,byte 94,byte 94,byte 94,byte 77, + byte 110,byte 110,byte 93,byte 93,byte 76,byte 109,byte 92,byte 75, + byte 95,byte 95,byte 111,byte 111,byte 94,byte 94,byte 94,byte 77, + byte 110,byte 110,byte 93,byte 93,byte 76,byte 109,byte 92,byte 75, + byte 108,byte 108,byte 124,byte 111,byte 107,byte 94,byte 94,byte 123, + byte 123,byte 106,byte 93,byte 93,byte 122,byte 105,byte 92,byte 75, + byte 108,byte 108,byte 108,byte 107,byte 107,byte 107,byte 90,byte 123, + byte 106,byte 106,byte 106,byte 89,byte 122,byte 105,byte 88,byte 75, + byte 91,byte 91,byte 107,byte 107,byte 107,byte 90,byte 90,byte 123, + byte 106,byte 106,byte 89,byte 89,byte 105,byte 105,byte 88,byte 71, + byte 91,byte 91,byte 91,byte 107,byte 90,byte 90,byte 90,byte 73, + byte 106,byte 106,byte 89,byte 89,byte 72,byte 88,byte 88,byte 71, + byte 91,byte 91,byte 91,byte 90,byte 90,byte 90,byte 73,byte 73, + byte 106,byte 89,byte 89,byte 72,byte 72,byte 88,byte 88,byte 71, + byte 74,byte 74,byte 120,byte 120,byte 120,byte 73,byte 73,byte 119, + byte 119,byte 102,byte 89,byte 72,byte 72,byte 101,byte 101,byte 71, + byte 104,byte 104,byte 120,byte 103,byte 103,byte 103,byte 103,byte 119, + byte 102,byte 102,byte 102,byte 118,byte 118,byte 101,byte 84,byte 67, + byte 104,byte 104,byte 103,byte 103,byte 103,byte 103,byte 86,byte 102, + byte 102,byte 102,byte 85,byte 85,byte 101,byte 101,byte 84,byte 67, + byte 87,byte 87,byte 87,byte 103,byte 86,byte 86,byte 86,byte 86, + byte 102,byte 85,byte 85,byte 85,byte 85,byte 84,byte 84,byte 67, + byte 87,byte 87,byte 87,byte 86,byte 86,byte 86,byte 69,byte 69, + byte 115,byte 85,byte 85,byte 85,byte 68,byte 68,byte 67,byte 67, + byte 70,byte 70,byte 116,byte 116,byte 99,byte 69,byte 69,byte 69, + byte 115,byte 98,byte 85,byte 68,byte 68,byte 97,byte 97,byte 79, + byte 100,byte 100,byte 99,byte 99,byte 99,byte 82,byte 82,byte 98, + byte 98,byte 98,byte 81,byte 68,byte 97,byte 97,byte 97,byte 79, + byte 83,byte 83,byte 83,byte 82,byte 82,byte 82,byte 82,byte 98, + byte 81,byte 81,byte 81,byte 64,byte 97,byte 97,byte 80,byte 79, + byte 66,byte 66,byte 66,byte 66,byte 65,byte 65,byte 65,byte 65, + byte 64,byte 64,byte 64,byte 64,byte 79,byte 79,byte 79,byte 79, + byte 78,byte 78,byte 49,byte 49,byte 94,byte 77,byte 77,byte 48, + byte 48,byte 93,byte 93,byte 76,byte 76,byte 63,byte 92,byte 75, + byte 78,byte 78,byte 49,byte 49,byte 94,byte 77,byte 77,byte 48, + byte 48,byte 93,byte 93,byte 76,byte 76,byte 63,byte 92,byte 75, + byte 62,byte 62,byte 61,byte 61,byte 61,byte 61,byte 77,byte 60, + byte 60,byte 60,byte 93,byte 76,byte 59,byte 59,byte 59,byte 75, + byte 62,byte 62,byte 61,byte 61,byte 61,byte 61,byte 90,byte 60, + byte 60,byte 60,byte 89,byte 59,byte 59,byte 59,byte 88,byte 75, + byte 91,byte 91,byte 61,byte 61,byte 61,byte 90,byte 73,byte 60, + byte 60,byte 60,byte 89,byte 72,byte 59,byte 59,byte 88,byte 71, + byte 74,byte 74,byte 61,byte 61,byte 90,byte 73,byte 73,byte 73, + byte 60,byte 89,byte 89,byte 72,byte 72,byte 72,byte 71,byte 71, + byte 74,byte 74,byte 74,byte 90,byte 73,byte 73,byte 73,byte 73, + byte 56,byte 89,byte 72,byte 72,byte 72,byte 72,byte 71,byte 71, + byte 58,byte 58,byte 57,byte 57,byte 57,byte 73,byte 73,byte 56, + byte 56,byte 56,byte 72,byte 72,byte 55,byte 55,byte 55,byte 71, + byte 58,byte 58,byte 57,byte 57,byte 57,byte 57,byte 56,byte 56, + byte 56,byte 56,byte 56,byte 55,byte 55,byte 55,byte 55,byte 67, + byte 87,byte 87,byte 57,byte 57,byte 57,byte 86,byte 86,byte 56, + byte 56,byte 56,byte 85,byte 85,byte 55,byte 55,byte 84,byte 67, + byte 87,byte 87,byte 87,byte 86,byte 86,byte 86,byte 69,byte 69, + byte 56,byte 85,byte 85,byte 85,byte 68,byte 68,byte 67,byte 67, + byte 70,byte 70,byte 70,byte 53,byte 69,byte 69,byte 69,byte 69, + byte 52,byte 85,byte 85,byte 68,byte 68,byte 68,byte 67,byte 67, + byte 70,byte 70,byte 53,byte 53,byte 53,byte 69,byte 69,byte 52, + byte 52,byte 52,byte 68,byte 68,byte 68,byte 51,byte 51,byte 79, + byte 54,byte 54,byte 53,byte 53,byte 53,byte 69,byte 69,byte 52, + byte 52,byte 52,byte 68,byte 68,byte 51,byte 51,byte 80,byte 79, + byte 83,byte 83,byte 53,byte 82,byte 82,byte 65,byte 65,byte 52, + byte 52,byte 81,byte 64,byte 64,byte 51,byte 80,byte 80,byte 79, + byte 66,byte 66,byte 66,byte 66,byte 65,byte 65,byte 65,byte 65, + byte 64,byte 64,byte 64,byte 64,byte 79,byte 79,byte 79,byte 79, + byte 50,byte 50,byte 49,byte 49,byte 49,byte 77,byte 77,byte 48, + byte 48,byte 48,byte 76,byte 76,byte 63,byte 63,byte 46,byte 12, + byte 50,byte 50,byte 49,byte 49,byte 49,byte 77,byte 77,byte 48, + byte 48,byte 48,byte 76,byte 76,byte 63,byte 63,byte 46,byte 12, + byte 62,byte 62,byte 61,byte 61,byte 61,byte 61,byte 77,byte 60, + byte 60,byte 60,byte 60,byte 59,byte 59,byte 59,byte 59,byte 12, + byte 62,byte 62,byte 61,byte 61,byte 61,byte 61,byte 60,byte 60, + byte 60,byte 60,byte 60,byte 59,byte 59,byte 59,byte 42,byte 12, + byte 62,byte 62,byte 61,byte 61,byte 61,byte 61,byte 73,byte 60, + byte 60,byte 60,byte 43,byte 59,byte 59,byte 59,byte 42,byte 8, + byte 74,byte 74,byte 61,byte 61,byte 61,byte 73,byte 73,byte 60, + byte 60,byte 60,byte 72,byte 72,byte 72,byte 59,byte 42,byte 8, + byte 74,byte 74,byte 74,byte 57,byte 73,byte 73,byte 73,byte 73, + byte 56,byte 56,byte 72,byte 72,byte 72,byte 72,byte 42,byte 8, + byte 58,byte 58,byte 57,byte 57,byte 57,byte 57,byte 73,byte 56, + byte 56,byte 56,byte 72,byte 55,byte 55,byte 55,byte 55,byte 8, + byte 58,byte 58,byte 57,byte 57,byte 57,byte 57,byte 56,byte 56, + byte 56,byte 56,byte 56,byte 55,byte 55,byte 55,byte 38,byte 4, + byte 58,byte 58,byte 57,byte 57,byte 57,byte 57,byte 56,byte 56, + byte 56,byte 56,byte 39,byte 55,byte 55,byte 55,byte 38,byte 4, + byte 70,byte 70,byte 57,byte 57,byte 40,byte 69,byte 69,byte 69, + byte 56,byte 39,byte 85,byte 68,byte 68,byte 38,byte 38,byte 4, + byte 70,byte 70,byte 53,byte 53,byte 53,byte 69,byte 69,byte 52, + byte 52,byte 52,byte 68,byte 68,byte 68,byte 51,byte 51,byte 4, + byte 54,byte 54,byte 53,byte 53,byte 53,byte 69,byte 69,byte 52, + byte 52,byte 52,byte 68,byte 68,byte 51,byte 51,byte 51,byte 0, + byte 54,byte 54,byte 53,byte 53,byte 53,byte 53,byte 69,byte 52, + byte 52,byte 52,byte 35,byte 51,byte 51,byte 51,byte 34,byte 0, + byte 37,byte 37,byte 53,byte 36,byte 36,byte 36,byte 36,byte 52, + byte 35,byte 35,byte 35,byte 51,byte 51,byte 34,byte 34,byte 0, + byte 3,byte 3,byte 3,byte 3,byte 2,byte 2,byte 2,byte 2, + byte 1,byte 1,byte 1,byte 1,byte 0,byte 0,byte 0,byte 0, + byte 33,byte 33,byte 49,byte 49,byte 32,byte 32,byte 77,byte 48, + byte 48,byte 47,byte 47,byte 63,byte 63,byte 46,byte 46,byte 12, + byte 33,byte 33,byte 49,byte 49,byte 32,byte 32,byte 77,byte 48, + byte 48,byte 47,byte 47,byte 63,byte 63,byte 46,byte 46,byte 12, + byte 62,byte 62,byte 61,byte 61,byte 61,byte 61,byte 60,byte 60, + byte 60,byte 43,byte 43,byte 59,byte 59,byte 59,byte 42,byte 12, + byte 62,byte 62,byte 61,byte 61,byte 61,byte 44,byte 44,byte 60, + byte 60,byte 43,byte 43,byte 59,byte 59,byte 42,byte 42,byte 12, + byte 45,byte 45,byte 61,byte 61,byte 44,byte 44,byte 44,byte 60, + byte 60,byte 43,byte 43,byte 59,byte 59,byte 42,byte 42,byte 8, + byte 45,byte 45,byte 61,byte 44,byte 44,byte 44,byte 73,byte 60, + byte 43,byte 43,byte 26,byte 72,byte 59,byte 42,byte 42,byte 8, + byte 74,byte 74,byte 57,byte 44,byte 44,byte 73,byte 73,byte 56, + byte 43,byte 43,byte 26,byte 72,byte 72,byte 42,byte 42,byte 8, + byte 58,byte 58,byte 57,byte 57,byte 57,byte 57,byte 56,byte 56, + byte 56,byte 56,byte 39,byte 55,byte 55,byte 55,byte 38,byte 8, + byte 58,byte 58,byte 57,byte 57,byte 57,byte 40,byte 40,byte 56, + byte 56,byte 39,byte 39,byte 55,byte 55,byte 38,byte 38,byte 4, + byte 41,byte 41,byte 40,byte 40,byte 40,byte 40,byte 40,byte 56, + byte 39,byte 39,byte 39,byte 55,byte 55,byte 38,byte 38,byte 4, + byte 41,byte 41,byte 40,byte 40,byte 40,byte 23,byte 23,byte 39, + byte 39,byte 39,byte 22,byte 68,byte 38,byte 38,byte 38,byte 4, + byte 54,byte 54,byte 53,byte 53,byte 53,byte 69,byte 69,byte 52, + byte 52,byte 52,byte 68,byte 68,byte 51,byte 51,byte 21,byte 4, + byte 54,byte 54,byte 53,byte 53,byte 53,byte 53,byte 69,byte 52, + byte 52,byte 52,byte 35,byte 51,byte 51,byte 51,byte 34,byte 0, + byte 37,byte 37,byte 53,byte 36,byte 36,byte 36,byte 36,byte 52, + byte 35,byte 35,byte 35,byte 51,byte 51,byte 34,byte 34,byte 0, + byte 37,byte 37,byte 36,byte 36,byte 36,byte 36,byte 36,byte 35, + byte 35,byte 35,byte 35,byte 18,byte 34,byte 34,byte 34,byte 0, + byte 3,byte 3,byte 3,byte 3,byte 2,byte 2,byte 2,byte 2, + byte 1,byte 1,byte 1,byte 1,byte 0,byte 0,byte 0,byte 0, + byte 16,byte 16,byte 32,byte 32,byte 31,byte 31,byte 31,byte 47, + byte 47,byte 30,byte 30,byte 30,byte 46,byte 46,byte 29,byte 12, + byte 16,byte 16,byte 32,byte 32,byte 31,byte 31,byte 31,byte 47, + byte 47,byte 30,byte 30,byte 30,byte 46,byte 46,byte 29,byte 12, + byte 45,byte 45,byte 44,byte 44,byte 44,byte 44,byte 31,byte 60, + byte 43,byte 43,byte 30,byte 59,byte 59,byte 42,byte 42,byte 12, + byte 45,byte 45,byte 44,byte 44,byte 44,byte 44,byte 27,byte 43, + byte 43,byte 43,byte 26,byte 26,byte 42,byte 42,byte 42,byte 12, + byte 28,byte 28,byte 44,byte 44,byte 27,byte 27,byte 27,byte 43, + byte 43,byte 26,byte 26,byte 26,byte 42,byte 42,byte 25,byte 8, + byte 28,byte 28,byte 44,byte 44,byte 27,byte 27,byte 27,byte 43, + byte 43,byte 26,byte 26,byte 9,byte 42,byte 42,byte 25,byte 8, + byte 28,byte 28,byte 28,byte 27,byte 27,byte 27,byte 10,byte 43, + byte 26,byte 26,byte 26,byte 9,byte 42,byte 42,byte 25,byte 8, + byte 41,byte 41,byte 57,byte 40,byte 40,byte 40,byte 40,byte 56, + byte 39,byte 39,byte 39,byte 55,byte 55,byte 38,byte 38,byte 8, + byte 41,byte 41,byte 40,byte 40,byte 40,byte 40,byte 23,byte 39, + byte 39,byte 39,byte 22,byte 55,byte 38,byte 38,byte 38,byte 4, + byte 24,byte 24,byte 40,byte 40,byte 23,byte 23,byte 23,byte 39, + byte 39,byte 22,byte 22,byte 22,byte 38,byte 38,byte 21,byte 4, + byte 24,byte 24,byte 24,byte 23,byte 23,byte 23,byte 23,byte 39, + byte 22,byte 22,byte 22,byte 5,byte 38,byte 38,byte 21,byte 4, + byte 24,byte 24,byte 53,byte 23,byte 23,byte 6,byte 6,byte 52, + byte 52,byte 22,byte 5,byte 5,byte 51,byte 21,byte 21,byte 4, + byte 37,byte 37,byte 53,byte 36,byte 36,byte 36,byte 36,byte 52, + byte 35,byte 35,byte 35,byte 51,byte 51,byte 34,byte 34,byte 0, + byte 37,byte 37,byte 36,byte 36,byte 36,byte 36,byte 36,byte 35, + byte 35,byte 35,byte 35,byte 18,byte 34,byte 34,byte 34,byte 0, + byte 20,byte 20,byte 36,byte 36,byte 19,byte 19,byte 19,byte 35, + byte 35,byte 18,byte 18,byte 18,byte 34,byte 34,byte 17,byte 0, + byte 3,byte 3,byte 3,byte 3,byte 2,byte 2,byte 2,byte 2, + byte 1,byte 1,byte 1,byte 1,byte 0,byte 0,byte 0,byte 0, + byte 15,byte 15,byte 15,byte 15,byte 14,byte 14,byte 14,byte 14, + byte 13,byte 13,byte 13,byte 13,byte 12,byte 12,byte 12,byte 12, + byte 15,byte 15,byte 15,byte 15,byte 14,byte 14,byte 14,byte 14, + byte 13,byte 13,byte 13,byte 13,byte 12,byte 12,byte 12,byte 12, + byte 15,byte 15,byte 15,byte 15,byte 14,byte 14,byte 14,byte 14, + byte 13,byte 13,byte 13,byte 13,byte 12,byte 12,byte 12,byte 12, + byte 15,byte 15,byte 15,byte 15,byte 14,byte 14,byte 14,byte 14, + byte 13,byte 13,byte 13,byte 13,byte 12,byte 12,byte 12,byte 12, + byte 11,byte 11,byte 11,byte 11,byte 10,byte 10,byte 10,byte 10, + byte 9,byte 9,byte 9,byte 9,byte 8,byte 8,byte 8,byte 8, + byte 11,byte 11,byte 11,byte 11,byte 10,byte 10,byte 10,byte 10, + byte 9,byte 9,byte 9,byte 9,byte 8,byte 8,byte 8,byte 8, + byte 11,byte 11,byte 11,byte 11,byte 10,byte 10,byte 10,byte 10, + byte 9,byte 9,byte 9,byte 9,byte 8,byte 8,byte 8,byte 8, + byte 11,byte 11,byte 11,byte 11,byte 10,byte 10,byte 10,byte 10, + byte 9,byte 9,byte 9,byte 9,byte 8,byte 8,byte 8,byte 8, + byte 7,byte 7,byte 7,byte 7,byte 6,byte 6,byte 6,byte 6, + byte 5,byte 5,byte 5,byte 5,byte 4,byte 4,byte 4,byte 4, + byte 7,byte 7,byte 7,byte 7,byte 6,byte 6,byte 6,byte 6, + byte 5,byte 5,byte 5,byte 5,byte 4,byte 4,byte 4,byte 4, + byte 7,byte 7,byte 7,byte 7,byte 6,byte 6,byte 6,byte 6, + byte 5,byte 5,byte 5,byte 5,byte 4,byte 4,byte 4,byte 4, + byte 7,byte 7,byte 7,byte 7,byte 6,byte 6,byte 6,byte 6, + byte 5,byte 5,byte 5,byte 5,byte 4,byte 4,byte 4,byte 4, + byte 3,byte 3,byte 3,byte 3,byte 2,byte 2,byte 2,byte 2, + byte 1,byte 1,byte 1,byte 1,byte 0,byte 0,byte 0,byte 0, + byte 3,byte 3,byte 3,byte 3,byte 2,byte 2,byte 2,byte 2, + byte 1,byte 1,byte 1,byte 1,byte 0,byte 0,byte 0,byte 0, + byte 3,byte 3,byte 3,byte 3,byte 2,byte 2,byte 2,byte 2, + byte 1,byte 1,byte 1,byte 1,byte 0,byte 0,byte 0,byte 0, + byte 3,byte 3,byte 3,byte 3,byte 2,byte 2,byte 2,byte 2, + byte 1,byte 1,byte 1,byte 1,byte 0,byte 0,byte 0,byte 0, +}; diff --git a/appl/wm/mpeg/decode.b b/appl/wm/mpeg/decode.b new file mode 100644 index 00000000..16906c99 --- /dev/null +++ b/appl/wm/mpeg/decode.b @@ -0,0 +1,831 @@ +implement Mpegd; + +include "sys.m"; +include "mpegio.m"; + +sys: Sys; +idct: IDCT; + +Mpegi, Picture, Slice, MacroBlock, YCbCr, Pair: import Mpegio; + +intra_tab := array[64] of { + 8, 16, 19, 22, 26, 27, 29, 34, + 16, 16, 22, 24, 27, 29, 34, 37, + 19, 22, 26, 27, 29, 34, 34, 38, + 22, 22, 26, 27, 29, 34, 37, 40, + 22, 26, 27, 29, 32, 35, 40, 48, + 26, 27, 29, 32, 35, 40, 48, 58, + 26, 27, 29, 34, 38, 46, 56, 69, + 27, 29, 35, 38, 46, 56, 69, 83, +}; + +nintra_tab := array[64] of { * => 16 }; + +CLOFF: con 256; + +intraQ, nintraQ: array of int; +rtmp: array of array of int; +rflag := array[6] of int; +rforw, dforw, rback, dback: int; +rforw2, dforw2, rback2, dback2: int; +ydb, ydf, cdb, cdf: int; +vflags: int; +past := array[3] of int; +pinit := array[3] of { * => 128 * 8 }; +zeros := array[64] of { * => 0 }; +zeros1: array of int; +clamp := array[CLOFF + 256 + CLOFF] of byte; +width, height, w2, h2: int; +mpi, mps, yadj, cadj, yskip: int; +I, B0: ref YCbCr; +Ps := array[2] of ref YCbCr; +Rs := array[2] of ref YCbCr; +P, B, R, M, N: ref YCbCr; +pn: int = 0; +rn: int = 0; + +zig := array[64] of { + 0, 1, 8, 16, 9, 2, 3, 10, 17, + 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, +}; + +init(m: ref Mpegi) +{ + sys = load Sys Sys->PATH; + idct = load IDCT IDCT->PATH; + if (idct == nil) { + sys->print("could not open %s: %r\n", IDCT->PATH); + exit; + } + idct->init(); + width = m.width; + height = m.height; + w2 = width >> 1; + h2 = height >> 1; + mps = width >> 4; + mpi = mps * height >> 4; + yskip = 8 * width; + yadj = 16 * width - (width - 16); + cadj = 8 * w2 - (w2 - 8); + I = frame(); + Ps[0] = frame(); + Ps[1] = frame(); + Rs[0] = Ps[0]; + Rs[1] = Ps[1]; + B0 = frame(); + for (i := 0; i < CLOFF; i++) + clamp[i] = byte 0; + for (i = 0; i < 256; i++) + clamp[i + CLOFF] = byte i; + for (i = CLOFF + 256; i < CLOFF + 256 + CLOFF; i++) + clamp[i] = byte 255; + if (m.intra == nil) + intraQ = intra_tab; + else + intraQ = zigof(m.intra); + if (m.nintra == nil) + nintraQ = nintra_tab; + else + nintraQ = zigof(m.nintra); + rtmp = array[6] of array of int; + for (i = 0; i < 6; i++) + rtmp[i] = array[64] of int; + zeros1 = zeros[1:]; +} + +zarray(n: int, v: byte): array of byte +{ + return array[n] of { * => v }; +} + +frame(): ref YCbCr +{ + y := zarray(width * height, byte 0); + b := zarray(w2 * h2, byte 128); + r := zarray(w2 * h2, byte 128); + return ref YCbCr(y, b, r); +} + +zigof(a: array of int): array of int +{ + z := array[64] of int; + for (i := 0; i < 64; i++) + z[zig[i]] = a[i]; + return z; +} + +invQ_intra(a: array of Pair, q: int, b: array of int) +{ + (nil, t) := a[0]; + b[0] = t * 8; + b[1:] = zeros1; + n := 1; + i := 1; + while (n < len a) { + (r, l) := a[n++]; + i += r; + x := zig[i++]; + if (l > 0) { + v := l * q * intraQ[x] >> 3; + if (v > 2047) + b[x] = 2047; + else + b[x] = (v - 1) | 1; + } else { + v := (l * q * intraQ[x] + 7) >> 3; + if (v < -2048) + b[x] = -2048; + else + b[x] = v | 1; + } + #sys->print("%d %d %d %d\n", x, r, l, b[x]); + } +} + +invQ_nintra(a: array of Pair, q: int, b: array of int) +{ + b[0:] = zeros; + i := 0; + for (n := 0; n < len a; n++) { + (r, l) := a[n]; + i += r; + if (l == 0) { + raisex("zero level"); + i++; + continue; + } + x := zig[i++]; + if (l > 0) { + v := ((l << 1) + 1) * q * nintraQ[x] >> 4; + if (v > 2047) + b[x] = 2047; + else + b[x] = (v - 1) | 1; + } else { + v := (((l << 1) - 1) * q * nintraQ[x] + 15) >> 4; + if (v < -2048) + b[x] = -2048; + else + b[x] = v | 1; + } + #sys->print("%d %d %d %d\n", x, r, l, b[x]); + } +} + +yzero(v: array of byte, base: int) +{ + x := 0; + i := 8; + do { + n := base; + j := 8; + do + v[n++] = byte 0; + while (--j > 0); + base += width; + } while (--i > 0); +} + +czero(v: array of byte, base: int) +{ + x := 0; + i := 8; + do { + n := base; + j := 8; + do + v[n++] = byte 128; + while (--j > 0); + base += w2; + } while (--i > 0); + +} + +blockzero(d: ref YCbCr) +{ + yzero(d.Y, ybase); + yzero(d.Y, ybase + 8); + yzero(d.Y, ybase + yskip); + yzero(d.Y, ybase + 8 + yskip); + czero(d.Cb, cbase); + czero(d.Cr, cbase); +} + +ydistr(a: array of int, v: array of byte, base: int) +{ + x := 0; + i := 8; + do { + n := base; + j := 8; + do + v[n++] = clamp[a[x++] + CLOFF]; + while (--j > 0); + base += width; + } while (--i > 0); +} + +cdistr(a: array of int, v: array of byte, base: int) +{ + x := 0; + i := 8; + do { + n := base; + j := 8; + do + v[n++] = clamp[a[x++] + CLOFF]; + while (--j > 0); + base += w2; + } while (--i > 0); + +} + +invQ_intra_block(b: array of array of Pair, q: int, pred: int, d: ref YCbCr) +{ + a, dc: array of int; + if (pred) + dc = past; + else + dc = pinit; + p := dc[0]; + for (i := 0; i < 4; i++) { + a = rtmp[i]; + #sys->print("%d\n", i); + invQ_intra(b[i], q, a); + p += a[0]; + a[0] = p; + #sys->print("%d\n", a[0]); + idct->idct(a); + } + past[0] = p; + for (i = 4; i < 6; i++) { + p = dc[i - 3]; + a = rtmp[i]; + #sys->print("%d\n", i); + invQ_intra(b[i], q, a); + p += a[0]; + a[0] = p; + #sys->print("%d\n", a[0]); + past[i - 3] = p; + idct->idct(a); + } + ydistr(rtmp[0], d.Y, ybase); + ydistr(rtmp[1], d.Y, ybase + 8); + ydistr(rtmp[2], d.Y, ybase + yskip); + ydistr(rtmp[3], d.Y, ybase + 8 + yskip); + cdistr(rtmp[4], d.Cb, cbase); + cdistr(rtmp[5], d.Cr, cbase); +} + +invQ_nintra_block(b: array of array of Pair, q: int) +{ + for (i := 0; i < 6; i++) { + p := b[i]; + if (p != nil) { + a := rtmp[i]; + #sys->print("%d\n", i); + invQ_nintra(p, q, a); + idct->idct(a); + rflag[i] = 1; + } else + rflag[i] = 0; + } +} + +mbr, ybase, cbase: int; + +nextmb() +{ + if (--mbr == 0) { + ybase += yadj; + cbase += cadj; + mbr = mps; + } else { + ybase += 16; + cbase += 8; + } +} + +copyblock(s, d: array of byte, b, n, w: int) +{ + i := 8; + do { + d[b:] = s[b:b+n]; + b += w; + } while (--i > 0); +} + +copyblockdisp(s, d: array of byte, b, n, w, p: int) +{ + i := 8; + p += b; + do { + d[b:] = s[p:p+n]; + b += w; + p += w; + } while (--i > 0); +} + +interpblock(s0, s1, d: array of byte, b, n, w, p0, p1: int) +{ + i := 8; + do { + dx := b; + s0x := b + p0; + s1x := b + p1; + j := n; + do + d[dx++] = byte ((int s0[s0x++] + int s1[s1x++] + 1) >> 1); + while (--j > 0); + b += w; + } while (--i > 0); +} + +deltablock(s: array of byte, r: array of int, d: array of byte, b, w, o: int) +{ + rx := 0; + i := 8; + do { + dx := b; + sx := b + o; + j := 8; + do + d[dx++] = clamp[CLOFF + int s[sx++] + r[rx++]]; + while (--j > 0); + b += w; + } while (--i > 0); +} + +deltainterpblock(s0, s1: array of byte, r: array of int, d: array of byte, b, w, o0, o1: int) +{ + rx := 0; + i := 8; + do { + dx := b; + s0x := b + o0; + s1x := b + o1; + j := 8; + do + d[dx++] = clamp[CLOFF + ((int s0[s0x++] + int s1[s1x++] + 1) >> 1) + r[rx++]]; + while (--j > 0); + b += w; + } while (--i > 0); +} + +dispblock(s, d: array of byte, n, b, w, o: int) +{ + if (rflag[n]) + deltablock(s, rtmp[n], d, b, w, o); + else + copyblockdisp(s, d, b, 8, w, o); +} + +genblock(s0, s1, d: array of byte, n, b, w, o0, o1: int) +{ + if (rflag[n]) + deltainterpblock(s0, s1, rtmp[n], d, b, w, o0, o1); + else + interpblock(s0, s1, d, b, 8, w, o0, o1); +} + +copymb() +{ + copyblock(R.Y, P.Y, ybase, 16, width); + copyblock(R.Y, P.Y, ybase + yskip, 16, width); + copyblock(R.Cb, P.Cb, cbase, 8, w2); + copyblock(R.Cr, P.Cr, cbase, 8, w2); +} + +deltamb() +{ + dispblock(R.Y, P.Y, 0, ybase, width, 0); + dispblock(R.Y, P.Y, 1, ybase + 8, width, 0); + dispblock(R.Y, P.Y, 2, ybase + yskip, width, 0); + dispblock(R.Y, P.Y, 3, ybase + 8 + yskip, width, 0); + dispblock(R.Cb, P.Cb, 4, cbase, w2, 0); + dispblock(R.Cr, P.Cr, 5, cbase, w2, 0); +} + +copymbforw() +{ + copyblockdisp(N.Y, B.Y, ybase, 16, width, ydf); + copyblockdisp(N.Y, B.Y, ybase + yskip, 16, width, ydf); + copyblockdisp(N.Cb, B.Cb, cbase, 8, w2, cdf); + copyblockdisp(N.Cr, B.Cr, cbase, 8, w2, cdf); +} + +copymbback() +{ + copyblockdisp(M.Y, B.Y, ybase, 16, width, ydb); + copyblockdisp(M.Y, B.Y, ybase + yskip, 16, width, ydb); + copyblockdisp(M.Cb, B.Cb, cbase, 8, w2, cdb); + copyblockdisp(M.Cr, B.Cr, cbase, 8, w2, cdb); +} + +copymbbackforw() +{ + interpblock(M.Y, N.Y, B.Y, ybase, 16, width, ydb, ydf); + interpblock(M.Y, N.Y, B.Y, ybase + yskip, 16, width, ydb, ydf); + interpblock(M.Cb, N.Cb, B.Cb, cbase, 8, w2, cdb, cdf); + interpblock(M.Cr, N.Cr, B.Cr, cbase, 8, w2, cdb, cdf); +} + +deltambforw() +{ + dispblock(N.Y, B.Y, 0, ybase, width, ydf); + dispblock(N.Y, B.Y, 1, ybase + 8, width, ydf); + dispblock(N.Y, B.Y, 2, ybase + yskip, width, ydf); + dispblock(N.Y, B.Y, 3, ybase + 8 + yskip, width, ydf); + dispblock(N.Cb, B.Cb, 4, cbase, w2, cdf); + dispblock(N.Cr, B.Cr, 5, cbase, w2, cdf); +} + +deltambback() +{ + dispblock(M.Y, B.Y, 0, ybase, width, ydb); + dispblock(M.Y, B.Y, 1, ybase + 8, width, ydb); + dispblock(M.Y, B.Y, 2, ybase + yskip, width, ydb); + dispblock(M.Y, B.Y, 3, ybase + 8 + yskip, width, ydb); + dispblock(M.Cb, B.Cb, 4, cbase, w2, cdb); + dispblock(M.Cr, B.Cr, 5, cbase, w2, cdb); +} + +deltambbackforw() +{ + genblock(M.Y, N.Y, B.Y, 0, ybase, width, ydb, ydf); + genblock(M.Y, N.Y, B.Y, 1, ybase + 8, width, ydb, ydf); + genblock(M.Y, N.Y, B.Y, 2, ybase + yskip, width, ydb, ydf); + genblock(M.Y, N.Y, B.Y, 3, ybase + 8 + yskip, width, ydb, ydf); + genblock(M.Cb, N.Cb, B.Cb, 4, cbase, w2, cdb, cdf); + genblock(M.Cr, N.Cr, B.Cr, 5, cbase, w2, cdb, cdf); +} + +deltambinterp() +{ + case vflags & (Mpegio->MB_MF | Mpegio->MB_MB) { + Mpegio->MB_MF => + deltambforw(); + Mpegio->MB_MB => + deltambback(); + Mpegio->MB_MF | Mpegio->MB_MB => + deltambbackforw(); + * => + raisex("bad vflags"); + } +} + +interpmb() +{ + case vflags & (Mpegio->MB_MF | Mpegio->MB_MB) { + Mpegio->MB_MF => + copymbforw(); + Mpegio->MB_MB => + copymbback(); + Mpegio->MB_MF | Mpegio->MB_MB => + copymbbackforw(); + * => + raisex("bad vflags"); + } +} + +Idecode(p: ref Picture): ref YCbCr +{ + sa := p.slices; + n := 0; + mbr = mps; + ybase = 0; + cbase = 0; + for (i := 0; i < len sa; i++) { + pred := 0; + ba := sa[i].blocks; + for (j := 0; j < len ba; j++) { + invQ_intra_block(ba[j].rls, ba[j].qscale, pred, I); + nextmb(); + n++; + pred = 1; + } + } + if (n != mpi) + raisex("I mb count"); + R = I; + Rs[rn] = I; + rn ^= 1; + return I; +} + +Pdecode(p: ref Picture): ref YCbCr +{ + rforwp, dforwp: int; + md, c: int; + P = Ps[pn]; + N = R; + B = P; + pn ^= 1; + fs := 1 << p.forwfc; + fsr := fs << 5; + fsmin := -(fs << 4); + fsmax := (fs << 4) - 1; + sa := p.slices; + n := 0; + mbr = mps; + ybase = 0; + cbase = 0; + for (i := 0; i < len sa; i++) { + pred := 0; + ipred := 0; + ba := sa[i].blocks; + for (j := 0; j < len ba; j++) { + mb := ba[j]; + while (n < mb.addr) { + copymb(); + ipred = 0; + pred = 0; + nextmb(); + n++; + } + if (mb.flags & Mpegio->MB_I) { + invQ_intra_block(mb.rls, mb.qscale, ipred, P); + #blockzero(P); + ipred = 1; + pred = 0; + } else { + if (mb.flags & Mpegio->MB_MF) { + if (fs == 1 || mb.mhfc == 0) + md = mb.mhfc; + else if ((c = mb.mhfc) < 0) + md = (c + 1) * fs - mb.mhfr - 1; + else + md = (c - 1) * fs + mb.mhfr + 1; + if (pred) + md += rforwp; + if (md > fsmax) + rforw = md - fsr; + else if (md < fsmin) + rforw = md + fsr; + else + rforw = md; + rforwp = rforw; + if (fs == 1 || mb.mvfc == 0) + md = mb.mvfc; + else if ((c = mb.mvfc) < 0) + md = (c + 1) * fs - mb.mvfr - 1; + else + md = (c - 1) * fs + mb.mvfr + 1; + if (pred) + md += dforwp; + if (md > fsmax) + dforw = md - fsr; + else if (md < fsmin) + dforw = md + fsr; + else + dforw = md; + dforwp = dforw; + if (p.flags & Mpegio->FPFV) { + rforw2 = rforw; + dforw2 = dforw; + rforw <<= 1; + dforw <<= 1; + ydf = rforw2 + dforw2 * width; + cdf = (rforw2 >> 1) + (dforw2 >> 1) * w2; + } else { + if (rforw < 0) + rforw2 = (rforw + 1) >> 1; + else + rforw2 = rforw >> 1; + if (dforw < 0) + dforw2 = (dforw + 1) >> 1; + else + dforw2 = dforw >> 1; + ydf = (rforw >> 1) + (dforw >> 1) * width; + cdf = (rforw2 >> 1) + (dforw2 >> 1) * w2; + } + pred = 1; + if (mb.rls != nil) { + invQ_nintra_block(mb.rls, mb.qscale); + deltambforw(); + } else + copymbforw(); + } else { + if (mb.rls == nil) + raisex("empty delta"); + invQ_nintra_block(mb.rls, mb.qscale); + deltamb(); + pred = 0; + } + ipred = 0; + } + nextmb(); + n++; + } + } + while (n < mpi) { + copymb(); + nextmb(); + n++; + } + R = P; + Rs[rn] = P; + rn ^= 1; + return P; +} + +Bdecode(p: ref Mpegio->Picture): ref Mpegio->YCbCr +{ + return Bdecode2(p, Rs[rn ^ 1], Rs[rn]); +} + +Bdecode2(p: ref Mpegio->Picture, f0, f1: ref Mpegio->YCbCr): ref Mpegio->YCbCr +{ + rforwp, dforwp, rbackp, dbackp: int; + md, c: int; + M = f0; + N = f1; + B = B0; + fs := 1 << p.forwfc; + fsr := fs << 5; + fsmin := -(fs << 4); + fsmax := (fs << 4) - 1; + bs := 1 << p.backfc; + bsr := bs << 5; + bsmin := -(bs << 4); + bsmax := (bs << 4) - 1; + sa := p.slices; + n := 0; + mbr = mps; + ybase = 0; + cbase = 0; + for (i := 0; i < len sa; i++) { + ipred := 0; + rback = 0; + rforw = 0; + dback = 0; + dforw = 0; + rbackp = 0; + rforwp = 0; + dbackp = 0; + dforwp = 0; + rback2 = 0; + rforw2 = 0; + dback2 = 0; + dforw2 = 0; + ydb = 0; + ydf = 0; + cdb = 0; + cdf = 0; + ba := sa[i].blocks; + for (j := 0; j < len ba; j++) { + mb := ba[j]; + while (n < mb.addr) { + interpmb(); + nextmb(); + ipred = 0; + n++; + } + if (mb.flags & Mpegio->MB_I) { + invQ_intra_block(mb.rls, mb.qscale, ipred, B); + ipred = 1; + rback = 0; + rforw = 0; + dback = 0; + dforw = 0; + rbackp = 0; + rforwp = 0; + dbackp = 0; + dforwp = 0; + rback2 = 0; + rforw2 = 0; + dback2 = 0; + dforw2 = 0; + ydb = 0; + ydf = 0; + cdb = 0; + cdf = 0; + } else { + if (mb.flags & Mpegio->MB_MF) { + if (fs == 1 || mb.mhfc == 0) + md = mb.mhfc; + else if ((c = mb.mhfc) < 0) + md = (c + 1) * fs - mb.mhfr - 1; + else + md = (c - 1) * fs + mb.mhfr + 1; + md += rforwp; + if (md > fsmax) + rforw = md - fsr; + else if (md < fsmin) + rforw = md + fsr; + else + rforw = md; + rforwp = rforw; + if (fs == 1 || mb.mvfc == 0) + md = mb.mvfc; + else if ((c = mb.mvfc) < 0) + md = (c + 1) * fs - mb.mvfr - 1; + else + md = (c - 1) * fs + mb.mvfr + 1; + md += dforwp; + if (md > fsmax) + dforw = md - fsr; + else if (md < fsmin) + dforw = md + fsr; + else + dforw = md; + dforwp = dforw; + if (p.flags & Mpegio->FPFV) { + rforw2 = rforw; + dforw2 = dforw; + rforw <<= 1; + dforw <<= 1; + ydf = rforw2 + dforw2 * width; + cdf = (rforw2 >> 1) + (dforw2 >> 1) * w2; + } else { + if (rforw < 0) + rforw2 = (rforw + 1) >> 1; + else + rforw2 = rforw >> 1; + if (dforw < 0) + dforw2 = (dforw + 1) >> 1; + else + dforw2 = dforw >> 1; + ydf = (rforw >> 1) + (dforw >> 1) * width; + cdf = (rforw2 >> 1) + (dforw2 >> 1) * w2; + } + } + if (mb.flags & Mpegio->MB_MB) { + if (bs == 1 || mb.mhbc == 0) + md = mb.mhbc; + else if ((c = mb.mhbc) < 0) + md = (c + 1) * bs - mb.mhbr - 1; + else + md = (c - 1) * bs + mb.mhbr + 1; + md += rbackp; + if (md > bsmax) + rback = md - bsr; + else if (md < bsmin) + rback = md + bsr; + else + rback = md; + rbackp = rback; + if (bs == 1 || mb.mvbc == 0) + md = mb.mvbc; + else if ((c = mb.mvbc) < 0) + md = (c + 1) * bs - mb.mvbr - 1; + else + md = (c - 1) * bs + mb.mvbr + 1; + md += dbackp; + if (md > bsmax) + dback = md - bsr; + else if (md < bsmin) + dback = md + bsr; + else + dback = md; + dbackp = dback; + if (p.flags & Mpegio->FPBV) { + rback2 = rback; + dback2 = dback; + rback <<= 1; + dback <<= 1; + ydb = rback2 + dback2 * width; + cdb = (rback2 >> 1) + (dback2 >> 1) * w2; + } else { + if (rback < 0) + rback2 = (rback + 1) >> 1; + else + rback2 = rback >> 1; + if (dback < 0) + dback2 = (dback + 1) >> 1; + else + dback2 = dback >> 1; + ydb = (rback >> 1) + (dback >> 1) * width; + cdb = (rback2 >> 1) + (dback2 >> 1) * w2; + } + } + vflags = mb.flags; + if (mb.rls != nil) { + invQ_nintra_block(mb.rls, mb.qscale); + deltambinterp(); + } else + interpmb(); + ipred = 0; + } + nextmb(); + n++; + } + } + while (n < mpi) { + interpmb(); + nextmb(); + n++; + } + return B; +} + +raisex(nil: string) +{ + raise "decode error"; +} diff --git a/appl/wm/mpeg/decode4.b b/appl/wm/mpeg/decode4.b new file mode 100644 index 00000000..c4a968e8 --- /dev/null +++ b/appl/wm/mpeg/decode4.b @@ -0,0 +1,709 @@ +implement Mpegd; + +include "sys.m"; +include "mpegio.m"; + +sys: Sys; +idct: IDCT; + +Mpegi, Picture, Slice, MacroBlock, YCbCr, Pair: import Mpegio; + +intra_tab := array[64] of { + 8, 16, 19, 22, 26, 27, 29, 34, + 16, 16, 22, 24, 27, 29, 34, 37, + 19, 22, 26, 27, 29, 34, 34, 38, + 22, 22, 26, 27, 29, 34, 37, 40, + 22, 26, 27, 29, 32, 35, 40, 48, + 26, 27, 29, 32, 35, 40, 48, 58, + 26, 27, 29, 34, 38, 46, 56, 69, + 27, 29, 35, 38, 46, 56, 69, 83, +}; + +nintra_tab := array[64] of { * => 16 }; + +CLOFF: con 256; + +intraQ, nintraQ: array of int; +rtmp: array of array of int; +rflag := array[6] of int; +rforw, dforw, rback, dback: int; +ydb, ydf: int; +vflags: int; +past := array[3] of int; +pinit := array[3] of { * => 128 * 8 }; +zeros := array[64] of { * => 0 }; +zeros1: array of int; +clamp := array[CLOFF + 256 + CLOFF] of byte; +width, height, w2, h2: int; +mpi, mps, yadj, yskip: int; +I, B0: ref YCbCr; +Ps := array[2] of ref YCbCr; +Rs := array[2] of ref YCbCr; +P, B, R, M, N: ref YCbCr; +pn: int = 0; +rn: int = 0; + +zig := array[64] of { + 0, 1, 8, 16, 9, 2, 3, 10, 17, + 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, +}; + +init(m: ref Mpegi) +{ + sys = load Sys Sys->PATH; + idct = load IDCT IDCT->SPATH; + if (idct == nil) { + sys->print("could not open %s: %r\n", IDCT->PATH); + exit; + } + idct->init(); + width = m.width; + height = m.height; + w2 = width >> 1; + h2 = height >> 1; + mps = width >> 4; + mpi = mps * height >> 4; + yskip = 8 * width; + yadj = 16 * width - (width - 16); + I = frame(); + Ps[0] = frame(); + Ps[1] = frame(); + Rs[0] = Ps[0]; + Rs[1] = Ps[1]; + B0 = frame(); + for (i := 0; i < CLOFF; i++) + clamp[i] = byte 0; + for (i = 0; i < 256; i++) + clamp[i + CLOFF] = byte i; + for (i = CLOFF + 256; i < CLOFF + 256 + CLOFF; i++) + clamp[i] = byte 255; + if (m.intra == nil) + intraQ = intra_tab; + else + intraQ = zigof(m.intra); + if (m.nintra == nil) + nintraQ = nintra_tab; + else + nintraQ = zigof(m.nintra); + rtmp = array[6] of array of int; + for (i = 0; i < 6; i++) + rtmp[i] = array[64] of int; + zeros1 = zeros[1:]; +} + +zarray(n: int, v: byte): array of byte +{ + return array[n] of { * => v }; +} + +frame(): ref YCbCr +{ + y := zarray(width * height, byte 0); + return ref YCbCr(y, nil, nil); +} + +zigof(a: array of int): array of int +{ + z := array[64] of int; + for (i := 0; i < 64; i++) + z[zig[i]] = a[i]; + return z; +} + +invQ_intra(a: array of Pair, q: int, b: array of int) +{ + (nil, t) := a[0]; + b[0] = t * 8; + b[1:] = zeros1; + n := 1; + i := 1; + while (n < len a) { + (r, l) := a[n++]; + i += r; + x := zig[i++]; + if (l > 0) { + v := l * q * intraQ[x] >> 3; + if (v > 2047) + b[x] = 2047; + else + b[x] = (v - 1) | 1; + } else { + v := (l * q * intraQ[x] + 7) >> 3; + if (v < -2048) + b[x] = -2048; + else + b[x] = v | 1; + } + #sys->print("%d %d %d %d\n", x, r, l, b[x]); + } +} + +invQ_nintra(a: array of Pair, q: int, b: array of int) +{ + b[0:] = zeros; + i := 0; + for (n := 0; n < len a; n++) { + (r, l) := a[n]; + i += r; + if (l == 0) { + raisex("zero level"); + i++; + continue; + } + x := zig[i++]; + if (l > 0) { + v := ((l << 1) + 1) * q * nintraQ[x] >> 4; + if (v > 2047) + b[x] = 2047; + else + b[x] = (v - 1) | 1; + } else { + v := (((l << 1) - 1) * q * nintraQ[x] + 15) >> 4; + if (v < -2048) + b[x] = -2048; + else + b[x] = v | 1; + } + #sys->print("%d %d %d %d\n", x, r, l, b[x]); + } +} + +yzero(v: array of byte, base: int) +{ + x := 0; + i := 8; + do { + n := base; + j := 8; + do + v[n++] = byte 0; + while (--j > 0); + base += width; + } while (--i > 0); +} + +blockzero(d: ref YCbCr) +{ + yzero(d.Y, ybase); + yzero(d.Y, ybase + 8); + yzero(d.Y, ybase + yskip); + yzero(d.Y, ybase + 8 + yskip); +} + +ydistr(a: array of int, v: array of byte, base: int) +{ + x := 0; + i := 8; + do { + n := base; + j := 8; + do + v[n++] = clamp[a[x++] + CLOFF]; + while (--j > 0); + base += width; + } while (--i > 0); +} + +invQ_intra_block(b: array of array of Pair, q: int, pred: int, d: ref YCbCr) +{ + a, dc: array of int; + if (pred) + dc = past; + else + dc = pinit; + p := dc[0]; + for (i := 0; i < 4; i++) { + a = rtmp[i]; + #sys->print("%d\n", i); + invQ_intra(b[i], q, a); + p += a[0]; + a[0] = p; + #sys->print("%d\n", a[0]); + idct->idct(a); + } + past[0] = p; + ydistr(rtmp[0], d.Y, ybase); + ydistr(rtmp[1], d.Y, ybase + 8); + ydistr(rtmp[2], d.Y, ybase + yskip); + ydistr(rtmp[3], d.Y, ybase + 8 + yskip); +} + +invQ_nintra_block(b: array of array of Pair, q: int) +{ + for (i := 0; i < 4; i++) { + p := b[i]; + if (p != nil) { + a := rtmp[i]; + #sys->print("%d\n", i); + invQ_nintra(p, q, a); + idct->idct(a); + rflag[i] = 1; + } else + rflag[i] = 0; + } +} + +mbr, ybase: int; + +nextmb() +{ + if (--mbr == 0) { + ybase += yadj; + mbr = mps; + } else + ybase += 16; +} + +copyblock(s, d: array of byte, b, n, w: int) +{ + i := 8; + do { + d[b:] = s[b:b+n]; + b += w; + } while (--i > 0); +} + +copyblockdisp(s, d: array of byte, b, n, w, p: int) +{ + i := 8; + p += b; + do { + d[b:] = s[p:p+n]; + b += w; + p += w; + } while (--i > 0); +} + +interpblock(s0, s1, d: array of byte, b, n, w, p0, p1: int) +{ + i := 8; + do { + dx := b; + s0x := b + p0; + s1x := b + p1; + j := n; + do + d[dx++] = byte ((int s0[s0x++] + int s1[s1x++] + 1) >> 1); + while (--j > 0); + b += w; + } while (--i > 0); +} + +deltablock(s: array of byte, r: array of int, d: array of byte, b, w, o: int) +{ + rx := 0; + i := 8; + do { + dx := b; + sx := b + o; + j := 8; + do + d[dx++] = clamp[CLOFF + int s[sx++] + r[rx++]]; + while (--j > 0); + b += w; + } while (--i > 0); +} + +deltainterpblock(s0, s1: array of byte, r: array of int, d: array of byte, b, w, o0, o1: int) +{ + rx := 0; + i := 8; + do { + dx := b; + s0x := b + o0; + s1x := b + o1; + j := 8; + do + d[dx++] = clamp[CLOFF + ((int s0[s0x++] + int s1[s1x++] + 1) >> 1) + r[rx++]]; + while (--j > 0); + b += w; + } while (--i > 0); +} + +dispblock(s, d: array of byte, n, b, w, o: int) +{ + if (rflag[n]) + deltablock(s, rtmp[n], d, b, w, o); + else + copyblockdisp(s, d, b, 8, w, o); +} + +genblock(s0, s1, d: array of byte, n, b, w, o0, o1: int) +{ + if (rflag[n]) + deltainterpblock(s0, s1, rtmp[n], d, b, w, o0, o1); + else + interpblock(s0, s1, d, b, 8, w, o0, o1); +} + +copymb() +{ + copyblock(R.Y, P.Y, ybase, 16, width); + copyblock(R.Y, P.Y, ybase + yskip, 16, width); +} + +deltamb() +{ + dispblock(R.Y, P.Y, 0, ybase, width, 0); + dispblock(R.Y, P.Y, 1, ybase + 8, width, 0); + dispblock(R.Y, P.Y, 2, ybase + yskip, width, 0); + dispblock(R.Y, P.Y, 3, ybase + 8 + yskip, width, 0); +} + +copymbforw() +{ + copyblockdisp(N.Y, B.Y, ybase, 16, width, ydf); + copyblockdisp(N.Y, B.Y, ybase + yskip, 16, width, ydf); +} + +copymbback() +{ + copyblockdisp(M.Y, B.Y, ybase, 16, width, ydb); + copyblockdisp(M.Y, B.Y, ybase + yskip, 16, width, ydb); +} + +copymbbackforw() +{ + interpblock(M.Y, N.Y, B.Y, ybase, 16, width, ydb, ydf); + interpblock(M.Y, N.Y, B.Y, ybase + yskip, 16, width, ydb, ydf); +} + +deltambforw() +{ + dispblock(N.Y, B.Y, 0, ybase, width, ydf); + dispblock(N.Y, B.Y, 1, ybase + 8, width, ydf); + dispblock(N.Y, B.Y, 2, ybase + yskip, width, ydf); + dispblock(N.Y, B.Y, 3, ybase + 8 + yskip, width, ydf); +} + +deltambback() +{ + dispblock(M.Y, B.Y, 0, ybase, width, ydb); + dispblock(M.Y, B.Y, 1, ybase + 8, width, ydb); + dispblock(M.Y, B.Y, 2, ybase + yskip, width, ydb); + dispblock(M.Y, B.Y, 3, ybase + 8 + yskip, width, ydb); +} + +deltambbackforw() +{ + genblock(M.Y, N.Y, B.Y, 0, ybase, width, ydb, ydf); + genblock(M.Y, N.Y, B.Y, 1, ybase + 8, width, ydb, ydf); + genblock(M.Y, N.Y, B.Y, 2, ybase + yskip, width, ydb, ydf); + genblock(M.Y, N.Y, B.Y, 3, ybase + 8 + yskip, width, ydb, ydf); +} + +deltambinterp() +{ + case vflags & (Mpegio->MB_MF | Mpegio->MB_MB) { + Mpegio->MB_MF => + deltambforw(); + Mpegio->MB_MB => + deltambback(); + Mpegio->MB_MF | Mpegio->MB_MB => + deltambbackforw(); + * => + raisex("bad vflags"); + } +} + +interpmb() +{ + case vflags & (Mpegio->MB_MF | Mpegio->MB_MB) { + Mpegio->MB_MF => + copymbforw(); + Mpegio->MB_MB => + copymbback(); + Mpegio->MB_MF | Mpegio->MB_MB => + copymbbackforw(); + * => + raisex("bad vflags"); + } +} + +Idecode(p: ref Picture): ref YCbCr +{ + sa := p.slices; + n := 0; + mbr = mps; + ybase = 0; + for (i := 0; i < len sa; i++) { + pred := 0; + ba := sa[i].blocks; + for (j := 0; j < len ba; j++) { + invQ_intra_block(ba[j].rls, ba[j].qscale, pred, I); + nextmb(); + n++; + pred = 1; + } + } + if (n != mpi) + raisex("I mb count"); + R = I; + Rs[rn] = I; + rn ^= 1; + return I; +} + +Pdecode(p: ref Picture): ref YCbCr +{ + rforwp, dforwp: int; + md, c: int; + P = Ps[pn]; + N = R; + B = P; + pn ^= 1; + fs := 1 << p.forwfc; + fsr := fs << 5; + fsmin := -(fs << 4); + fsmax := (fs << 4) - 1; + sa := p.slices; + n := 0; + mbr = mps; + ybase = 0; + for (i := 0; i < len sa; i++) { + pred := 0; + ipred := 0; + ba := sa[i].blocks; + for (j := 0; j < len ba; j++) { + mb := ba[j]; + while (n < mb.addr) { + copymb(); + ipred = 0; + pred = 0; + nextmb(); + n++; + } + if (mb.flags & Mpegio->MB_I) { + invQ_intra_block(mb.rls, mb.qscale, ipred, P); + #blockzero(P); + ipred = 1; + pred = 0; + } else { + if (mb.flags & Mpegio->MB_MF) { + if (fs == 1 || mb.mhfc == 0) + md = mb.mhfc; + else if ((c = mb.mhfc) < 0) + md = (c + 1) * fs - mb.mhfr - 1; + else + md = (c - 1) * fs + mb.mhfr + 1; + if (pred) + md += rforwp; + if (md > fsmax) + rforw = md - fsr; + else if (md < fsmin) + rforw = md + fsr; + else + rforw = md; + rforwp = rforw; + if (fs == 1 || mb.mvfc == 0) + md = mb.mvfc; + else if ((c = mb.mvfc) < 0) + md = (c + 1) * fs - mb.mvfr - 1; + else + md = (c - 1) * fs + mb.mvfr + 1; + if (pred) + md += dforwp; + if (md > fsmax) + dforw = md - fsr; + else if (md < fsmin) + dforw = md + fsr; + else + dforw = md; + dforwp = dforw; + if (p.flags & Mpegio->FPFV) { + ydf = rforw + dforw * width; + rforw <<= 1; + dforw <<= 1; + } else + ydf = (rforw >> 1) + (dforw >> 1) * width; + pred = 1; + if (mb.rls != nil) { + invQ_nintra_block(mb.rls, mb.qscale); + deltambforw(); + } else + copymbforw(); + } else { + if (mb.rls == nil) + raisex("empty delta"); + invQ_nintra_block(mb.rls, mb.qscale); + deltamb(); + pred = 0; + } + ipred = 0; + } + nextmb(); + n++; + } + } + while (n < mpi) { + copymb(); + nextmb(); + n++; + } + R = P; + Rs[rn] = P; + rn ^= 1; + return P; +} + +Bdecode(p: ref Mpegio->Picture): ref Mpegio->YCbCr +{ + return Bdecode2(p, Rs[rn ^ 1], Rs[rn]); +} + +Bdecode2(p: ref Mpegio->Picture, f0, f1: ref Mpegio->YCbCr): ref Mpegio->YCbCr +{ + rforwp, dforwp, rbackp, dbackp: int; + md, c: int; + M = f0; + N = f1; + B = B0; + fs := 1 << p.forwfc; + fsr := fs << 5; + fsmin := -(fs << 4); + fsmax := (fs << 4) - 1; + bs := 1 << p.backfc; + bsr := bs << 5; + bsmin := -(bs << 4); + bsmax := (bs << 4) - 1; + sa := p.slices; + n := 0; + mbr = mps; + ybase = 0; + for (i := 0; i < len sa; i++) { + ipred := 0; + rback = 0; + rforw = 0; + dback = 0; + dforw = 0; + rbackp = 0; + rforwp = 0; + dbackp = 0; + dforwp = 0; + ydb = 0; + ydf = 0; + ba := sa[i].blocks; + for (j := 0; j < len ba; j++) { + mb := ba[j]; + while (n < mb.addr) { + interpmb(); + nextmb(); + ipred = 0; + n++; + } + if (mb.flags & Mpegio->MB_I) { + invQ_intra_block(mb.rls, mb.qscale, ipred, B); + ipred = 1; + rback = 0; + rforw = 0; + dback = 0; + dforw = 0; + rbackp = 0; + rforwp = 0; + dbackp = 0; + dforwp = 0; + ydb = 0; + ydf = 0; + } else { + if (mb.flags & Mpegio->MB_MF) { + if (fs == 1 || mb.mhfc == 0) + md = mb.mhfc; + else if ((c = mb.mhfc) < 0) + md = (c + 1) * fs - mb.mhfr - 1; + else + md = (c - 1) * fs + mb.mhfr + 1; + md += rforwp; + if (md > fsmax) + rforw = md - fsr; + else if (md < fsmin) + rforw = md + fsr; + else + rforw = md; + rforwp = rforw; + if (fs == 1 || mb.mvfc == 0) + md = mb.mvfc; + else if ((c = mb.mvfc) < 0) + md = (c + 1) * fs - mb.mvfr - 1; + else + md = (c - 1) * fs + mb.mvfr + 1; + md += dforwp; + if (md > fsmax) + dforw = md - fsr; + else if (md < fsmin) + dforw = md + fsr; + else + dforw = md; + dforwp = dforw; + if (p.flags & Mpegio->FPFV) { + ydf = rforw + dforw * width; + rforw <<= 1; + dforw <<= 1; + } else + ydf = (rforw >> 1) + (dforw >> 1) * width; + } + if (mb.flags & Mpegio->MB_MB) { + if (bs == 1 || mb.mhbc == 0) + md = mb.mhbc; + else if ((c = mb.mhbc) < 0) + md = (c + 1) * bs - mb.mhbr - 1; + else + md = (c - 1) * bs + mb.mhbr + 1; + md += rbackp; + if (md > bsmax) + rback = md - bsr; + else if (md < bsmin) + rback = md + bsr; + else + rback = md; + rbackp = rback; + if (bs == 1 || mb.mvbc == 0) + md = mb.mvbc; + else if ((c = mb.mvbc) < 0) + md = (c + 1) * bs - mb.mvbr - 1; + else + md = (c - 1) * bs + mb.mvbr + 1; + md += dbackp; + if (md > bsmax) + dback = md - bsr; + else if (md < bsmin) + dback = md + bsr; + else + dback = md; + dbackp = dback; + if (p.flags & Mpegio->FPBV) { + ydb = rback + dback * width; + rback <<= 1; + dback <<= 1; + } else + ydb = (rback >> 1) + (dback >> 1) * width; + } + vflags = mb.flags; + if (mb.rls != nil) { + invQ_nintra_block(mb.rls, mb.qscale); + deltambinterp(); + } else + interpmb(); + ipred = 0; + } + nextmb(); + n++; + } + } + while (n < mpi) { + interpmb(); + nextmb(); + n++; + } + return B; +} + +raisex(nil: string) +{ + raise "decode error"; +} diff --git a/appl/wm/mpeg/fixidct.b b/appl/wm/mpeg/fixidct.b new file mode 100644 index 00000000..992cf837 --- /dev/null +++ b/appl/wm/mpeg/fixidct.b @@ -0,0 +1,188 @@ +implement IDCT; + +include "sys.m"; +include "mpegio.m"; + +init() +{ +} + +# IDCT based on Arai, Agui, and Nakajima, using flow chart Figure 4.8 +# of Pennebaker & Mitchell, JPEG: Still Image Data Compression Standard. +# Remember IDCT is reverse of flow of DCT. +# Nasty truncated integer version (not compliant). + +B0: con 16; +B1: con 16; +M: con (1 << B0); +N: con (1 << B1); + +a0: con 1.414; +a1: con 0.707; +a2: con 0.541; +a3: con 0.707; +a4: con 1.307; +a5: con -0.383; + +A0: con int (a0 * real N); +A1: con int (a1 * real M); +A2: con int (a2 * real M); +A3: con int (a3 * real M); +A4: con int (a4 * real M); +A5: con int (a5 * real M); + +# scaling factors from eqn 4-35 of P&M +s1: con 1.0196; +s2: con 1.0823; +s3: con 1.2026; +s4: con 1.4142; +s5: con 1.8000; +s6: con 2.6131; +s7: con 5.1258; + +S1: con int (s1 * real N); +S2: con int (s2 * real N); +S3: con int (s3 * real N); +S4: con int (s4 * real N); +S5: con int (s5 * real N); +S6: con int (s6 * real N); +S7: con int (s7 * real N); + +# overall normalization of 1/16, folded into premultiplication on vertical pass +S: con 4; +scale: con 0.0625; + +idct(b: array of int) +{ + x, y: int; + + r := array[8*8] of int; + + # transform horizontally + for(y=0; y<8; y++){ + eighty := y<<3; + # if all non-DC components are zero, just propagate the DC term + if(b[eighty+1]==0) + if(b[eighty+2]==0 && b[eighty+3]==0) + if(b[eighty+4]==0 && b[eighty+5]==0) + if(b[eighty+6]==0 && b[eighty+7]==0){ + v := b[eighty]*A0; + r[eighty+0] = v; + r[eighty+1] = v; + r[eighty+2] = v; + r[eighty+3] = v; + r[eighty+4] = v; + r[eighty+5] = v; + r[eighty+6] = v; + r[eighty+7] = v; + continue; + } + + # step 5 + in1 := S1*b[eighty+1]; + in3 := S3*b[eighty+3]; + in5 := S5*b[eighty+5]; + in7 := S7*b[eighty+7]; + f2 := S2*b[eighty+2]; + f3 := S6*b[eighty+6]; + f5 := (in1+in7); + f7 := (in5+in3); + + # step 4 + g2 := f2-f3; + g4 := (in5-in3); + g6 := (in1-in7); + g7 := f5+f7; + + # step 3.5 + t := ((g4+g6)>>B0)*A5; + + # step 3 + f0 := A0*b[eighty+0]; + f1 := S4*b[eighty+4]; + f3 += f2; + f2 = A1*(g2>>B0); + + # step 2 + g0 := f0+f1; + g1 := f0-f1; + g3 := f2+f3; + g4 = t-A2*(g4>>B0); + g5 := A3*((f5-f7)>>B0); + g6 = A4*(g6>>B0)+t; + + # step 1 + f0 = g0+g3; + f1 = g1+f2; + f2 = g1-f2; + f3 = g0-g3; + f5 = g5-g4; + f6 := g5+g6; + f7 = g6+g7; + + # step 6 + r[eighty+0] = (f0+f7); + r[eighty+1] = (f1+f6); + r[eighty+2] = (f2+f5); + r[eighty+3] = (f3-g4); + r[eighty+4] = (f3+g4); + r[eighty+5] = (f2-f5); + r[eighty+6] = (f1-f6); + r[eighty+7] = (f0-f7); + } + + # transform vertically + for(x=0; x<8; x++){ + # step 5 + in1 := S1*(r[x+8]>>(B1+S)); + in3 := S3*(r[x+24]>>(B1+S)); + in5 := S5*(r[x+40]>>(B1+S)); + in7 := S7*(r[x+56]>>(B1+S)); + f2 := S2*(r[x+16]>>(B1+S)); + f3 := S6*(r[x+48]>>(B1+S)); + f5 := (in1+in7); + f7 := (in5+in3); + + # step 4 + g2 := f2-f3; + g4 := (in5-in3); + g6 := (in1-in7); + g7 := f5+f7; + + # step 3.5 + t := ((g4+g6)>>B0)*A5; + + # step 3 + f0 := A0*(r[x]>>(B1+S)); + f1 := S4*(r[x+32]>>(B1+S)); + f3 += f2; + f2 = A1*(g2>>B0); + + # step 2 + g0 := f0+f1; + g1 := f0-f1; + g3 := f2+f3; + g4 = t-A2*(g4>>B0); + g5 := A3*((f5-f7)>>B0); + g6 = A4*(g6>>B0)+t; + + # step 1 + f0 = g0+g3; + f1 = g1+f2; + f2 = g1-f2; + f3 = g0-g3; + f5 = g5-g4; + f6 := g5+g6; + f7 = g6+g7; + + # step 6 + b[x] = (f0+f7)>>B1; + b[x+8] = (f1+f6)>>B1; + b[x+16] = (f2+f5)>>B1; + b[x+24] = (f3-g4)>>B1; + b[x+32] = (f3+g4)>>B1; + b[x+40] = (f2-f5)>>B1; + b[x+48] = (f1-f6)>>B1; + b[x+56] = (f0-f7)>>B1; + } +} diff --git a/appl/wm/mpeg/fltidct.b b/appl/wm/mpeg/fltidct.b new file mode 100644 index 00000000..24c80fe2 --- /dev/null +++ b/appl/wm/mpeg/fltidct.b @@ -0,0 +1,177 @@ +implement IDCT; + +include "sys.m"; +include "mpegio.m"; + +init() +{ +} + +# IDCT based on Arai, Agui, and Nakajima, using flow chart Figure 4.8 +# of Pennebaker & Mitchell, JPEG: Still Image Data Compression Standard. +# Remember IDCT is reverse of flow of DCT. +# Based on rob's readjpeg.b + +a0: con 1.414; +a1: con 0.707; +a2: con 0.541; +a3: con 0.707; +a4: con 1.307; +a5: con -0.383; + +# scaling factors from eqn 4-35 of P&M +s1: con 1.0196; +s2: con 1.0823; +s3: con 1.2026; +s4: con 1.4142; +s5: con 1.8000; +s6: con 2.6131; +s7: con 5.1258; + +# overall normalization of 1/16, folded into premultiplication on vertical pass +scale: con 0.0625; + +ridct(zin: array of real, zout: array of real) +{ + x, y: int; + + r := array[8*8] of real; + + # transform horizontally + for(y=0; y<8; y++){ + eighty := y<<3; + # if all non-DC components are zero, just propagate the DC term + if(zin[eighty+1]==0.) + if(zin[eighty+2]==0. && zin[eighty+3]==0.) + if(zin[eighty+4]==0. && zin[eighty+5]==0.) + if(zin[eighty+6]==0. && zin[eighty+7]==0.){ + v := zin[eighty]*a0; + r[eighty+0] = v; + r[eighty+1] = v; + r[eighty+2] = v; + r[eighty+3] = v; + r[eighty+4] = v; + r[eighty+5] = v; + r[eighty+6] = v; + r[eighty+7] = v; + continue; + } + + # step 5 + in1 := s1*zin[eighty+1]; + in3 := s3*zin[eighty+3]; + in5 := s5*zin[eighty+5]; + in7 := s7*zin[eighty+7]; + f2 := s2*zin[eighty+2]; + f3 := s6*zin[eighty+6]; + f5 := (in1+in7); + f7 := (in5+in3); + + # step 4 + g2 := f2-f3; + g4 := (in5-in3); + g6 := (in1-in7); + g7 := f5+f7; + + # step 3.5 + t := (g4+g6)*a5; + + # step 3 + f0 := a0*zin[eighty+0]; + f1 := s4*zin[eighty+4]; + f3 += f2; + f2 = a1*g2; + + # step 2 + g0 := f0+f1; + g1 := f0-f1; + g3 := f2+f3; + g4 = t-a2*g4; + g5 := a3*(f5-f7); + g6 = a4*g6+t; + + # step 1 + f0 = g0+g3; + f1 = g1+f2; + f2 = g1-f2; + f3 = g0-g3; + f5 = g5-g4; + f6 := g5+g6; + f7 = g6+g7; + + # step 6 + r[eighty+0] = (f0+f7); + r[eighty+1] = (f1+f6); + r[eighty+2] = (f2+f5); + r[eighty+3] = (f3-g4); + r[eighty+4] = (f3+g4); + r[eighty+5] = (f2-f5); + r[eighty+6] = (f1-f6); + r[eighty+7] = (f0-f7); + } + + # transform vertically + for(x=0; x<8; x++){ + # step 5 + in1 := scale*s1*r[x+8]; + in3 := scale*s3*r[x+24]; + in5 := scale*s5*r[x+40]; + in7 := scale*s7*r[x+56]; + f2 := scale*s2*r[x+16]; + f3 := scale*s6*r[x+48]; + f5 := (in1+in7); + f7 := (in5+in3); + + # step 4 + g2 := f2-f3; + g4 := (in5-in3); + g6 := (in1-in7); + g7 := f5+f7; + + # step 3.5 + t := (g4+g6)*a5; + + # step 3 + f0 := scale*a0*r[x]; + f1 := scale*s4*r[x+32]; + f3 += f2; + f2 = a1*g2; + + # step 2 + g0 := f0+f1; + g1 := f0-f1; + g3 := f2+f3; + g4 = t-a2*g4; + g5 := a3*(f5-f7); + g6 = a4*g6+t; + + # step 1 + f0 = g0+g3; + f1 = g1+f2; + f2 = g1-f2; + f3 = g0-g3; + f5 = g5-g4; + f6 := g5+g6; + f7 = g6+g7; + + # step 6 + zout[x] = (f0+f7); + zout[x+8] = (f1+f6); + zout[x+16] = (f2+f5); + zout[x+24] = (f3-g4); + zout[x+32] = (f3+g4); + zout[x+40] = (f2-f5); + zout[x+48] = (f1-f6); + zout[x+56] = (f0-f7); + } +} + +idct(b: array of int) +{ + tmp := array[64] of real; + for (i := 0; i < 64; i++) + tmp[i] = real b[i]; + ridct(tmp, tmp); + for (i = 0; i < 64; i++) + b[i] = int tmp[i]; +} diff --git a/appl/wm/mpeg/mai.tab b/appl/wm/mpeg/mai.tab new file mode 100644 index 00000000..8884adad --- /dev/null +++ b/appl/wm/mpeg/mai.tab @@ -0,0 +1,2053 @@ +# vlc mai +mai_size: con 2048; +mai_bits: con 11; +mai_table:= array[] of { + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (11, 33), + (11, 32), + (11, 31), + (11, 30), + (11, 29), + (11, 28), + (11, 27), + (11, 26), + (11, 25), + (11, 24), + (11, 23), + (11, 22), + (10, 21), + (10, 21), + (10, 20), + (10, 20), + (10, 19), + (10, 19), + (10, 18), + (10, 18), + (10, 17), + (10, 17), + (10, 16), + (10, 16), + (8, 15), + (8, 15), + (8, 15), + (8, 15), + (8, 15), + (8, 15), + (8, 15), + (8, 15), + (8, 14), + (8, 14), + (8, 14), + (8, 14), + (8, 14), + (8, 14), + (8, 14), + (8, 14), + (8, 13), + (8, 13), + (8, 13), + (8, 13), + (8, 13), + (8, 13), + (8, 13), + (8, 13), + (8, 12), + (8, 12), + (8, 12), + (8, 12), + (8, 12), + (8, 12), + (8, 12), + (8, 12), + (8, 11), + (8, 11), + (8, 11), + (8, 11), + (8, 11), + (8, 11), + (8, 11), + (8, 11), + (8, 10), + (8, 10), + (8, 10), + (8, 10), + (8, 10), + (8, 10), + (8, 10), + (8, 10), + (7, 9), + (7, 9), + (7, 9), + (7, 9), + (7, 9), + (7, 9), + (7, 9), + (7, 9), + (7, 9), + (7, 9), + (7, 9), + (7, 9), + (7, 9), + (7, 9), + (7, 9), + (7, 9), + (7, 8), + (7, 8), + (7, 8), + (7, 8), + (7, 8), + (7, 8), + (7, 8), + (7, 8), + (7, 8), + (7, 8), + (7, 8), + (7, 8), + (7, 8), + (7, 8), + (7, 8), + (7, 8), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 7), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (4, 4), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (3, 2), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), + (1, 1), +}; diff --git a/appl/wm/mpeg/mai.vlc b/appl/wm/mpeg/mai.vlc new file mode 100644 index 00000000..108c4658 --- /dev/null +++ b/appl/wm/mpeg/mai.vlc @@ -0,0 +1,35 @@ +# Macroblock Address Increment +# vlc mai < mai.vlc > mai.tab +1 1 +011 2 +010 3 +0011 4 +0010 5 +00011 6 +00010 7 +0000111 8 +0000110 9 +00001011 10 +00001010 11 +00001001 12 +00001000 13 +00000111 14 +00000110 15 +0000010111 16 +0000010110 17 +0000010101 18 +0000010100 19 +0000010011 20 +0000010010 21 +00000100011 22 +00000100010 23 +00000100001 24 +00000100000 25 +00000011111 26 +00000011110 27 +00000011101 28 +00000011100 29 +00000011011 30 +00000011010 31 +00000011001 32 +00000011000 33 diff --git a/appl/wm/mpeg/makergbvmap.b b/appl/wm/mpeg/makergbvmap.b new file mode 100644 index 00000000..9e7e7ffa --- /dev/null +++ b/appl/wm/mpeg/makergbvmap.b @@ -0,0 +1,31 @@ +implement MakeRGBVMap; + +include "sys.m"; +include "draw.m"; + +draw: Draw; +sys: Sys; + +Display: import draw; + +MakeRGBVMap: module +{ + init: fn(ctxt: ref Draw->Context, nil: list of string); +}; + +init(ctxt: ref Draw->Context, nil: list of string) +{ + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + if (draw == nil) { + sys->print("could not load %s: %r\n", Draw->PATH); + exit; + } + d := ctxt.display; + sys->print("rgbvmap := array[3*256] of {\n"); + for (i := 0; i < 256; i++) { + (r, g, b) := d.cmap2rgb(i); + sys->print("\tbyte\t%d,byte\t%d,byte\t%d,\n", r, g, b); + } + sys->print("};\n"); +} diff --git a/appl/wm/mpeg/maketables b/appl/wm/mpeg/maketables new file mode 100644 index 00000000..d663a36e --- /dev/null +++ b/appl/wm/mpeg/maketables @@ -0,0 +1,36 @@ +echo motion: +vlc motion < motion.vlc > motion.tab +echo rl0f: +vlc -c rl0f < rl0f.vlc > rl0f.tab +echo rl0n: +vlc -c rl0n < rl0n.vlc > rl0n.tab +echo c0: +vlc -uUNDEF,UNDEF c0 < c0.vlc > c0.tab +echo c1: +vlc -cfp c1 < c1.vlc > c1.tab +echo c2: +vlc -cfp c2 < c2.vlc > c2.tab +echo c3: +vlc -cfp c3 < c3.vlc > c3.tab +echo c4: +vlc -cfp c4 < c4.vlc > c4.tab +echo c5: +vlc -cfp c5 < c5.vlc > c5.tab +echo c6: +vlc -cfp c6 < c6.vlc > c6.tab +echo c7: +vlc -cfp c7 < c7.vlc > c7.tab +echo mai: +vlc mai < mai.vlc > mai.tab +echo mbi: +vlc mbi < mbi.vlc > mbi.tab +echo mbp: +vlc mbp < mbp.vlc > mbp.tab +echo mbb: +vlc mbb < mbb.vlc > mbb.tab +echo cbp: +vlc cbp < cbp.vlc > cbp.tab +echo cdc: +vlc cdc < cdc.vlc > cdc.tab +echo ydc: +vlc ydc < ydc.vlc > ydc.tab diff --git a/appl/wm/mpeg/mbb.tab b/appl/wm/mpeg/mbb.tab new file mode 100644 index 00000000..4707d394 --- /dev/null +++ b/appl/wm/mpeg/mbb.tab @@ -0,0 +1,69 @@ +# vlc mbb +mbb_size: con 64; +mbb_bits: con 6; +mbb_table:= array[] of { + (0, UNDEF), + (6, 10), + (6, 6), + (6, 4), + (5, 8), + (5, 8), + (5, 9), + (5, 9), + (4, 0), + (4, 0), + (4, 0), + (4, 0), + (4, 3), + (4, 3), + (4, 3), + (4, 3), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 5), + (3, 5), + (3, 5), + (3, 5), + (3, 5), + (3, 5), + (3, 5), + (3, 5), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 7), + (2, 7), + (2, 7), + (2, 7), + (2, 7), + (2, 7), + (2, 7), + (2, 7), + (2, 7), + (2, 7), + (2, 7), + (2, 7), + (2, 7), + (2, 7), + (2, 7), + (2, 7), +}; diff --git a/appl/wm/mpeg/mbb.vlc b/appl/wm/mpeg/mbb.vlc new file mode 100644 index 00000000..1cc57796 --- /dev/null +++ b/appl/wm/mpeg/mbb.vlc @@ -0,0 +1,13 @@ +# Macroblock Type-B +# vlc mbb < mbb.vlc > mbb.tab +0010 0 +010 1 +10 2 +0011 3 +000011 4 +011 5 +000010 6 +11 7 +00010 8 +00011 9 +000001 10 diff --git a/appl/wm/mpeg/mbi.tab b/appl/wm/mpeg/mbi.tab new file mode 100644 index 00000000..7a72eb3f --- /dev/null +++ b/appl/wm/mpeg/mbi.tab @@ -0,0 +1,9 @@ +# vlc mbi +mbi_size: con 4; +mbi_bits: con 2; +mbi_table:= array[] of { + (0, UNDEF), + (2, 1), + (1, 0), + (1, 0), +}; diff --git a/appl/wm/mpeg/mbi.vlc b/appl/wm/mpeg/mbi.vlc new file mode 100644 index 00000000..4b4349bc --- /dev/null +++ b/appl/wm/mpeg/mbi.vlc @@ -0,0 +1,4 @@ +# Macroblock Type-I +# vlc mbi < mbi.vlc > mbi.tab +1 0 +01 1 diff --git a/appl/wm/mpeg/mbp.tab b/appl/wm/mpeg/mbp.tab new file mode 100644 index 00000000..26b41fec --- /dev/null +++ b/appl/wm/mpeg/mbp.tab @@ -0,0 +1,69 @@ +# vlc mbp +mbp_size: con 64; +mbp_bits: con 6; +mbp_table:= array[] of { + (0, UNDEF), + (6, 6), + (5, 2), + (5, 2), + (5, 4), + (5, 4), + (5, 5), + (5, 5), + (3, 0), + (3, 0), + (3, 0), + (3, 0), + (3, 0), + (3, 0), + (3, 0), + (3, 0), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (1, 3), + (1, 3), + (1, 3), + (1, 3), + (1, 3), + (1, 3), + (1, 3), + (1, 3), + (1, 3), + (1, 3), + (1, 3), + (1, 3), + (1, 3), + (1, 3), + (1, 3), + (1, 3), + (1, 3), + (1, 3), + (1, 3), + (1, 3), + (1, 3), + (1, 3), + (1, 3), + (1, 3), + (1, 3), + (1, 3), + (1, 3), + (1, 3), + (1, 3), + (1, 3), + (1, 3), + (1, 3), +}; diff --git a/appl/wm/mpeg/mbp.vlc b/appl/wm/mpeg/mbp.vlc new file mode 100644 index 00000000..8893ff9b --- /dev/null +++ b/appl/wm/mpeg/mbp.vlc @@ -0,0 +1,9 @@ +# Macroblock Type-P +# vlc mbp < mbp.vlc > mbp.tab +001 0 +01 1 +00001 2 +1 3 +00010 4 +00011 5 +000001 6 diff --git a/appl/wm/mpeg/mkfile b/appl/wm/mpeg/mkfile new file mode 100644 index 00000000..1b23f370 --- /dev/null +++ b/appl/wm/mpeg/mkfile @@ -0,0 +1,47 @@ +<../../../mkconfig + +TARG=\ + decode.dis\ + decode4.dis\ + fixidct.dis\ + fltidct.dis\ + makergbvmap.dis\ + mpegio.dis\ + refidct.dis\ + remap.dis\ + remap1.dis\ + remap2.dis\ + remap4.dis\ + remap24.dis\ + remap8.dis\ + scidct.dis\ + vlc.dis\ + +MODULES=\ + closest.m\ + mpegio.m\ + rgbvmap.m\ + +SYSMODULES=\ + bufio.m\ + draw.m\ + math.m\ + sys.m\ + tk.m\ + wmlib.m\ + +DISBIN=$ROOT/dis/mpeg + +<$ROOT/mkfiles/mkdis + +all:V: mpeg.dis + +install:V: $ROOT/dis/mpeg/mpeg.dis + +$ROOT/dis/mpeg/mpeg.dis: mpeg.dis + rm -f $target && cp mpeg.dis $target + +mpeg.dis: $MODULES $SYS_MODULE + +nuke:V: + rm -f $ROOT/dis/mpeg/mpeg.dis diff --git a/appl/wm/mpeg/motion.tab b/appl/wm/mpeg/motion.tab new file mode 100644 index 00000000..ca619976 --- /dev/null +++ b/appl/wm/mpeg/motion.tab @@ -0,0 +1,2053 @@ +# vlc motion +motion_size: con 2048; +motion_bits: con 11; +motion_table:= array[] of { + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (0, UNDEF), + (11, 16), + (11, -16), + (11, 15), + (11, -15), + (11, 14), + (11, -14), + (11, 13), + (11, -13), + (11, 12), + (11, -12), + (11, 11), + (11, -11), + (10, 10), + (10, 10), + (10, -10), + (10, -10), + (10, 9), + (10, 9), + (10, -9), + (10, -9), + (10, 8), + (10, 8), + (10, -8), + (10, -8), + (8, 7), + (8, 7), + (8, 7), + (8, 7), + (8, 7), + (8, 7), + (8, 7), + (8, 7), + (8, -7), + (8, -7), + (8, -7), + (8, -7), + (8, -7), + (8, -7), + (8, -7), + (8, -7), + (8, 6), + (8, 6), + (8, 6), + (8, 6), + (8, 6), + (8, 6), + (8, 6), + (8, 6), + (8, -6), + (8, -6), + (8, -6), + (8, -6), + (8, -6), + (8, -6), + (8, -6), + (8, -6), + (8, 5), + (8, 5), + (8, 5), + (8, 5), + (8, 5), + (8, 5), + (8, 5), + (8, 5), + (8, -5), + (8, -5), + (8, -5), + (8, -5), + (8, -5), + (8, -5), + (8, -5), + (8, -5), + (7, 4), + (7, 4), + (7, 4), + (7, 4), + (7, 4), + (7, 4), + (7, 4), + (7, 4), + (7, 4), + (7, 4), + (7, 4), + (7, 4), + (7, 4), + (7, 4), + (7, 4), + (7, 4), + (7, -4), + (7, -4), + (7, -4), + (7, -4), + (7, -4), + (7, -4), + (7, -4), + (7, -4), + (7, -4), + (7, -4), + (7, -4), + (7, -4), + (7, -4), + (7, -4), + (7, -4), + (7, -4), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, 3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (5, -3), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, 2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (4, -2), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, 1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (3, -1), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), + (1, 0), +}; diff --git a/appl/wm/mpeg/motion.vlc b/appl/wm/mpeg/motion.vlc new file mode 100644 index 00000000..db98581e --- /dev/null +++ b/appl/wm/mpeg/motion.vlc @@ -0,0 +1,19 @@ +# Motion Codes +# vlc motion < motion.vlc > motion.tab +1 0 +01s 1 +001s 2 +0001s 3 +000011s 4 +0000101s 5 +0000100s 6 +0000011s 7 +000001011s 8 +000001010s 9 +000001001s 10 +0000010001s 11 +0000010000s 12 +0000001111s 13 +0000001110s 14 +0000001101s 15 +0000001100s 16 diff --git a/appl/wm/mpeg/mpeg.b b/appl/wm/mpeg/mpeg.b new file mode 100644 index 00000000..1ac8c276 --- /dev/null +++ b/appl/wm/mpeg/mpeg.b @@ -0,0 +1,285 @@ +implement WmMpeg; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + Point, Rect, Display, Image: import draw; + +include "tk.m"; + tk: Tk; + Toplevel: import tk; + +include "tkclient.m"; + tkclient: Tkclient; + ctxt: ref Draw->Context; + +include "dialog.m"; + dialog: Dialog; + +include "selectfile.m"; + selectfile: Selectfile; + +include "mpegio.m"; + +include "arg.m"; + +mio: Mpegio; +decode: Mpegd; +remap: Remap; +Mpegi: import mio; + +WmMpeg: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +Stopped, Playing, Stepping, Paused: con iota; +state := Stopped; +depth := -1; +sdepth: int; +cvt: ref Image; + +pixelrec: Draw->Rect; + +decoders := array[] of { +1=> Mpegd->PATH4, +2=> Mpegd->PATH4, +4=> Mpegd->PATH4, +8 or 16 or 24 or 32 => Mpegd->PATH, +}; + +remappers := array[] of { +1=> Remap->PATH1, +2=> Remap->PATH2, +4=> Remap->PATH4, +8 or 16 or 24 or 32 => Remap->PATH, +}; + +task_cfg := array[] of { + "canvas .c", + "frame .b", + "button .b.File -text File -command {send cmd file}", + "button .b.Stop -text Stop -command {send cmd stop}", + "button .b.Pause -text Pause -command {send cmd pause}", + "button .b.Step -text Step -command {send cmd step}", + "button .b.Play -text Play -command {send cmd play}", + "frame .f", + "label .f.file -text {File:}", + "label .f.name", + "pack .f.file .f.name -side left", + "pack .b.File .b.Stop .b.Pause .b.Step .b.Play -side left", + "pack .f -fill x", + "pack .b -anchor w", + "pack .c -side bottom -fill both -expand 1", + "pack propagate . 0", +}; + +init(xctxt: ref Draw->Context, argv: list of string) +{ + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient= load Tkclient Tkclient->PATH; + dialog = load Dialog Dialog->PATH; + selectfile= load Selectfile Selectfile->PATH; + + ctxt = xctxt; + tkclient->init(); + dialog->init(); + selectfile->init(); + + darg, tkarg: string; + arg := load Arg Arg->PATH; + arg->init(argv); + while((c := arg->opt()) != 0) + case c { + 'x' => + tkarg = arg->arg(); + 'd' => + darg = arg->arg(); + } + args := arg->argv(); + arg = nil; + if(darg != nil) + depth = int darg; + sdepth = ctxt.display.image.depth; + if (depth < 0 || depth > sdepth) + depth = sdepth; + (t, menubut) := tkclient->toplevel(ctxt, tkarg, "MPEG Player", 0); + + cmd := chan of string; + tk->namechan(t, cmd, "cmd"); + + for(i:=0; i<len task_cfg; i++) + tk->cmd(t, task_cfg[i]); + + tk->cmd(t, "bind . <Configure> {send cmd resize}"); + tk->cmd(t, "update"); + tkclient->onscreen(t, nil); + tkclient->startinput(t, "kbd"::"ptr"::nil); + + mio = load Mpegio Mpegio->PATH; + decode = load Mpegd decoders[depth]; + remap = load Remap remappers[depth]; + if(mio == nil || decode == nil || remap == nil) { + dialog->prompt(ctxt, t.image, "error -fg red", "Loading Interfaces", + "Failed to load the MPEG\ninterface: "+sys->sprint("%r"), + 0, "Exit"::nil); + return; + } + mio->init(); + + fname := ""; + ctl := chan of string; + state = Stopped; + + for(;;) alt { + s := <-t.ctxt.kbd => + tk->keyboard(t, s); + s := <-t.ctxt.ptr => + tk->pointer(t, *s); + s := <-t.ctxt.ctl or + s = <-t.wreq => + tkclient->wmctl(t, s); + s := <-menubut => + if(s == "exit"){ + state = Stopped; + return; + } + tkclient->wmctl(t, s); + press := <-cmd => + case press { + "file" => + state = Stopped; + patterns := list of { + "*.mpg (MPEG movie files)", + "* (All Files)" + }; + fname = selectfile->filename(ctxt, t.image, "Locate MPEG files", + patterns, nil); + if(fname != nil) { + tk->cmd(t, ".f.name configure -text {"+fname+"}"); + tk->cmd(t, "update"); + } + "play" => + if (state != Stopped) { + state = Playing; + continue; + } + if(fname != nil) { + state = Playing; + spawn play(t, fname); + } + "step" => + if (state != Stopped) { + state = Stepping; + continue; + } + if(fname != nil) { + state = Stepping; + spawn play(t, fname); + } + "pause" => + if(state == Playing) + state = Paused; + "stop" => + state = Stopped; + } + } +} + +play(t: ref Toplevel, file: string) +{ + sp := list of { "Stop Play" }; + + fd := sys->open(file, Sys->OREAD); + if(fd == nil) { + dialog->prompt(ctxt, t.image, "error -fg red", "Open MPEG file", sys->sprint("%r"), 0, sp); + return; + } + m := mio->prepare(fd, file); + m.streaminit(Mpegio->VIDEO_STR0); + p := m.getpicture(1); + decode->init(m); + remap->init(m); + + canvr := canvsize(t); + o := Point(0, 0); + dx := canvr.dx(); + if(dx > m.width) + o.x = (dx - m.width)/2; + dy := canvr.dy(); + if(dy > m.height) + o.y = (dy - m.height)/2; + canvr.min = canvr.min.add(o); + canvr.max = canvr.min.add(Point(m.width, m.height)); + + if (depth != sdepth){ + chans := Draw->CMAP8; + case depth { + 0 => chans = Draw->GREY1; + 1 => chans = Draw->GREY2; + 2 => chans = Draw->GREY4; + 3 => chans = Draw->CMAP8; + 4 => chans = Draw->RGB16; + 5 => chans = Draw->RGB24; # ? + } + cvt = ctxt.display.newimage(Rect((0, 0), (m.width, m.height)), chans, 0, 0); + } + + f, pf: ref Mpegio->YCbCr; + for(;;) { + if(state == Stopped) + break; + case p.ptype { + Mpegio->IPIC => + f = decode->Idecode(p); + Mpegio->PPIC => + f = decode->Pdecode(p); + Mpegio->BPIC => + f = decode->Bdecode(p); + } + while(state == Paused) + sys->sleep(0); + if (p.ptype == Mpegio->BPIC) { + writepixels(t, canvr, remap->remap(f)); + if(state == Stepping) + state = Paused; + } else { + if (pf != nil) { + writepixels(t, canvr, remap->remap(pf)); + if(state == Stepping) + state = Paused; + } + pf = f; + } + if ((p = m.getpicture(1)) == nil) { + writepixels(t, canvr, remap->remap(pf)); + break; + } + } + state = Stopped; +} + +writepixels(t: ref Toplevel, r: Rect, b: array of byte) +{ + if (cvt != nil) { + cvt.writepixels(cvt.r, b); + t.image.draw(r, cvt, nil, (0, 0)); + } else + t.image.writepixels(r, b); +} + +canvsize(t: ref Toplevel): Rect +{ + r: Rect; + + r.min.x = int tk->cmd(t, ".c cget -actx"); + r.min.y = int tk->cmd(t, ".c cget -acty"); + r.max.x = r.min.x + int tk->cmd(t, ".c cget -width"); + r.max.y = r.min.y + int tk->cmd(t, ".c cget -height"); + + return r; +} diff --git a/appl/wm/mpeg/mpegio.b b/appl/wm/mpeg/mpegio.b new file mode 100644 index 00000000..3206776b --- /dev/null +++ b/appl/wm/mpeg/mpegio.b @@ -0,0 +1,870 @@ +implement Mpegio; + +# +# MPEG ISO 11172 IO module. +# + +include "sys.m"; +include "mpegio.m"; + +sys: Sys; + +init() +{ + sys = load Sys Sys->PATH; +} + +raisex(s: string) +{ + raise MEXCEPT + s; +} + +prepare(fd: ref Sys->FD, name: string): ref Mpegi +{ + m := ref Mpegi; + m.fd = fd; + m.name = name; + m.seek = 0; + m.looked = 0; + m.index = 0; + m.size = 0; + m.buff = array[MBSZ] of byte; + return m; +} + +Mpegi.startsys(m: self ref Mpegi) +{ + # 2.4.3.2 + m.xnextsc(PACK_SC); + m.packhdr(); + m.xnextsc(SYSHD_SC); + m.syssz = m.getw(); + m.boundmr = m.get22("boundmr"); + m.syspar = m.getw(); + if ((m.syspar & 16r20) == 0 || m.getb() != 16rFF) + m.fmterr("syspar"); + t := m.syssz - 6; + if (t <= 0 || (t % 3) != 0) + m.fmterr("syssz"); + t /= 3; + m.nstream = t; + m.streams = array[t] of Stream; + for (i := 0; i < t; i++) { + v := m.getb(); + if ((v & 16r80) == 0) + m.fmterr("streamid"); + w := m.getb(); + if ((w & 16rC0) != 16rC0) + m.fmterr("stream mark"); + m.streams[i] = (byte v, byte ((w >> 5) & 1), ((w & 16r1F) << 8) | m.getb(), nil); + } +} + +Mpegi.packetcp(m: self ref Mpegi): int +{ + while ((c := m.nextsc()) != STREAM_EC) { + case c { + PACK_SC => + m.packhdr(); + SYSHD_SC => + m.syshdr(); + * => + if (c < STREAM_BASE) + m.fmterr(sys->sprint("stream code %x", c)); + # 2.4.3.3 + l := m.getw(); + fd := m.getfd(c); + if (fd != nil) { + if (c != PRIVSTREAM2) + l -= m.stamps(); + if (m.log != nil) + sys->fprint(m.log, "%x %d %d\n", c & 16rFF, m.tell(), l); + m.cpn(fd, l); + } else + m.skipn(l); + return 1; + } + } + return 0; +} + +Mpegi.getfd(m: self ref Mpegi, c: int): ref Sys->FD +{ + id := byte c; + n := m.nstream; + for (i := 0; i < n; i++) { + if (m.streams[i].id == id) + return m.streams[i].fd; + } + return nil; +} + +Mpegi.packhdr(m: self ref Mpegi) +{ + # 2.4.3.2 + t := m.getb(); + if ((t & 16rF1) != 16r21) + m.fmterr("pack tag"); + m.packt0 = (t >> 1) & 7; + v := m.getb() << 22; + t = m.getb(); + if ((t & 1) == 0) + m.fmterr("packt mark 1"); + v |= ((t & ~1) << 15) | (m.getb() << 7); + t = m.getb(); + if ((t & 1) == 0) + m.fmterr("packt mark 2"); + m.packt1 = v | (t >> 1); + m.packmr = m.get22("packmr"); +} + +Mpegi.syshdr(m: self ref Mpegi) +{ + l := m.getw(); + if (l != m.syssz) + m.fmterr("syshdr size mismatch"); + m.skipn(l); +} + +Mpegi.stamps(m: self ref Mpegi): int +{ + # 2.4.3.3 + n := 1; + while ((c := m.getb()) == 16rFF) + n++; + if ((c >> 6) == 1) { + m.getb(); + c = m.getb(); + n += 2; + } + case c >> 4 { + 2 => + m.skipn(4); + n += 4; + 3 => + m.skipn(9); + n += 9; + * => + if (c != 16rF) + m.fmterr("stamps"); + } + return n; +} + +Mpegi.streaminit(m: self ref Mpegi, c: int) +{ + m.inittables(); + m.sid = c; + s := m.peeksc(); + if (s == PACK_SC) { + m.startsys(); + f := 0; + id := byte m.sid; + for (i := 0; i < m.nstream; i++) { + if (m.streams[i].id == id) { + f = 1; + break; + } + } + if (!f) + m.fmterr(sys->sprint("%x: stream not found", c)); + m.sseek(); + } else if (s == SEQUENCE_SC) { + m.sresid = -1; + m.slim = m.size; + } else + m.fmterr(sys->sprint("start code = %x", s)); + m.sbits = 0; +} + +Mpegi.sseek(m: self ref Mpegi) +{ + while ((c := m.nextsc()) != STREAM_EC) { + case c { + PACK_SC => + m.packhdr(); + SYSHD_SC => + m.syshdr(); + * => + if (c < STREAM_BASE) + m.fmterr(sys->sprint("stream code %x", c)); + # 2.4.3.3 + l := m.getw(); + if (c == m.sid) { + if (c != PRIVSTREAM2) + l -= m.stamps(); + n := m.size - m.index; + if (l <= n) { + m.slim = m.index + l; + m.sresid = 0; + } else { + m.slim = m.size; + m.sresid = l - n; + } + return; + } else + m.skipn(l); + } + } + m.fmterr("end of stream"); +} + +Mpegi.getpicture(m: self ref Mpegi, detail: int): ref Picture +{ + g := 0; + for (;;) { + case c := m.snextsc() { + SEQUENCE_SC => + m.seqhdr(); + GROUP_SC => + m.grphdr(); + g = 1; + PICTURE_SC => + p := m.picture(detail); + if (g) + p.flags |= GSTART; + return p; + SEQUENCE_EC => + return nil; + * => + m.fmterr(sys->sprint("start code %x", c)); + } + } +} + +Mpegi.seqhdr(m: self ref Mpegi) +{ + # 2.4.2.3 + c := m.sgetb(); + d := m.sgetb(); + m.width = (c << 4) | (d >> 4); + m.height = ((d & 16rF) << 8) | m.sgetb(); + c = m.sgetb(); + m.aspect = c >> 4; + m.frames = c & 16rF; + m.rate = m.sgetn(18); + m.smarker(); + m.vbv = m.sgetn(10); + m.flags = 0; + if (m.sgetn(1)) + m.flags |= CONSTRAINED; + if (m.sgetn(1)) + m.intra = m.getquant(); + if (m.sgetn(1)) + m.nintra = m.getquant(); + if (m.speeksc() == EXTENSION_SC) + m.sseeksc(); + if (m.speeksc() == USER_SC) + m.sseeksc(); +} + +Mpegi.grphdr(m: self ref Mpegi) +{ + # 2.4.2.4 + v := m.sgetb() << 17; + v |= m.sgetb() << 9; + v |= m.sgetb() << 1; + c := m.sgetb(); + m.smpte = v | (c >> 7); + if (c & (1 << 6)) + m.flags |= CLOSED; + else + m.flags &= ~CLOSED; + if (c & (1 << 5)) + m.flags |= BROKEN; + else + m.flags &= ~BROKEN; + if (m.speeksc() == EXTENSION_SC) + m.sseeksc(); + if (m.speeksc() == USER_SC) + m.sseeksc(); +} + +Mpegi.getquant(m: self ref Mpegi): array of int +{ + a := array[64] of int; + for (i := 0; i < 64; i++) + a[i] = m.sgetn(8); + return a; +} + +Mpegi.picture(m: self ref Mpegi, detail: int): ref Picture +{ + # 2.4.2.5 + p := ref Picture; + p.temporal = m.sgetn(10); + p.ptype = m.sgetn(3); + p.vbvdelay = m.sgetn(16); + p.flags = 0; + if (p.ptype == PPIC || p.ptype == BPIC) { + if (m.sgetn(1)) + p.flags |= FPFV; + p.forwfc = m.sgetn(3); + if (p.forwfc == 0) + m.fmterr("forwfc"); + p.forwfc--; + if (p.ptype == BPIC) { + if (m.sgetn(1)) + p.flags |= FPBV; + p.backfc = m.sgetn(3); + if (p.backfc == 0) + m.fmterr("backfc"); + p.backfc--; + } else + p.backfc = 0; + } else { + p.forwfc = 0; + p.backfc = 0; + } + while (m.sgetn(1)) + m.sgetn(8); + if (m.speeksc() == EXTENSION_SC) + m.sseeksc(); + if (m.speeksc() == USER_SC) + m.sseeksc(); + p.seek = m.tell() - 3; + if (m.sresid < 0) + p.eos = -1; + else + p.eos = m.seek - m.size + m.slim + m.sresid; + if (detail) + m.detail(p); + else + m.skipdetail(); + return p; +} + +Mpegi.detail(m: self ref Mpegi, p: ref Picture) +{ + l: list of ref Slice; + p.addr = -1; + while ((c := m.speeksc()) >= SLICE1_SC && c <= SLICEN_SC) + l = m.slice(p) :: l; + if (l == nil) + m.fmterr("slice sc"); + n := len l; + a := array[n] of ref Slice; + while (--n >= 0) { + a[n] = hd l; + l = tl l; + } + p.slices = a; +} + +Mpegi.skipdetail(m: self ref Mpegi) +{ + while ((c := m.speeksc()) >= SLICE1_SC && c <= SLICEN_SC) { + m.looked = 0; + m.sseeksc(); + } +} + +ESC, EOB, C0, C1, C2, C3, C4, C5, C6, C7: con -(iota + 1); + +include "mai.tab"; +include "mbi.tab"; +include "mbp.tab"; +include "mbb.tab"; +include "motion.tab"; +include "cbp.tab"; +include "cdc.tab"; +include "ydc.tab"; +include "rl0f.tab"; +include "rl0n.tab"; +include "c0.tab"; +include "c1.tab"; +include "c2.tab"; +include "c3.tab"; +include "c4.tab"; +include "c5.tab"; +include "c6.tab"; +include "c7.tab"; + +mbif := array[] of { + MB_I, + MB_I | MB_Q, +}; + +mbpf := array[] of { + MB_MF, + MB_P, + MB_P | MB_Q, + MB_P | MB_MF, + MB_P | MB_MF | MB_Q, + MB_I, + MB_I | MB_Q, +}; + +mbbf := array[] of { + MB_MF, + MB_MB, + MB_MB | MB_MF, + MB_P | MB_MF, + MB_P | MB_MF | MB_Q, + MB_P | MB_MB, + MB_P | MB_MB | MB_Q, + MB_P | MB_MB | MB_MF, + MB_P | MB_MB | MB_MF | MB_Q, + MB_I, + MB_I | MB_Q, +}; + +c_bits := array[] of { + c1_bits, + c2_bits, + c3_bits, + c4_bits, + c5_bits, + c6_bits, + c7_bits, +}; + +c_tables: array of array of Pair; + +patcode := array[] of { + 1<<5, 1<<4, 1<<3, 1<<2, 1<<1, 1<<0, +}; + +Mpegi.inittables() +{ + if (c_tables == nil) { + c_tables = array[] of { + c1_table, + c2_table, + c3_table, + c4_table, + c5_table, + c6_table, + c7_table, + }; + } +} + +Mpegi.slice(m: self ref Mpegi, p: ref Picture): ref Slice +{ + m.snextsc(); + s := ref Slice; + q := m.sgetn(5); + while (m.sgetn(1)) + m.sgetn(8); + x := p.addr; + l: list of ref MacroBlock; + while (m.speekn(23) != 0) { + while (m.speekn(11) == 16rF) + m.sbits -= 11; + while (m.speekn(11) == 16r8) { + x += 33; + m.sbits -= 11; + } + i := m.svlc(mai_table, mai_bits, "mai"); + x += i; + b := ref MacroBlock; + b.addr = x; + case p.ptype { + IPIC => + b.flags = mbif[m.svlc(mbi_table, mbi_bits, "mbi")]; + PPIC => + b.flags = mbpf[m.svlc(mbp_table, mbp_bits, "mbp")]; + BPIC => + b.flags = mbbf[m.svlc(mbb_table, mbb_bits, "mbb")]; + DPIC => + if (!m.sgetn(1)) + m.fmterr("mbd flags"); + b.flags = MB_I; + * => + m.fmterr("ptype"); + } + if (b.flags & MB_Q) + q = m.sgetn(5); + b.qscale = q; + if (b.flags & MB_MF) { + i = m.svlc(motion_table, motion_bits, "mhfc"); + b.mhfc = i; + if (i != 0 && p.forwfc != 0) + b.mhfr = m.sgetn(p.forwfc); + i = m.svlc(motion_table, motion_bits, "mvfc"); + b.mvfc = i; + if (i != 0 && p.forwfc != 0) + b.mvfr = m.sgetn(p.forwfc); + } + if (b.flags & MB_MB) { + i = m.svlc(motion_table, motion_bits, "mhbc"); + b.mhbc = i; + if (i != 0 && p.backfc != 0) + b.mhbr = m.sgetn(p.backfc); + i = m.svlc(motion_table, motion_bits, "mvbc"); + b.mvbc = i; + if (i != 0 && p.backfc != 0) + b.mvbr = m.sgetn(p.backfc); + } + if (b.flags & MB_I) + i = 16r3F; + else if (b.flags & MB_P) + i = m.svlc(cbp_table, cbp_bits, "cbp"); + else + i = 0; + b.pcode = i; + if (i != 0) { + b.rls = array[6] of array of Pair; + for (j := 0; j < 6; j++) { + if (i & patcode[j]) { + rl: list of Pair; + R, L: int; + if (b.flags & MB_I) { + if (j < 4) + L = m.svlc(ydc_table, ydc_bits, "ydc"); + else + L = m.svlc(cdc_table, cdc_bits, "cdc"); + if (L != 0) + L = m.sdiffn(L); + rl = (0, L) :: nil; + } else + rl = m.sdct(rl0f_table, "rl0f") :: nil; + if (p.ptype != DPIC) { + for (;;) { + (R, L) = m.sdct(rl0n_table, "rl0n"); + if (R == EOB) + break; + rl = (R, L) :: rl; + } + } + mn := len rl; + ma := array[mn] of Pair; + while (--mn >= 0) { + ma[mn] = hd rl; + rl = tl rl; + } + b.rls[j] = ma; + } + } + } + l = b :: l; + } + p.addr = x; + if (l == nil) + m.fmterr("macroblock"); + n := len l; + a := array[n] of ref MacroBlock; + while (--n >= 0) { + a[n] = hd l; + l = tl l; + } + s.blocks = a; + return s; +} + +Mpegi.cpn(m: self ref Mpegi, fd: ref Sys->FD, n: int) +{ + for (;;) { + r := m.size - m.index; + if (r >= n) { + if (sys->write(fd, m.buff[m.index:], n) < 0) + raisex(X_WRITE); + m.index += n; + return; + } + if (sys->write(fd, m.buff[m.index:], r) < 0) + raisex(X_WRITE); + m.fill(); + n -= r; + } +} + +Mpegi.fill(m: self ref Mpegi) +{ + n := sys->read(m.fd, m.buff, MBSZ); + if (n < 0) { + m.error = sys->sprint("%r"); + raisex(X_READ); + } + if (n == 0) + raisex(X_EOF); + m.seek += n; + m.index = 0; + m.size = n; +} + +Mpegi.tell(m: self ref Mpegi): int +{ + return m.seek - m.size + m.index; +} + +Mpegi.skipn(m: self ref Mpegi, n: int) +{ + for (;;) { + r := m.size - m.index; + if (r >= n) { + m.index += n; + return; + } + n -= r; + m.fill(); + } +} + +Mpegi.getb(m: self ref Mpegi): int +{ + if (m.index == m.size) + m.fill(); + return int m.buff[m.index++]; +} + +Mpegi.getw(m: self ref Mpegi): int +{ + t := m.getb(); + return (t << 8) | m.getb(); +} + +Mpegi.get22(m: self ref Mpegi, s: string): int +{ + u := m.getb(); + if ((u & 16r80) == 0) + m.fmterr(s + " mark 0"); + v := m.getb(); + w := m.getb(); + if ((w & 1) == 0) + m.fmterr(s + " mark 1"); + return ((u & 16r7F) << 15) | (v << 7) | (w >> 1); +} + +Mpegi.getsc(m: self ref Mpegi): int +{ + if (m.getb() != 0 || m.getb() != 0) + m.fmterr("start code 0s"); + while ((c := m.getb()) == 0) + ; + if (c != 1) + m.fmterr("start code 1"); + return 16r100 | m.getb(); +} + +Mpegi.nextsc(m: self ref Mpegi): int +{ + if (m.looked) { + m.looked = 0; + return m.value; + } else + return m.getsc(); +} + +Mpegi.peeksc(m: self ref Mpegi): int +{ + if (!m.looked) { + m.value = m.getsc(); + m.looked = 1; + } + return m.value; +} + +Mpegi.xnextsc(m: self ref Mpegi, x: int) +{ + c := m.nextsc(); + if (c != x) + m.fmterr(sys->sprint("startcode %x, got %x", x, c)); +} + +Mpegi.sfill(m: self ref Mpegi) +{ + r := m.sresid; + if (r < 0) { + m.fill(); + m.slim = m.size; + } else if (r > 0) { + m.fill(); + if (r <= m.size) { + m.slim = r; + m.sresid = 0; + } else { + m.slim = m.size; + m.sresid = r - m.size; + } + } else + m.sseek(); +} + +bits := array[] of { + 0, + 16r1, 16r3, 16r7, 16rF, + 16r1F, 16r3F, 16r7F, 16rFF, + 16r1FF, 16r3FF, 16r7FF, 16rFFF, + 16r1FFF, 16r3FFF, 16r7FFF, 16rFFFF, + 16r1FFFF, 16r3FFFF, 16r7FFFF, 16rFFFFF, + 16r1FFFFF, 16r3FFFFF, 16r7FFFFF, 16rFFFFFF, + 16r1FFFFFF, 16r3FFFFFF, 16r7FFFFFF, 16rFFFFFFF, + 16r1FFFFFFF, 16r3FFFFFFF, 16r7FFFFFFF, int 16rFFFFFFFF, +}; + +sign := array[] of { + 0, + 16r1, 16r2, 16r4, 16r8, + 16r10, 16r20, 16r40, 16r80, +}; + +Mpegi.sgetn(m: self ref Mpegi, n: int): int +{ + b := m.sbits; + v := m.svalue; + if (b < n) { + do { + v = (v << 8) | m.sgetb(); + b += 8; + } while (b < n); + m.svalue = v; + } + b -= n; + m.sbits = b; + return (v >> b) & bits[n]; +} + +Mpegi.sdiffn(m: self ref Mpegi, n: int): int +{ + i := m.sgetn(n); + if (i & sign[n]) + return i; + else + return i - bits[n]; +} + +Mpegi.speekn(m: self ref Mpegi, n: int): int +{ + b := m.sbits; + v := m.svalue; + if (b < n) { + do { + v = (v << 8) | m.sgetb(); + b += 8; + } while (b < n); + m.sbits = b; + m.svalue = v; + } + return (v >> (b - n)) & bits[n]; +} + +Mpegi.sgetb(m: self ref Mpegi): int +{ + if (m.index == m.slim) + m.sfill(); + return int m.buff[m.index++]; +} + +Mpegi.smarker(m: self ref Mpegi) +{ + if (!m.sgetn(1)) + m.fmterr("marker"); +} + +Mpegi.sgetsc(m: self ref Mpegi): int +{ + b := m.sbits; + if (b >= 8) { + if (b >= 16) { + if (b >= 24) { + case m.svalue & 16rFFFFFF { + 0 => + break; + 1 => + m.sbits = 0; + return 16r100 | m.sgetb(); + * => + m.fmterr("start code 0s - 3"); + } + } else if ((m.svalue & 16rFFFF) != 0) + m.fmterr("start code 0s - 2"); + } else if ((m.svalue & 16rFF) != 0 || m.sgetb() != 0) + m.fmterr("start code 0s - 1"); + } else if (m.sgetb() != 0 || m.sgetb() != 0) + m.fmterr("start code 0s"); + m.sbits = 0; + while ((c := m.sgetb()) == 0) + ; + if (c != 1) + m.fmterr("start code 1"); + return 16r100 | m.sgetb(); +} + +Mpegi.snextsc(m: self ref Mpegi): int +{ + if (m.looked) { + m.looked = 0; + return m.value; + } else + return m.sgetsc(); +} + +Mpegi.speeksc(m: self ref Mpegi): int +{ + if (!m.looked) { + m.value = m.sgetsc(); + m.looked = 1; + } + return m.value; +} + +Mpegi.sseeksc(m: self ref Mpegi) +{ + n := 0; + for (;;) { + case m.sgetb() { + 0 => + n++; + 1 => + if (n >= 2) { + m.value = 16r100 | m.sgetb(); + m.looked = 1; + return; + } + n = 0; + * => + n = 0; + } + } +} + +Mpegi.svlc(m: self ref Mpegi, a: array of Pair, n: int, s: string): int +{ + (b, v) := a[m.speekn(n)]; + if (v == UNDEF) + m.fmterr(s + " vlc"); + m.sbits -= b; + return v; +} + +Mpegi.sdct(m: self ref Mpegi, a: array of Triple, s: string): Pair +{ + (b, l, r) := a[m.speekn(rl0f_bits)]; + m.sbits -= b; + if (r < 0) { + case r { + EOB => + break; + ESC => + r = m.sgetn(6); + l = m.sgetn(8); + if (l == 0) { + l = m.sgetn(8); + if (l < 128) + m.fmterr(s + " esc +7"); + } else if (l == 128) { + l = m.sgetn(8) - 256; + if (l > -128) + m.fmterr(s + " esc -7"); + } else + l = (l << 24) >> 24; + C0 => + (b, l, r) = c0_table[m.speekn(c0_bits)]; + if (r == UNDEF) + m.fmterr(s + " c0 vlc"); + m.sbits -= b; + * => + r = C1 - r; + (l, r) = c_tables[r][m.sgetn(c_bits[r])]; + } + } + return (r, l); +} + +Mpegi.fmterr(m: self ref Mpegi, s: string) +{ + m.error = s; + raisex(X_FORMAT); +} diff --git a/appl/wm/mpeg/mpegio.m b/appl/wm/mpeg/mpegio.m new file mode 100644 index 00000000..378db8aa --- /dev/null +++ b/appl/wm/mpeg/mpegio.m @@ -0,0 +1,218 @@ +# +# MPEG ISO 11172 IO module. +# +Mpegio: module +{ + PATH: con "/dis/mpeg/mpegio.dis"; + + MBSZ: con Sys->ATOMICIO; + + PICTURE_SC: con 16r100; + SLICE1_SC: con 16r101; + SLICEN_SC: con 16r1AF; + USER_SC: con 16r1B2; + SEQUENCE_SC: con 16r1B3; + EXTENSION_SC: con 16r1B5; + SEQUENCE_EC: con 16r1B7; + GROUP_SC: con 16r1B8; + STREAM_EC: con 16r1B9; + PACK_SC: con 16r1BA; + SYSHD_SC: con 16r1BB; + STREAM_BASE: con 16r1BC; + PRIVSTREAM2: con 16r1BF; + AUDIO_STR0: con 16r1C0; + VIDEO_STR0: con 16r1E0; + + MEXCEPT: con "mpeg: "; + X_FORMAT: con "fmt error"; + X_READ: con "read error"; + X_WRITE: con "write error"; + X_EOF: con "premature eof"; + + UNDEF: con 100; + + CONSTRAINED, CLOSED, BROKEN: con 1 << iota; + FPFV, FPBV, GSTART: con 1 << iota; + + IPIC: con 1; + PPIC: con 2; + BPIC: con 3; + DPIC: con 4; + + ptypes: con "0IPBD"; + + MB_Q, MB_MF, MB_MB, MB_P, MB_I: con 1 << iota; + + Stream: adt + { + id: byte; + scale: byte; + bound: int; + fd: ref Sys->FD; + }; + + Picture: adt + { + seek: int; + eos: int; + temporal: int; + ptype: int; + vbvdelay: int; + flags: int; + forwfc: int; + backfc: int; + slices: array of ref Slice; + addr: int; + }; + + Slice: adt + { + blocks: array of ref MacroBlock; + }; + + MacroBlock: adt + { + flags: int; + qscale: int; + mhfc, mhfr, mvfc, mvfr: int; + mhbc, mhbr, mvbc, mvbr: int; + pcode: int; + rls: array of array of Pair; + addr: int; + }; + + YCbCr: adt + { + Y, Cb, Cr: array of byte; + }; + + Pair: type (int, int); + Triple: type (int, int, int); + + Mpegi: adt + { + fd: ref Sys->FD; + name: string; + error: string; + looked: int; + value: int; + # info + width: int; + height: int; + aspect: int; + frames: int; + rate: int; + vbv: int; + flags: int; + intra: array of int; + nintra: array of int; + smpte: int; + # real buffer + seek: int; + index: int; + size: int; + buff: array of byte; + # stream buffer + sid: int; # stream id + slim: int; # stream limit <= size + sresid: int; # stream residual (-1 entire file) + sbits: int; # bits remaining + svalue: int; # current value + + packt0: int; + packt1: int; + packmr: int; + syssz: int; + boundmr: int; + syspar: int; + nstream: int; + streams: array of Stream; + log: ref Sys->FD; + + startsys: fn(m: self ref Mpegi); + packhdr: fn(m: self ref Mpegi); + syshdr: fn(m: self ref Mpegi); + packetcp: fn(m: self ref Mpegi): int; + getfd: fn(m: self ref Mpegi, c: int): ref Sys->FD; + stamps: fn(m: self ref Mpegi): int; + + streaminit: fn(m: self ref Mpegi, c: int); + inittables: fn(); + sseek: fn(m: self ref Mpegi); + seqhdr: fn(m: self ref Mpegi); + grphdr: fn(m: self ref Mpegi); + getquant: fn(m: self ref Mpegi): array of int; + getpicture: fn(m: self ref Mpegi, detail: int): ref Picture; + picture: fn(m: self ref Mpegi, detail: int): ref Picture; + detail: fn(m: self ref Mpegi, p: ref Picture); + skipdetail: fn(m: self ref Mpegi); + slice: fn(m: self ref Mpegi, p: ref Picture): ref Slice; + + cpn: fn(m: self ref Mpegi, fd: ref Sys->FD, n: int); + fill: fn(m: self ref Mpegi); + tell: fn(m: self ref Mpegi): int; + skipn: fn(m: self ref Mpegi, n: int); + getb: fn(m: self ref Mpegi): int; + getw: fn(m: self ref Mpegi): int; + get22: fn(m: self ref Mpegi, s: string): int; + getsc: fn(m: self ref Mpegi): int; + nextsc: fn(m: self ref Mpegi): int; + peeksc: fn(m: self ref Mpegi): int; + xnextsc: fn(m: self ref Mpegi, code: int); + + sfill: fn(m: self ref Mpegi); + sgetb: fn(m: self ref Mpegi): int; + sgetn: fn(m: self ref Mpegi, n: int): int; + sdiffn: fn(m: self ref Mpegi, n: int): int; + sdct: fn(m: self ref Mpegi, a: array of Triple, s: string): Pair; + speekn: fn(m: self ref Mpegi, n: int): int; + smarker: fn(m: self ref Mpegi); + sgetsc: fn(m: self ref Mpegi): int; + snextsc: fn(m: self ref Mpegi): int; + speeksc: fn(m: self ref Mpegi): int; + sseeksc: fn(m: self ref Mpegi); + svlc: fn(m: self ref Mpegi, a: array of Pair, n: int, s: string): int; + + fmterr: fn(m: self ref Mpegi, s: string); + }; + + init: fn(); + prepare: fn(fd: ref Sys->FD, name: string): ref Mpegi; + raisex: fn(s: string); +}; + +Mpegd: module +{ + PATH: con "/dis/mpeg/decode.dis"; + PATH4: con "/dis/mpeg/decode4.dis"; + + init: fn(m: ref Mpegio->Mpegi); + Idecode: fn(p: ref Mpegio->Picture): ref Mpegio->YCbCr; + Pdecode: fn(p: ref Mpegio->Picture): ref Mpegio->YCbCr; + Bdecode: fn(p: ref Mpegio->Picture): ref Mpegio->YCbCr; + Bdecode2: fn(p: ref Mpegio->Picture, f0, f1: ref Mpegio->YCbCr): ref Mpegio->YCbCr; +}; + +IDCT: module +{ + FPATH: con "/dis/mpeg/fltidct.dis"; # based on rob's jpeg + RPATH: con "/dis/mpeg/refidct.dis"; # reference (full idct) + SPATH: con "/dis/mpeg/scidct.dis"; # scaled integer implementation + XPATH: con "/dis/mpeg/fixidct.dis"; # nasty fixed point + PATH: con SPATH; + + init: fn(); + idct: fn(block: array of int); +}; + +Remap: module +{ + PATH: con "/dis/mpeg/remap.dis"; + PATH1: con "/dis/mpeg/remap1.dis"; + PATH2: con "/dis/mpeg/remap2.dis"; + PATH4: con "/dis/mpeg/remap4.dis"; + PATH24: con "/dis/mpeg/remap24.dis"; + + init: fn(m: ref Mpegio->Mpegi); + remap: fn(p: ref Mpegio->YCbCr): array of byte; +}; diff --git a/appl/wm/mpeg/refidct.b b/appl/wm/mpeg/refidct.b new file mode 100644 index 00000000..e02ab1f7 --- /dev/null +++ b/appl/wm/mpeg/refidct.b @@ -0,0 +1,58 @@ +implement IDCT; + +include "sys.m"; +include "math.m"; +include "mpegio.m"; + +sys: Sys; +math: Math; + +# +# Reference IDCT. Full expanded 2-d IDCT. +# + +coeff: array of array of real; + +init() +{ + sys = load Sys Sys->PATH; + math = load Math Math->PATH; + if (math == nil) { + sys->fprint(sys->fildes(2), "could not load %s: %r\n", Math->PATH); + exit; + } + init_idct(); +} + +init_idct() +{ + coeff = array[8] of array of real; + for (f := 0; f < 8; f++) { + coeff[f] = array[8] of real; + s := 0.5; + if (f == 0) + s = math->sqrt(0.125); + a := real f * (Math->Pi / 8.0); + for (t := 0; t < 8; t++) + coeff[f][t] = s * math->cos(a * (real t + 0.5)); + } +} + +idct(block: array of int) +{ + tmp := array[64] of real; + for (i := 0; i < 8; i++) + for (j := 0; j < 8; j++) { + p := 0.0; + for (k := 0; k < 8; k++) + p += coeff[k][j] * real block[8 * i + k]; + tmp[8 * i + j] = p; + } + for (j = 0; j < 8; j++) + for (i = 0; i < 8; i++) { + p := 0.0; + for (k := 0; k < 8; k++) + p += coeff[k][i] * tmp[8 * k + j]; + block[8 * i + j] = int p; + } +} diff --git a/appl/wm/mpeg/remap.b b/appl/wm/mpeg/remap.b new file mode 100644 index 00000000..4432048c --- /dev/null +++ b/appl/wm/mpeg/remap.b @@ -0,0 +1,128 @@ +implement Remap; + +include "sys.m"; +include "mpegio.m"; + +Mpegi, YCbCr: import Mpegio; + +CLOFF: con 255; + +width, height, w2, h2: int; +out: array of byte; +ered, egrn, eblu: array of int; +b0r1, b1, r0: array of int; +clamp := array[CLOFF + 256 + CLOFF] of int; +clamp16 := array[CLOFF + 256 + CLOFF] of int; + +init(m: ref Mpegi) +{ + width = m.width; + height = m.height; + w2 = width >> 1; + h2 = height >> 1; + out = array[width * height] of byte; + b0r1 = array[w2] of int; + b1 = array[w2] of int; + r0 = array[w2] of int; + ered = array[width + 1] of int; + egrn = array[width + 1] of int; + eblu = array[width + 1] of int; + for (i := 0; i < CLOFF; i++) { + clamp[i] = 0; + clamp16[i] = 0; + } + for (i = 0; i < 256; i++) { + clamp[i + CLOFF] = i; + clamp16[i + CLOFF] = i >> 4; + } + for (i = CLOFF + 256; i < CLOFF + 256 + CLOFF; i++) { + clamp[i] = 255; + clamp16[i] = 255 >> 4; + } +} + +include "closest.m"; +include "rgbvmap.m"; + +# rgb(y, cb, cr: int): (int, int, int) +# { +# Y := real y; +# Cb := real (cb - 128); +# Cr := real (cr - 128); +# r := int (Y+1.402*Cr); +# g := int (Y-0.34414*Cb-0.71414*Cr); +# b := int (Y+1.772*Cb); +# return (r, g, b); +# } + +B: con 16; +M: con (1 << B); +B0: con int (-0.34414 * real M); +B1: con int (1.772 * real M); +R0: con int (1.402 * real M); +R1: con int (-0.71414 * real M); + +remap(p: ref Mpegio->YCbCr): array of byte +{ + Y := p.Y; + Cb := p.Cb; + Cr := p.Cr; + for (e := 0; e <= width; e++) + ered[e] = 0; + egrn[0:] = ered[0:]; + eblu[0:] = ered[0:]; + m := 0; + n := 0; + for (i := 0; i < h2; i++) { + for (j := 0; j < w2; j++) { + cb := int Cb[m] - 128; + cr := int Cr[m] - 128; + b0r1[j] = B0 * cb + R1 * cr; + b1[j] = B1 * cb; + r0[j] = R0 * cr; + m++; + } + j = 2; + do { + ex := 0; + er := 0; + eg := 0; + eb := 0; + for (k := 0; k < w2; k++) { + l := 2; + do { + y := int Y[n] << B; + r := clamp[((y + r0[k]) >> B) + CLOFF] + ered[ex]; + g := clamp[((y + b0r1[k]) >> B) + CLOFF] + egrn[ex]; + b := clamp[((y + b1[k]) >> B) + CLOFF] + eblu[ex]; + rc := clamp16[r + CLOFF]; + gc := clamp16[g + CLOFF]; + bc := clamp16[b + CLOFF]; + col := int closest[bc + 16 * (gc + 16 * rc)]; + out[n++] = byte col; + + col *= 3; + r -= int rgbvmap[col + 0]; + t := (3 * r) >> 4; + ered[ex] = t + er; + ered[ex + 1] += t; + er = r - 3 * t; + + g -= int rgbvmap[col + 1]; + t = (3 * g) >> 4; + egrn[ex] = t + eg; + egrn[ex + 1] += t; + eg = g - 3 * t; + + b -= int rgbvmap[col + 2]; + t = (3 * b) >> 4; + eblu[ex] = t + eb; + eblu[ex + 1] += t; + eb = b - 3 * t; + ex++; + } while (--l > 0); + } + } while (--j > 0); + } + return out; +} diff --git a/appl/wm/mpeg/remap1.b b/appl/wm/mpeg/remap1.b new file mode 100644 index 00000000..09ad4646 --- /dev/null +++ b/appl/wm/mpeg/remap1.b @@ -0,0 +1,116 @@ +implement Remap; + +include "sys.m"; +include "mpegio.m"; + +Mpegi, YCbCr: import Mpegio; + +CLOFF: con 511; + +width, height, w8: int; +out: array of byte; +elum: array of int; +clamp2 := array[CLOFF + 256 + CLOFF] of int; + +init(m: ref Mpegi) +{ + width = m.width; + height = m.height; + w8 = width >> 3; + out = array[w8 * height] of byte; + elum = array[width + 1] of int; + for (i := 0; i < CLOFF; i++) + clamp2[i] = 0; + for (i = 0; i < 256; i++) + clamp2[i + CLOFF] = i >> 7; + for (i = CLOFF + 256; i < CLOFF + 256 + CLOFF; i++) + clamp2[i] = 255 >> 7; +} + +remap(p: ref Mpegio->YCbCr): array of byte +{ + Y := p.Y; + for (e := 0; e <= width; e++) + elum[e] = 0; + m := 0; + n := 0; + for (i := 0; i < height; i++) { + el := 0; + ex := 0; + for (k := 0; k < w8; k++) { + y := (256 - int Y[n++]) + elum[ex]; + l := clamp2[y + CLOFF] << 7; + b := l; + y -= l; + t := (3 * y) >> 4; + elum[ex] = t + el; + elum[ex + 1] += t; + el = y - 3 * t; + ex++; + y = (256 - int Y[n++]) + elum[ex]; + l = clamp2[y + CLOFF]; + b |= l << 6; + y -= l << 7; + t = (3 * y) >> 4; + elum[ex] = t + el; + elum[ex + 1] += t; + el = y - 3 * t; + ex++; + y = (256 - int Y[n++]) + elum[ex]; + l = clamp2[y + CLOFF]; + b |= l << 5; + y -= l << 7; + t = (3 * y) >> 4; + elum[ex] = t + el; + elum[ex + 1] += t; + el = y - 3 * t; + ex++; + y = (256 - int Y[n++]) + elum[ex]; + l = clamp2[y + CLOFF]; + b |= l << 4; + y -= l << 7; + t = (3 * y) >> 4; + elum[ex] = t + el; + elum[ex + 1] += t; + el = y - 3 * t; + ex++; + y = (256 - int Y[n++]) + elum[ex]; + l = clamp2[y + CLOFF]; + b |= l << 3; + y -= l << 7; + t = (3 * y) >> 4; + elum[ex] = t + el; + elum[ex + 1] += t; + el = y - 3 * t; + ex++; + y = (256 - int Y[n++]) + elum[ex]; + l = clamp2[y + CLOFF]; + b |= l << 2; + y -= l << 7; + t = (3 * y) >> 4; + elum[ex] = t + el; + elum[ex + 1] += t; + el = y - 3 * t; + ex++; + y = (256 - int Y[n++]) + elum[ex]; + l = clamp2[y + CLOFF]; + b |= l << 1; + y -= l << 7; + t = (3 * y) >> 4; + elum[ex] = t + el; + elum[ex + 1] += t; + el = y - 3 * t; + ex++; + y = (256 - int Y[n++]) + elum[ex]; + l = clamp2[y + CLOFF]; + out[m++] = byte (b | l); + y -= l << 7; + t = (3 * y) >> 4; + elum[ex] = t + el; + elum[ex + 1] += t; + el = y - 3 * t; + ex++; + } + } + return out; +} diff --git a/appl/wm/mpeg/remap2.b b/appl/wm/mpeg/remap2.b new file mode 100644 index 00000000..5bae164f --- /dev/null +++ b/appl/wm/mpeg/remap2.b @@ -0,0 +1,80 @@ +implement Remap; + +include "sys.m"; +include "mpegio.m"; + +Mpegi, YCbCr: import Mpegio; + +CLOFF: con 255; + +width, height, w4: int; +out: array of byte; +elum: array of int; +clamp4 := array[CLOFF + 256 + CLOFF] of int; + +init(m: ref Mpegi) +{ + width = m.width; + height = m.height; + w4 = width >> 2; + out = array[w4 * height] of byte; + elum = array[width + 1] of int; + for (i := 0; i < CLOFF; i++) + clamp4[i] = 0; + for (i = 0; i < 256; i++) + clamp4[i + CLOFF] = i >> 6; + for (i = CLOFF + 256; i < CLOFF + 256 + CLOFF; i++) + clamp4[i] = 255 >> 6; +} + +remap(p: ref Mpegio->YCbCr): array of byte +{ + Y := p.Y; + for (e := 0; e <= width; e++) + elum[e] = 0; + m := 0; + n := 0; + for (i := 0; i < height; i++) { + el := 0; + ex := 0; + for (k := 0; k < w4; k++) { + y := (256 - int Y[n++]) + elum[ex]; + l := clamp4[y + CLOFF] << 6; + b := l; + y -= l; + t := (3 * y) >> 4; + elum[ex] = t + el; + elum[ex + 1] += t; + el = y - 3 * t; + ex++; + y = (256 - int Y[n++]) + elum[ex]; + l = clamp4[y + CLOFF]; + b |= l << 4; + y -= l << 6; + t = (3 * y) >> 4; + elum[ex] = t + el; + elum[ex + 1] += t; + el = y - 3 * t; + ex++; + y = (256 - int Y[n++]) + elum[ex]; + l = clamp4[y + CLOFF]; + b |= l << 2; + y -= l << 6; + t = (3 * y) >> 4; + elum[ex] = t + el; + elum[ex + 1] += t; + el = y - 3 * t; + ex++; + y = (256 - int Y[n++]) + elum[ex]; + l = clamp4[y + CLOFF]; + out[m++] = byte (b | l); + y -= l << 6; + t = (3 * y) >> 4; + elum[ex] = t + el; + elum[ex + 1] += t; + el = y - 3 * t; + ex++; + } + } + return out; +} diff --git a/appl/wm/mpeg/remap24.b b/appl/wm/mpeg/remap24.b new file mode 100644 index 00000000..23e80815 --- /dev/null +++ b/appl/wm/mpeg/remap24.b @@ -0,0 +1,82 @@ +implement Remap; + +include "sys.m"; +include "mpegio.m"; + +Mpegi, YCbCr: import Mpegio; + +CLOFF: con 255; + +width, height, w2, h2: int; +out: array of byte; +b0r1, b1, r0: array of int; +clamp := array[CLOFF + 256 + CLOFF] of byte; + +init(m: ref Mpegi) +{ + width = m.width; + height = m.height; + w2 = width >> 1; + h2 = height >> 1; + out = array[3 * width * height] of byte; + b0r1 = array[w2] of int; + b1 = array[w2] of int; + r0 = array[w2] of int; + for (i := 0; i < CLOFF; i++) + clamp[i] = byte 0; + for (i = 0; i < 256; i++) + clamp[i + CLOFF] = byte i; + for (i = CLOFF + 256; i < CLOFF + 256 + CLOFF; i++) + clamp[i] = byte 255; +} + +# rgb(y, cb, cr: int): (int, int, int) +# { +# Y := real y; +# Cb := real (cb - 128); +# Cr := real (cr - 128); +# r := int (Y+1.402*Cr); +# g := int (Y-0.34414*Cb-0.71414*Cr); +# b := int (Y+1.772*Cb); +# return (r, g, b); +# } + +B: con 16; +M: con (1 << B); +B0: con int (-0.34414 * real M); +B1: con int (1.772 * real M); +R0: con int (1.402 * real M); +R1: con int (-0.71414 * real M); + +remap(p: ref Mpegio->YCbCr): array of byte +{ + Y := p.Y; + Cb := p.Cb; + Cr := p.Cr; + m := 0; + n := 0; + x := 0; + for (i := 0; i < h2; i++) { + for (j := 0; j < w2; j++) { + cb := int Cb[m] - 128; + cr := int Cr[m] - 128; + b0r1[j] = B0 * cb + R1 * cr; + b1[j] = B1 * cb; + r0[j] = R0 * cr; + m++; + } + j = 2; + do { + for (k := 0; k < w2; k++) { + l := 2; + do { + y := int Y[n++] << B; + out[x++] = clamp[((y + r0[k]) >> B) + CLOFF]; + out[x++] = clamp[((y + b0r1[k]) >> B) + CLOFF]; + out[x++] = clamp[((y + b1[k]) >> B) + CLOFF]; + } while (--l > 0); + } + } while (--j > 0); + } + return out; +} diff --git a/appl/wm/mpeg/remap4.b b/appl/wm/mpeg/remap4.b new file mode 100644 index 00000000..20566fc2 --- /dev/null +++ b/appl/wm/mpeg/remap4.b @@ -0,0 +1,62 @@ +implement Remap; + +include "sys.m"; +include "mpegio.m"; + +Mpegi, YCbCr: import Mpegio; + +CLOFF: con 255; + +width, height, w2: int; +out: array of byte; +elum: array of int; +clamp16 := array[CLOFF + 256 + CLOFF] of int; + +init(m: ref Mpegi) +{ + width = m.width; + height = m.height; + w2 = width >> 1; + out = array[w2 * height] of byte; + elum = array[width + 1] of int; + for (i := 0; i < CLOFF; i++) + clamp16[i] = 0; + for (i = 0; i < 256; i++) + clamp16[i + CLOFF] = i >> 4; + for (i = CLOFF + 256; i < CLOFF + 256 + CLOFF; i++) + clamp16[i] = 255 >> 4; +} + +remap(p: ref Mpegio->YCbCr): array of byte +{ + Y := p.Y; + for (e := 0; e <= width; e++) + elum[e] = 0; + m := 0; + n := 0; + for (i := 0; i < height; i++) { + el := 0; + ex := 0; + for (k := 0; k < w2; k++) { + y := (256 - int Y[n++]) + elum[ex]; + l := clamp16[y + CLOFF] << 4; + b := l; + y -= l; + t := (3 * y) >> 4; + elum[ex] = t + el; + elum[ex + 1] += t; + el = y - 3 * t; + ex++; + y = (256 - int Y[n++]) + elum[ex]; + l = clamp16[y + CLOFF]; + out[m++] = byte (b | l); + y -= l << 4; + t = (3 * y) >> 4; + elum[ex] = t + el; + elum[ex + 1] += t; + el = y - 3 * t; + ex++; + } + } + return out; +} diff --git a/appl/wm/mpeg/remap8.b b/appl/wm/mpeg/remap8.b new file mode 100644 index 00000000..957b72aa --- /dev/null +++ b/appl/wm/mpeg/remap8.b @@ -0,0 +1,84 @@ +implement Remap; + +include "sys.m"; +include "mpegio.m"; + +Mpegi, YCbCr: import Mpegio; + +CLOFF: con 255; + +width, height, w2, h2: int; +out: array of byte; +b0r1, b1, r0: array of int; +clamp16 := array[CLOFF + 256 + CLOFF] of int; + +init(m: ref Mpegi) +{ + width = m.width; + height = m.height; + w2 = width >> 1; + h2 = height >> 1; + out = array[width * height] of byte; + b0r1 = array[w2] of int; + b1 = array[w2] of int; + r0 = array[w2] of int; + for (i := 0; i < CLOFF; i++) + clamp16[i] = 0; + for (i = 0; i < 256; i++) + clamp16[i + CLOFF] = i >> 4; + for (i = CLOFF + 256; i < CLOFF + 256 + CLOFF; i++) + clamp16[i] = 255 >> 4; +} + +include "closest.m"; + +# rgb(y, cb, cr: int): (int, int, int) +# { +# Y := real y; +# Cb := real (cb - 128); +# Cr := real (cr - 128); +# r := int (Y+1.402*Cr); +# g := int (Y-0.34414*Cb-0.71414*Cr); +# b := int (Y+1.772*Cb); +# return (r, g, b); +# } + +B: con 16; +M: con (1 << B); +B0: con int (-0.34414 * real M); +B1: con int (1.772 * real M); +R0: con int (1.402 * real M); +R1: con int (-0.71414 * real M); + +remap(p: ref Mpegio->YCbCr): array of byte +{ + Y := p.Y; + Cb := p.Cb; + Cr := p.Cr; + m := 0; + n := 0; + for (i := 0; i < h2; i++) { + for (j := 0; j < w2; j++) { + cb := int Cb[m] - 128; + cr := int Cr[m] - 128; + b0r1[j] = B0 * cb + R1 * cr; + b1[j] = B1 * cb; + r0[j] = R0 * cr; + m++; + } + j = 2; + do { + for (k := 0; k < w2; k++) { + l := 2; + do { + y := int Y[n] << B; + rc := clamp16[((y + r0[k]) >> B) + CLOFF]; + gc := clamp16[((y + b0r1[k]) >> B) + CLOFF]; + bc := clamp16[((y + b1[k]) >> B) + CLOFF]; + out[n++] = closest[bc + 16 * (gc + 16 * rc)]; + } while (--l > 0); + } + } while (--j > 0); + } + return out; +} diff --git a/appl/wm/mpeg/rgbvmap.m b/appl/wm/mpeg/rgbvmap.m new file mode 100644 index 00000000..d53b1b04 --- /dev/null +++ b/appl/wm/mpeg/rgbvmap.m @@ -0,0 +1,258 @@ +rgbvmap := array[3*256] of { + byte 255,byte 255,byte 255, + byte 255,byte 255,byte 170, + byte 255,byte 255,byte 85, + byte 255,byte 255,byte 0, + byte 255,byte 170,byte 255, + byte 255,byte 170,byte 170, + byte 255,byte 170,byte 85, + byte 255,byte 170,byte 0, + byte 255,byte 85,byte 255, + byte 255,byte 85,byte 170, + byte 255,byte 85,byte 85, + byte 255,byte 85,byte 0, + byte 255,byte 0,byte 255, + byte 255,byte 0,byte 170, + byte 255,byte 0,byte 85, + byte 255,byte 0,byte 0, + byte 238,byte 0,byte 0, + byte 238,byte 238,byte 238, + byte 238,byte 238,byte 158, + byte 238,byte 238,byte 79, + byte 238,byte 238,byte 0, + byte 238,byte 158,byte 238, + byte 238,byte 158,byte 158, + byte 238,byte 158,byte 79, + byte 238,byte 158,byte 0, + byte 238,byte 79,byte 238, + byte 238,byte 79,byte 158, + byte 238,byte 79,byte 79, + byte 238,byte 79,byte 0, + byte 238,byte 0,byte 238, + byte 238,byte 0,byte 158, + byte 238,byte 0,byte 79, + byte 221,byte 0,byte 73, + byte 221,byte 0,byte 0, + byte 221,byte 221,byte 221, + byte 221,byte 221,byte 147, + byte 221,byte 221,byte 73, + byte 221,byte 221,byte 0, + byte 221,byte 147,byte 221, + byte 221,byte 147,byte 147, + byte 221,byte 147,byte 73, + byte 221,byte 147,byte 0, + byte 221,byte 73,byte 221, + byte 221,byte 73,byte 147, + byte 221,byte 73,byte 73, + byte 221,byte 73,byte 0, + byte 221,byte 0,byte 221, + byte 221,byte 0,byte 147, + byte 204,byte 0,byte 136, + byte 204,byte 0,byte 68, + byte 204,byte 0,byte 0, + byte 204,byte 204,byte 204, + byte 204,byte 204,byte 136, + byte 204,byte 204,byte 68, + byte 204,byte 204,byte 0, + byte 204,byte 136,byte 204, + byte 204,byte 136,byte 136, + byte 204,byte 136,byte 68, + byte 204,byte 136,byte 0, + byte 204,byte 68,byte 204, + byte 204,byte 68,byte 136, + byte 204,byte 68,byte 68, + byte 204,byte 68,byte 0, + byte 204,byte 0,byte 204, + byte 170,byte 255,byte 170, + byte 170,byte 255,byte 85, + byte 170,byte 255,byte 0, + byte 170,byte 170,byte 255, + byte 187,byte 187,byte 187, + byte 187,byte 187,byte 93, + byte 187,byte 187,byte 0, + byte 170,byte 85,byte 255, + byte 187,byte 93,byte 187, + byte 187,byte 93,byte 93, + byte 187,byte 93,byte 0, + byte 170,byte 0,byte 255, + byte 187,byte 0,byte 187, + byte 187,byte 0,byte 93, + byte 187,byte 0,byte 0, + byte 170,byte 255,byte 255, + byte 158,byte 238,byte 238, + byte 158,byte 238,byte 158, + byte 158,byte 238,byte 79, + byte 158,byte 238,byte 0, + byte 158,byte 158,byte 238, + byte 170,byte 170,byte 170, + byte 170,byte 170,byte 85, + byte 170,byte 170,byte 0, + byte 158,byte 79,byte 238, + byte 170,byte 85,byte 170, + byte 170,byte 85,byte 85, + byte 170,byte 85,byte 0, + byte 158,byte 0,byte 238, + byte 170,byte 0,byte 170, + byte 170,byte 0,byte 85, + byte 170,byte 0,byte 0, + byte 153,byte 0,byte 0, + byte 147,byte 221,byte 221, + byte 147,byte 221,byte 147, + byte 147,byte 221,byte 73, + byte 147,byte 221,byte 0, + byte 147,byte 147,byte 221, + byte 153,byte 153,byte 153, + byte 153,byte 153,byte 76, + byte 153,byte 153,byte 0, + byte 147,byte 73,byte 221, + byte 153,byte 76,byte 153, + byte 153,byte 76,byte 76, + byte 153,byte 76,byte 0, + byte 147,byte 0,byte 221, + byte 153,byte 0,byte 153, + byte 153,byte 0,byte 76, + byte 136,byte 0,byte 68, + byte 136,byte 0,byte 0, + byte 136,byte 204,byte 204, + byte 136,byte 204,byte 136, + byte 136,byte 204,byte 68, + byte 136,byte 204,byte 0, + byte 136,byte 136,byte 204, + byte 136,byte 136,byte 136, + byte 136,byte 136,byte 68, + byte 136,byte 136,byte 0, + byte 136,byte 68,byte 204, + byte 136,byte 68,byte 136, + byte 136,byte 68,byte 68, + byte 136,byte 68,byte 0, + byte 136,byte 0,byte 204, + byte 136,byte 0,byte 136, + byte 85,byte 255,byte 85, + byte 85,byte 255,byte 0, + byte 85,byte 170,byte 255, + byte 93,byte 187,byte 187, + byte 93,byte 187,byte 93, + byte 93,byte 187,byte 0, + byte 85,byte 85,byte 255, + byte 93,byte 93,byte 187, + byte 119,byte 119,byte 119, + byte 119,byte 119,byte 0, + byte 85,byte 0,byte 255, + byte 93,byte 0,byte 187, + byte 119,byte 0,byte 119, + byte 119,byte 0,byte 0, + byte 85,byte 255,byte 255, + byte 85,byte 255,byte 170, + byte 79,byte 238,byte 158, + byte 79,byte 238,byte 79, + byte 79,byte 238,byte 0, + byte 79,byte 158,byte 238, + byte 85,byte 170,byte 170, + byte 85,byte 170,byte 85, + byte 85,byte 170,byte 0, + byte 79,byte 79,byte 238, + byte 85,byte 85,byte 170, + byte 102,byte 102,byte 102, + byte 102,byte 102,byte 0, + byte 79,byte 0,byte 238, + byte 85,byte 0,byte 170, + byte 102,byte 0,byte 102, + byte 102,byte 0,byte 0, + byte 79,byte 238,byte 238, + byte 73,byte 221,byte 221, + byte 73,byte 221,byte 147, + byte 73,byte 221,byte 73, + byte 73,byte 221,byte 0, + byte 73,byte 147,byte 221, + byte 76,byte 153,byte 153, + byte 76,byte 153,byte 76, + byte 76,byte 153,byte 0, + byte 73,byte 73,byte 221, + byte 76,byte 76,byte 153, + byte 85,byte 85,byte 85, + byte 85,byte 85,byte 0, + byte 73,byte 0,byte 221, + byte 76,byte 0,byte 153, + byte 85,byte 0,byte 85, + byte 85,byte 0,byte 0, + byte 68,byte 0,byte 0, + byte 68,byte 204,byte 204, + byte 68,byte 204,byte 136, + byte 68,byte 204,byte 68, + byte 68,byte 204,byte 0, + byte 68,byte 136,byte 204, + byte 68,byte 136,byte 136, + byte 68,byte 136,byte 68, + byte 68,byte 136,byte 0, + byte 68,byte 68,byte 204, + byte 68,byte 68,byte 136, + byte 68,byte 68,byte 68, + byte 68,byte 68,byte 0, + byte 68,byte 0,byte 204, + byte 68,byte 0,byte 136, + byte 68,byte 0,byte 68, + byte 0,byte 255,byte 0, + byte 0,byte 170,byte 255, + byte 0,byte 187,byte 187, + byte 0,byte 187,byte 93, + byte 0,byte 187,byte 0, + byte 0,byte 85,byte 255, + byte 0,byte 93,byte 187, + byte 0,byte 119,byte 119, + byte 0,byte 119,byte 0, + byte 0,byte 0,byte 255, + byte 0,byte 0,byte 187, + byte 0,byte 0,byte 119, + byte 51,byte 51,byte 51, + byte 0,byte 255,byte 255, + byte 0,byte 255,byte 170, + byte 0,byte 255,byte 85, + byte 0,byte 238,byte 79, + byte 0,byte 238,byte 0, + byte 0,byte 158,byte 238, + byte 0,byte 170,byte 170, + byte 0,byte 170,byte 85, + byte 0,byte 170,byte 0, + byte 0,byte 79,byte 238, + byte 0,byte 85,byte 170, + byte 0,byte 102,byte 102, + byte 0,byte 102,byte 0, + byte 0,byte 0,byte 238, + byte 0,byte 0,byte 170, + byte 0,byte 0,byte 102, + byte 34,byte 34,byte 34, + byte 0,byte 238,byte 238, + byte 0,byte 238,byte 158, + byte 0,byte 221,byte 147, + byte 0,byte 221,byte 73, + byte 0,byte 221,byte 0, + byte 0,byte 147,byte 221, + byte 0,byte 153,byte 153, + byte 0,byte 153,byte 76, + byte 0,byte 153,byte 0, + byte 0,byte 73,byte 221, + byte 0,byte 76,byte 153, + byte 0,byte 85,byte 85, + byte 0,byte 85,byte 0, + byte 0,byte 0,byte 221, + byte 0,byte 0,byte 153, + byte 0,byte 0,byte 85, + byte 17,byte 17,byte 17, + byte 0,byte 221,byte 221, + byte 0,byte 204,byte 204, + byte 0,byte 204,byte 136, + byte 0,byte 204,byte 68, + byte 0,byte 204,byte 0, + byte 0,byte 136,byte 204, + byte 0,byte 136,byte 136, + byte 0,byte 136,byte 68, + byte 0,byte 136,byte 0, + byte 0,byte 68,byte 204, + byte 0,byte 68,byte 136, + byte 0,byte 68,byte 68, + byte 0,byte 68,byte 0, + byte 0,byte 0,byte 204, + byte 0,byte 0,byte 136, + byte 0,byte 0,byte 68, + byte 0,byte 0,byte 0, +}; diff --git a/appl/wm/mpeg/rl0f.tab b/appl/wm/mpeg/rl0f.tab new file mode 100644 index 00000000..38da34ee --- /dev/null +++ b/appl/wm/mpeg/rl0f.tab @@ -0,0 +1,517 @@ +# vlc -c rl0f +rl0f_size: con 512; +rl0f_bits: con 9; +rl0f_table:= array[] of { + (9, 0,C0), + (9, 0,C1), + (9, 0,C2), + (9, 0,C3), + (9, 0,C4), + (9, 0,C5), + (9, 0,C6), + (9, 0,C7), + (6, 0,ESC), + (6, 0,ESC), + (6, 0,ESC), + (6, 0,ESC), + (6, 0,ESC), + (6, 0,ESC), + (6, 0,ESC), + (6, 0,ESC), + (8, 2,2), + (8, 2,2), + (8, -2,2), + (8, -2,2), + (8, 1,9), + (8, 1,9), + (8, -1,9), + (8, -1,9), + (8, 4,0), + (8, 4,0), + (8, -4,0), + (8, -4,0), + (8, 1,8), + (8, 1,8), + (8, -1,8), + (8, -1,8), + (7, 1,7), + (7, 1,7), + (7, 1,7), + (7, 1,7), + (7, -1,7), + (7, -1,7), + (7, -1,7), + (7, -1,7), + (7, 1,6), + (7, 1,6), + (7, 1,6), + (7, 1,6), + (7, -1,6), + (7, -1,6), + (7, -1,6), + (7, -1,6), + (7, 2,1), + (7, 2,1), + (7, 2,1), + (7, 2,1), + (7, -2,1), + (7, -2,1), + (7, -2,1), + (7, -2,1), + (7, 1,5), + (7, 1,5), + (7, 1,5), + (7, 1,5), + (7, -1,5), + (7, -1,5), + (7, -1,5), + (7, -1,5), + (9, 1,13), + (9, -1,13), + (9, 6,0), + (9, -6,0), + (9, 1,12), + (9, -1,12), + (9, 1,11), + (9, -1,11), + (9, 2,3), + (9, -2,3), + (9, 3,1), + (9, -3,1), + (9, 5,0), + (9, -5,0), + (9, 1,10), + (9, -1,10), + (6, 3,0), + (6, 3,0), + (6, 3,0), + (6, 3,0), + (6, 3,0), + (6, 3,0), + (6, 3,0), + (6, 3,0), + (6, -3,0), + (6, -3,0), + (6, -3,0), + (6, -3,0), + (6, -3,0), + (6, -3,0), + (6, -3,0), + (6, -3,0), + (6, 1,4), + (6, 1,4), + (6, 1,4), + (6, 1,4), + (6, 1,4), + (6, 1,4), + (6, 1,4), + (6, 1,4), + (6, -1,4), + (6, -1,4), + (6, -1,4), + (6, -1,4), + (6, -1,4), + (6, -1,4), + (6, -1,4), + (6, -1,4), + (6, 1,3), + (6, 1,3), + (6, 1,3), + (6, 1,3), + (6, 1,3), + (6, 1,3), + (6, 1,3), + (6, 1,3), + (6, -1,3), + (6, -1,3), + (6, -1,3), + (6, -1,3), + (6, -1,3), + (6, -1,3), + (6, -1,3), + (6, -1,3), + (5, 2,0), + (5, 2,0), + (5, 2,0), + (5, 2,0), + (5, 2,0), + (5, 2,0), + (5, 2,0), + (5, 2,0), + (5, 2,0), + (5, 2,0), + (5, 2,0), + (5, 2,0), + (5, 2,0), + (5, 2,0), + (5, 2,0), + (5, 2,0), + (5, -2,0), + (5, -2,0), + (5, -2,0), + (5, -2,0), + (5, -2,0), + (5, -2,0), + (5, -2,0), + (5, -2,0), + (5, -2,0), + (5, -2,0), + (5, -2,0), + (5, -2,0), + (5, -2,0), + (5, -2,0), + (5, -2,0), + (5, -2,0), + (5, 1,2), + (5, 1,2), + (5, 1,2), + (5, 1,2), + (5, 1,2), + (5, 1,2), + (5, 1,2), + (5, 1,2), + (5, 1,2), + (5, 1,2), + (5, 1,2), + (5, 1,2), + (5, 1,2), + (5, 1,2), + (5, 1,2), + (5, 1,2), + (5, -1,2), + (5, -1,2), + (5, -1,2), + (5, -1,2), + (5, -1,2), + (5, -1,2), + (5, -1,2), + (5, -1,2), + (5, -1,2), + (5, -1,2), + (5, -1,2), + (5, -1,2), + (5, -1,2), + (5, -1,2), + (5, -1,2), + (5, -1,2), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, 1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), + (2, -1,0), +}; diff --git a/appl/wm/mpeg/rl0f.vlc b/appl/wm/mpeg/rl0f.vlc new file mode 100644 index 00000000..1f9eb590 --- /dev/null +++ b/appl/wm/mpeg/rl0f.vlc @@ -0,0 +1,34 @@ +# Run/Level First base (first 9 bits) +# vlc -c rl0f < rl0f.vlc > rl0f.tab +1s 1,0 +0100s 2,0 +00101s 3,0 +0000110s 4,0 +00100110s 5,0 +00100001s 6,0 +000000101 0,C5 +000000011 0,C3 +000000010 0,C2 +000000001 0,C1 +000000000 0,C0 +011s 1,1 +000110s 2,1 +00100101s 3,1 +000000110 0,C6 +0101s 1,2 +0000100s 2,2 +00111s 1,3 +00100100s 2,3 +00110s 1,4 +000000111 0,C7 +000111s 1,5 +000000100 0,C4 +000101s 1,6 +000100s 1,7 +0000111s 1,8 +0000101s 1,9 +00100111s 1,10 +00100011s 1,11 +00100010s 1,12 +00100000s 1,13 +000001 0,ESC diff --git a/appl/wm/mpeg/rl0n.tab b/appl/wm/mpeg/rl0n.tab new file mode 100644 index 00000000..2820979b --- /dev/null +++ b/appl/wm/mpeg/rl0n.tab @@ -0,0 +1,517 @@ +# vlc -c rl0n +rl0n_size: con 512; +rl0n_bits: con 9; +rl0n_table:= array[] of { + (9, 0,C0), + (9, 0,C1), + (9, 0,C2), + (9, 0,C3), + (9, 0,C4), + (9, 0,C5), + (9, 0,C6), + (9, 0,C7), + (6, 0,ESC), + (6, 0,ESC), + (6, 0,ESC), + (6, 0,ESC), + (6, 0,ESC), + (6, 0,ESC), + (6, 0,ESC), + (6, 0,ESC), + (8, 2,2), + (8, 2,2), + (8, -2,2), + (8, -2,2), + (8, 1,9), + (8, 1,9), + (8, -1,9), + (8, -1,9), + (8, 4,0), + (8, 4,0), + (8, -4,0), + (8, -4,0), + (8, 1,8), + (8, 1,8), + (8, -1,8), + (8, -1,8), + (7, 1,7), + (7, 1,7), + (7, 1,7), + (7, 1,7), + (7, -1,7), + (7, -1,7), + (7, -1,7), + (7, -1,7), + (7, 1,6), + (7, 1,6), + (7, 1,6), + (7, 1,6), + (7, -1,6), + (7, -1,6), + (7, -1,6), + (7, -1,6), + (7, 2,1), + (7, 2,1), + (7, 2,1), + (7, 2,1), + (7, -2,1), + (7, -2,1), + (7, -2,1), + (7, -2,1), + (7, 1,5), + (7, 1,5), + (7, 1,5), + (7, 1,5), + (7, -1,5), + (7, -1,5), + (7, -1,5), + (7, -1,5), + (9, 1,13), + (9, -1,13), + (9, 6,0), + (9, -6,0), + (9, 1,12), + (9, -1,12), + (9, 1,11), + (9, -1,11), + (9, 2,3), + (9, -2,3), + (9, 3,1), + (9, -3,1), + (9, 5,0), + (9, -5,0), + (9, 1,10), + (9, -1,10), + (6, 3,0), + (6, 3,0), + (6, 3,0), + (6, 3,0), + (6, 3,0), + (6, 3,0), + (6, 3,0), + (6, 3,0), + (6, -3,0), + (6, -3,0), + (6, -3,0), + (6, -3,0), + (6, -3,0), + (6, -3,0), + (6, -3,0), + (6, -3,0), + (6, 1,4), + (6, 1,4), + (6, 1,4), + (6, 1,4), + (6, 1,4), + (6, 1,4), + (6, 1,4), + (6, 1,4), + (6, -1,4), + (6, -1,4), + (6, -1,4), + (6, -1,4), + (6, -1,4), + (6, -1,4), + (6, -1,4), + (6, -1,4), + (6, 1,3), + (6, 1,3), + (6, 1,3), + (6, 1,3), + (6, 1,3), + (6, 1,3), + (6, 1,3), + (6, 1,3), + (6, -1,3), + (6, -1,3), + (6, -1,3), + (6, -1,3), + (6, -1,3), + (6, -1,3), + (6, -1,3), + (6, -1,3), + (5, 2,0), + (5, 2,0), + (5, 2,0), + (5, 2,0), + (5, 2,0), + (5, 2,0), + (5, 2,0), + (5, 2,0), + (5, 2,0), + (5, 2,0), + (5, 2,0), + (5, 2,0), + (5, 2,0), + (5, 2,0), + (5, 2,0), + (5, 2,0), + (5, -2,0), + (5, -2,0), + (5, -2,0), + (5, -2,0), + (5, -2,0), + (5, -2,0), + (5, -2,0), + (5, -2,0), + (5, -2,0), + (5, -2,0), + (5, -2,0), + (5, -2,0), + (5, -2,0), + (5, -2,0), + (5, -2,0), + (5, -2,0), + (5, 1,2), + (5, 1,2), + (5, 1,2), + (5, 1,2), + (5, 1,2), + (5, 1,2), + (5, 1,2), + (5, 1,2), + (5, 1,2), + (5, 1,2), + (5, 1,2), + (5, 1,2), + (5, 1,2), + (5, 1,2), + (5, 1,2), + (5, 1,2), + (5, -1,2), + (5, -1,2), + (5, -1,2), + (5, -1,2), + (5, -1,2), + (5, -1,2), + (5, -1,2), + (5, -1,2), + (5, -1,2), + (5, -1,2), + (5, -1,2), + (5, -1,2), + (5, -1,2), + (5, -1,2), + (5, -1,2), + (5, -1,2), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, 1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (4, -1,1), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (2, 0,EOB), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, 1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), + (3, -1,0), +}; diff --git a/appl/wm/mpeg/rl0n.vlc b/appl/wm/mpeg/rl0n.vlc new file mode 100644 index 00000000..8cd5da23 --- /dev/null +++ b/appl/wm/mpeg/rl0n.vlc @@ -0,0 +1,35 @@ +# Run/Level Next base (first 9 bits) +# vlc -c rl0n < rl0n.vlc > rl0n.tab +11s 1,0 +0100s 2,0 +00101s 3,0 +0000110s 4,0 +00100110s 5,0 +00100001s 6,0 +000000101 0,C5 +000000011 0,C3 +000000010 0,C2 +000000001 0,C1 +000000000 0,C0 +011s 1,1 +000110s 2,1 +00100101s 3,1 +000000110 0,C6 +0101s 1,2 +0000100s 2,2 +00111s 1,3 +00100100s 2,3 +00110s 1,4 +000000111 0,C7 +000111s 1,5 +000000100 0,C4 +000101s 1,6 +000100s 1,7 +0000111s 1,8 +0000101s 1,9 +00100111s 1,10 +00100011s 1,11 +00100010s 1,12 +00100000s 1,13 +10 0,EOB +000001 0,ESC diff --git a/appl/wm/mpeg/scidct.b b/appl/wm/mpeg/scidct.b new file mode 100644 index 00000000..f59c2217 --- /dev/null +++ b/appl/wm/mpeg/scidct.b @@ -0,0 +1,160 @@ +implement IDCT; + +include "sys.m"; +include "mpegio.m"; + +init() +{ +} + +# Scaled integer implementation. +# inverse two dimensional DCT, Chen-Wang algorithm +# (IEEE ASSP-32, pp. 803-816, Aug. 1984) +# 32-bit integer arithmetic (8 bit coefficients) +# 11 mults, 29 adds per DCT +# +# coefficients extended to 12 bit for IEEE1180-1990 +# compliance + +W1: con 2841; # 2048*sqrt(2)*cos(1*pi/16) +W2: con 2676; # 2048*sqrt(2)*cos(2*pi/16) +W3: con 2408; # 2048*sqrt(2)*cos(3*pi/16) +W5: con 1609; # 2048*sqrt(2)*cos(5*pi/16) +W6: con 1108; # 2048*sqrt(2)*cos(6*pi/16) +W7: con 565; # 2048*sqrt(2)*cos(7*pi/16) + +W1pW7: con 3406; # W1+W7 +W1mW7: con 2276; # W1-W7 +W3pW5: con 4017; # W3+W5 +W3mW5: con 799; # W3-W5 +W2pW6: con 3784; # W2+W6 +W2mW6: con 1567; # W2-W6 + +R2: con 181; # 256/sqrt(2) + +idct(b: array of int) +{ + # transform horizontally + for(y:=0; y<8; y++){ + eighty := y<<3; + # if all non-DC components are zero, just propagate the DC term + if(b[eighty+1]==0) + if(b[eighty+2]==0 && b[eighty+3]==0) + if(b[eighty+4]==0 && b[eighty+5]==0) + if(b[eighty+6]==0 && b[eighty+7]==0){ + v := b[eighty]<<3; + b[eighty+0] = v; + b[eighty+1] = v; + b[eighty+2] = v; + b[eighty+3] = v; + b[eighty+4] = v; + b[eighty+5] = v; + b[eighty+6] = v; + b[eighty+7] = v; + continue; + } + # prescale + x0 := (b[eighty+0]<<11)+128; + x1 := b[eighty+4]<<11; + x2 := b[eighty+6]; + x3 := b[eighty+2]; + x4 := b[eighty+1]; + x5 := b[eighty+7]; + x6 := b[eighty+5]; + x7 := b[eighty+3]; + # first stage + x8 := W7*(x4+x5); + x4 = x8 + W1mW7*x4; + x5 = x8 - W1pW7*x5; + x8 = W3*(x6+x7); + x6 = x8 - W3mW5*x6; + x7 = x8 - W3pW5*x7; + # second stage + x8 = x0 + x1; + x0 -= x1; + x1 = W6*(x3+x2); + x2 = x1 - W2pW6*x2; + x3 = x1 + W2mW6*x3; + x1 = x4 + x6; + x4 -= x6; + x6 = x5 + x7; + x5 -= x7; + # third stage + x7 = x8 + x3; + x8 -= x3; + x3 = x0 + x2; + x0 -= x2; + x2 = (R2*(x4+x5)+128)>>8; + x4 = (R2*(x4-x5)+128)>>8; + # fourth stage + b[eighty+0] = (x7+x1)>>8; + b[eighty+1] = (x3+x2)>>8; + b[eighty+2] = (x0+x4)>>8; + b[eighty+3] = (x8+x6)>>8; + b[eighty+4] = (x8-x6)>>8; + b[eighty+5] = (x0-x4)>>8; + b[eighty+6] = (x3-x2)>>8; + b[eighty+7] = (x7-x1)>>8; + } + # transform vertically + for(x:=0; x<8; x++){ + # if all non-DC components are zero, just propagate the DC term + if(b[x+8*1]==0) + if(b[x+8*2]==0 && b[x+8*3]==0) + if(b[x+8*4]==0 && b[x+8*5]==0) + if(b[x+8*6]==0 && b[x+8*7]==0){ + v := (b[x+8*0]+32)>>6; + b[x+8*0] = v; + b[x+8*1] = v; + b[x+8*2] = v; + b[x+8*3] = v; + b[x+8*4] = v; + b[x+8*5] = v; + b[x+8*6] = v; + b[x+8*7] = v; + continue; + } + # prescale + x0 := (b[x+8*0]<<8)+8192; + x1 := b[x+8*4]<<8; + x2 := b[x+8*6]; + x3 := b[x+8*2]; + x4 := b[x+8*1]; + x5 := b[x+8*7]; + x6 := b[x+8*5]; + x7 := b[x+8*3]; + # first stage + x8 := W7*(x4+x5) + 4; + x4 = (x8+W1mW7*x4)>>3; + x5 = (x8-W1pW7*x5)>>3; + x8 = W3*(x6+x7) + 4; + x6 = (x8-W3mW5*x6)>>3; + x7 = (x8-W3pW5*x7)>>3; + # second stage + x8 = x0 + x1; + x0 -= x1; + x1 = W6*(x3+x2) + 4; + x2 = (x1-W2pW6*x2)>>3; + x3 = (x1+W2mW6*x3)>>3; + x1 = x4 + x6; + x4 -= x6; + x6 = x5 + x7; + x5 -= x7; + # third stage + x7 = x8 + x3; + x8 -= x3; + x3 = x0 + x2; + x0 -= x2; + x2 = (R2*(x4+x5)+128)>>8; + x4 = (R2*(x4-x5)+128)>>8; + # fourth stage + b[x+8*0] = (x7+x1)>>14; + b[x+8*1] = (x3+x2)>>14; + b[x+8*2] = (x0+x4)>>14; + b[x+8*3] = (x8+x6)>>14; + b[x+8*4] = (x8-x6)>>14; + b[x+8*5] = (x0-x4)>>14; + b[x+8*6] = (x3-x2)>>14; + b[x+8*7] = (x7-x1)>>14; + } +} diff --git a/appl/wm/mpeg/vlc.b b/appl/wm/mpeg/vlc.b new file mode 100644 index 00000000..96e136e9 --- /dev/null +++ b/appl/wm/mpeg/vlc.b @@ -0,0 +1,213 @@ +implement Vlc; + +include "sys.m"; +include "draw.m"; +include "bufio.m"; + +# +# Construct expanded Vlc (variable length code) tables +# from vlc description files. +# + +sys: Sys; +bufio: Bufio; +Iobuf: import bufio; + +stderr: ref Sys->FD; + +sv: adt +{ + s: int; + v: string; +}; + +s2list: type list of (string, string); +bits, size: int; +table: array of sv; +prog: string; +undef: string = "UNDEF"; +xfixed: int = 0; +complete: int = 0; +paren: int = 0; + +Vlc: module +{ + init: fn(nil: ref Draw->Context, args: list of string); +}; + +init(nil: ref Draw->Context, args: list of string) +{ + sys = load Sys Sys->PATH; + stderr = sys->fildes(2); + sargs := makestr(args); + prog = hd args; + args = tl args; + bufio = load Bufio Bufio->PATH; + if (bufio == nil) { + sys->fprint(stderr, "%s: could not load %s: %r\n", prog, Bufio->PATH); + return; + } + inf := bufio->fopen(sys->fildes(0), Bufio->OREAD); + if (inf == nil) { + sys->fprint(stderr, "%s: fopen stdin failed: %r\n", prog); + return; + } + while (args != nil && len hd args && (a := hd args)[0] == '-') { + flag: + for (x := 1; x < len a; x++) { + case a[x] { + 'c' => + complete = 1; + 'f' => + xfixed = 1; + 'p' => + paren = 1; + 'u' => + if (++x == len a) { + args = tl args; + if (args == nil) + usage(); + undef = hd args; + } else + undef = a[x:]; + break flag; + * => + usage(); + return; + } + } + args = tl args; + } + vlc := "vlc"; + if (args != nil) { + if (tl args != nil) { + usage(); + return; + } + vlc = hd args; + } + il: s2list; + while ((l := inf.gets('\n')) != nil) { + if (l[0] == '#') + continue; + (n, t) := sys->tokenize(l, " \t\n"); + if (n != 2) { + sys->fprint(stderr, "%s: bad input: %s", prog, l); + return; + } + il = (hd t, hd tl t) :: il; + } + (n, nl) := expand(il); + bits = n; + size = 1 << bits; + table = array[size] of sv; + maketable(nl); + printtable(vlc, sargs); +} + +usage() +{ + sys->fprint(stderr, "usage: %s [-cfp] [-u undef] [stem]\n", prog); +} + +makestr(l: list of string): string +{ + s, t: string; + while (l != nil) { + s = s + t + hd l; + t = " "; + l = tl l; + } + return s; +} + +expand(l: s2list): (int, s2list) +{ + nl: s2list; + max := 0; + while (l != nil) { + (bs, val) := hd l; + n := len bs; + if (n > max) + max = n; + if (bs[n - 1] == 's') { + t := bs[:n - 1]; + nl = (t + "0", val) :: (t + "1", "-" + val) :: nl; + } else + nl = (bs, val) :: nl; + l = tl l; + } + return (max, nl); +} + +maketable(l: s2list) +{ + while (l != nil) { + (bs, val) := hd l; + z := len bs; + if (xfixed && z != bits) + error(sys->sprint("string %s too short", bs)); + s := bits - z; + v := value(bs) << s; + n := 1 << s; + for (i := 0; i < n; i++) { + if (table[v].v != nil) + error(sys->sprint("repeat match for %x", v)); + table[v] = (z, val); + v++; + } + l = tl l; + } +} + +value(s: string): int +{ + n := len s; + v := 0; + for (i := 0; i < n; i++) { + case s[i] { + '0' => + v <<= 1; + '1'=> + v = (v << 1) | 1; + * => + error("bad bitstream: " + s); + } + } + return v; +} + +printtable(s, a: string) +{ + sys->print("# %s\n", a); + sys->print("%s_size: con %d;\n", s, size); + sys->print("%s_bits: con %d;\n", s, bits); + sys->print("%s_table:= array[] of {\n", s); + for (i := 0; i < size; i++) { + if (table[i].v != nil) { + if (xfixed) { + if (paren) + sys->print("\t(%s),\n", table[i].v); + else + sys->print("\t%s,\n", table[i].v); + } else + sys->print("\t(%d, %s),\n", table[i].s, table[i].v); + } else if (!complete) { + if (xfixed) { + if (paren) + sys->print("\t(%s),\n", undef); + else + sys->print("\t%s,\n", undef); + } else + sys->print("\t(0, %s),\n", undef); + } else + error(sys->sprint("no match for %x", i)); + } + sys->print("};\n"); +} + +error(s: string) +{ + sys->fprint(stderr, "%s: error: %s\n", prog, s); + exit; +} diff --git a/appl/wm/mpeg/ydc.tab b/appl/wm/mpeg/ydc.tab new file mode 100644 index 00000000..f2dff729 --- /dev/null +++ b/appl/wm/mpeg/ydc.tab @@ -0,0 +1,133 @@ +# vlc ydc +ydc_size: con 128; +ydc_bits: con 7; +ydc_table:= array[] of { + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 1), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (2, 2), + (3, 0), + (3, 0), + (3, 0), + (3, 0), + (3, 0), + (3, 0), + (3, 0), + (3, 0), + (3, 0), + (3, 0), + (3, 0), + (3, 0), + (3, 0), + (3, 0), + (3, 0), + (3, 0), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 3), + (3, 4), + (3, 4), + (3, 4), + (3, 4), + (3, 4), + (3, 4), + (3, 4), + (3, 4), + (3, 4), + (3, 4), + (3, 4), + (3, 4), + (3, 4), + (3, 4), + (3, 4), + (3, 4), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (4, 5), + (5, 6), + (5, 6), + (5, 6), + (5, 6), + (6, 7), + (6, 7), + (7, 8), + (0, UNDEF), +}; diff --git a/appl/wm/mpeg/ydc.vlc b/appl/wm/mpeg/ydc.vlc new file mode 100644 index 00000000..660ce582 --- /dev/null +++ b/appl/wm/mpeg/ydc.vlc @@ -0,0 +1,11 @@ +# Luminance DC +# vlc ydc < ydc.vlc > ydc.tab +100 0 +00 1 +01 2 +101 3 +110 4 +1110 5 +11110 6 +111110 7 +1111110 8 diff --git a/appl/wm/mprof.b b/appl/wm/mprof.b new file mode 100644 index 00000000..625f085d --- /dev/null +++ b/appl/wm/mprof.b @@ -0,0 +1,314 @@ +implement Wmmprof; + +include "sys.m"; + sys: Sys; +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; +include "draw.m"; + draw: Draw; +include "tk.m"; + tk: Tk; +include "tkclient.m"; + tkclient: Tkclient; +include "arg.m"; + arg: Arg; +include "profile.m"; + +Prof: module{ + init0: fn(ctxt: ref Draw->Context, argv: list of string): Profile->Prof; +}; + +prof: Prof; + +Wmmprof: module{ + init: fn(ctxt: ref Draw->Context, argl: list of string); +}; + +usage(s: string) +{ + sys->fprint(sys->fildes(2), "wm/mprof: %s\n", s); + sys->fprint(sys->fildes(2), "usage: wm/mprof [-e] [-m modname]... cmd [arg ... ]"); + exit; +} + +TXTBEGIN: con 3; + +init(ctxt: ref Draw->Context, argl: list of string) +{ + sys = load Sys Sys->PATH; + bufio = load Bufio Bufio->PATH; + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + arg = load Arg Arg->PATH; + + if(ctxt == nil) + fatal("wm not running"); + sys->pctl(Sys->NEWPGRP, nil); + + arg->init(argl); + while((o := arg->opt()) != 0){ + case(o){ + '1' or '2' or '3' or 'e' => ; + 'm' => + if(arg->arg() == nil) + usage("missing module/file"); + * => + usage(sys->sprint("unknown option -%c", o)); + } + } + + stats := execprof(ctxt, argl); + if(stats.mods == nil) + exit; + + tkclient->init(); + (win, wmc) := tkclient->toplevel(ctxt, nil, hd argl, Tkclient->Resize|Tkclient->Hide); + tkc := chan of string; + tk->namechan(win, tkc, "tkc"); + for(i := 0; i < len wincfg; i++) + cmd(win, wincfg[i]); + tkclient->onscreen(win, nil); + tkclient->startinput(win, "kbd"::"ptr"::nil); + createmenu(win, stats); + curc := 0; + cura := newprint(win, stats, curc); + + for(;;){ + alt{ + c := <-win.ctxt.kbd => + tk->keyboard(win, c); + c := <-win.ctxt.ptr => + tk->pointer(win, *c); + c := <-win.ctxt.ctl or + c = <-win.wreq or + c = <-wmc => + tkclient->wmctl(win, c); + c := <- tkc => + (nil, toks) := sys->tokenize(c, " "); + case(hd toks){ + "b" => + if(curc > 0) + cura = newprint(win, stats, --curc); + "f" => + if(curc < len stats.mods - 1) + cura = newprint(win, stats, ++curc); + "s" => + if(cura != nil) + scroll(win, cura); + "m" => + x := cmd(win, ".f cget actx"); + y := cmd(win, ".f cget acty"); + cmd(win, ".f.menu post " + x + " " + y); + * => + curc = int hd toks; + cura = newprint(win, stats, curc); + } + } + } +} + +execprof(ctxt: ref Draw->Context, argl: list of string): Profile->Prof +{ + { + prof = load Prof "/dis/mprof.dis"; + if(prof == nil) + fatal("cannot load profiler"); + return prof->init0(ctxt, hd argl :: "-g" :: tl argl); + } + exception{ + "fail:*" => + return (nil, 0, nil); + } + return (nil, 0, nil); +} + +newprint(win: ref Tk->Toplevel, p: Profile->Prof, i: int): array of int +{ + cmd(win, ".f.t delete 1.0 end"); + cmd(win, "update"); + m0, m1: list of Profile->Modprof; + for(m := p.mods; m != nil && --i >= 0; m = tl m) + m0 = m; + if(m == nil) + return nil; + m1 = tl m; + (name, nil, spath, nil, line, nil, nil, tot, tots, nil) := hd m; + name0 := name1 := "nil"; + if(m0 != nil) + name0 = (hd m0).name; + if(m1 != nil) + name1 = (hd m1).name; + a := len name; + name += sys->sprint(" (%d %d) ", tot, tots[0]); + cmd(win, ".f.t insert end {" + name + " <- " + name0 + " -> " + name1 + "}"); + tag := gettag(win, tot+tots[0], p.total+p.totals[0]); + cmd(win, ".f.t tag add " + tag + " " + "1.0" + " " + "1." + string a); + cmd(win, ".f.t insert end \n\n"); + cmd(win, "update"); + lineno := TXTBEGIN; + bio := bufio->open(spath, Bufio->OREAD); + if(bio == nil) + return nil; + i = 1; + ll := len line/2; + while((s := bio.gets('\n')) != nil){ + f := g := 0; + if(i < ll){ + f = line[2*i]; + g = line[2*i+1]; + } + a = len s; + s = sys->sprint("%d\t%d\t%s", f, g, s); + b := len s; + cmd(win, ".f.t insert end " + tk->quote(s)); + tag = gettag(win, f+g, tot+tots[0]); + cmd(win, ".f.t tag add " + tag + " " + string lineno + "." + string (b-a) + " " + string lineno + "." + string (b-1)); + cmd(win, "update"); + lineno++; + i++; + } + return line; +} + +index(win: ref Tk->Toplevel, x: int, y: int): int +{ + t := cmd(win, ".f.t index @" + string x + "," + string y); + (nil, l) := sys->tokenize(t, "."); +# sys->print("%d,%d -> %s\n", x, y, t); + return int hd l; +} + +winextent(win: ref Tk->Toplevel): (int, int) +{ + w := int cmd(win, ".f.t cget -actwidth"); + h := int cmd(win, ".f.t cget -actheight"); + lw := index(win, 0, 0); + uw := index(win, w-1, h-1); + return (lw, uw); +} + +see(win: ref Tk->Toplevel, line: int) +{ + cmd(win, ".f.t see " + string line + ".0"); + cmd(win, "update"); +} + +scroll(win: ref Tk->Toplevel, line: array of int) +{ + (nil, uw) := winextent(win); + lno := TXTBEGIN; + ll := len line/2; + for(i := 1; i < ll; i++){ + n := line[2*i]+line[2*i+1]; + if(n > 0 && lno > uw){ + see(win, lno); + return; + } + lno++; + } + lno = TXTBEGIN; + ll = len line/2; + for(i = 1; i < ll; i++){ + n := line[2*i]+line[2*i+1]; + if(n > 0){ + see(win, lno); + return; + } + lno++; + } +} + +cmd(top: ref Tk->Toplevel, s: string): string +{ + # sys->print("%s\n", s); + e := tk->cmd(top, s); + if (e != nil && e[0] == '!') + sys->fprint(sys->fildes(2), "tk error on '%s': %s\n", s, e); + return e; +} + +fatal(s: string) +{ + sys->fprint(sys->fildes(2), "%s\n", s); + exit; +} + +MENUMAX: con 20; + +createmenu(top: ref Tk->Toplevel, p: Profile->Prof ) +{ + mn := ".f.menu"; + cmd(top, "menu " + mn); + i := j := 0; + for(m := p.mods; m != nil; m = tl m){ + name := (hd m).name; + cmd(top, mn + " add command -label " + name + " -command {send tkc " + string i + "}"); + i++; + j++; + if(j == MENUMAX && tl m != nil){ + cmd(top, mn + " add cascade -label MORE -menu " + mn + ".menu"); + mn += ".menu"; + cmd(top, "menu " + mn); + j = 0; + } + } +} + +tags := array[256] of { * => byte 0 }; + +gettag(win: ref Tk->Toplevel, n: int, d: int): string +{ + i := int ((real n/real d) * real 15); + if(i < 0 || i > 15) + i = 0; + s := "tag" + string i; + if(tags[i] == byte 0){ + rgb := "#" + hex2(255-64*0)+hex2(255-64*(i/4))+hex2(255-64*(i%4)); + cmd(win, ".f.t tag configure " + s + " -fg black -bg " + rgb); + tags[i] = byte 1; + } + return s; +} + +hex(i: int): int +{ + if(i < 10) + return i+'0'; + else + return i-10+'A'; +} + +hex2(i: int): string +{ + s := "00"; + s[0] = hex(i/16); + s[1] = hex(i%16); + return s; +} + +wincfg := array[] of { + "frame .f", + "text .f.t -width 809 -height 500 -state disabled -wrap char -bg white -yscrollcommand {.f.s set}", + "scrollbar .f.s -orient vertical -command {.f.t yview}", + "frame .i", + "button .i.b -bitmap small_color_left.bit -command {send tkc b}", + "button .i.f -bitmap small_color_right.bit -command {send tkc f}", + "button .i.s -bitmap small_find.bit -command {send tkc s}", + "button .i.m -bitmap small_reload.bit -command {send tkc m}", + + "pack .i.b -side left", + "pack .i.f -side left", + "pack .i.s -side left", + "pack .i.m -side left", + + "pack .f.s -fill y -side left", + "pack .f.t -fill both -expand 1", + + "pack .i -fill x", + "pack .f -fill both -expand 1", + "pack propagate . 0", + + "update", +};
\ No newline at end of file diff --git a/appl/wm/pen.b b/appl/wm/pen.b new file mode 100644 index 00000000..17b2be39 --- /dev/null +++ b/appl/wm/pen.b @@ -0,0 +1,447 @@ +implement Pen; + +# +# pen input on touch screen +# +# Copyright © 2001,2002 Vita Nuova Holdings Limited. All rights reserved. +# +# This may be used or modified by anyone for any purpose. +# + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + Point, Rect: import draw; + +include "tk.m"; + tk: Tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; + +include "strokes.m"; + strokes: Strokes; + Classifier, Penpoint, Stroke: import strokes; + readstrokes: Readstrokes; + +include "arg.m"; + +Pen: module +{ + init: fn(nil: ref Draw->Context, nil: list of string); +}; + +debug := 0; +stderr: ref Sys->FD; + +tkconfig := array[] of{ + "canvas .c -borderwidth 0 -bg white -height 80 -width 80", + ".c create text 0 0 -anchor nw -width 5w -fill gray -tags mode", + ".c create text 30 0 -anchor nw -width 3w -fill blue -tags char", + "bind .c <Button-1> {grab set .c; send cmd push %x %y}", + "bind .c <Motion-Button-1> {send cmd move %x %y}", + "bind .c <ButtonRelease-1> {grab release .c; send cmd release %x %y}", + "bind .c <Enter> {send cmd move %x %y}", # does nothing if not previously down +# "bind .c <Leave> {send cmd leave %x %y}", # ditto + "pack .c -expand 1 -fill both -padx 5 -pady 5", +}; + +usage() +{ + sys->fprint(sys->fildes(2), "Usage: pen [-t] [-e] [classifier ...]\n"); + raise "fail:usage"; +} + +init(ctxt: ref Draw->Context, args: list of string) +{ + sys = load Sys Sys->PATH; + if (ctxt == nil) { + sys->fprint(sys->fildes(2), "pen: no window context\n"); + raise "fail:bad context"; + } + stderr = sys->fildes(2); + draw = load Draw Draw->PATH; + bufio = load Bufio Bufio->PATH; + tk = load Tk Tk->PATH; + if(tk == nil) + nomod(Tk->PATH); + tkclient = load Tkclient Tkclient->PATH; + if(tkclient == nil) + nomod(Tkclient->PATH); + strokes = load Strokes Strokes->PATH; + if(strokes == nil) + nomod(Strokes->PATH); + strokes->init(); + readstrokes = load Readstrokes Readstrokes->PATH; + if(readstrokes == nil) + nomod(Readstrokes->PATH); + readstrokes->init(strokes); + + arg := load Arg Arg->PATH; + if(arg == nil) + nomod(Arg->PATH); + arg->init(args); + taskbar := 0; + noexit := 0; + winopts := Tkclient->Appl; + corner := 1; + while((opt := arg->opt()) != 0) + case opt { + 't' => + taskbar = 1; + 'e' => + noexit = 1; + 'r' => + winopts &= ~Tkclient->Resize; + 'c' => + corner = 0; + * => + usage(); + } + args = arg->argv(); + arg = nil; + + if(args == nil) + args = "/lib/strokes/letters.clx" :: "/lib/strokes/digits.clx" :: "/lib/strokes/punc.clx" :: nil; + csets := array[len args] of ref Classifier; + cs := 0; + for(; args != nil; args = tl args){ + file := hd args; + (err, rc) := readstrokes->read_classifier(file, 1, 0); + if(rc == nil) + error(sys->sprint("can't read classifier %s: %s", file, err)); + csets[cs++] = rc; + } + readstrokes = nil; + + rec := csets[0]; + digits: ref Classifier; + if(len csets > 1) + digits = csets[1]; # need not actually be digits + + sys->pctl(Sys->NEWPGRP, nil); + tkclient->init(); + (top, ctl) := tkclient->toplevel(ctxt, nil, "Pen", winopts); + cmd := chan of string; + tk->namechan(top, cmd, "cmd"); + for (i1 := 0; i1 < len tkconfig; i1++) + tkcmd(top, tkconfig[i1]); + if(winopts & Tkclient->Resize) + tkcmd(top, "pack propagate . 0"); + + + if(corner){ + (w, h) := (int tk->cmd(top, ". cget -width"), int tk->cmd(top, ". cget -height")); + r := ctxt.display.image.r; + tkcmd(top, sys->sprint(". configure -x %d -y %d", r.max.x-w, r.max.y-h)); + } + + + shift := 0; + punct := 0; + points := array[1000] of Penpoint; + npoint := 0; + + tkclient->onscreen(top, nil); + tkclient->startinput(top, "ptr"::nil); + if(taskbar) + tkclient->wmctl(top, "task"); + tk->cmd(top, "update"); + + for(;;){ + if(punct) + drawmode(top, "#&*"); + else if(rec == digits) + drawmode(top, "123"); + else if(shift == 1) + drawmode(top, "Abc"); + else if(shift == 2) + drawmode(top, "ABC"); + else if(shift) + drawmode(top, "S "+string shift); + else + drawmode(top, "abc"); + tk->cmd(top, "update"); + alt{ + s := <-top.ctxt.ptr => + tk->pointer(top, *s); + + s := <-top.ctxt.ctl or + s = <-top.wreq or + s = <-ctl => + if(s == "exit" && noexit) + s = "task"; + tkclient->wmctl(top, s); + + s := <-cmd => + (nf, flds) := sys->tokenize(s, " \t"); + if(nf < 3) + break; + p := Penpoint(int hd tl flds, int hd tl tl flds, 0); + case hd flds { + "push" => + tkcmd(top, "raise ."); + tk->cmd(top, "update"); + npoint = 0; + points[npoint++] = p; + "leave" => + npoint = 0; + tkcmd(top, ".c delete stuff"); + "release" => + if(npoint == 0) + break; + points[npoint++] = p; + (n, tap) := recognize_stroke(top, rec, ref Stroke(npoint, points[0:npoint], 0, 0), debug); + drawchars(top, ""); + name: string = nil; + if(n >= 0){ + name = rec.cnames[n]; + if(debug > 1){ + ex: ref Stroke = nil; + if(rec.canonex != nil) + ex = rec.canonex[n]; + drawshape(top, "stuff", ex, "blue", rec.dompts[n], "yellow"); + sys->fprint(stderr, "match: %s\n", name); + } + case c := name[0] { + 'S' => + shift = (shift+1)%3; + name = nil; + 'A' => + name = " "; + 'B' => + name = "\b"; + 'R' => + name = "\n"; + 'T' => + name = "\t"; + 'N' => + # num lock + if(rec == digits) + rec = csets[0]; + else + rec = digits; + name = nil; + * => + if(c >= 'A' && c <= 'Z'){ # other gestures, not yet implemented + shift = 0; + punct = 0; + rec = csets[0]; + name = nil; + unknown(top); + break; + } + if(punct){ + rec = csets[0]; + punct = 0; + } + if(shift){ + for(i := 0; i < len name; i++) + if((c = name[i]) >= 'a' && c <= 'z') + name[i] += 'A'-'a'; + if(shift < 2) + shift = 0; + } + } + }else if(tap != nil){ + if(punct == 0){ + if(len csets > 2){ + rec = csets[2]; + punct = 1; + } + name = nil; + }else{ + rec = csets[0]; + punct = 0; + name = "."; + } + }else + unknown(top); + if(name != nil){ + drawchars(top, name); + for(i := 0; i < len name; i++) + sys->fprint(top.ctxt.connfd, "key %d", name[i]); + # tk->keyboard(top, name[i]); + } + tkcmd(top, ".c delete stuff"); + npoint = 0; + * => + if(npoint){ + q := points[npoint-1]; + points[npoint++] = p; + tkcmd(top, sys->sprint(".c create line %d %d %d %d -tags stuff; update", q.x, q.y, p.x, p.y)); + } + } + } + } +} + +unknown(top: ref Tk->Toplevel) +{ + drawquery(top, (10, 10), 3); + tk->cmd(top, "update"); + sys->sleep(300); + tkcmd(top, ".c delete query"); + tk->cmd(top, "update"); +} + +drawchars(top: ref Tk->Toplevel, s: string) +{ + t := ""; + for(i := 0; i < len s; i++){ + c := s[i]; + case c { + '\n' => t += "\\n"; + '\b' => t += "\\b"; + '\t' => t += "\\t"; + 4 => t += "eot"; + * => + if(c < ' ') + t += sys->sprint("\\%3.3o", c); + else + t[len t] = c; + } + } + tkcmd(top, ".c itemconfigure char -text '"+t); +} + +drawmode(top: ref Tk->Toplevel, mode: string) +{ + tkcmd(top, ".c itemconfigure mode -text '"+mode); +} + +drawquery(top: ref Tk->Toplevel, p: Point, scale: int) +{ + width := 2; + size := 1<<scale; + if(size < 4) + width = 1; + o := Point(p.x-size/2, p.x+size/2); + if(o.x < 0) + o.x = 0; + if(o.y < 0) + o.y = 0; + c := o.add((size, size)); + m := o.add(c).div(2); + b := c.add((0, size)); + tkcmd(top, sys->sprint(".c create arc %d %d %d %d -start 150 -extent -240 -style arc -tags query -width %d -outline red", o.x, o.y, c.x, c.y, width)); + tkcmd(top, sys->sprint(".c create line %d %d %d %d -fill red -width %d -tags query", m.x, c.y, m.x, b.y, width)); + tkcmd(top, sys->sprint(".c create arc %d %d %d %d -start 0 -extent 360 -fill red -width %d -tags query -style arc -outline red", m.x-width, b.y+2*width, m.x+width, b.y+3*width, width)); +} + +tkcmd(top: ref Tk->Toplevel, s: string) +{ + e := tk->cmd(top, s); + if(e != nil && e[0]=='!') + sys->fprint(sys->fildes(2), "pen: tk error: %s in [%s]\n", e, s); +} + +drawshape(top: ref Tk->Toplevel, tag: string, stroke: ref Stroke, colour: string, dompts: ref Stroke, domcol: string) +{ + if(top == nil) + return; + if(stroke != nil) + for(i := 1; i < stroke.npts; i++){ + p := stroke.pts[i-1]; + q := stroke.pts[i]; + tkcmd(top, sys->sprint(".c create line %d %d %d %d -fill %s -tags %s", p.x, p.y, q.x, q.y, colour, tag)); + } + if(dompts != nil) + for(i = 0; i < dompts.npts; i++){ + p := dompts.pts[i]; + tkcmd(top, sys->sprint(".c create oval %d %d %d %d -fill %s -tags %s", p.x-1, p.y-1, p.x+1, p.y+1, domcol, tag)); + } + tk->cmd(top, "update"); +} + +# +# duplicate function of strokes module temporarily +# to allow for experiment +# + +#DIST_THLD: con 3200; # x100 +DIST_THLD: con 3300; # x100 + +# Tap-handling parameters +TAP_TIME_THLD: con 150; # msec +TAP_DIST_THLD: con 75; # dx*dx + dy*dy +TAP_PATHLEN: con 10*100; # x100 + +recognize_stroke(top: ref Tk->Toplevel, rec: ref Classifier, stroke: ref Stroke, debug: int): (int, string) +{ + + if(stroke.npts < 1) + return (-1, nil); + + stroke = stroke.filter(); # filter out close points + + if(stroke.npts == 1 || stroke.length() < TAP_PATHLEN) + return (-1, "."); # considered a tap regardless of elapsed time + + strokes->preprocess_stroke(stroke); + + # Compute its dominant points. + dompts := stroke.interpolate().dominant(); + + if(debug) + drawshape(top, "stuff", stroke, "green", dompts, "red"); + + if(rec == nil) + return (-1, nil); + + best_dist := Strokes->MAXDIST; + best_i := -1; + + # Score input stroke against every class in classifier. + for(i := 0; i < rec.nclasses; i++){ + name := rec.cnames[i]; + (sim, dist) := strokes->score_stroke(dompts, rec.dompts[i]); + if(debug > 1 && dist < Strokes->MAXDIST) + sys->fprint(stderr, "(%s, %d, %d) ", name, sim, dist); + if(dist < DIST_THLD){ + if(debug > 1) + sys->fprint(stderr, "(%s, %d, %d) ", name, sim, dist); + # Is it the best so far? + if(dist < best_dist){ + best_dist = dist; + best_i = i; + } + } + } + + if(debug > 1) + sys->fprint(stderr, "\n"); + + return (best_i, nil); +} + +objrect(t: ref Tk->Toplevel, path: string, addbd: int): Rect +{ + r: Rect; + r.min.x = int tk->cmd(t, path+" cget -actx"); + if(addbd) + r.min.x += int tk->cmd(t, path+" cget -bd"); + r.min.y = int tk->cmd(t, ".f cget -acty"); + if(addbd) + r.min.y += int tk->cmd(t, path+" cget -bd"); + r.max.x = r.min.x + int tk->cmd(t, path+" cget -actwidth"); + r.max.y = r.min.y + int tk->cmd(t, path+" cget -actheight"); + return r; +} + +nomod(s: string) +{ + error(sys->sprint("can't load %s: %r", s)); +} + +error(s: string) +{ + sys->fprint(sys->fildes(2), "scribble: %s\n", s); + raise "fail:error"; +} diff --git a/appl/wm/polyhedra.b b/appl/wm/polyhedra.b new file mode 100644 index 00000000..b6d7088d --- /dev/null +++ b/appl/wm/polyhedra.b @@ -0,0 +1,800 @@ +implement WmPolyhedra; + +include "sys.m"; + sys: Sys; +include "draw.m"; + draw: Draw; + Point, Rect, Pointer, Image, Screen, Display: import draw; +include "tk.m"; + tk: Tk; + Toplevel: import tk; +include "tkclient.m"; + tkclient: Tkclient; +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; +include "math.m"; + math: Math; + sin, cos, tan, sqrt: import math; +include "rand.m"; + rand: Rand; +include "daytime.m"; + daytime: Daytime; +include "math/polyhedra.m"; + polyhedra: Polyhedra; + Polyhedron: import Polyhedra; + scanpolyhedra, getpolyhedron: import polyhedra; +include "math/polyfill.m"; + polyfill: Polyfill; + initzbuf, clearzbuf, fillpoly: import polyfill; +include "smenu.m"; + smenu: Smenu; + Scrollmenu: import smenu; + +WmPolyhedra : module +{ + init : fn(nil : ref Draw->Context, argv : list of string); +}; + +WIDTH, HEIGHT: con 400; + +mainwin: ref Toplevel; +Disp, black, white, opaque: ref Image; +Dispr: Rect; +pinit := 40; + +init(ctxt : ref Draw->Context, argv : list of string) +{ + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + bufio = load Bufio Bufio->PATH; + math = load Math Math->PATH; + rand = load Rand Rand->PATH; + daytime = load Daytime Daytime->PATH; + polyhedra = load Polyhedra Polyhedra->PATH; + polyfill = load Polyfill Polyfill->PATH; + smenu = load Smenu Smenu->PATH; + rand->init(daytime->now()); + daytime = nil; + polyfill->init(); + √2 = sqrt(2.0); + √3 = sqrt(3.0); + cursor := ""; + + tkclient->init(); + if(ctxt == nil){ + ctxt = tkclient->makedrawcontext(); + # sys->fprint(sys->fildes(2), "wm not running\n"); + # exit; + } + argv = tl argv; + while(argv != nil){ + case hd argv{ + "-p" => + argv = tl argv; + if(argv != nil) + pinit = int hd argv; + "-r" => + pinit = -1; + "-c" => + argv = tl argv; + if(argv != nil) + cursor = hd argv; + } + if(argv != nil) + argv = tl argv; + } + (win, wmcmd) := tkclient->toplevel(ctxt, "", "Polyhedra", Tkclient->Resize | Tkclient->Hide); + mainwin = win; + sys->pctl(Sys->NEWPGRP, nil); + cmdch := chan of string; + tk->namechan(win, cmdch, "cmd"); + for(i := 0; i < len win_config; i++) + cmd(win, win_config[i]); + if(cursor != nil) + cmd(win, "cursor -bitmap " + cursor); + tkclient->onscreen(win, nil); + tkclient->startinput(win, "kbd"::"ptr"::nil); + fittoscreen(win); + pid := -1; + sync := chan of int; + chanθ := chan of real; + geo := newgeom(); + setimage(win, geo); + cmd(win, "update"); + display := win.image.display; + white = display.color(Draw->White); + black = display.color(Draw->Black); + opaque = display.opaque; + shade = array[NSHADES] of ref Image; + for(i = 0; i < NSHADES; i++){ + # v := (255*i)/(NSHADES-1); # NSHADES=17 + v := (192*i)/(NSHADES-1)+32; # NSHADES=13 + # v := (128*i)/(NSHADES-1)+64; # NSHADES=9 + shade[i] = display.rgb(v, v, v); + # shade[i] = rgba(display, v, v, v, 16r7f); + } + (geo.npolyhedra, geo.polyhedra, geo.b) = scanpolyhedra("/lib/polyhedra.all"); + if(geo.npolyhedra == 0){ + sys->fprint(sys->fildes(2), "cannot open polyhedra database\n"); + exit; + } + yieldc := chan of int; + # spawn yieldproc(yieldc); + # ypid := <- yieldc; + initgeom(geo); + sm := array[2] of ref Scrollmenu; + sm[0] = scrollmenu(win, ".f.menu", geo.polyhedra, geo.npolyhedra, 0); + sm[1] = scrollmenu(win, ".f.menud", geo.polyhedra, geo.npolyhedra, 1); + # createmenu(win, geo.polyhedra); + spawn drawpolyhedron(geo, sync, chanθ, yieldc); + pid = <- sync; + newproc := 0; + + for(;;){ + alt{ + c := <-win.ctxt.kbd => + tk->keyboard(win, c); + c := <-win.ctxt.ptr => + tk->pointer(win, *c); + c := <-win.ctxt.ctl or + c = <-win.wreq => + tkclient->wmctl(win, c); + c := <- wmcmd => + case c{ + "exit" => + exits(pid, sm); + * => + sync <-= 0; + tkclient->wmctl(win, c); + if(c[0] == '!'){ + if(setimage(win, geo) <= 0) + exits(pid, sm); + } + sync <-= 1; + } + c := <- cmdch => + (nil, toks) := sys->tokenize(c, " "); + case hd toks{ + "prev" => + geo.curpolyhedron = geo.curpolyhedron.prv; + getpoly(geo, -1); + newproc = 1; + "next" => + geo.curpolyhedron = geo.curpolyhedron.nxt; + getpoly(geo, 1); + newproc = 1; + "dual" => + geo.dual = !geo.dual; + newproc = 1; + "edges" => + edges = !edges; + "faces" => + faces = !faces; + "clear" => + clear = !clear; + "slow" => + if(geo.θ > ε){ + if(geo.θ < 2.) + chanθ <-= geo.θ/2.; + else + chanθ <-= geo.θ-1.; + } + "fast" => + if(geo.θ < 45.){ + if(geo.θ < 1.) + chanθ <-= 2.*geo.θ; + else + chanθ <-= geo.θ+1.; + } + "axis" => + setaxis(geo); + initmatrix(geo); + newproc = 1; + "menu" => + x := int cmd(win, ".p cget actx"); + y := int cmd(win, ".p cget acty"); + w := int cmd(win, ".p cget -actwidth"); + h := int cmd(win, ".p cget -actheight"); + sm[geo.dual].post(x+w/8, y+h/8, cmdch, ""); + # cmd(win, ".f.menu post " + x + " " + y); + * => + i = int hd toks; + fp := geo.polyhedra; + for(p := fp; p != nil; p = p.nxt){ + if(p.indx == i){ + geo.curpolyhedron = p; + getpoly(geo, 1); + newproc = 1; + break; + } + if(p.nxt == fp) + break; + } + } + } + if(newproc){ + sync <-= 0; # stop it first + kill(pid); + spawn drawpolyhedron(geo, sync, chanθ, yieldc); + pid = <- sync; + newproc = 0; + } + } +} + +setimage(win: ref Toplevel, geo: ref Geom): int +{ + panelw := int tk->cmd(win, ".p cget -actwidth"); + panelh := int tk->cmd(win, ".p cget -actheight"); + if(panelw < 3) + panelw = 3; + if(panelh < 3) + panelh = 3; + Dispr = Rect((0,0), (panelw, panelh)); + Disp = win.image.display.newimage(Dispr, win.image.chans, 0, Draw->Black); + if(Disp == nil){ + sys->fprint(sys->fildes(2), "not enough image memory\n"); + return 0; + } + tk->putimage(win, ".p", Disp, nil); + if(Dispr.dx() > Dispr.dy()) + h := Dispr.dy(); + else + h = Dispr.dx(); + rr: Rect = ((0, 0), (h, h)); + corner := ((Dispr.min.x+Dispr.max.x-rr.max.x)/2, (Dispr.min.y+Dispr.max.y-rr.max.y)/2); + geo.r = (rr.min.add(corner), rr.max.add(corner)); + geo.h = h; + geo.sx = real ((3*h)/8); + geo.sy = - real ((3*h)/8); + geo.tx = h/2+geo.r.min.x; + geo.ty = h/2+geo.r.min.y; + geo.zstate = initzbuf(geo.r); + return 1; +} + +# yieldcpu(c: chan of int) +# { +# c <-= 1; +# <-c; +# } + +# yieldproc(c: chan of int) +# { +# c <-= sys->pctl(0, nil); +# for (;;) { +# <-c; +# c <-= 1; +# } +# } + +π: con Math->Pi; +√2, √3: real; +∞: con 1<<30; +ε: con 0.001; + +Axis: adt{ + λ, μ, ν: int; +}; + +Vector: adt{ + x, y, z: real; +}; + +Geom: adt{ + h: int; # length, breadth of r below + r: Rect; # area on screen to update + sx, sy: real; # x, y scale + tx, ty: int; # x, y translation + θ: real; # angle of rotation + TM: array of array of real; # rotation matrix + axis: Axis; # direction cosines of rotation + view: Vector; + light: Vector; + npolyhedra: int; + polyhedra: ref Polyhedron; + curpolyhedron: ref Polyhedron; + b: ref Iobuf; # of polyhedra file + dual: int; + zstate: ref Polyfill->Zstate; +}; + +NSHADES: con 13; # odd +shade: array of ref Image; + +clear, faces: int = 1; +edges: int = 0; + +setview(geo: ref Geom) +{ + geo.view = (0.0, 0.0, 1.0); + geo.light = (0.0, -1.0, 0.0); +} + +map(v: Vector, geo: ref Geom): Point +{ + return (int (geo.sx*v.x)+geo.tx, int (geo.sy*v.y)+geo.ty); +} + +minus(v1: Vector): Vector +{ + return (-v1.x, -v1.y, -v1.z); +} + +add(v1, v2: Vector): Vector +{ + return (v1.x+v2.x, v1.y+v2.y, v1.z+v2.z); +} + +sub(v1, v2: Vector): Vector +{ + return (v1.x-v2.x, v1.y-v2.y, v1.z-v2.z); +} + +mul(v1: Vector, l: real): Vector +{ + return (l*v1.x, l*v1.y, l*v1.z); +} + +div(v1: Vector, l: real): Vector +{ + return (v1.x/l, v1.y/l, v1.z/l); +} + +normalize(v1: Vector): Vector +{ + return div(v1, sqrt(dot(v1, v1))); +} + +dot(v1, v2: Vector): real +{ + return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z; +} + +cross(v1, v2: Vector): Vector +{ + return (v1.y*v2.z-v2.y*v1.z, v1.z*v2.x-v2.z*v1.x, v1.x*v2.y-v2.x*v1.y); +} + +drawpolyhedron(geo: ref Geom, sync: chan of int, chanθ: chan of real, yieldc: chan of int) +{ + s: string; + + sync <-= sys->pctl(0, nil); + p := geo.curpolyhedron; + if(!geo.dual || p.anti){ + s = p.name; + s += " (" + string p.indx + ")"; + puts(s); + drawpolyhedron0(p.V, p.F, p.concave, p.allf || p.anti, p.v, p.f, p.fv, p.inc, geo, sync, chanθ, yieldc); + } + else{ + s = p.dname; + s += " (" + string p.indx + ")"; + puts(s); + drawpolyhedron0(p.F, p.V, p.concave, p.anti, p.f, p.v, p.vf, 0.0, geo, sync, chanθ, yieldc); + } +} + +drawpolyhedron0(V, F, concave, allf: int, v, f: array of Vector, fv: array of array of int, inc: real, geo: ref Geom, sync: chan of int, chanθ: chan of real, yieldc: chan of int) +{ + norm : array of array of Vector; + newn, oldn : array of Vector; + + yieldc = nil; # not used now + θ := geo.θ; + totθ := 0.; + if(θ != 0.) + n := int ((360.+θ/2.)/θ); + else + n = ∞; + p := n; + t := 0; + vec := array[2] of array of Vector; + vec[0] = array[V] of Vector; + vec[1] = array[V] of Vector; + if(concave){ + norm = array[2] of array of Vector; + norm[0] = array[F] of Vector; + norm[1] = array[F] of Vector; + } + Disp.draw(geo.r, black, opaque, (0, 0)); + reveal(geo.r); + for(i := 0; ; i = (i+1)%p){ + alt{ + <- sync => + <- sync; + θ = <- chanθ => + geo.θ = θ; + initmatrix(geo); + if(θ != 0.){ + n = int ((360.+θ/2.)/θ); + p = int ((360.-totθ+θ/2.)/θ); + } + else + n = p = ∞; + if(p == 0) + i = 0; + else + i = 1; + * => + # yieldcpu(yieldc); + sys->sleep(0); + } + if(concave) + clearzbuf(geo.zstate); + new := vec[t]; + old := vec[!t]; + if(concave){ + newn = norm[t]; + oldn = norm[!t]; + } + t = !t; + if(i == 0){ + for(j := 0; j < V; j++) + new[j] = v[j]; + if(concave){ + for(j = 0; j < F; j++) + newn[j] = f[j]; + } + setview(geo); + totθ = 0.; + p = n; + } + else{ + for(j := 0; j < V; j++) + new[j] = mulm(geo.TM, old[j]); + if(concave){ + for(j = 0; j < F; j++) + newn[j] = mulm(geo.TM, oldn[j]); + } + else{ + geo.view = mulmi(geo.TM, geo.view); + geo.light = mulmi(geo.TM, geo.light); + } + totθ += θ; + } + if(clear) + Disp.draw(geo.r, black, opaque, (0, 0)); + for(j := 0; j < F; j++){ + if(concave){ + if(allf || dot(geo.view, newn[j]) < 0.0) + polyfilla(fv[j], new, newn[j], dot(geo.light, newn[j]), geo, concave, inc); + } + else{ + if(dot(geo.view, f[j]) < 0.0) + polyfilla(fv[j], new, f[j], dot(geo.light, f[j]), geo, concave, 0.0); + } + } + reveal(geo.r); + } +} + +ZSCALE: con real (1<<20); +LIMIT: con real (1<<11); + +polyfilla(fv: array of int, v: array of Vector, f: Vector, ill: real, geo: ref Geom, concave: int, inc: real) +{ + dc, dx, dy: int; + + d := 0.0; + n := fv[0]; + ap := array[n+1] of Point; + for(j := 0; j < n; j++){ + vtx := v[fv[j+1]]; + # vtx = add(vtx, mul(f, 0.1)); # interesting effects with -/larger factors + ap[j] = map(vtx, geo); + d += dot(f, vtx); + } + ap[n] = ap[0]; + d /= real n; + if(concave){ + if(fv[n+1] != 1) + d += inc; + if(f.z > -ε && f.z < ε) + return; + α := geo.sx; + β := real geo.tx; + γ := geo.sy; + δ := real geo.ty; + c := f.z; + a := -f.x/(c*α); + if(a <= -LIMIT || a >= LIMIT) + return; + b := -f.y/(c*γ); + if(b <= -LIMIT || b >= LIMIT) + return; + d = d/c-β*a-δ*b; + if(d <= -LIMIT || d >= LIMIT) + return; + dx = int (a*ZSCALE); + dy = int (b*ZSCALE); + dc = int (d*ZSCALE); + } + edge := white; + face := shade[int ((real ((NSHADES-1)/2))*(1.0-ill))]; + if(concave){ + if(!faces) + face = black; + if(!edges) + edge = nil; + fillpoly(Disp, ap, ~0, face, (0, 0), geo.zstate, dc, dx, dy); + } + else{ + if(faces) + Disp.fillpoly(ap, ~0, face, (0, 0)); + if(edges) + Disp.poly(ap, Draw->Endsquare, Draw->Endsquare, 0, edge, (0, 0)); + } +} + +getpoly(geo: ref Geom, dir: int) +{ + p := geo.curpolyhedron; + if(0){ + while(p.anti){ + if(dir > 0) + p = p.nxt; + else + p = p.prv; + } + } + geo.curpolyhedron = p; + getpolyhedron(p, geo.b); +} + +degtorad(α: real): real +{ + return α*π/180.0; +} + +initmatrix(geo: ref Geom) +{ + TM := geo.TM; + φ := degtorad(geo.θ); + sinθ := sin(φ); + cosθ := cos(φ); + (l, m, n) := normalize((real geo.axis.λ, real geo.axis.μ, real geo.axis.ν)); + f := 1.0-cosθ; + TM[1][1] = (1.0-l*l)*cosθ + l*l; + TM[1][2] = l*m*f-n*sinθ; + TM[1][3] = l*n*f+m*sinθ; + TM[2][1] = l*m*f+n*sinθ; + TM[2][2] = (1.0-m*m)*cosθ + m*m; + TM[2][3] = m*n*f-l*sinθ; + TM[3][1] = l*n*f-m*sinθ; + TM[3][2] = m*n*f+l*sinθ; + TM[3][3] = (1.0-n*n)*cosθ + n*n; +} + +mulm(TM: array of array of real, v: Vector): Vector +{ + x := v.x; + y := v.y; + z := v.z; + v.x = TM[1][1]*x + TM[1][2]*y + TM[1][3]*z; + v.y = TM[2][1]*x + TM[2][2]*y + TM[2][3]*z; + v.z = TM[3][1]*x + TM[3][2]*y + TM[3][3]*z; + return v; +} + +mulmi(TM: array of array of real, v: Vector): Vector +{ + x := v.x; + y := v.y; + z := v.z; + v.x = TM[1][1]*x + TM[2][1]*y + TM[3][1]*z; + v.y = TM[1][2]*x + TM[2][2]*y + TM[3][2]*z; + v.z = TM[1][3]*x + TM[2][3]*y + TM[3][3]*z; + return v; +} + +reveal(r: Rect) +{ + cmd := sys->sprint(".p dirty %d %d %d %d", r.min.x, r.min.y, r.max.x, r.max.y); + tk->cmd(mainwin, cmd); + tk->cmd(mainwin, "update"); +} + +newgeom(): ref Geom +{ + geo := ref Geom; + TM := array[4] of array of real; + for(i := 0; i < 4; i++) + TM[i] = array[4] of real; + geo.θ = 10.; + geo.TM = TM; + geo.axis = (1, 1, 1); + geo.view = (1., 1., 1.); + geo.light = (1., 1., 1.); + geo.dual = 0; + return geo; +} + +setaxis(geo: ref Geom) +{ + oaxis := geo.axis; + # while(geo.axis == Axis (0, 0, 0) || geo.axis = oaxis) not allowed + while((geo.axis.λ == 0 && geo.axis.μ == 0 && geo.axis.ν == 0) || (geo.axis.λ == oaxis.λ && geo.axis.μ == oaxis.μ && geo.axis.ν == oaxis.ν)) + geo.axis = (rand->rand(5) - 2, rand->rand(5) - 2, rand->rand(5) - 2); +} + +initgeom(geo: ref Geom) +{ + if(pinit < 0) + pn := rand->rand(geo.npolyhedra); + else + pn = pinit; + for(p := geo.polyhedra; --pn >= 0; p = p.nxt) + ; + geo.curpolyhedron = p; + getpoly(geo, 1); + setaxis(geo); + geo.θ = real (rand->rand(5)+1); + geo.dual = 0; + initmatrix(geo); + setview(geo); + Disp.draw(geo.r, black, opaque, (0, 0)); + reveal(geo.r); +} + +kill(pid: int): int +{ + fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE); + if(fd == nil) + return -1; + if(sys->write(fd, array of byte "kill", 4) != 4) + return -1; + return 0; +} + +exits(pid: int, sm: array of ref Scrollmenu) +{ + if(pid != -1) + kill(pid); + # kill(ypid); + sm[0].destroy(); + sm[1].destroy(); + exit; +} + +cmd(top: ref Toplevel, s: string): string +{ + e := tk->cmd(top, s); + if (e != nil && e[0] == '!') + sys->fprint(sys->fildes(2), "polyhedra: tk error on '%s': %s\n", s, e); + return e; +} + +puts(s: string) +{ + cmd(mainwin, ".f1.txt configure -text {" + s + "}"); + cmd(mainwin, "update"); +} + +MENUMAX: con 10; + +scrollmenu(top: ref Tk->Toplevel, mname: string, p: ref Polyhedron, n: int, dual: int): ref Scrollmenu +{ + labs := array[n] of string; + i := 0; + for(q := p; q != nil && i < n; q = q.nxt){ + if(dual) + name := q.dname; + else + name = q.name; + labs[i++] = string q.indx + " " + name; + } + sm := Scrollmenu.new(top, mname, labs, MENUMAX, (n-MENUMAX)/2); + cmd(top, mname + " configure -borderwidth 3"); + return sm; +} + +createmenu(top: ref Tk->Toplevel, p: ref Polyhedron) +{ + mn := ".f.menu"; + cmd(top, "menu " + mn); + i := j := 0; + for(q := p ; q != nil; q = q.nxt){ + cmd(top, mn + " add command -label {" + string q.indx + " " + q.name + "} -command {send cmd " + string q.indx + "}"); + if(q.nxt == p) + break; + i++; + j++; + if(j == MENUMAX && q.nxt != nil){ + cmd(top, mn + " add cascade -label MORE -menu " + mn + ".menu"); + mn += ".menu"; + cmd(top, "menu " + mn); + j = 0; + } + } +} + +fittoscreen(win: ref Tk->Toplevel) +{ + Point: import draw; + if (win.image == nil || win.image.screen == nil) + return; + r := win.image.screen.image.r; + scrsize := Point((r.max.x - r.min.x), (r.max.y - r.min.y)); + bd := int cmd(win, ". cget -bd"); + winsize := Point(int cmd(win, ". cget -actwidth") + bd * 2, int cmd(win, ". cget -actheight") + bd * 2); + if (winsize.x > scrsize.x) + cmd(win, ". configure -width " + string (scrsize.x - bd * 2)); + if (winsize.y > scrsize.y) + cmd(win, ". configure -height " + string (scrsize.y - bd * 2)); + actr: Rect; + actr.min = Point(int cmd(win, ". cget -actx"), int cmd(win, ". cget -acty")); + actr.max = actr.min.add((int cmd(win, ". cget -actwidth") + bd*2, + int cmd(win, ". cget -actheight") + bd*2)); + (dx, dy) := (actr.dx(), actr.dy()); + if (actr.max.x > r.max.x) + (actr.min.x, actr.max.x) = (r.max.x - dx, r.max.x); + if (actr.max.y > r.max.y) + (actr.min.y, actr.max.y) = (r.max.y - dy, r.max.y); + if (actr.min.x < r.min.x) + (actr.min.x, actr.max.x) = (r.min.x, r.min.x + dx); + if (actr.min.y < r.min.y) + (actr.min.y, actr.max.y) = (r.min.y, r.min.y + dy); + cmd(win, ". configure -x " + string actr.min.x + " -y " + string actr.min.y); +} + +win_config := array[] of { + "frame .f", + "button .f.prev -text {prev} -command {send cmd prev}", + "button .f.next -text {next} -command {send cmd next}", + "checkbutton .f.dual -text {dual} -command {send cmd dual} -variable dual", + ".f.dual deselect", + "pack .f.prev -side left", + "pack .f.next -side right", + "pack .f.dual -side top", + + "frame .f0", + "checkbutton .f0.edges -text {edges} -command {send cmd edges} -variable edges", + ".f0.edges deselect", + "checkbutton .f0.faces -text {faces} -command {send cmd faces} -variable faces", + ".f0.faces select", + "checkbutton .f0.clear -text {clear} -command {send cmd clear} -variable clear", + ".f0.clear select", + "pack .f0.edges -side left", + "pack .f0.faces -side right", + "pack .f0.clear -side top", + + "frame .f2", + "button .f2.slow -text {slow} -command {send cmd slow}", + "button .f2.fast -text {fast} -command {send cmd fast}", + "button .f2.axis -text {axis} -command {send cmd axis}", + "pack .f2.slow -side left", + "pack .f2.fast -side right", + "pack .f2.axis -side top", + + "frame .f1", + "label .f1.txt -text { } -width " + string WIDTH, + "pack .f1.txt -side top -fill x", + + "frame .f3", + "button .f3.menu -text {menu} -command {send cmd menu}", + "pack .f3.menu -side left", + + "frame .pbd -bd 3", + "panel .p -width " + string WIDTH + " -height " + string HEIGHT, + + "pack .f -side top -fill x", + "pack .f0 -side top -fill x", + "pack .f2 -side top -fill x", + "pack .f1 -side top -fill x", + "pack .f3 -side top -fill x", + "pack .p -in .pbd -fill both -expand 1", + "pack .pbd -side bottom -fill both -expand 1", + "pack propagate . 0", + +}; + +rgba(d: ref Display, r: int, g: int, b: int, α: int): ref Image +{ + c := draw->setalpha((r<<24)|(g<<16)|(b<<8), α); + return d.newimage(((0, 0), (1, 1)), d.image.chans, 1, c); +} diff --git a/appl/wm/prof.b b/appl/wm/prof.b new file mode 100644 index 00000000..71327e52 --- /dev/null +++ b/appl/wm/prof.b @@ -0,0 +1,323 @@ +implement Wmprof; + +include "sys.m"; + sys: Sys; +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; +include "draw.m"; + draw: Draw; +include "tk.m"; + tk: Tk; +include "tkclient.m"; + tkclient: Tkclient; +include "arg.m"; + arg: Arg; +include "profile.m"; + +Prof: module{ + init0: fn(ctxt: ref Draw->Context, argv: list of string): Profile->Prof; +}; + +prof: Prof; + +Wmprof: module{ + init: fn(ctxt: ref Draw->Context, argl: list of string); +}; + +usage(s: string) +{ + sys->fprint(sys->fildes(2), "wm/prof: %s\n", s); + sys->fprint(sys->fildes(2), "usage: wm/prof [-e] [-m modname]... cmd [arg ... ]"); + exit; +} + +TXTBEGIN: con 3; + +init(ctxt: ref Draw->Context, argl: list of string) +{ + sys = load Sys Sys->PATH; + bufio = load Bufio Bufio->PATH; + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + arg = load Arg Arg->PATH; + + if(ctxt == nil) + fatal("wm not running"); + sys->pctl(Sys->NEWPGRP, nil); + + arg->init(argl); + while((o := arg->opt()) != 0){ + case(o){ + 'e' => ; + 'm' => + if(arg->arg() == nil) + usage("missing module/file"); + 's' => + if(arg->arg() == nil) + usage("missing sample rate"); + * => + usage(sys->sprint("unknown option -%c", o)); + } + } + + stats := execprof(ctxt, argl); + if(stats.mods == nil) + exit; + + tkclient->init(); + (win, wmc) := tkclient->toplevel(ctxt, nil, hd argl, Tkclient->Resize|Tkclient->Hide); + tkc := chan of string; + tk->namechan(win, tkc, "tkc"); + for(i := 0; i < len wincfg; i++) + cmd(win, wincfg[i]); + tkclient->onscreen(win, nil); + tkclient->startinput(win, "kbd"::"ptr"::nil); + createmenu(win, stats); + curc := 0; + cura := newprint(win, stats, curc); + + for(;;){ + alt{ + c := <-win.ctxt.kbd => + tk->keyboard(win, c); + c := <-win.ctxt.ptr => + tk->pointer(win, *c); + c := <-win.ctxt.ctl or + c = <-win.wreq or + c = <-wmc => + tkclient->wmctl(win, c); + c := <- tkc => + (nil, toks) := sys->tokenize(c, " "); + case(hd toks){ + "b" => + if(curc > 0) + cura = newprint(win, stats, --curc); + "f" => + if(curc < len stats.mods - 1) + cura = newprint(win, stats, ++curc); + "s" => + if(cura != nil) + scroll(win, cura); + "m" => + x := cmd(win, ".f cget actx"); + y := cmd(win, ".f cget acty"); + cmd(win, ".f.menu post " + x + " " + y); + * => + curc = int hd toks; + cura = newprint(win, stats, curc); + } + } + } +} + +execprof(ctxt: ref Draw->Context, argl: list of string): Profile->Prof +{ + { + prof = load Prof "/dis/prof.dis"; + if(prof == nil) + fatal("cannot load profiler"); + return prof->init0(ctxt, hd argl :: "-g" :: tl argl); + } + exception{ + "fail:*" => + return (nil, 0, nil); + } + return (nil, 0, nil); +} + +newprint(win: ref Tk->Toplevel, p: Profile->Prof, i: int): array of int +{ + cmd(win, ".f.t delete 1.0 end"); + cmd(win, "update"); + m0, m1: list of Profile->Modprof; + for(m := p.mods; m != nil && --i >= 0; m = tl m) + m0 = m; + if(m == nil) + return nil; + m1 = tl m; + (name, nil, spath, nil, line, nil, nil, tot, nil, nil) := hd m; + name0 := name1 := "nil"; + if(m0 != nil) + name0 = (hd m0).name; + if(m1 != nil) + name1 = (hd m1).name; + a := len name; + name += sys->sprint(" (%d%%) ", percent(tot, p.total)); + cmd(win, ".f.t insert end {" + name + " <- " + name0 + " -> " + name1 + "}"); + tag := gettag(win, tot, p.total); + cmd(win, ".f.t tag add " + tag + " " + "1.0" + " " + "1." + string a); + cmd(win, ".f.t insert end \n\n"); + cmd(win, "update"); + lineno := TXTBEGIN; + bio := bufio->open(spath, Bufio->OREAD); + if(bio == nil) + return nil; + i = 1; + ll := len line; + while((s := bio.gets('\n')) != nil){ + f := 0; + if(i < ll) + f = line[i]; + a = len s; + if(f > 0) + s = sys->sprint("%d%%\t%s", percent(f, tot), s); + else + s = sys->sprint("- \t%s", s); + b := len s; + cmd(win, ".f.t insert end " + tk->quote(s)); + tag = gettag(win, f, tot); + cmd(win, ".f.t tag add " + tag + " " + string lineno + "." + string (b-a) + " " + string lineno + "." + string (b-1)); + cmd(win, "update"); + lineno++; + i++; + } + return line; +} + +index(win: ref Tk->Toplevel, x: int, y: int): int +{ + t := cmd(win, ".f.t index @" + string x + "," + string y); + (nil, l) := sys->tokenize(t, "."); +# sys->print("%d,%d -> %s\n", x, y, t); + return int hd l; +} + +winextent(win: ref Tk->Toplevel): (int, int) +{ + w := int cmd(win, ".f.t cget -actwidth"); + h := int cmd(win, ".f.t cget -actheight"); + lw := index(win, 0, 0); + uw := index(win, w-1, h-1); + return (lw, uw); +} + +see(win: ref Tk->Toplevel, line: int) +{ + cmd(win, ".f.t see " + string line + ".0"); + cmd(win, "update"); +} + +scroll(win: ref Tk->Toplevel, line: array of int) +{ + (nil, uw) := winextent(win); + lno := TXTBEGIN; + ll := len line; + for(i := 1; i < ll; i++){ + n := line[i]; + if(n > 0 && lno > uw){ + see(win, lno); + return; + } + lno++; + } + lno = TXTBEGIN; + ll = len line; + for(i = 1; i < ll; i++){ + n := line[i]; + if(n > 0){ + see(win, lno); + return; + } + lno++; + } +} + +cmd(top: ref Tk->Toplevel, s: string): string +{ + # sys->print("%s\n", s); + e := tk->cmd(top, s); + if (e != nil && e[0] == '!') + sys->fprint(sys->fildes(2), "tk error on '%s': %s\n", s, e); + return e; +} + +fatal(s: string) +{ + sys->fprint(sys->fildes(2), "%s\n", s); + exit; +} + +MENUMAX: con 20; + +createmenu(top: ref Tk->Toplevel, p: Profile->Prof ) +{ + mn := ".f.menu"; + cmd(top, "menu " + mn); + i := j := 0; + for(m := p.mods; m != nil; m = tl m){ + name := (hd m).name; + cmd(top, mn + " add command -label " + name + " -command {send tkc " + string i + "}"); + i++; + j++; + if(j == MENUMAX && tl m != nil){ + cmd(top, mn + " add cascade -label MORE -menu " + mn + ".menu"); + mn += ".menu"; + cmd(top, "menu " + mn); + j = 0; + } + } +} + +tags := array[256] of { * => byte 0 }; + +gettag(win: ref Tk->Toplevel, n: int, d: int): string +{ + i := int ((real n/real d) * real 15); + if(i < 0 || i > 15) + i = 0; + s := "tag" + string i; + if(tags[i] == byte 0){ + rgb := "#" + hex2(255-64*0)+hex2(255-64*(i/4))+hex2(255-64*(i%4)); + cmd(win, ".f.t tag configure " + s + " -fg black -bg " + rgb); + tags[i] = byte 1; + } + return s; +} + +percent(n: int, d: int): int +{ + return int ((real n/real d) * real 100); +} + +hex(i: int): int +{ + if(i < 10) + return i+'0'; + else + return i-10+'A'; +} + +hex2(i: int): string +{ + s := "00"; + s[0] = hex(i/16); + s[1] = hex(i%16); + return s; +} + +wincfg := array[] of { + "frame .f", + "text .f.t -width 809 -height 500 -state disabled -wrap char -bg white -yscrollcommand {.f.s set}", + "scrollbar .f.s -orient vertical -command {.f.t yview}", + "frame .i", + "button .i.b -bitmap small_color_left.bit -command {send tkc b}", + "button .i.f -bitmap small_color_right.bit -command {send tkc f}", + "button .i.s -bitmap small_find.bit -command {send tkc s}", + "button .i.m -bitmap small_reload.bit -command {send tkc m}", + + "pack .i.b -side left", + "pack .i.f -side left", + "pack .i.s -side left", + "pack .i.m -side left", + + "pack .f.s -fill y -side left", + "pack .f.t -fill both -expand 1", + + "pack .i -fill x", + "pack .f -fill both -expand 1", + "pack propagate . 0", + + "update", +};
\ No newline at end of file diff --git a/appl/wm/qt.b b/appl/wm/qt.b new file mode 100644 index 00000000..de1cbcd8 --- /dev/null +++ b/appl/wm/qt.b @@ -0,0 +1,161 @@ +implement WmQt; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + +include "tk.m"; + tk: Tk; + Toplevel: import tk; + +include "tkclient.m"; + tkclient: Tkclient; + ctxt: ref Draw->Context; + +include "quicktime.m"; + qt: QuickTime; + +WmQt: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +Stopped, Playing: con iota; + +task_cfg := array[] of { + "canvas .c", + "frame .b", + "button .b.File -text File -command {send cmd file}", + "button .b.Stop -text Stop -command {send cmd stop}", + "button .b.Pause -text Pause -command {send cmd pause}", + "button .b.Play -text Play -command {send cmd play}", + "frame .f", + "label .f.file -text {File:}", + "label .f.name", + "pack .f.file .f.name -side left", + "pack .b.File .b.Stop .b.Pause .b.Play -side left", + "pack .f -fill x", + "pack .b -anchor w", + "pack .c -side bottom -fill both -expand 1", + "pack propagate . 0", +}; + +init(xctxt: ref Draw->Context, nil: list of string) +{ + sys = load Sys Sys->PATH; + if (ctxt == nil) { + sys->fprint(sys->fildes(2), "qt: no window context\n"); + raise "fail:bad context"; + } + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient= load Tkclient Tkclient->PATH; + + ctxt = xctxt; + + tkclient->init(); + (t, menubut) := tkclient->toplevel(ctxt.screen, "", "QuickTime Player", 0); + + cmd := chan of string; + tk->namechan(t, cmd, "cmd"); + + tkclient->tkcmds(t, task_cfg); + + tk->cmd(t, "bind . <Configure> {send cmd resize}"); + tk->cmd(t, "update"); + + qt = load QuickTime QuickTime->PATH; + if(qt == nil) { + tkclient->dialog(t, "error -fg red", "Load Module", + "Failed to load the QuickTime interface:\n"+ + sys->sprint("%r"), + 0, "Exit"::nil); + return; + } + qt->init(); + + fname := ""; + ctl := chan of string; + state := Stopped; + + for(;;) alt { + menu := <-menubut => + if(menu == "exit") + return; + tkclient->wmctl(t, menu); + press := <-cmd => + case press { + "file" => + pat := list of { + "*.mov (Apple QuickTime Movie)" + }; + fname = tkclient->filename(ctxt.screen, t, "Locate Movie", pat, ""); + if(fname != nil) { + s := fname; + if(len s > 25) + s = "..."+fname[len s - 25:]; + tk->cmd(t, ".f.name configure -text {"+s+"}"); + tk->cmd(t, "update"); + } + "play" => + if(fname != nil) + spawn play(t, fname); + } + } +} + +# +# Parse the atoms describing a movie +# +moov(t: ref Toplevel, q: ref QuickTime->QD) +{ + for(;;) { + (h, l) := qt->q.atomhdr(); + if(l < 0) + break; + case h { + * => + qt->q.skipatom(l); + "mvhd" => + err := qt->q.mvhd(l); + if(err == nil) + break; + tkclient->dialog(t, "error -fg red", "Parse Headers", + err, + 0, "Exit"::nil); + exit; + "trak" => + err := qt->q.trak(l); + if(err == nil) + break; + tkclient->dialog(t, "error -fg red", "Parse Track", + err, + 0, "Exit"::nil); + exit; + } + } +} + +play(t: ref Toplevel, file: string) +{ + (q, err) := qt->open(file); + if(err != nil) { + tkclient->dialog(t, "error -fg red", "Open Movie", + "Failed to open \""+file+"\"\n"+err, + 0, "Continue"::nil); + return; + } + for(;;) { + (h, l) := qt->q.atomhdr(); + if(l < 0) + break; + case h { + * => + qt->q.skipatom(l); + "moov" => + moov(t, q); + } + } +} diff --git a/appl/wm/readmail.b b/appl/wm/readmail.b new file mode 100644 index 00000000..e674bb4b --- /dev/null +++ b/appl/wm/readmail.b @@ -0,0 +1,885 @@ +implement WmReadmail; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + Context: import draw; + +include "tk.m"; + tk: Tk; + Toplevel: import tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "dialog.m"; + dialog: Dialog; + +include "selectfile.m"; + selectfile: Selectfile; + +include "string.m"; + str: String; + +include "keyring.m"; + kr: Keyring; + +WmReadmail: module +{ + init: fn(ctxt: ref Draw->Context, args: list of string); +}; + +WmSendmail: module +{ + init: fn(ctxt: ref Draw->Context, args: list of string); +}; + +srv: Sys->Connection; +main: ref Toplevel; +ctxt: ref Context; +nmesg: int; +cmesg: int; +map: array of byte; +Ok, Deleted: con iota; +username: string; + +mail_cfg := array[] of { + "frame .top", + "label .top.l -bitmap email.bit", + "frame .top.con", + "frame .top.con.b", + "button .top.con.b.con -bitmap mailcon -command {send msg connect}", + "bind .top.con.b.con <Enter> +{.top.status configure -text {connect/disconnect to mail server}}", + "button .top.con.b.next -bitmap mailnext -command {send msg next}", + "bind .top.con.b.next <Enter> +{.top.status configure -text {next message}}", + "button .top.con.b.prev -bitmap mailprev -command {send msg prev}", + "bind .top.con.b.prev <Enter> +{.top.status configure -text {previous message}}", + "button .top.con.b.del -bitmap maildel -command {send msg dele}", + "bind .top.con.b.del <Enter> +{.top.status configure -text {delete message}}", + "button .top.con.b.reply -bitmap mailreply -command {send msg reply}", + "bind .top.con.b.reply <Enter> +{.top.status configure -text {reply to message}}", + "button .top.con.b.fwd -bitmap mailforward", + "bind .top.con.b.fwd <Enter> +{.top.status configure -text {forward message}}", + "button .top.con.b.hdr -bitmap mailhdr -command {send msg hdrs}", + "bind .top.con.b.hdr <Enter> +{.top.status configure -text {fetch message headers}}", + "button .top.con.b.save -bitmap mailsave -command {send msg save}", + "bind .top.con.b.save <Enter> +{.top.status configure -text {save message}}", + "pack .top.con.b.con .top.con.b.prev .top.con.b.next .top.con.b.del .top.con.b.reply .top.con.b.fwd .top.con.b.hdr .top.con.b.save -padx 2 -side left", + "label .top.status -text {not connected ...} -anchor w", + "pack .top.l -side left", + "pack .top.con -side left -padx 10", + "pack .top.con.b .top.status -in .top.con -fill x -expand 1", + "frame .hdr", + "scrollbar .hdr.scroll -command {.hdr.t yview}", + "text .hdr.t -height 3c -yscrollcommand {.hdr.scroll set} -bg white", + "frame .hdr.pad -width 2c", + "pack .hdr.t -side left -fill x -expand 1", + "pack .hdr.scroll -side left -fill y", + "pack .hdr.pad", + "frame .body", + "scrollbar .body.scroll -command {.body.t yview}", + "text .body.t -width 15c -height 7c -yscrollcommand {.body.scroll set} -bg white", + "pack .body.t -side left -expand 1 -fill both", + "pack .body.scroll -side left -fill y", + "pack .top -anchor w -padx 5", + "pack .hdr -fill x -anchor w -padx 5 -pady 5", + "pack .body -expand 1 -fill both -padx 5 -pady 5", + "pack .b -padx 5 -pady 5 -fill x", + "pack propagate . 0", + "update" +}; + +con_cfg := array[] of { + "frame .b", + "button .b.ok -text {Connect} -command {send cmd ok}", + "button .b.can -text {Cancel} -command {send cmd can}", + "pack .b.ok .b.can -side left -fill x -padx 10 -pady 10 -expand 1", + "frame .l", + "label .l.h -text {Mail Server:} -anchor w", + "label .l.u -text {User Name:} -anchor w", + "label .l.s -text {Secret:} -anchor w", + "pack .l.h .l.u .l.s -fill both -expand 1", + "frame .e", + "entry .e.h", + "entry .e.u", + "entry .e.s -show •", + "pack .e.h .e.u .e.s -fill x", + "frame .f -borderwidth 2 -relief raised", + "pack .l .e -fill both -expand 1 -side left -in .f", + "pack .f", + "pack .b -fill x -expand 1", + "bind .e.h <Key-\n> {send cmd ok}", + "bind .e.u <Key-\n> {send cmd ok}", + "bind .e.s <Key-\n> {send cmd ok}", + "focus .e.s", +}; + +hdr_cfg := array[] of { + "scrollbar .sh -orient horizontal -command {.f.l xview}", + "scrollbar .f.sv -command {.f.l yview}", + "frame .f", + "listbox .f.l -width 80w -height 20h -yscrollcommand { .f.sv set} -xscrollcommand { .sh set}", + "pack .f.l -side left -fill both -expand 1", + "pack .f.sv -side left -fill y", + "pack .f -fill both -expand 1", + "pack .sh -fill x", + "pack propagate . 0", + "bind .f.l <Double-Button> { send tomain [.f.l get [.f.l curselection]] }", + "update", +}; + +init(xctxt: ref Context, argv: list of string) +{ + sys = load Sys Sys->PATH; + if (xctxt == nil) { + sys->fprint(sys->fildes(2), "readmail: no window context\n"); + raise "fail:bad context"; + } + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + dialog = load Dialog Dialog->PATH; + selectfile = load Selectfile Selectfile->PATH; + str = load String String->PATH; + kr = load Keyring Keyring->PATH; + + ctxt = xctxt; + + tkclient->init(); + dialog->init(); + selectfile->init(); + + tkargs := ""; + argv = tl argv; + if(argv != nil) { + tkargs = hd argv; + argv = tl argv; + } + + titlectl := chan of string; + (main, titlectl) = tkclient->toplevel(ctxt, tkargs, "Readmail: Reader", Tkclient->Appl); + + msg := chan of string; + tk->namechan(main, msg, "msg"); + hdr := chan of string; + + for (c:=0; c<len mail_cfg; c++) + tk->cmd(main, mail_cfg[c]); + tkclient->onscreen(main, nil); + tkclient->startinput(main, "kbd"::"ptr"::nil); + + for(;;) alt { + s := <-main.ctxt.kbd => + tk->keyboard(main, s); + s := <-main.ctxt.ptr => + tk->pointer(main, *s); + s := <-main.ctxt.ctl or + s = <-main.wreq or + s = <-titlectl => + if(s == "exit") { + if(srv.dfd != nil) { + status("Updating mail box..."); + pop3cmd("QUIT"); + } + return; + } + tkclient->wmctl(main, s); + cmd := <-msg => + case cmd { + "connect" => + if(srv.dfd == nil) { + connect(main); + if(srv.dfd != nil) + initialize(); + break; + } + disconnect(); + "prev" => + if(cmesg > nmesg) { + status("no more messages."); + break; + } + for(new := cmesg+1; new <= nmesg; new++) { + if(map[new] == byte Ok) { + cmesg = new; + loadmesg(); + break; + } + } + "next" => + for(new := cmesg-1; new >= 1; new--) { + if(map[new] == byte Ok) { + cmesg = new; + loadmesg(); + break; + } + } + "dele" => + delete(); + if(cmesg > 0) { + cmesg--; + loadmesg(); + } + "hdrs" => + headers(hdr); + "save" => + save(); + "reply" => + reply(); + } + get := <-hdr => + new := int get; + if(new < 1 || new > nmesg || map[new] != byte Ok) + break; + cmesg = new; + loadmesg(); + } +} + +headers(tomain: chan of string) +{ + (hdr, hdrctl) := tkclient->toplevel(ctxt, nil, + "Readmail: Headers", Tkclient->Appl); + + tk->namechan(hdr, tomain, "tomain"); + + for (c:=0; c<len hdr_cfg; c++) + tk->cmd(hdr, hdr_cfg[c]); + + for(i := 1; i <= nmesg; i++) { + if(map[i] == byte Deleted) { + info := sys->sprint("%4d ...Deleted...\n", i); + tk->cmd(hdr, ".f.l insert 0 '"+info); + continue; + } + if(topit(hdr, i) == 0) + break; + alt { + s := <-hdrctl => + if(s == "exit") + return; + tkclient->wmctl(hdr, s); + * => + ; + } + if((i%10) == 9) + tk->cmd(hdr, "update"); + } + tk->cmd(hdr, "update"); + tkclient->onscreen(hdr, nil); + tkclient->startinput(hdr, "kbd"::"ptr"::nil); + + spawn hproc(hdrctl, hdr); +} + +trunc(name: string): string +{ + for(i := 0; i < len name; i++) + if(name[i] == '<') + break; + i++; + if(i >= len name) + return name; + for(j := i; j < len name; j++) + if(name[j] == '>') + break; + return name[i:j]; +} + +topit(hdr: ref Toplevel, msg: int): int +{ + (err, s) := pop3cmd("TOP "+string msg+" 0"); + if(err != nil) { + dialog->prompt(ctxt, hdr.image, "error -fg red", "POP3 Error", + "Ecountered a problem fetching headers\n"+err, + 0, "Dismiss"::nil); + return 0; + } + + size := int s; + b := pop3body(size); + if(b == nil) + return 0; + + from := getfield("from", b); + from = trunc(from); + date := getfield("date", b); + subj := getfield("subject", b); + if(len subj > 20) + subj = subj[0:19]; + + if(len subj > 0) + info := sys->sprint("%4d %5d %s \"%s\" %s", msg, size, from, subj, date); + else + info = sys->sprint("%4d %5d %s %s", msg, size, from, date); + + tk->cmd(hdr, ".f.l insert 0 '"+info); + return 1; +} + +mapdown(b: array of byte): string +{ + lb := len b; + l := array[lb] of byte; + for(i := 0; i < lb; i++) { + c := b[i]; + if(c >= byte 'A' && c <= byte 'Z') + c += byte('a' - 'A'); + l[i] = c; + } + return string l; +} + +getfield(key: string, text: array of byte): string +{ + key[len key] = ':'; + lk := len key; + cl := byte key[0]; + cu := cl - byte ('a' - 'A'); + + lc: byte; + for(i := 0; i < len text - lk; i++) { + t := text[i]; + if(t == byte '\n' && lc == byte '\n') # end header + break; + lc = t; + if(t != cu && t != cl) + continue; + if(key == mapdown(text[i:i+lk])) { + i += lk+1; + for(j := i+1; j < len text; j++) { + c := text[j]; + if(c == byte '\r' || c == byte '\n') + break; + } + return string text[i:j]; + } + } + return ""; +} + +hproc(wmctl: chan of string, top: ref Toplevel) +{ + for(;;) { + alt { + s := <-top.ctxt.kbd => + tk->keyboard(top, s); + s := <-top.ctxt.ptr => + tk->pointer(top, *s); + s := <-top.ctxt.ctl or + s = <-top.wreq or + s = <-wmctl => + if(s == "exit") + return; + tkclient->wmctl(top, s); + } + } +} + +reply() +{ + if(cmesg == 0) { + dialog->prompt(ctxt, main.image, "error -fg red", "Reply", + "No message to reply to", + 0, "Abort"::nil); + return; + } + + hdr := tk->cmd(main, ".hdr.t get 1.0 end"); + if(hdr == "") { + dialog->prompt(ctxt, main.image, "error -fg red", "Reply", + "Mail has no header to reply to", + 0, "Abort"::nil); + return; + } + + wmsender := load WmSendmail "/dis/wm/sendmail.dis"; + if(wmsender == nil) { + dialog->prompt(ctxt, main.image, "error -fg red", "Reply", + "Failed to load mail sender:\n"+sys->sprint("%r"), + 0, "Abort"::nil); + return; + } + + spawn wmsender->init(ctxt, "sendmail" :: hdr :: nil); +} + +save() +{ + if(cmesg == 0) { + dialog->prompt(ctxt, main.image, "error -fg red", "Save", + "No current message", + 0, "Continue"::nil); + return; + } + pat := list of { + "*.let (Saved mail)", + "* (All files)" + }; + + fd: ref Sys->FD; + fname: string; + for(;;) { + fname = selectfile->filename(ctxt, main.image, "Save in Mailbox", + pat, "/usr/"+username+"/mail"); + if(fname == nil) + return; + + fd = sys->create(fname, sys->OWRITE, 8r660); + if(fd != nil) + break; + + labs := list of { + "New name", + "Abort" + }; + + r := dialog->prompt(ctxt, main.image, "error -fg red", "Save", + "Failed to create "+sys->sprint("%s\n%r", fname), + 0, labs); + if(r == 1) + return; + } + s := tk->cmd(main, ".hdr.t get 1.0 end"); + b := array of byte s; + r := sys->write(fd, b, len b); + if(r < 0) { + dialog->prompt(ctxt, main.image, "error -fg red", "Save", + "Error writing file"+sys->sprint("%s\n%r", fname), + 0, "Continue (not saved)":: nil); + return; + } + s = tk->cmd(main, ".body.t get 1.0 end"); + b = array of byte s; + n := sys->write(fd, b, len b); + if(n < 0) { + dialog->prompt(ctxt, main.image, "error -fg red", "Save", + "Error writing file"+sys->sprint("%s\n%r", fname), + 0, "Continue (not saved)":: nil); + return; + } + status("wrote "+string(n+r)+" bytes."); +} + +delete() +{ + if(srv.dfd == nil) { + dialog->prompt(ctxt, main.image, "warning -fg yellow", "Delete", + "You must be connected to delete messages", + 0, "Continue"::nil); + return; + } + (err, s) := pop3cmd("DELE "+string cmesg); + if(err != nil) { + dialog->prompt(ctxt, main.image, "error -fg red", "Delete", + "Encountered POP3 problem during delete\n"+err, + 0, "Continue"::nil); + return; + } + map[cmesg] = byte Deleted; + status(s); +} + +status(msg: string) +{ + tk->cmd(main, ".top.status configure -text {"+msg+"}; update"); +} + +disconnect() +{ + (err, s) := pop3cmd("QUIT"); + srv.dfd = nil; + tk->cmd(main, ".top.con configure -text Connect"); + if(err != nil) { + dialog->prompt(ctxt, main.image, "error -fg red", "Disconnect", + "POP3 protocol problem\n"+err, + 0, "Proceed"::nil); + return; + } + status(s); +} + +connect(parent: ref Toplevel) +{ + (t, conctl) := tkclient->toplevel(ctxt, postposn(parent), + "Connection Parameters", 0); + + cmd := chan of string; + tk->namechan(t, cmd, "cmd"); + + for (c:=0; c<len con_cfg; c++) + tk->cmd(t, con_cfg[c]); + + username = rf("/dev/user"); + sv := rf("/usr/"+username+"/mail/popserver"); + if(sv != "") + tk->cmd(t, ".e.h insert 0 '"+sv); + + u := tk->cmd(t, ".e.u get"); + if(u == "") + tk->cmd(t, ".e.u insert 0 '"+username); + + tk->cmd(t, "update"); + tkclient->onscreen(t, nil); + tkclient->startinput(t, "kbd"::"ptr"::nil); + + for(;;) alt { + s := <-t.ctxt.kbd => + tk->keyboard(t, s); + s := <-t.ctxt.ptr => + tk->pointer(t, *s); + s := <-t.ctxt.ctl or + s = <-t.wreq or + s = <-conctl => + if(s == "exit") + return; + tkclient->wmctl(t, s); + s := <-cmd => + if(s == "can") + return; + server := tk->cmd(t, ".e.h get"); + if(server == "") { + dialog->prompt(ctxt, t.image, "error -fg red", "Connect", + "You must supply a server address", + 0, "Proceed"::nil); + break; + } + user := tk->cmd(t, ".e.u get"); + if(user == "") { + dialog->prompt(ctxt, t.image, "error -fg red", "Connect", + "You must supply a user name", + 0, "Proceed"::nil); + break; + } + pass := tk->cmd(t, ".e.s get"); + if(pass == "") { + dialog->prompt(ctxt, t.image, "error -fg red", "Connect", + "You must give a secret or password", + 0, "Proceed"::nil); + break; + } + if(dialer(t, server, user, pass) != 0) + return; + status("not connected"); + } + srv.dfd = nil; +} + +initialize() +{ + (err, s) := pop3cmd("STAT"); + if(err != nil) { + dialog->prompt(ctxt, main.image, "error -fg red", "Mailbox Status", + "The following error occurred while "+ + "checking your mailbox:\n"+err, + 0, "Dismiss"::nil); + srv.dfd = nil; + status("not connected"); + return; + } + + tk->cmd(main, ".top.con configure -text Disconnect; update"); + nmesg = int s; + if(nmesg == 0) { + status("There are no messages."); + return; + } + + map = array[nmesg+1] of byte; + for(i := 0; i <= nmesg; i++) + map[i] = byte Ok; + + s = ""; + if(nmesg > 1) + s = "s"; + status("You have "+string nmesg+" message"+s); + cmesg = nmesg; + loadmesg(); +} + +loadmesg() +{ + if(srv.dfd == nil) { + dialog->prompt(ctxt, main.image, "warning -fg yellow", "Read", + "You must be connected to read messages", + 0, "Continue"::nil); + return; + } + (err, s) := pop3cmd("RETR "+sys->sprint("%d", cmesg)); + if(err != nil) { + dialog->prompt(ctxt, main.image, "error -fg red", "Read", + "Error retrieving message:\n"+err, + 0, "Continue"::nil); + return; + } + + tk->cmd(main, ".hdr.t delete 1.0 end; .body.t delete 1.0 end"); + size := int s; + + status("reading "+string size+" bytes ..."); + + b := pop3body(size); + + (headr, body) := split(string b); + b = nil; + tk->cmd(main, ".hdr.t insert end '"+headr); + tk->cmd(main, ".body.t insert end '"+body); + tk->cmd(main, ".hdr.t see 1.0; .body.t see 1.0"); + status("read message "+string cmesg+" of "+string nmesg+" , ready..."); +} + +split(text: string): (string, string) +{ + c, lc: int; + hdr, body: string; + + hp := 0; + for(i := 0; i < len text; i++) { + c = text[i]; + if(c == '\r') + continue; + hdr[hp++] = c; + if(lc == '\n' && c == '\n') + break; + lc = c; + } + bp := 0; + while(i < len text) { + c = text[i++]; + if(c != '\r') + body[bp++] = c; + } + return (hdr, body); +} + +dialer(t: ref Toplevel, server, user, pass: string): int +{ + ok: int; + + for(;;) { + status("dialing server..."); + (ok, srv) = sys->dial(netmkaddr(server, nil, "110"), nil); + if(ok >= 0) + break; + + labs := list of { + "Retry", + "Cancel" + }; + ok = dialog->prompt(ctxt, t.image, "error -fg", "Connect", + "The following error occurred while\n"+ + "dialing the server: "+sys->sprint("%r"), + 0, labs); + if(ok != 0) + return 0; + } + status("connected..."); + (err, s) := pop3resp(); + if(err != nil) { + dialog->prompt(ctxt, t.image, "error -fg red", "Connect", + "An error occurred during sign on.\n"+err, + 0, "Proceed"::nil); + return 0; + } + status(s); + (nil, s) = str->splitl(s, "<"); + (chal, nil) := str->splitr(s, ">"); + if(chal != nil){ + ca := array of byte chal; + digest := array[kr->MD5dlen] of byte; + md5state := kr->md5(ca, len ca, nil, nil); + pa := array of byte pass; + kr->md5(pa, len pa, digest, md5state); + s = nil; + for(i := 0; i < kr->MD5dlen; i++) + s += sys->sprint("%2.2ux", int digest[i]); + (err, s) = pop3cmd("APOP "+user+" "+s); + if(err == nil) { + status("ready to serve..."); + return 1; + } else { + dialog->prompt(ctxt, t.image, "error -fg red", "Connect", + "Challenge/response failed.\n"+err, + 0, "Proceed"::nil); + return 0; + } + } + (err, s) = pop3cmd("USER "+user); + if(err != nil) { + dialog->prompt(ctxt, t.image, "error -fg red", "Connect", + "An error occurred during login.\n"+err, + 0, "Proceed"::nil); + return 0; + } + (err, s) = pop3cmd("PASS "+pass); + if(err != nil) { + dialog->prompt(ctxt, t.image, "error -fg red", "Connect", + "An error occurred during login.\n"+err, + 0, "Proceed"::nil); + return 0; + } + status("ready to serve..."); + return 1; +} + +rf(file: string): string +{ + fd := sys->open(file, sys->OREAD); + if(fd == nil) + return ""; + + buf := array[Sys->NAMEMAX] of byte; + n := sys->read(fd, buf, len buf); + if(n < 0) + return ""; + + return string buf[0:n]; +} + +postposn(parent: ref Toplevel): string +{ + x := int tk->cmd(parent, ".top.con cget -actx"); + y := int tk->cmd(parent, ".top.con cget -acty"); + h := int tk->cmd(parent, ".top.con cget -height"); + + return "-x "+string(x-2)+" -y "+string(y+h+2); +} + +# +# Talk POP3 +# +pop3cmd(cmd: string): (string, string) +{ + cmd += "\r\n"; +# sys->print("->%s", cmd); + b := array of byte cmd; + l := len b; + n := sys->write(srv.dfd, b, l); + if(n != l) + return ("send to server:"+sys->sprint("%r"), nil); + + return pop3resp(); +} + +pop3resp(): (string, string) +{ + s := ""; + i := 0; + lastc := 0; + for(;;) { + c := pop3getc(); + if(c == -1) + return ("read from server:"+sys->sprint("%r"), nil); + if(lastc == '\r' && c == '\n') + break; + s[i++] = c; + lastc = c; + } +# sys->print("<-%s\n", s); + if(i < 3) + return ("short read from server", nil); + s = s[0:i-1]; + if(s[0:3] == "+OK") { + i = 3; + while(i < len s && s[i] == ' ') + i++; + return (nil, s[i:]); + } + if(s[0:4] == "-ERR") { + i = 4; + while(s[i] == ' ' && i < len s) + i++; + return (s[i:], nil); + } + return ("invalid server response", nil); +} + +pop3body(size: int): array of byte +{ + size += 512; + b := array[size] of byte; + + cnt := emptypopbuf(b); + size -= cnt; + + for(;;) { + + if(cnt > 5 && string b[cnt-5:cnt] == "\r\n.\r\n") { + b = b[0:cnt-5]; + break; + } + # resize buffer + if(size == 0) { + nb := array[len b + 4096] of byte; + nb[0:] = b; + size = len nb - len b; + b = nb; + nb = nil; + } + n := sys->read(srv.dfd, b[cnt:], len b - cnt); + if(n <= 0) { + dialog->prompt(ctxt, main.image, "error -fg red", "Read", + sys->sprint("Error retrieving message: %r"), + 0, "Continue"::nil); + return nil; + } + size -= n; + cnt += n; + } + return b; +} + +Iob: adt +{ + nbyte: int; + posn: int; + buf: array of byte; +}; +popbuf: Iob; + +pop3getc(): int +{ + if(popbuf.nbyte > 0) { + popbuf.nbyte--; + return int popbuf.buf[popbuf.posn++]; + } + if(popbuf.buf == nil) + popbuf.buf = array[512] of byte; + + popbuf.posn = 0; + n := sys->read(srv.dfd, popbuf.buf, len popbuf.buf); + if(n < 0) + return -1; + + popbuf.nbyte = n-1; + return int popbuf.buf[popbuf.posn++]; +} + +emptypopbuf(a: array of byte) : int +{ + i := popbuf.nbyte; + + if (i) { + a[0:] = popbuf.buf[popbuf.posn:(popbuf.posn+popbuf.nbyte)]; + popbuf.nbyte = 0; + } + + return i; +} + +netmkaddr(addr, net, svc: string): string +{ + if(net == nil) + net = "net"; + (n, l) := sys->tokenize(addr, "!"); + if(n <= 1){ + if(svc== nil) + return sys->sprint("%s!%s", net, addr); + return sys->sprint("%s!%s!%s", net, addr, svc); + } + if(svc == nil || n > 2) + return addr; + return sys->sprint("%s!%s", addr, svc); +} diff --git a/appl/wm/remotelogon.b b/appl/wm/remotelogon.b new file mode 100644 index 00000000..cb0be876 --- /dev/null +++ b/appl/wm/remotelogon.b @@ -0,0 +1,314 @@ +implement WmLogon; +# +# get a certificate to enable remote access. +# +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + Screen, Display, Image, Context, Point, Rect: import draw; + +include "tk.m"; + tk: Tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "arg.m"; +include "sh.m"; +include "newns.m"; +include "keyring.m"; + keyring: Keyring; +include "security.m"; + login: Login; + +# XXX where to put the certificate: is the username already set to +# something appropriate, with a home directory and keyring directory in that? + +# how do we find out the signer; presumably from the registry? +# should do that before signing on; if we can't get it, then prompt for it. +WmLogon: module { + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +cfg := array[] of { + "label .p -bitmap @/icons/inferno.bit -borderwidth 2 -relief raised", + "label .ul -text {User Name:} -anchor w", + "entry .ue -bg white", + "label .pl -text {Password:} -anchor w", + "entry .pe -bg white -show *", + "frame .f -borderwidth 2 -relief raised", + "grid .ul .ue -in .f", + "grid .pl .pe -in .f", + "pack .p .f -fill x", + "bind .ue <Key-\n> {focus next}", + "bind .ue {<Key-\t>} {focus next}", + "bind .pe <Key-\n> {send cmd ok}", + "bind .pe {<Key-\t>} {focus next}", + "focus .e", +}; + +init(ctxt: ref Draw->Context, args: list of string) +{ + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + if(tkclient == nil){ + sys->fprint(stderr(), "logon: cannot load %s: %r\n", Tkclient->PATH); + raise "fail:bad module"; + } + login = load Login Login->PATH; + if(login == nil){ + sys->fprint(stderr(), "logon: cannot load %s: %r\n", Login->PATH); + raise "fail:bad module"; + } + keyring = load Keyring Keyring->PATH; + if(keyring == nil){ + sys->fprint(stderr(), "logon: cannot load %s: %r\n", Keyring->PATH); + raise "fail:bad module"; + } + sys->pctl(sys->NEWPGRP, nil); + tkclient->init(); + + (ctlwin, nil) := tkclient->toplevel(ctxt, nil, nil, Tkclient->Plain); + if(sys->fprint(ctlwin.ctxt.connfd, "request") == -1){ + sys->fprint(stderr(), "logon: must be run as principal wm application\n"); + raise "fail:lack of control"; + } + addr: con "tcp!127.0.0.1!inflogin"; + usr := ""; + passwd := ""; + arg := load Arg Arg->PATH; + if(arg != nil){ + arg->init(args); + arg->setusage("usage: logon [-u user] [-p passwd] command [arg...]]\n"); + while((opt := arg->opt()) != 0){ + case opt{ + 'u' => + usr = arg->earg(); + 'p' => + passwd = arg->earg(); + * => + arg->usage(); + } + } + args = arg->argv(); + arg = nil; + } else + args = nil; + if(ctxt == nil) + sys->fprint(stderr(), "logon: must run under a window manager\n"); + + if (usr == nil || !logon(ctxt, usr, passwd, addr)) { + (panel, cmd) := makepanel(ctxt); + stop := chan of int; + spawn tkclient->handler(panel, stop); + for(;;) { + tk->cmd(panel, "focus .ue; update"); + <-cmd; + usr = tk->cmd(panel, ".ue get"); + if(usr == nil) { + notice(ctxt, "You must supply a user name to login"); + continue; + } + passwd = tk->cmd(panel, ".pe get"); + + if(logon(ctxt, usr, passwd, addr)) { + panel = nil; + stop <-= 1; + break; + } + tk->cmd(panel, ".ue delete 0 end"); + tk->cmd(panel, ".pe delete 0 end"); + } + } + (ok, nil) := sys->stat("namespace"); + if(ok >= 0) { + ns := load Newns Newns->PATH; + if(ns == nil) + notice(ctxt, "failed to load namespace builder"); + else if ((nserr := ns->newns(nil, nil)) != nil) + notice(ctxt, "namespace error:\n"+nserr); + } + tkclient->wmctl(ctlwin, "endcontrol"); + errch := chan of string; + spawn exec(ctxt, args, errch); + err := <-errch; + if (err != nil) { + sys->fprint(stderr(), "logon: %s\n", err); + raise "fail:exec failed"; + } +} + +makepanel(ctxt: ref Draw->Context): (ref Tk->Toplevel, chan of string) +{ + (t, nil) := tkclient->toplevel(ctxt, "-bg silver", nil, Tkclient->Plain); + + cmd := chan of string; + tk->namechan(t, cmd, "cmd"); + + for(i := 0; i < len cfg; i++) + tk->cmd(t, cfg[i]); + err := tk->cmd(t, "variable lasterr"); + if(err != nil) { + sys->fprint(stderr(), "logon: tk error: %s\n", err); + raise "fail:config error"; + } + tk->cmd(t, "update"); + org: Point; + ir := tk->rect(t, ".", Tk->Border|Tk->Required); + org.x = t.screenr.dx() / 2 - ir.dx() / 2; + org.y = t.screenr.dy() / 3 - ir.dy() / 2; + if (org.y < 0) + org.y = 0; + tk->cmd(t, ". configure -x " + string org.x + " -y " + string org.y); + tkclient->startinput(t, "kbd" :: "ptr" :: nil); + tkclient->onscreen(t, "onscreen"); + return (t, cmd); +} + +exec(ctxt: ref Draw->Context, argv: list of string, errch: chan of string) +{ + sys->pctl(sys->NEWFD, 0 :: 1 :: 2 :: nil); + if(argv == nil) + argv = "/dis/wm/toolbar.dis" :: nil; + else { + sh := load Sh Sh->PATH; + if(sh != nil){ + sh->run(ctxt, "{$* &}" :: argv); + errch <-= nil; + exit; + } + } + { + cmd := load Command hd argv; + if (cmd == nil) { + errch <-= sys->sprint("cannot load %s: %r", hd argv); + } else { + errch <-= nil; + spawn cmd->init(ctxt, argv); + } + }exception{ + "fail:*" => + exit; + } +} + +logon(ctxt: ref Draw->Context, uname, passwd, addr: string): int +{ + (err, info) := login->login(uname, passwd, addr); + if(err != nil){ + notice(ctxt, "Login failed:\n" + err); + return 0; + } + + keys := "/usr/" + user() + "/keyring"; + if(sys->bind("#s", keys, Sys->MBEFORE) == -1){ + notice(ctxt, sys->sprint("Cannot access keyring: %r")); + return 0; + } + fio := sys->file2chan(keys, "default"); + if(fio == nil){ + notice(ctxt, sys->sprint("Cannot create key file: %r")); + return 0; + } + sync := chan of int; + spawn infofile(fio, sync); + <-sync; + + if(keyring->writeauthinfo(keys + "/default", info) == -1){ + notice(ctxt, sys->sprint("Cannot write key file: %r")); + return 0; + } + + return 1; +} + +notecmd := array[] of { + "frame .f", + "label .f.l -bitmap error -foreground red", + "button .b -text Continue -command {send cmd done}", + "focus .f", + "bind .f <Key-\n> {send cmd done}", + "pack .f.l .f.m -side left -expand 1", + "pack .f .b", + "pack propagate . 0", +}; + +centre(t: ref Tk->Toplevel) +{ + sz := Point(int tk->cmd(t, ". cget -width"), int tk->cmd(t, ". cget -height")); + r := t.screenr; + if (sz.x > r.dx()) + tk->cmd(t, ". configure -width " + string r.dx()); + org: Point; + org.x = r.dx() / 2 - tk->rect(t, ".", 0).dx() / 2; + org.y = r.dy() / 3 - tk->rect(t, ".", 0).dy() / 2; + if (org.y < 0) + org.y = 0; + tk->cmd(t, ". configure -x " + string org.x + " -y " + string org.y); +} + +notice(ctxt: ref Draw->Context, message: string) +{ + (t, nil) := tkclient->toplevel(ctxt, "-borderwidth 2 -relief raised", nil, Tkclient->Plain); + cmd := chan of string; + tk->namechan(t, cmd, "cmd"); + tk->cmd(t, "label .f.m -anchor nw -text '"+message); + for(i := 0; i < len notecmd; i++) + tk->cmd(t, notecmd[i]); + centre(t); + tkclient->onscreen(t, "onscreen"); + tkclient->startinput(t, "kbd"::"ptr"::nil); + stop := chan of int; + spawn tkclient->handler(t, stop); + tk->cmd(t, "update; cursor -default"); + <-cmd; + stop <-= 1; +} + +stderr(): ref Sys->FD +{ + return sys->fildes(2); +} + +user(): string +{ + fd := sys->open("/dev/user", Sys->OREAD); + buf := array[8192] of byte; + if((n := sys->read(fd, buf, len buf)) > 0) + return string buf[0:n]; + return "none"; +} + +infofile(fileio: ref Sys->FileIO, sync: chan of int) +{ + sys->pctl(Sys->NEWPGRP|Sys->NEWFD|Sys->NEWNS, nil); + sync <-= 1; + + infodata: array of byte; + for(;;) alt { + (off, nbytes, fid, rc) := <-fileio.read => + if(rc == nil) + break; + if(off > len infodata) + off = len infodata; + rc <-= (infodata[off:], nil); + + (off, data, fid, wc) := <-fileio.write => + if(wc == nil) + break; + + if(off != len infodata){ + wc <-= (0, "cannot be rewritten"); + } else { + nid := array[len infodata+len data] of byte; + nid[0:] = infodata; + nid[len infodata:] = data; + infodata = nid; + wc <-= (len data, nil); + } + } +} diff --git a/appl/wm/reversi.b b/appl/wm/reversi.b new file mode 100644 index 00000000..b1a85057 --- /dev/null +++ b/appl/wm/reversi.b @@ -0,0 +1,903 @@ +implement Reversi; + +# +# Copyright © 2000 Vita Nuova Limited. All rights reserved. +# + +include "sys.m"; + sys: Sys; +include "draw.m"; + draw: Draw; + Point, Rect, Image, Font, Context, Screen, Display: import draw; +include "tk.m"; + tk: Tk; + Toplevel: import tk; +include "tkclient.m"; + tkclient: Tkclient; +include "daytime.m"; + daytime: Daytime; +include "rand.m"; + rand: Rand; + +# adtize and modularize + +stderr: ref Sys->FD; + +Reversi: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +nosleep, printout, auto: int; +display: ref Draw->Display; + +init(ctxt: ref Draw->Context, argv: list of string) +{ + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + tkclient->init(); + daytime = load Daytime Daytime->PATH; + rand = load Rand Rand->PATH; + + argv = tl argv; + while(argv != nil){ + s := hd argv; + if(s != nil && s[0] == '-'){ + for(i := 1; i < len s; i++){ + case s[i]{ + 'a' => auto = 1; + 'p' => printout = 1; + 's' => nosleep = 1; + } + } + } + argv = tl argv; + } + stderr = sys->fildes(2); + rand->init(daytime->now()); + daytime = nil; + + if(ctxt == nil) + ctxt = tkclient->makedrawcontext(); + display = ctxt.display; + (win, wmctl) := tkclient->toplevel(ctxt, "", "Reversi", Tkclient->Resize | Tkclient->Hide); + mainwin = win; + sys->pctl(Sys->NEWPGRP, nil); + cmdch := chan of string; + tk->namechan(win, cmdch, "cmd"); + for(i := 0; i < len win_config; i++) + cmd(win, win_config[i]); + fittoscreen(win); + pid := -1; + sync := chan of int; + mvch := chan of (int, int, int); + initboard(); + setimage(); + spawn game(sync, mvch, 0); + pid = <- sync; + tkclient->onscreen(win, nil); + tkclient->startinput(win, "kbd"::"ptr"::nil); + lasts := 1; + for(;;){ + alt{ + s := <-win.ctxt.kbd => + tk->keyboard(win, s); + s := <-win.ctxt.ptr => + tk->pointer(win, *s); + s := <-win.ctxt.ctl or + s = <-win.wreq => + tkclient->wmctl(win, s); + c := <- wmctl => + case c{ + "exit" => + if(pid != -1) + kill(pid); + exit; + * => + e := tkclient->wmctl(win, c); + if(e == nil && c[0] == '!'){ + setimage(); + drawboard(); + } + } + c := <- cmdch => + (nil, toks) := sys->tokenize(c, " "); + case hd toks{ + "b1" or "b2" or "b3" => + alt{ + mvch <-= (SQUARE, int hd tl toks, int hd tl tl toks) => lasts = 1; + * => ; + } + "bh" or "bm" or "wh" or "wm" => + col := BLACK; + knd := HUMAN; + if((hd toks)[0] == 'w') + col = WHITE; + if((hd toks)[1] == 'm') + knd = MACHINE; + kind[col] = knd; + "blev" or "wlev" => + col := BLACK; + e := "be"; + if((hd toks)[0] == 'w'){ + col = WHITE; + e = "we"; + } + sk := int cmd(win, ".f0." + e + " get"); + if(sk > MAXPLIES) + sk = MAXPLIES; + if(sk >= 0) + skill[col] = sk; + "last" => + alt{ + mvch <-= (REPLAY, lasts, 0) => lasts++; + * => ; + } + * => + ; + } + <- sync => + pid = -1; + # exit; + spawn game(sync, mvch, 0); + pid = <- sync; + } + } +} + +SQUARE, REPLAY: con iota; + +WIDTH: con 400; +HEIGHT: con 400; + +SZB: con 8; # must be even +SZF: con SZB+2; +MC1: con SZB/2; +MC2: con MC1+1; +PIECES: con SZB*SZB; +SQUARES: con PIECES-4; +MAXMOVES: con 3*PIECES/2; +NOMOVE: con SZF*SZF - 1; + +BLACK, WHITE, EMPTY, BORDER: con iota; +MACHINE, HUMAN: con iota; +SKILLB : con 6; +SKILLW : con 0; +MAXPLIES: con 6; + +moves: array of int; +board: array of array of int; # for display +brd: array of array of int; # for calculations +val: array of array of int; +order: array of (int, int); +pieces: array of int; +value: array of int; +kind: array of int; +skill: array of int; +name: array of string; + +mainwin: ref Toplevel; +brdimg: ref Image; +brdr: Rect; +brdx, brdy: int; + +black, white, green: ref Image; + +movech: chan of (int, int, int); + +setimage() +{ + brdw := int tk->cmd(mainwin, ".p cget -actwidth"); + brdh := int tk->cmd(mainwin, ".p cget -actheight"); +# if (brdw > display.image.r.dx()) +# brdw = display.image.r.dx() - 4; +# if (brdh > display.image.r.dy()) +# brdh = display.image.r.dy() - 40; + + brdr = Rect((0,0), (brdw, brdh)); + brdimg = display.newimage(brdr, display.image.chans, 0, Draw->White); + if(brdimg == nil) + fatal("not enough image memory"); + tk->putimage(mainwin, ".p", brdimg, nil); +} + +game(sync: chan of int, mvch: chan of (int, int, int), again: int) +{ + sync <-= sys->pctl(0, nil); + movech = mvch; + initbrd(); + drawboard(); + if(again) + replay(moves); + else + play(); + sync <-= 0; +} + +ordrect() +{ + i, j : int; + + n := 0; + for(i = 1; i <= SZB; i++){ + for(j = 1; j <= SZB; j++){ + if(i < SZB/2 || j < SZB/2 || i > SZB/2+1 || j > SZB/2+1) + order[n++] = (i, j); + } + } + for(k := 0; k < SQUARES-1; k++){ + for(l := k+1; l < SQUARES; l++){ + (i, j) = order[k]; + (a, b) := order[l]; + if(val[i][j] > val[a][b]) + (order[k], order[l]) = (order[l], order[k]); + } + } +} + +initboard() +{ + i, j, k: int; + + moves = array[MAXMOVES+1] of int; + board = array[SZF] of array of int; + brd = array[SZF] of array of int; + for(i = 0; i < SZF; i++){ + board[i] = array[SZF] of int; + brd[i] = array[SZF] of int; + } + val = array[SZF] of array of int; + s := -pow(-1, SZB/2); + for(i = 0; i < SZF; i++){ + val[i] = array[SZF] of int; + val[i][0] = val[i][SZF-1] = 0; + for(j = 1; j <= SZB; j++){ + for(k = SZB/2; k > 0; k--){ + if(i == k || i == SZB+1-k || j == k || j == SZB+1-k){ + val[i][j] = s*pow(-7, SZB/2-k); + break; + } + } + } + } + order = array[SQUARES] of (int, int); + ordrect(); + pieces = array[2] of int; + value = array[2] of int; + kind = array[2] of int; + kind[BLACK] = MACHINE; + if(auto) + kind[WHITE] = MACHINE; + else + kind[WHITE] = HUMAN; + skill = array[2] of int; + skill[BLACK] = SKILLB; + skill[WHITE] = SKILLW; + name = array[2] of string; + name[BLACK] = "black"; + name[WHITE] = "white"; + black = display.color(Draw->Black); + white = display.color(Draw->White); + green = display.color(Draw->Green); +} + +initbrd() +{ + i, j: int; + + for(i = 0; i < SZF; i++) + for(j = 0; j < SZF; j++) + brd[i][j] = EMPTY; + for(i = 0; i < SZF; i++) + brd[i][0] = brd[i][SZF-1] = BORDER; + for(j = 0; j< SZF; j++) + brd[0][j] = brd[SZF-1][j] = BORDER; + brd[MC1][MC1] = brd[MC2][MC2] = BLACK; + brd[MC1][MC2] = brd[MC2][MC1] = WHITE; + for(i = 0; i < SZF; i++) + for(j = 0; j < SZF; j++) + board[i][j] = brd[i][j]; + pieces[BLACK] = pieces[WHITE] = 2; + value[BLACK] = value[WHITE] = -2; +} + +plays := 0; +bscore := 0; +wscore := 0; +bwins := 0; +wwins := 0; + +play() +{ + n := 0; + for(i := 0; i <= MAXMOVES; i++) + moves[i] = NOMOVE; + if(plays&1) + (first, second) := (WHITE, BLACK); + else + (first, second) = (BLACK, WHITE); + if(printout) + sys->print("%d\n", first); + moves[n++] = first; + m1 := m2 := 1; + for(;;){ + if(pieces[BLACK]+pieces[WHITE] == PIECES) + break; + m2 = m1; + m1 = move(first, second); + if(printout) + sys->print("%d\n", m1); + moves[n++] = m1; + if(!m1 && !m2) + break; + (first, second) = (second, first); + } + if(auto) + sys->print("score: %d-%d\n", pieces[BLACK], pieces[WHITE]); + bscore += pieces[BLACK]; + wscore += pieces[WHITE]; + if(pieces[BLACK] > pieces[WHITE]) + bwins++; + else if(pieces[BLACK] < pieces[WHITE]) + wwins++; + plays++; + if(auto) + sys->print(" black: %d white: %d draw: %d total: (%d-%d)\n", bwins, wwins, plays-bwins-wwins, bscore, wscore); + puts(sys->sprint("black %d:%d white", pieces[BLACK], pieces[WHITE])); + sleep(2000); + puts(sys->sprint("black %d:%d white", bwins, wwins)); + sleep(2000); +} + +replay(moves: array of int) +{ + n := 0; + first := moves[n++]; + second := BLACK+WHITE-first; + m1 := m2 := 1; + while (pieces[BLACK]+pieces[WHITE] < PIECES){ + m2 = m1; + m1 = moves[n++]; + if(m1 == NOMOVE) + break; + if(m1 != 0) + makemove(m1/SZF, m1%SZF, first, second, 1, 0); + if(!m1 && !m2) + break; + (first, second) = (second, first); + } + # sys->print("score: %d-%d\n", pieces[BLACK], pieces[WHITE]); +} + +lastmoves(p: int, moves: array of int) +{ + initbrd(); + k := MAXMOVES+1; + for(i := 0; i <= MAXMOVES; i++){ + if(moves[i] == NOMOVE){ + k = i; + break; + } + } + if(k-p < 1) + p = k-1; + for(i = k-p; i < k; i++) + if(moves[i] == 0) + p++; + if(k-p < 1) + p = k-1; + n := 0; + me := moves[n++]; + you := BLACK+WHITE-me; + while(n < k-p){ + m := moves[n++]; + if(m != 0) + makemove(m/SZF, m%SZF, me, you, 1, 1); + (me, you) = (you, me); + } + for(i = 0; i < SZF; i++) + for(j := 0; j < SZF; j++) + board[i][j] = brd[i][j]; + drawboard(); + sleep(1000); + while(n < k){ + m := moves[n++]; + if(m != 0) + makemove(m/SZF, m%SZF, me, you, 1, 0); + if(n < k) + sleep(500); + (me, you) = (you, me); + } +} + +move(me: int, you: int): int +{ + if(kind[me] == MACHINE){ + puts("machine " + name[me] + " move"); + m := genmove(me, you); + if(!m){ + puts("machine " + name[me] + " cannot go"); + sleep(2000); + } + return m; + } + else{ + m, n: int; + + mvs := findmoves(me, you); + if(mvs == nil){ + puts("human " + name[me] + " cannot go"); + sleep(2000); + return 0; + } + for(;;){ + puts("human " + name[me] + " move"); + (m, n) = getmove(); + if(m < 1 || n < 1 || m > SZB || n > SZB) + continue; + if(brd[m][n] == EMPTY) + (valid, nil) := makemove(m, n, me, you, 0, 0); + else + valid = 0; + if(valid) + break; + puts("illegal move"); + sleep(2000); + } + makemove(m, n, me, you, 1, 0); + return m*SZF+n; + } +} + +fullsrch: int; + +genmove(me: int, you: int): int +{ + m, n, v: int; + + mvs := findmoves(me, you); + if(mvs == nil) + return 0; + if(skill[me] == 0){ + l := len mvs; + r := rand->rand(l); + # r = 0; + while(--r >= 0) + mvs = tl mvs; + (m, n) = hd mvs; + } + else{ + plies := skill[me]; + left := PIECES-(pieces[BLACK]+pieces[WHITE]); + if(left < plies) # limit search + plies = left; + else if(left < 2*plies) # expand search to end + plies = left; + else{ # expand search nearer end of game + k := left/plies; + if(k < 3) + plies = ((k+2)*plies)/(k+1); + } + fullsrch = plies == left; + visits = leaves = 0; + (v, (m, n)) = minimax(me, you, plies, ∞, 1); + if(0){ + # if((m==2&&n==2&&brd[1][1]!=BLACK) || + # (m==2&&n==7&&brd[1][8]!=BLACK) || + # (m==7&&n==2&&brd[8][1]!=BLACK) || + # (m==7&&n==7&&brd[8][8]!=BLACK)){ + while(mvs != nil){ + (a, b) := hd mvs; + (nil, sqs) := makemove(a, b, me, you, 1, 1); + (v0, nil) := minimax(you, me, plies-1, ∞, 1); + sys->print(" (%d, %d): %d\n", a, b, v0); + undomove(a, b, me, you, sqs); + mvs = tl mvs; + } + if(!fullsrch){ + sys->print("best move is %d, %d\n", m, n); + kind[WHITE] = HUMAN; + } + } + if(auto) + sys->print("eval = %d plies=%d goes=%d visits=%d\n", v, plies, len mvs, leaves); + } + makemove(m, n, me, you, 1, 0); + return m*SZF+n; +} + +findmoves(me: int, you: int): list of (int, int) +{ + mvs: list of (int, int); + + for(k := 0; k < SQUARES; k++){ + (i, j) := order[k]; + if(brd[i][j] == EMPTY){ + (valid, nil) := makemove(i, j, me, you, 0, 0); + if(valid) + mvs = (i, j) :: mvs; + } + } + return mvs; +} + +makemove(m: int, n: int, me: int, you: int, move: int, gen: int): (int, list of (int, int)) +{ + sqs: list of (int, int); + + if(move){ + pieces[me]++; + value[me] += val[m][n]; + brd[m][n] = me; + if(!gen){ + board[m][n] = me; + drawpiece(m, n, me, 1); + panelupdate(); + sleep(1000); + } + } + valid := 0; + for(i := -1; i < 2; i++){ + for(j := -1; j < 2; j++){ + if(i != 0 || j != 0){ + v: int; + + (v, sqs) = dirmove(m, n, i, j, me, you, move, gen, sqs); + valid |= v; + if (valid && !move) + return (1, sqs); + } + } + } + if(!valid && move) + fatal(sys->sprint("bad makemove call (%d, %d)", m, n)); + return (valid, sqs); +} + +dirmove(m: int, n: int, dx: int, dy: int, me: int, you: int, move: int, gen: int, sqs: list of (int, int)): (int, list of (int, int)) +{ + p := 0; + m += dx; + n += dy; + while(brd[m][n] == you){ + m += dx; + n += dy; + p++; + } + if(p > 0 && brd[m][n] == me){ + if(move){ + pieces[me] += p; + pieces[you] -= p; + m -= p*dx; + n -= p*dy; + while(--p >= 0){ + brd[m][n] = me; + value[me] += val[m][n]; + value[you] -= val[m][n]; + if(gen) + sqs = (m, n) :: sqs; + else{ + board[m][n] = me; + drawpiece(m, n, me, 0); + # sleep(500); + panelupdate(); + } + m += dx; + n += dy; + } + } + return (1, sqs); + } + return (0, sqs); +} + +undomove(m: int, n: int, me: int, you: int, sqs: list of (int, int)) +{ + brd[m][n] = EMPTY; + pieces[me]--; + value[me] -= val[m][n]; + for(; sqs != nil; sqs = tl sqs){ + (x, y) := hd sqs; + brd[x][y] = you; + pieces[me]--; + pieces[you]++; + value[me] -= val[x][y]; + value[you] += val[x][y]; + } +} + +getmove(): (int, int) +{ + k, x, y: int; + + (k, x, y) = <- movech; + if(k == REPLAY){ + lastmoves(x, moves); + return getmove(); + } + return (x/brdx+1, y/brdy+1); +} + +drawboard() +{ + brdx = brdr.dx()/SZB; + brdy = brdr.dy()/SZB; + brdimg.draw(brdr, green, nil, (0, 0)); + for(i := 1; i < SZB; i++) + drawline(lmap(i, 0), lmap(i, SZB)); + for(j := 1; j < SZB; j++) + drawline(lmap(0, j), lmap(SZB, j)); + for(i = 1; i <= SZB; i++){ + for(j = 1; j <= SZB; j++){ + if (board[i][j] == BLACK || board[i][j] == WHITE) + drawpiece(i, j, board[i][j], 0); + } + } + panelupdate(); +} + +drawpiece(m, n, p, flash: int) +{ + if(p == BLACK) + src := black; + else + src = white; + if(0 && flash && kind[p] == MACHINE){ + for(i := 0; i < 4; i++){ + brdimg.fillellipse(cmap(m, n), 3*brdx/8, 3*brdy/8, src, (0, 0)); + panelupdate(); + sys->sleep(250); + brdimg.fillellipse(cmap(m, n), 3*brdx/8, 3*brdy/8, green, (0, 0)); + panelupdate(); + sys->sleep(250); + } + } + brdimg.fillellipse(cmap(m, n), 3*brdx/8, 3*brdy/8, src, (0, 0)); +} + +panelupdate() +{ + tk->cmd(mainwin, sys->sprint(".p dirty %d %d %d %d", brdr.min.x, brdr.min.y, brdr.max.x, brdr.max.y)); + tk->cmd(mainwin, "update"); +} + +drawline(p0, p1: Point) +{ + brdimg.line(p0, p1, Draw->Endsquare, Draw->Endsquare, 0, brdimg.display.black, (0, 0)); +} + +cmap(m, n: int): Point +{ + return brdr.min.add((m*brdx-brdx/2, n*brdy-brdy/2)); +} + +lmap(m, n: int): Point +{ + return brdr.min.add((m*brdx, n*brdy)); +} + +∞: con (1<<30); +MAXVISITS: con 1024; + +visits, leaves : int; + +minimax(me: int, you: int, plies: int, αβ: int, mv: int): (int, (int, int)) +{ + if(plies == 0){ + visits++; + leaves++; + if(visits == MAXVISITS){ + visits = 0; + sys->sleep(0); + } + return (eval(me, you), (0, 0)); + } + mvs := findmoves(me, you); + if(mvs == nil){ + if(mv) + (v, nil) := minimax(you, me, plies, ∞, 0); + else + (v, nil) = minimax(you, me, plies-1, ∞, 0); + return (-v, (0, 0)); + } + bestv := -∞; + bestm := (0, 0); + e := 0; + for(; mvs != nil; mvs = tl mvs){ + (m, n) := hd mvs; + (nil, sqs) := makemove(m, n, me, you, 1, 1); + (v, nil) := minimax(you, me, plies-1, -bestv, 1); + v = -v; + undomove(m, n, me, you, sqs); + if(v > bestv || (v == bestv && rand->rand(++e) == 0)){ + if(v > bestv) + e = 1; + bestv = v; + bestm = (m, n); + if(bestv >= αβ) + return (∞, (0, 0)); + } + } + return (bestv, bestm); +} + +eval(me: int, you: int): int +{ + d := pieces[me]-pieces[you]; + if(fullsrch) + return d; + n := pieces[me]+pieces[you]; + v := 0; + for(i := 1; i <= SZB; i += SZB-1) + for(j := 1; j <= SZB; j += SZB-1) + v += line(i, j, me, you); + return (PIECES-n)*(value[me]-value[you]+v) + n*d; +} + +line(m: int, n: int, me: int, you: int): int +{ + if(brd[m][n] == EMPTY) + return 0; + dx := dy := -1; + if(m == 1) + dx = 1; + if(n == 1) + dy = 1; + return line0(m, n, 0, dy, me, you) + + line0(m, n, dx, 0, me, you) + + line0(m, n, dx, dy, me, you); +} + +line0(m: int, n: int, dx: int, dy: int, me: int, you: int): int +{ + v := 0; + p := brd[m][n]; + i := val[1][1]; + while(brd[m][n] == p){ + v += i; + m += dx; + n += dy; + } + if(p == you) + return -v; + if(p == me) + return v; + return v; +} + +pow(n: int, m: int): int +{ + p := 1; + while(--m >= 0) + p *= n; + return p; +} + +fatal(s: string) +{ + sys->fprint(stderr, "%s\n", s); + exit; +} + +sleep(t: int) +{ + if(nosleep) + sys->sleep(0); + else + sys->sleep(t); +} + +kill(pid: int): int +{ + fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE); + if(fd == nil) + return -1; + if(sys->write(fd, array of byte "kill", 4) != 4) + return -1; + return 0; +} + +cmd(top: ref Toplevel, s: string): string +{ + e := tk->cmd(top, s); + if (e != nil && e[0] == '!') + sys->fprint(stderr, "reversi: tk error on '%s': %s\n", s, e); + return e; +} + +# swidth: int; +# sfont: ref Font; + +# gettxtattrs() +# { +# swidth = int cmd(mainwin, ".f1.txt cget -width"); # always initial value ? +# f := cmd(mainwin, ".f1.txt cget -font"); +# sfont = Font.open(brdimg.display, f); +# } + +puts(s: string) +{ + # while(sfont.width(s) > swidth) + # s = s[0: len s -1]; + cmd(mainwin, ".f1.txt configure -text {" + s + "}"); + cmd(mainwin, "update"); +} + +fittoscreen(win: ref Tk->Toplevel) +{ + Point: import draw; + if (display.image == nil) + return; + r := display.image.r; + scrsize := Point(r.dx(), r.dy()); + bd := int cmd(win, ". cget -bd"); + winsize := Point(int cmd(win, ". cget -actwidth") + bd * 2, int cmd(win, ". cget -actheight") + bd * 2); + if (winsize.x > scrsize.x) + cmd(win, ". configure -width " + string (scrsize.x - bd * 2)); + if (winsize.y > scrsize.y) + cmd(win, ". configure -height " + string (scrsize.y - bd * 2)); + actr: Rect; + actr.min = Point(int cmd(win, ". cget -actx"), int cmd(win, ". cget -acty")); + actr.max = actr.min.add((int cmd(win, ". cget -actwidth") + bd*2, + int cmd(win, ". cget -actheight") + bd*2)); + (dx, dy) := (actr.dx(), actr.dy()); + if (actr.max.x > r.max.x) + (actr.min.x, actr.max.x) = (r.max.x - dx, r.max.x); + if (actr.max.y > r.max.y) + (actr.min.y, actr.max.y) = (r.max.y - dy, r.max.y); + if (actr.min.x < r.min.x) + (actr.min.x, actr.max.x) = (r.min.x, r.min.x + dx); + if (actr.min.y < r.min.y) + (actr.min.y, actr.max.y) = (r.min.y, r.min.y + dy); + cmd(win, ". configure -x " + string actr.min.x + " -y " + string actr.min.y); + cmd(win, "update"); +} + +win_config := array[] of { + "frame .f", + "button .f.last -text {last move} -command {send cmd last}", + "menubutton .f.bk -text Black -menu .f.bk.bm", + "menubutton .f.wk -text White -menu .f.wk.wm", + "menu .f.bk.bm", + ".f.bk.bm add command -label Human -command { send cmd bh }", + ".f.bk.bm add command -label Machine -command { send cmd bm }", + "menu .f.wk.wm", + ".f.wk.wm add command -label Human -command { send cmd wh }", + ".f.wk.wm add command -label Machine -command { send cmd wm }", + "pack .f.bk -side left", + "pack .f.wk -side right", + "pack .f.last -side top", + + "frame .f0", + "label .f0.bl -text {Black level}", + "label .f0.wl -text {White level}", + "entry .f0.be -width 32", + "entry .f0.we -width 32", + ".f0.be insert 0 " + string SKILLB, + ".f0.we insert 0 " + string SKILLW, + "pack .f0.bl -side left", + "pack .f0.be -side left", + "pack .f0.wl -side right", + "pack .f0.we -side right", + + "frame .f1", + "label .f1.txt -text { } -width " + string WIDTH, + "pack .f1.txt -side top -fill x", + + "panel .p -width " + string WIDTH + " -height " + string HEIGHT, + + "pack .f -side top -fill x", + "pack .f0 -side top -fill x", + "pack .f1 -side top -fill x", + "pack .p -side bottom -fill both -expand 1", + "pack propagate . 0", + + "bind .p <Button-1> {send cmd b1 %x %y}", + "bind .p <Button-2> {send cmd b2 %x %y}", + "bind .p <Button-3> {send cmd b3 %x %y}", + "bind .f0.be <Key-\n> {send cmd blev}", + "bind .f0.we <Key-\n> {send cmd wlev}", + "update", +}; diff --git a/appl/wm/rmtdir.b b/appl/wm/rmtdir.b new file mode 100644 index 00000000..d63d409a --- /dev/null +++ b/appl/wm/rmtdir.b @@ -0,0 +1,215 @@ +implement WmRmtdir; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + +include "tk.m"; + tk: Tk; + Toplevel: import tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "keyring.m"; +include "security.m"; + +t: ref Toplevel; + +WmRmtdir: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +Wm: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +rmt_config := array[] of { + "frame .f", + "label .f.l -text Address:", + "entry .f.e", + "pack .f.l .f.e -side left", + "label .status -text {Enter net!machine ...} -anchor w", + "pack .Wm_t .status .f -fill x", + "bind .f.e <Key-\n> {send cmd dial}", + "frame .b", + "radiobutton .b.none -variable alg -value none -anchor w -text '"+ + "Authentication without SSL", + "radiobutton .b.clear -variable alg -value clear -anchor w -text '"+ + "Authentication with SSL clear", + "radiobutton .b.sha -variable alg -value sha -anchor w -text '"+ + "Authentication with SHA hash", + "radiobutton .b.md5 -variable alg -value md5 -anchor w -text '"+ + "Authentication with MD5 hash", + "radiobutton .b.rc4 -variable alg -value rc4 -anchor w -text '"+ + "Authentication with RC4 encryption", + "radiobutton .b.sharc4 -variable alg -value sha/rc4 -anchor w -text '"+ + "Authentication with SHA and RC4", + "radiobutton .b.md5rc4 -variable alg -value md5/rc4 -anchor w -text '"+ + "Authentication with MD5 and RC4", + "pack .b.none .b.clear .b.sha .b.md5 .b.rc4 .b.sharc4 .b.md5rc4 -fill x", + "pack .b -fill x", + ".b.none invoke", + "update", +}; + +init(ctxt: ref Draw->Context, nil: list of string) +{ + menubut : chan of string; + + sys = load Sys Sys->PATH; + if (ctxt == nil) { + sys->fprint(sys->fildes(2), "rmtdir: no window context\n"); + raise "fail:bad context"; + } + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient= load Tkclient Tkclient->PATH; + + tkclient->init(); + + (t, menubut) = tkclient->toplevel(ctxt, "", sysname()+": Remote Connection", 0); + + cmd := chan of string; + tk->namechan(t, cmd, "cmd"); + + for (i:=0; i<len rmt_config; i++) + tk->cmd(t, rmt_config[i]); + tkclient->onscreen(t, nil); + tkclient->startinput(t, "kbd"::"ptr"::nil); + + for(;;) alt { + s := <-t.ctxt.kbd => + tk->keyboard(t, s); + s := <-t.ctxt.ptr => + tk->pointer(t, *s); + s := <-t.ctxt.ctl or + s = <-t.wreq or + s = <-menubut => + tkclient->wmctl(t, s); + <-cmd => + addr := tk->cmd(t, ".f.e get"); + status("Dialing"); + (ok, c) := sys->dial(netmkaddr(addr, "tcp", "styx"), nil); + if(ok < 0) { + tk->cmd(t, ".status configure -text {Failed: "+ + sys->sprint("%r")+"}; update"); + break; + } + status("Authenticate"); + alg := tk->cmd(t, "variable alg"); + + kr := load Keyring Keyring->PATH; + if(kr == nil){ + tk->cmd(t, ".status configure -text {Error: can't load module Keyring "+ + sys->sprint("%r")+"}; update"); + break; + } + + user := user(); + kd := "/usr/" + user + "/keyring/"; + cert := kd + netmkaddr(addr, "tcp", ""); + (ok, nil) = sys->stat(cert); + if(ok < 0) + cert = kd + "default"; + + ai := kr->readauthinfo(cert); + if(ai == nil){ + tk->cmd(t, ".status configure -text {Error: certificate for "+ + sys->sprint("%s",addr)+" not found}; update"); + wmgetauthinfo := load Wm "/dis/wm/wmgetauthinfo.dis"; + if(wmgetauthinfo == nil){ + tk->cmd(t, ".status configure -text {Error: can't load module wmgetauthinfo.dis}; update"); + exit; + } + spawn wmgetauthinfo->init(ctxt, nil); + break; + } + + au := load Auth Auth->PATH; + if(au == nil){ + tk->cmd(t, ".status configure -text {Error: can't load module Auth "+ + sys->sprint("%r")+"; update"); + break; + } + + err := au->init(); + if(err != nil){ + tk->cmd(t, ".status configure -text {Error: "+ + sys->sprint("%s", err)+"; update"); + break; + } + + fd: ref Sys->FD; + (fd, err) = au->client(alg, ai, c.dfd); + if(fd == nil){ + tk->cmd(t, ".status configure -text {Error: authentication failed: "+ + sys->sprint("%s",err)+"; update"); + break; + } + + status("Mount"); + sys->pctl(sys->FORKNS, nil); # don't fork before authentication + n := sys->mount(fd, nil, "/n/remote", sys->MREPL, ""); + if(n < 0) { + tk->cmd(t, ".status configure -text {Mount failed: "+ + sys->sprint("%r")+"}; update"); + break; + } + wmdir := load Wm "/dis/wm/dir.dis"; + spawn wmdir->init(ctxt, "wm/dir" :: "/n/remote" :: nil); + return; + } +} + +status(s: string) +{ + tk->cmd(t, ".status configure -text {"+s+"}; update"); +} + +sysname(): string +{ + fd := sys->open("#c/sysname", sys->OREAD); + if(fd == nil) + return "Anon"; + buf := array[128] of byte; + n := sys->read(fd, buf, len buf); + if(n < 0) + return "Anon"; + return string buf[0:n]; +} + +user(): string +{ + sys = load Sys Sys->PATH; + + fd := sys->open("/dev/user", sys->OREAD); + if(fd == nil) + return ""; + + buf := array[128] of byte; + n := sys->read(fd, buf, len buf); + if(n < 0) + return ""; + + return string buf[0:n]; +} + +netmkaddr(addr, net, svc: string): string +{ + if(net == nil) + net = "net"; + (n, l) := sys->tokenize(addr, "!"); + if(n <= 1){ + if(svc== nil) + return sys->sprint("%s!%s", net, addr); + return sys->sprint("%s!%s!%s", net, addr, svc); + } + if(svc == nil || n > 2) + return addr; + return sys->sprint("%s!%s", addr, svc); +} diff --git a/appl/wm/rt.b b/appl/wm/rt.b new file mode 100644 index 00000000..4bf26463 --- /dev/null +++ b/appl/wm/rt.b @@ -0,0 +1,701 @@ +implement WmRt; + +include "sys.m"; + sys: Sys; + sprint: import sys; + +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; + +include "draw.m"; + +include "tk.m"; + tk: Tk; + Toplevel: import tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "dialog.m"; + dialog: Dialog; + +include "selectfile.m"; + selectfile: Selectfile; + +include "dis.m"; + dis: Dis; + Inst, Type, Data, Link, Mod: import dis; + XMAGIC: import Dis; + MUSTCOMPILE, DONTCOMPILE: import Dis; + AMP, AFP, AIMM, AXXX, AIND, AMASK: import Dis; + ARM, AXNON, AXIMM, AXINF, AXINM: import Dis; + DEFB, DEFW, DEFS, DEFF, DEFA, DIND, DAPOP, DEFL: import Dis; + +WmRt: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +gctxt: ref Draw->Context; +t: ref Toplevel; +disfile: string; + +TK: con 1; + +m: ref Mod; +rt := 0; +ss := -1; + +rt_cfg := array[] of { + "frame .m", + "menubutton .m.open -text File -menu .file", + "menubutton .m.prop -text Properties -menu .prop", + "menubutton .m.view -text View -menu .view", + "label .m.l", + "pack .m.open .m.view .m.prop -side left", + "pack .m.l -side right", + "frame .b", + "text .b.t -width 12c -height 7c -yscrollcommand {.b.s set} -bg white", + "scrollbar .b.s -command {.b.t yview}", + "pack .b.s -fill y -side left", + "pack .b.t -fill both -expand 1", + "pack .m -anchor w -fill x", + "pack .b -fill both -expand 1", + "pack propagate . 0", + "update", + + "menu .prop", + ".prop add checkbutton -text {Must compile} -command {send cmd must}", + ".prop add checkbutton -text {Don't compile} -command {send cmd dont}", + ".prop add separator", + ".prop add command -text {Set stack extent} -command {send cmd stack}", + ".prop add command -text {Sign module} -command {send cmd sign}", + + "menu .view", + ".view add command -text {Header} -command {send cmd hdr}", + ".view add command -text {Code segment} -command {send cmd code}", + ".view add command -text {Data segment} -command {send cmd data}", + ".view add command -text {Type descriptors} -command {send cmd type}", + ".view add command -text {Link descriptors} -command {send cmd link}", + ".view add command -text {Import descriptors} -command {send cmd imports}", + ".view add command -text {Exception handlers} -command {send cmd handlers}", + + "menu .file", + ".file add command -text {Open module} -command {send cmd open}", + ".file add separator", + ".file add command -text {Write .dis module} -command {send cmd save}", + ".file add command -text {Write .s file} -command {send cmd list}", +}; + +init(ctxt: ref Draw->Context, nil: list of string) +{ + sys = load Sys Sys->PATH; + if (ctxt == nil) { + sys->fprint(sys->fildes(2), "rt: no window context\n"); + raise "fail:bad context"; + } + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + dialog = load Dialog Dialog->PATH; + selectfile = load Selectfile Selectfile->PATH; + + sys->pctl(Sys->NEWPGRP, nil); + + tkclient->init(); + dialog->init(); + selectfile->init(); + + gctxt = ctxt; + + menubut: chan of string; + (t, menubut) = tkclient->toplevel(ctxt, "", "Dis Module Manager", Tkclient->Appl); + + cmd := chan of string; + + tk->namechan(t, cmd, "cmd"); + tkcmds(t, rt_cfg); + tkclient->onscreen(t, nil); + tkclient->startinput(t, "kbd"::"ptr"::nil); + + dis = load Dis Dis->PATH; + if(dis == nil) { + dialog->prompt(ctxt, t.image, "error -fg red", "Load Module", + "wmrt requires Dis", + 0, "Exit"::nil); + return; + } + dis->init(); + + for(;;) alt { + s := <-t.ctxt.kbd => + tk->keyboard(t, s); + s := <-t.ctxt.ptr => + tk->pointer(t, *s); + s := <-t.ctxt.ctl or + s = <-t.wreq => + tkclient->wmctl(t, s); + menu := <-menubut => + if(menu == "exit") + return; + tkclient->wmctl(t, menu); + s := <-cmd => + case s { + "open" => + openfile(ctxt); + "save" => + writedis(); + "list" => + writeasm(); + "hdr" => + hdr(); + "code" => + das(TK); + "data" => + dat(TK); + "type" => + desc(TK); + "link" => + link(TK); + "imports" => + imports(TK); + "handlers" => + handlers(TK); + "must" => + rt ^= MUSTCOMPILE; + "dont" => + rt ^= DONTCOMPILE; + "stack" => + spawn stack(ctxt); + "sign" => + dialog->prompt(ctxt, t.image, "error -fg red", "Signed Modules", + "not implemented", + 0, "Continue"::nil); + } + } +} + +stack_cfg := array[] of { + "scale .s -length 200 -to 32768 -resolution 128 -orient horizontal", + "frame .f", + "pack .s .f -pady 5 -fill x -expand 1", +}; + +stack(ctxt: ref Draw->Context) +{ + # (s, sbut) := tkclient->toplevel(ctxt, tkclient->geom(t), "Dis Stack", 0); + (s, sbut) := tkclient->toplevel(ctxt, "", "Dis Stack", 0); + + cmd := chan of string; + tk->namechan(s, cmd, "cmd"); + tkcmds(s, stack_cfg); + tk->cmd(s, ".s set " + string ss); + tk->cmd(s, "update"); + tkclient->onscreen(s, nil); + tkclient->startinput(s, "kbd"::"ptr"::nil); + + for(;;) alt { + c := <-s.ctxt.kbd => + tk->keyboard(s, c); + c := <-s.ctxt.ptr => + tk->pointer(s, *c); + c := <-s.ctxt.ctl or + c = <-s.wreq => + tkclient->wmctl(s, c); + wmctl := <-sbut => + if(wmctl == "exit") { + ss = int tk->cmd(s, ".s get"); + return; + } + tkclient->wmctl(s, wmctl); + } +} + +openfile(ctxt: ref Draw->Context) +{ + pattern := list of { + "*.dis (Dis VM module)", + "* (All files)" + }; + + for(;;) { + disfile = selectfile->filename(ctxt, t.image, "Dis file", pattern, nil); + if(disfile == "") + break; + + s: string; + (m, s) = dis->loadobj(disfile); + if(s == nil) { + ss = m.ssize; + rt = m.rt; + tk->cmd(t, ".m.l configure -text {"+m.name+"}"); + das(TK); + return; + } + + r := dialog->prompt(ctxt, t.image, "error -fg red", "Open Dis File", + s, + 0, "Retry" :: "Abort" :: nil); + if(r == 1) + return; + } +} + +writedis() +{ + if(m == nil || m.magic == 0) { + dialog->prompt(gctxt, t.image, "error -fg red", "Write .dis", + "no module loaded", + 0, "Continue"::nil); + return; + } + if(rt < 0) + rt = m.rt; + if(ss < 0) + ss = m.ssize; + if(rt == m.rt && ss == m.ssize) + return; + while((fd := sys->open(disfile, Sys->OREAD)) == nil){ + if(dialog->prompt(gctxt, t.image, "error -fg red", "Open Dis File", "open failed: "+sprint("%r"), + 0, "Retry" :: "Abort" :: nil)) + return; + } + if(len discona(rt) == len discona(m.rt) && len discona(ss) == len discona(m.ssize)){ + sys->seek(fd, big 4, Sys->SEEKSTART); # skip magic + discon(fd, rt); + discon(fd, ss); + m.rt = rt; + m.ssize = ss; + return; + } + # rt and ss representations changed in length: read the file in, + # make a copy and update rt and ss when copying + (ok, d) := sys->fstat(fd); + if(ok < 0){ + ioerror("Reading Dis file "+disfile, "can't find file length: "+sprint("%r")); + return; + } + length := int d.length; + disbuf := array[length] of byte; + if(sys->read(fd, disbuf, length) != length){ + ioerror("Reading Dis file "+disfile, "read error: "+sprint("%r")); + return; + } + outbuf := array[length+2*4] of byte; # could avoid this buffer if required, by writing portions of disbuf + (magic, i) := operand(disbuf, 0); + o := putoperand(outbuf, magic); + if(magic == Dis->SMAGIC){ + ns: int; + (ns, i) = operand(disbuf, i); + o += putoperand(outbuf[o:], ns); + sign := disbuf[i:i+ns]; + i += ns; + outbuf[o:] = sign; + o += ns; + } + (nil, i) = operand(disbuf, i); + (nil, i) = operand(disbuf, i); + if(i < 0){ + ioerror("Reading Dis file "+disfile, "Dis header too short"); + return; + } + o += putoperand(outbuf[o:], rt); + o += putoperand(outbuf[o:], ss); + outbuf[o:] = disbuf[i:]; + o += len disbuf - i; + fd = sys->create(disfile, Sys->OWRITE, 8r666); + if(fd == nil){ + ioerror("Rewriting "+disfile, sys->sprint("can't create %s: %r",disfile)); + return; + } + if(sys->write(fd, outbuf, o) != o) + ioerror("Rewriting "+disfile, "write error: "+sprint("%r")); + m.rt = rt; + m.ssize = ss; +} + +ioerror(title: string, err: string) +{ + dialog->prompt(gctxt, t.image, "error -fg red", title, err, 0, "Dismiss" :: nil); +} + +putoperand(out: array of byte, v: int): int +{ + a := discona(v); + out[0:] = a; + return len a; +} + +discona(val: int): array of byte +{ + if(val >= -64 && val <= 63) + return array[] of { byte(val & ~16r80) }; + else if(val >= -8192 && val <= 8191) + return array[] of { byte((val>>8) & ~16rC0 | 16r80), byte val }; + else + return array[] of { byte(val>>24 | 16rC0), byte(val>>16), byte(val>>8), byte val }; +} + +discon(fd: ref Sys->FD, val: int) +{ + a := discona(val); + sys->write(fd, a, len a); +} + +operand(disobj: array of byte, o: int): (int, int) +{ + if(o >= len disobj) + return (-1, -1); + b := int disobj[o++]; + case b & 16rC0 { + 16r00 => + return (b, o); + 16r40 => + return (b | ~16r7F, o); + 16r80 => + if(o >= len disobj) + return (-1, -1); + if(b & 16r20) + b |= ~16r3F; + else + b &= 16r3F; + b = (b<<8) | int disobj[o++]; + return (b, o); + 16rC0 => + if(o+2 >= len disobj) + return (-1, -1); + if(b & 16r20) + b |= ~16r3F; + else + b &= 16r3F; + b = b<<24 | + (int disobj[o]<<16) | + (int disobj[o+1]<<8)| + int disobj[o+2]; + o += 3; + return (b, o); + } + return (0, -1); # can't happen +} + +fasm: ref Iobuf; + +writeasm() +{ + if(m == nil || m.magic == 0) { + dialog->prompt(gctxt, t.image, "error -fg red", "Write .s", + "no module loaded", + 0, "Continue"::nil); + return; + } + + bufio = load Bufio Bufio->PATH; + if(bufio == nil) { + dialog->prompt(gctxt, t.image, "error -fg red", "Write .s", + "Bufio load failed: "+sprint("%r"), + 0, "Exit"::nil); + return; + } + + for(;;) { + asmfile: string; + if(len disfile > 4 && disfile[len disfile-4:] == ".dis") + asmfile = disfile[0:len disfile-3] + "s"; + else + asmfile = disfile + ".s"; + fasm = bufio->create(asmfile, Sys->OWRITE|Sys->OTRUNC, 8r666); + if(fasm != nil) + break; + r := dialog->prompt(gctxt, t.image, "error -fg red", "Create .s file", + "open failed: "+sprint("%r"), + 0, "Retry" :: "Abort" :: nil); + if(r == 0) + continue; + else + return; + } + das(!TK); + fasm.puts("\tentry\t" + string m.entry + "," + string m.entryt + "\n"); + desc(!TK); + dat(!TK); + fasm.puts("\tmodule\t" + m.name + "\n"); + link(!TK); + imports(!TK); + handlers(!TK); + fasm.close(); +} + +link(flag: int) +{ + if(m == nil || m.magic == 0) { + dialog->prompt(gctxt, t.image, "error -fg red", "Link Descriptors", + "no module loaded", + 0, "Continue"::nil); + return; + } + + if(flag == TK) + tk->cmd(t, ".b.t delete 1.0 end"); + + for(i := 0; i < m.lsize; i++) { + l := m.links[i]; + s := sprint(" link %d,%d, 0x%ux, \"%s\"\n", + l.desc, l.pc, l.sig, l.name); + if(flag == TK) + tk->cmd(t, ".b.t insert end '"+s); + else + fasm.puts(s); + } + if(flag == TK) + tk->cmd(t, ".b.t see 1.0; update"); +} + +imports(flag: int) +{ + if(m == nil || m.magic == 0) { + dialog->prompt(gctxt, t.image, "error -fg red", "Import Descriptors", + "no module loaded", + 0, "Continue"::nil); + return; + } + + if(flag == TK) + tk->cmd(t, ".b.t delete 1.0 end"); + + mi := m.imports; + for(i := 0; i < len mi; i++) { + a := mi[i]; + for(j := 0; j < len a; j++) { + ai := a[j]; + s := sprint(" import 0x%ux, \"%s\"\n", ai.sig, ai.name); + if(flag == TK) + tk->cmd(t, ".b.t insert end '"+s); + else + fasm.puts(s); + } + } + if(flag == TK) + tk->cmd(t, ".b.t see 1.0; update"); +} + +handlers(flag: int) +{ + if(m == nil || m.magic == 0) { + dialog->prompt(gctxt, t.image, "error -fg red", "Exception Handlers", + "no module loaded", + 0, "Continue"::nil); + return; + } + + if(flag == TK) + tk->cmd(t, ".b.t delete 1.0 end"); + + hs := m.handlers; + for(i := 0; i < len hs; i++) { + h := hs[i]; + tt := -1; + for(j := 0; j < len m.types; j++) { + if(h.t == m.types[j]) { + tt = j; + break; + } + } + s := sprint(" %d-%d, o=%d, e=%d t=%d\n", h.pc1, h.pc2, h.eoff, h.ne, tt); + if(flag == TK) + tk->cmd(t, ".b.t insert end '"+s); + else + fasm.puts(s); + et := h.etab; + for(j = 0; j < len et; j++) { + e := et[j]; + if(e.s == nil) + s = sprint(" %d *\n", e.pc); + else + s = sprint(" %d \"%s\"\n", e.pc, e.s); + if(flag == TK) + tk->cmd(t, ".b.t insert end '"+s); + else + fasm.puts(s); + } + } + if(flag == TK) + tk->cmd(t, ".b.t see 1.0; update"); +} + +desc(flag: int) +{ + if(m == nil || m.magic == 0) { + dialog->prompt(gctxt, t.image, "error -fg red", "Type Descriptors", + "no module loaded", + 0, "Continue"::nil); + return; + } + + if(flag == TK) + tk->cmd(t, ".b.t delete 1.0 end"); + + for(i := 0; i < m.tsize; i++) { + h := m.types[i]; + s := sprint(" desc $%d, %d, \"", i, h.size); + for(j := 0; j < h.np; j++) + s += sprint("%.2ux", int h.map[j]); + s += "\"\n"; + if(flag == TK) + tk->cmd(t, ".b.t insert end '"+s); + else + fasm.puts(s); + } + if(flag == TK) + tk->cmd(t, ".b.t see 1.0; update"); +} + +hdr() +{ + if(m == nil || m.magic == 0) { + dialog->prompt(gctxt, t.image, "error -fg red", "Header", + "no module loaded", + 0, "Continue"::nil); + return; + } + + tk->cmd(t, ".b.t delete 1.0 end"); + + s := sprint("%.8ux Version %d Dis VM\n", m.magic, m.magic - XMAGIC + 1); + s += sprint("%.8ux Runtime flags %s\n", m.rt, rtflag(m.rt)); + s += sprint("%8d bytes per stack extent\n\n", m.ssize); + + + s += sprint("%8d instructions\n", m.isize); + s += sprint("%8d data size\n", m.dsize); + s += sprint("%8d heap type descriptors\n", m.tsize); + s += sprint("%8d link directives\n", m.lsize); + s += sprint("%8d entry pc\n", m.entry); + s += sprint("%8d entry type descriptor\n\n", m.entryt); + + if(m.sign == nil) + s += "Module is Insecure\n"; + + tk->cmd(t, ".b.t insert end '"+s); + tk->cmd(t, ".b.t see 1.0; update"); +} + +rtflag(flag: int): string +{ + if(flag == 0) + return ""; + + s := "["; + + if(flag & MUSTCOMPILE) + s += "MustCompile"; + if(flag & DONTCOMPILE) { + if(flag & MUSTCOMPILE) + s += "|"; + s += "DontCompile"; + } + s[len s] = ']'; + + return s; +} + +das(flag: int) +{ + if(m == nil || m.magic == 0) { + dialog->prompt(gctxt, t.image, "error -fg red", "Assembly", + "no module loaded", + 0, "Continue"::nil); + return; + } + + if(flag == TK) + tk->cmd(t, ".b.t delete 1.0 end"); + + for(i := 0; i < m.isize; i++) { + prefix := ""; + if(flag == TK) + prefix = sprint(".b.t insert end '%4d ", i); + else { + if(i % 10 == 0) + fasm.puts("#" + string i + "\n"); + prefix = sprint("\t"); + } + s := prefix + dis->inst2s(m.inst[i]) + "\n"; + + if(flag == TK) + tk->cmd(t, s); + else + fasm.puts(s); + } + if(flag == TK) + tk->cmd(t, ".b.t see 1.0; update"); +} + +dat(flag: int) +{ + if(m == nil || m.magic == 0) { + dialog->prompt(gctxt, t.image, "error -fg red", "Module Data", + "no module loaded", + 0, "Continue"::nil); + return; + } + s := sprint(" var @mp, %d\n", m.types[0].size); + if(flag == TK) { + tk->cmd(t, ".b.t delete 1.0 end"); + tk->cmd(t, ".b.t insert end '"+s); + } else + fasm.puts(s); + + s = ""; + for(d := m.data; d != nil; d = tl d) { + pick dat := hd d { + Bytes => + s = sprint("\tbyte @mp+%d", dat.off); + for(n := 0; n < dat.n; n++) + s += sprint(",%d", int dat.bytes[n]); + Words => + s = sprint("\tword @mp+%d", dat.off); + for(n := 0; n < dat.n; n++) + s += sprint(",%d", dat.words[n]); + String => + s = sprint("\tstring @mp+%d, \"%s\"", dat.off, mapstr(dat.str)); + Reals => + s = sprint("\treal @mp+%d", dat.off); + for(n := 0; n < dat.n; n++) + s += sprint(", %g", dat.reals[n]); + break; + Array => + s = sprint("\tarray @mp+%d,$%d,%d", dat.off, dat.typex, dat.length); + Aindex => + s = sprint("\tindir @mp+%d,%d", dat.off, dat.index); + Arestore => + s = "\tapop"; + break; + Bigs => + s = sprint("\tlong @mp+%d", dat.off); + for(n := 0; n < dat.n; n++) + s += sprint(", %bd", dat.bigs[n]); + } + if(flag == TK) + tk->cmd(t, ".b.t insert end '"+s+"\n"); + else + fasm.puts(s+"\n"); + } + + if(flag == TK) + tk->cmd(t, ".b.t see 1.0; update"); +} + +mapstr(s: string): string +{ + for(i := 0; i < len s; i++) { + if(s[i] == '\n') + s = s[0:i] + "\\n" + s[i+1:]; + } + return s; +} + +tkcmds(t: ref Toplevel, cfg: array of string) +{ + for(i := 0; i < len cfg; i++) + tk->cmd(t, cfg[i]); +} diff --git a/appl/wm/sam.b b/appl/wm/sam.b new file mode 100644 index 00000000..02f12f4d --- /dev/null +++ b/appl/wm/sam.b @@ -0,0 +1,230 @@ +implement Samterm; + +include "sys.m"; +sys: Sys; +fprint, sprint, FD: import sys; +stderr, logfd: ref FD; + +include "draw.m"; +draw: Draw; + +include "samterm.m"; + +include "samtk.m"; +samtk: Samtk; + +include "samstub.m"; +samstub: Samstub; +Samio, Sammsg: import samstub; + +samio: ref Samio; + +ctxt: ref Context; + +init(context: ref draw->Context, nil: list of string) +{ + recvsam: chan of ref Sammsg; + + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + stderr = sys->fildes(2); + + logfd = sys->create("samterm.log", sys->OWRITE, 8r666); + if (logfd == nil) { + fprint(stderr, "Can't create samterm.log\n"); + logfd = stderr; + } + + fprint(logfd, "Samterm started\n"); + + pgrp := sys->pctl(sys->NEWPGRP, nil); + + ctxt = ref Context( + context, + 1000, # initial tag + + 0, # lock + + nil, # keysel + nil, # scrollsel + nil, # buttonsel + nil, # menu2sel + nil, # menu3sel + nil, # titlesel + nil, # tags + + nil, # menus + nil, # texts + + nil, # cmd + nil, # which + nil, # work + pgrp, # pgrp + logfd # logging file descriptor + ); + + samtk = load Samtk Samtk->PATH; + if (samtk == nil) { + fprint(stderr, "Can't load %s\n", Samtk->PATH); + return; + } + samtk->init(ctxt); + + samstub = load Samstub Samstub->PATH; + if (samstub == nil) { + fprint(stderr, "Can't load %s\n", Samstub->PATH); + return; + } + samstub->init(ctxt); + + (samio, recvsam) = samstub->start(); + if (samio == nil) { + fprint(stderr, "couldn't start samstub\n"); + return; + } + samstub->outTs(samstub->Tversion, samstub->VERSION); + + samstub->startcmdfile(); + + samstub->setlock(); + + for(;;) if (ctxt.lock == 0) alt { + (win, menu) := <-ctxt.titlesel => + samstub->cleanout(); + fl := ctxt.flayers[win]; + tag := fl.tag; + if ((i := samtk->whichtext(tag)) < 0) + samtk->panic("samterm: whichtext"); + t := ctxt.texts[i]; + samtk->newcur(t, fl); + case menu { + "exit" => + if (ctxt.flayers[win].tag == 0) { + samstub->outT0(samstub->Texit); + f := sprint("#p/%d/ctl", pgrp); + if ((fd := sys->open(f, sys->OWRITE)) != nil) + sys->write(fd, array of byte "killgrp\n", 8); + return; + } + samstub->close(win, tag); + "resize" => + samtk->resize(fl); + samstub->scrollto(fl, fl.scope.first); + "task" => + spawn samtk->titlectl(win, menu); + * => + samtk->titlectl(win, menu); + } + + + (win, m1) := <-ctxt.buttonsel => + samstub->cleanout(); + fl := ctxt.flayers[win]; + tag := fl.tag; + if (samtk->buttonselect(fl, m1)) { + samstub->outTsl(samstub->Tdclick, tag, fl.dot.first); + samstub->setlock(); + } + (win, m2) := <-ctxt.menu2sel => + samstub->cleanout(); + fl := ctxt.flayers[win]; + tag := fl.tag; + if ((i := samtk->whichtext(tag)) < 0) + samtk->panic("samterm: whichtext"); + t := ctxt.texts[i]; + samtk->newcur(t, fl); + case m2 { + "cut" => + samstub->cut(t, fl); + "paste" => + samstub->paste(t, fl); + "snarf" => + samstub->snarf(t, fl); + "look" => + samstub->look(t, fl); + "exch" => + fprint(ctxt.logfd, "debug -- exch: %d, %s\n", win, m2); + "send" => + samstub->send(t, fl); + "search" => + samstub->search(t, fl); + * => + samtk->panic("samterm: editmenu"); + } + (win, m3) := <-ctxt.menu3sel => + samstub->cleanout(); + fl := ctxt.flayers[win]; + tag := fl.tag; + if ((i := samtk->whichtext(tag)) < 0) + samtk->panic("samterm: whichtext"); + t := ctxt.texts[i]; + samtk->newcur(t, fl); + case m3 { + "new" => + samstub->startnewfile(); + "zerox" => + samstub->zerox(t); + "close" => + if (win != 0) { + samstub->close(win, tag); + } + "write" => + samstub->outTs(samstub->Twrite, tag); + samstub->setlock(); + * => + for (i = 0; i < len ctxt.menus; i++) { + if (ctxt.menus[i].name == m3) { + break; + } + } + if (i == len ctxt.menus) + samtk->panic("init: can't find m3"); + t = ctxt.menus[i].text; + t.flayers = samtk->append(tl t.flayers, hd t.flayers); + samtk->newcur(t, hd t.flayers); + + } + (win, c) := <-ctxt.keysel => + if (ctxt.which != ctxt.flayers[win]) { + fprint(ctxt.logfd, "probably can't happen\n"); + samstub->cleanout(); + tag := ctxt.flayers[win].tag; + if ((i := samtk->whichtext(tag)) < 0) + samtk->panic("samterm: whichtext"); + samtk->newcur(ctxt.texts[i], ctxt.flayers[win]); + } + samstub->keypress(c[1:len c -1]); + (win, c) := <-ctxt.scrollsel => + if (ctxt.which != ctxt.flayers[win]) { + samstub->cleanout(); + tag := ctxt.flayers[win].tag; + if ((i := samtk->whichtext(tag)) < 0) + samtk->panic("samterm: whichtext"); + samtk->newcur(ctxt.texts[i], ctxt.flayers[win]); + } + (pos, lines) := samtk->scroll(ctxt.which, c); + if (lines > 0) { + samstub->outTsll(samstub->Torigin, + ctxt.which.tag, pos, lines); + samstub->setlock(); + } else if (pos != -1) + samstub->scrollto(ctxt.which, pos); + h := <-recvsam => + if (samstub->inmesg(h)) { + samstub->outT0(samstub->Texit); + fname := sprint("#p/%d/ctl", pgrp); + if ((fdesc := sys->open(fname, sys->OWRITE)) != nil) + sys->write(fdesc, array of byte "killgrp\n", 8); + return; + } + } else { + h := <-recvsam; + if (samstub->inmesg(h)) { + samstub->outT0(samstub->Texit); + fname := sprint("#p/%d/ctl", pgrp); + if ((fdesc := sys->open(fname, sys->OWRITE)) != nil) + sys->write(fdesc, array of byte "killgrp\n", 8); + return; + } + } +} diff --git a/appl/wm/samstub.b b/appl/wm/samstub.b new file mode 100644 index 00000000..bdf708ff --- /dev/null +++ b/appl/wm/samstub.b @@ -0,0 +1,1338 @@ +implement Samstub; + +include "sys.m"; +sys: Sys; +fprint, FD, fildes: import sys; + +stderr: ref FD; + +include "draw.m"; +draw: Draw; + +include "samterm.m"; +samterm: Samterm; +Text, Menu, Context, Flayer, Section: import samterm; + +include "samtk.m"; +samtk: Samtk; +panic, whichtext, whichmenu: import samtk; + +include "samstub.m"; + +sendsam: chan of ref Sammsg; +recvsam: chan of ref Sammsg; + +snarflen: int; + +ctxt: ref Context; + +requested: list of (int, int); + +tname := array [] of { + "Tversion", + "Tstartcmdfile", + "Tcheck", + "Trequest", + "Torigin", + "Tstartfile", + "Tworkfile", + "Ttype", + "Tcut", + "Tpaste", + "Tsnarf", + "Tstartnewfile", + "Twrite", + "Tclose", + "Tlook", + "Tsearch", + "Tsend", + "Tdclick", + "Tstartsnarf", + "Tsetsnarf", + "Tack", + "Texit", +}; + +hname := array [] of { + "Hversion", + "Hbindname", + "Hcurrent", + "Hnewname", + "Hmovname", + "Hgrow", + "Hcheck0", + "Hcheck", + "Hunlock", + "Hdata", + "Horigin", + "Hunlockfile", + "Hsetdot", + "Hgrowdata", + "Hmoveto", + "Hclean", + "Hdirty", + "Hcut", + "Hsetpat", + "Hdelname", + "Hclose", + "Hsetsnarf", + "Hsnarflen", + "Hack", + "Hexit", +}; + +init(c: ref Context) +{ + ctxt = c; + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + + stderr = fildes(2); + + samterm = load Samterm Samterm->PATH; + + samtk = load Samtk Samtk->PATH; + samtk->init(ctxt); + + requested = nil; +} + +start(): (ref Samio, chan of ref Sammsg) +{ + sys = load Sys Sys->PATH; + + sys->bind("#C", "/", sys->MAFTER); + + # Allocate a cmd device + ctl := sys->open("/cmd/clone", sys->ORDWR); + if(ctl == nil) { + fprint(stderr, "can't open /cmd/clone\n"); + return (nil, nil); + } + + # Find out which one + buf := array[32] of byte; + n := sys->read(ctl, buf, len buf); + if(n <= 0) { + fprint(stderr, "can't read cmd device\n"); + return (nil, nil); + } + + dir := "/cmd/"+string buf[0:n]; + + # Start the Command + n = sys->fprint(ctl, "exec "+ SAM); + if(n <= 0) { + fprint(stderr, "can't exec %s\n", SAM); + return (nil, nil); + } + + data := sys->open(dir+"/data", sys->ORDWR); + if(data == nil) { + fprint(stderr, "can't open cmd data file\n"); + return (nil, nil); + } + + sendsam = chan of ref Sammsg; + recvsam = chan of ref Sammsg; + + samio := ref Samio(ctl, data, array[1] of byte, 0, 0); + + spawn sender(samio, sendsam); + spawn receiver(samio, recvsam); + + return (samio, recvsam); +} + +sender(samio: ref Samio, c: chan of ref Sammsg) +{ + fprint(ctxt.logfd, "sender started\n"); + for (;;) { + h := <- c; + if (h == nil) return; + buf := array[3 + len h.mdata] of byte; + buf[0] = byte h.mtype; + buf[1] = byte h.mcount; + buf[2] = byte (h.mcount >> 8); + buf[3:] = h.mdata; + sys->write(samio.data, buf, len buf); + } +} + +receiver(samio: ref Samio, msgchan: chan of ref Sammsg) +{ + c: int; + + fprint(ctxt.logfd, "receiver started\n"); + + state := 0; + i := 0; + errs := 0; + + h: ref Sammsg; + + for (;;) { + if (samio.count == 0) { + n := sys->read(samio.data, samio.buffer, len samio.buffer); + if (n <= 0) { + fprint(stderr, "Read error on sam's pipe\n"); + return; + } + samio.index = 0; + samio.count = n; + } + samio.count--; + + c = int samio.buffer[samio.index++]; + + case state { + 0 => + h = ref Sammsg(c, 0, nil); + state++; + continue; + 1 => + h.mcount = c; + state++; + continue; + 2 => + h.mcount = h.mcount|(c<<8); + if (h.mcount > DATASIZE || h.mcount < 0) + panic("receiver: count>DATASIZE"); + if(h.mcount != 0) { + h.mdata = array[h.mcount] of byte; + i = 0; + state++; + continue; + } + 3 => + h.mdata[i++] = byte c; + if(i < h.mcount){ + continue; + } + } + msgchan <- = h; + h = nil; + state = 0; + } +} + +inmesg(h: ref Sammsg): int +{ + + case h.mtype { + + Hversion => + m := h.inshort(0); + fprint(ctxt.logfd, "Hversion: %d\n", m); + + Hbindname => + m := h.inshort(0); + vl := h.invlong(2); + fprint(ctxt.logfd, "Hbindname: %ux, %bux\n", m, vl); + bindname(m, int vl); + + Hcurrent => + m := h.inshort(0); + fprint(ctxt.logfd, "Hcurrent: %d\n", m); + hcurrent(m); + + Hmovname => + m := h.inshort(0); + fprint(ctxt.logfd, "Hmovname: %d, %s\n", m, string h.mdata[2:]); + movename(m, string h.mdata[2:]); + + Hgrow => + m := h.inshort(0); + l1 := h.inlong(2); + l2 := h.inlong(6); + fprint(ctxt.logfd, "Hgrow: %d, %d, %d\n", m, l1, l2); + hgrow(m, l1, l2); + + Hnewname => + m := h.inshort(0); + fprint(ctxt.logfd, "Hnewname: %d\n", m); + newname(m); + + Hcheck0 => + m := h.inshort(0); + fprint(ctxt.logfd, "Hcheck0: %d\n", m); + i := whichmenu(m); + if (i >= 0) { + t := ctxt.menus[i].text; + if (t != nil) + t.lock++; + outTs(Tcheck, m); + } + + Hcheck => + m := h.inshort(0); + fprint(ctxt.logfd, "Hcheck: %d\n", m); + i := whichmenu(m); + if (i >= 0) { + t := ctxt.menus[i].text; + if (t != nil && t.lock) + t.lock--; + hcheck(t); + } + + Hunlock => + fprint(ctxt.logfd, "Hunlock\n"); + clrlock(); + + Hdata => + m := h.inshort(0); + l := h.inlong(2); + fprint(ctxt.logfd, "Hdata: %d, %d, %s\n", + m, l, contract(string h.mdata[6:])); + hdata(m, l, string h.mdata[6:]); + + Horigin => + m := h.inshort(0); + l := h.inlong(2); + fprint(ctxt.logfd, "Horigin: %d, %d\n", m, l); + horigin(m, l); + + Hunlockfile => + m := h.inshort(0); + fprint(ctxt.logfd, "Hunlockfile: %d\n", m); + clrlock(); + + Hsetdot => + m := h.inshort(0); + l1 := h.inlong(2); + l2 := h.inlong(6); + fprint(ctxt.logfd, "Hsetdot: %d, %d, %d\n", m, l1, l2); + hsetdot(m, l1, l2); + + Hgrowdata => + m := h.inshort(0); + l1 := h.inlong(2); + l2 := h.inlong(6); + fprint(ctxt.logfd, "Hgrowdata: %d, %d, %d, %s\n", + m, l1, l2, contract(string h.mdata[10:])); + hgrowdata(m, l1, l2, string h.mdata[10:]); + + Hmoveto => + m := h.inshort(0); + l := h.inlong(2); + fprint(ctxt.logfd, "Hmoveto: %d, %d\n", m, l); + hmoveto(m, l); + + Hclean => + m := h.inshort(0); + fprint(ctxt.logfd, "Hclean: %d\n", m); + hclean(m); + + Hdirty => + m := h.inshort(0); + fprint(ctxt.logfd, "Hdirty: %d\n", m); + hdirty(m); + + Hdelname => + m := h.inshort(0); + fprint(ctxt.logfd, "Hdelname: %d\n", m); + hdelname(m); + + Hcut => + m := h.inshort(0); + l1 := h.inlong(2); + l2 := h.inlong(6); + fprint(ctxt.logfd, "Hcut: %d, %d, %d\n", + m, l1, l2); + hcut(m, l1, l2); + + Hclose => + m := h.inshort(0); + fprint(ctxt.logfd, "Hclose: %d\n", m); + hclose(m); + + Hsetpat => + fprint(ctxt.logfd, "Hsetpat: %s\n", string h.mdata); + samtk->hsetpat(string h.mdata); + + Hsetsnarf => + m := h.inshort(0); + fprint(ctxt.logfd, "Hsetsnarf: %d\n", m); + + Hsnarflen => + snarflen = h.inlong(0); + fprint(ctxt.logfd, "Hsnarflen: %d\n", snarflen); + + Hack => + fprint(ctxt.logfd, "Hack\n"); + outT0(Tack); + + Hexit => + fprint(ctxt.logfd, "Hexit\n"); + return 1; + + -1 => + panic("rcv error"); + + * => + fprint(ctxt.logfd, "type %d\n", h.mtype); + panic("rcv unknown"); + } + return 0; +} + +Sammsg.inshort(h: self ref Sammsg, n: int): int +{ + return ((int h.mdata[n+1])<<8) | + ((int h.mdata[n])); +} + +Sammsg.inlong(h: self ref Sammsg, n: int): int +{ + return ((int h.mdata[n+3])<<24) | + ((int h.mdata[n+2])<<16) | + ((int h.mdata[n+1])<< 8) | + ((int h.mdata[n])); +} + +Sammsg.invlong(h: self ref Sammsg, n: int): big +{ + return ((big h.mdata[n+7])<<56) | + ((big h.mdata[n+6])<<48) | + ((big h.mdata[n+5])<<40) | + ((big h.mdata[n+4])<<32) | + ((big h.mdata[n+3])<<24) | + ((big h.mdata[n+2])<<16) | + ((big h.mdata[n+1])<< 8) | + ((big h.mdata[n])); +} + +Sammsg.outcopy(h: self ref Sammsg, pos: int, data: array of byte) +{ + h.mdata[pos:] = data; +} + +Sammsg.outshort(h: self ref Sammsg, pos: int, s: int) +{ + h.mdata[pos++] = byte s; + h.mdata[pos] = byte (s >> 8); +} + +Sammsg.outlong(h: self ref Sammsg, pos: int, s: int) +{ + h.mdata[pos++] = byte s; + h.mdata[pos++] = byte (s >> 8); + h.mdata[pos++] = byte (s >> 16); + h.mdata[pos] = byte (s >> 24); +} + +Sammsg.outvlong(h: self ref Sammsg, pos: int, s: big) +{ + h.mdata[pos++] = byte s; + h.mdata[pos++] = byte (s >> 8); + h.mdata[pos++] = byte (s >> 16); + h.mdata[pos++] = byte (s >> 24); + h.mdata[pos++] = byte (s >> 32); + h.mdata[pos++] = byte (s >> 40); + h.mdata[pos++] = byte (s >> 48); + h.mdata[pos] = byte (s >> 56); +} + +outT0(t: int) +{ + fprint(ctxt.logfd, "\t\t\t\t\t%s\n", tname[t]); + h := ref Sammsg(t, 0, nil); + sendsam <- = h; +} + +outTs(t, s: int) +{ + fprint(ctxt.logfd, "\t\t\t\t\t%s %ux\n", tname[t], s); + a := array[2] of byte; + h := ref Sammsg(t, 2, a); + h.outshort(0, s); + sendsam <- = h; +} + +outTv(t: int, i: big) +{ + fprint(ctxt.logfd, "\t\t\t\t\t%s %bux\n", tname[t], i); + a := array[8] of byte; + h := ref Sammsg(t, 8, a); + h.outvlong(0, i); + sendsam <- = h; +} + +outTsll(t, m, l1, l2: int) +{ fprint(ctxt.logfd, "\t\t\t\t\t%s %d %d %d\n", tname[t], m, l1, l2); + a := array[10] of byte; + h := ref Sammsg(t, 10, a); + h.outshort(0, m); + h.outlong(2, l1); + h.outlong(6, l2); + sendsam <- = h; +} + +outTsl(t, m, l: int) +{ fprint(ctxt.logfd, "\t\t\t\t\t%s %d %d\n", tname[t], m, l); + a := array[6] of byte; + h := ref Sammsg(t, 6, a); + h.outshort(0, m); + h.outlong(2, l); + sendsam <- = h; +} + +outTsls(t, m, l1, l2: int) +{ fprint(ctxt.logfd, "\t\t\t\t\t%s %d %d %d\n", tname[t], m, l1, l2); + a := array[8] of byte; + h := ref Sammsg(t, 8, a); + h.outshort(0, m); + h.outlong(2, l1); + h.outshort(6, l2); + sendsam <- = h; +} + +outTslS(t, s1, l1: int, s: string) +{ + fprint(ctxt.logfd, "\t\t\t\t\t%s %d %d %s\n", tname[t], s1, l1, s); + a := array[6 + len array of byte s] of byte; + h := ref Sammsg(t, len a, a); + h.outshort(0, s1); + h.outlong(2, l1); + h.outcopy(6, array of byte s); + sendsam <- = h; +} + +newname(tag: int) +{ + menuins(0, "dummy", nil, tag); +} + +bindname(tag, l: int) +{ + if ((m := whichmenu(tag)) < 0) panic("bindname: whichmenu"); + if ((l = whichtext(l)) < 0) panic("bindname: whichtext"); + if (ctxt.menus[m].text != nil) + return; # Already bound + t := ctxt.texts[l]; + t.tag = tag; + for (fls := t.flayers; fls != nil; fls = tl fls) (hd fls).tag = tag; + ctxt.menus[m].text = t; +} + +menuins(m: int, s: string, t: ref Text, tag: int) +{ + newmenus := array [len ctxt.menus+1] of ref Menu; + menu := ref Menu( + tag, # tag + s, # name + t # text + ); + if (m > 0) + newmenus[0:] = ctxt.menus[0:m]; + newmenus[m] = menu; + if (m < len ctxt.menus) + newmenus[m+1:] = ctxt.menus[m:]; + ctxt.menus = newmenus; + + samtk->menuins(m, s); +} + +menudel(m: int) +{ + if (len ctxt.menus == 0 || m >= len ctxt.menus || ctxt.menus[m].text != nil) + panic("menudel"); + newmenus := array [len ctxt.menus - 1] of ref Menu; + newmenus[0:] = ctxt.menus[0:m]; + newmenus[m:] = ctxt.menus[m+1:]; + ctxt.menus = newmenus; + samtk->menudel(m); +} + +outcmd() { + if(ctxt.work != nil) { + fl := ctxt.work; + outTsll(Tworkfile, fl.tag, fl.dot.first, fl.dot.last); + } +} + +hclose(m: int) +{ + i: int; + + # close LAST window of a file + if((m = whichmenu(m)) < 0) panic("hclose: whichmenu"); + t := ctxt.menus[m].text; + if (tl t.flayers != nil) panic("hclose: flayers"); + fl := hd t.flayers; + fl.t = nil; + for (i = 0; i< len ctxt.flayers; i++) + if (ctxt.flayers[i] == fl) break; + if (i == len ctxt.flayers) panic("hclose: ctxt.flayers"); + samtk->chandel(i); + t.flayers = nil; + for (i = 0; i< len ctxt.texts; i++) + if (ctxt.texts[i] == ctxt.menus[m].text) break; + if (i == len ctxt.texts) panic("hclose: ctxt.texts"); + ctxt.texts[i:] = ctxt.texts[i+1:]; + ctxt.texts = ctxt.texts[:len ctxt.texts - 1]; + ctxt.menus[m].text = nil; + ctxt.which = nil; + samtk->focus(hd ctxt.cmd.flayers); +} + +close(win, tag: int) +{ + nfls: list of ref Flayer; + + if ((m := whichtext(tag)) < 0) panic("close: text"); + t := ctxt.texts[m]; + if ((m = whichmenu(tag)) < 0) panic("close: menu"); + if (len t.flayers == 1) { + outTs(Tclose, tag); + setlock(); + return; + } + fl := ctxt.flayers[win]; + nfls = nil; + for (fls := t.flayers; fls != nil; fls = tl fls) + if (hd fls != fl) nfls = hd fls :: nfls; + t.flayers = nfls; + samtk->chandel(win); + fl.t = nil; + samtk->settitle(t, ctxt.menus[m].name); + ctxt.which = nil; +} + +hdelname(m: int) +{ + # close LAST window of a file + if((m = whichmenu(m)) < 0) panic("hdelname: whichmenu"); + if (ctxt.menus[m].text != nil) panic("hdelname: text"); + ctxt.menus[m:] = ctxt.menus[m+1:]; + ctxt.menus = ctxt.menus[:len ctxt.menus - 1]; + samtk->menudel(m); + ctxt.which = nil; +} + +hdirty(m: int) +{ + if((m = whichmenu(m)) < 0) panic("hdirty: whichmenu"); + if (ctxt.menus[m].text == nil) panic("hdirty: text"); + ctxt.menus[m].text.state |= Samterm->Dirty; + samtk->settitle(ctxt.menus[m].text, ctxt.menus[m].name); +} + +hclean(m: int) +{ + if((m = whichmenu(m)) < 0) panic("hclean: whichmenu"); + if (ctxt.menus[m].text == nil) panic("hclean: text"); + ctxt.menus[m].text.state &= ~Samterm->Dirty; + samtk->settitle(ctxt.menus[m].text, ctxt.menus[m].name); +} + +movename(tag: int, s: string) +{ + i := whichmenu(tag); + if (i < 0) panic("movename: whichmenu"); + + t := ctxt.menus[i].text; + + ctxt.menus[i].text = nil; # suppress panic in menudel + menudel(i); + + if(t == ctxt.cmd) + i = 0; + else { + if (len ctxt.menus > 0 && ctxt.menus[0].text == ctxt.cmd) + i = 1; + else + i = 0; + for(; i < len ctxt.menus; i++) { + if (s < ctxt.menus[i].name) + break; + } + } + if (t != nil) samtk->settitle(t, s); + menuins(i, s, t, tag); +} + +hcheck(t: ref Text) +{ + if (t == nil) { + fprint(ctxt.logfd, "hcheck: no text in menu entry\n"); + return; + } + for (fls := t.flayers; fls != nil; fls = tl fls) { + fl := hd fls; + scrollto(fl, fl.scope.first); + } +} + +setlock() +{ + ctxt.lock++; + samtk->allflayers("cursor -bitmap cursor.wait"); +} + +clrlock() +{ + if (ctxt.lock > 0) + ctxt.lock--; + else + fprint(ctxt.logfd, "lock: wasn't locked\n"); + if (ctxt.lock == 0) + samtk->allflayers("cursor -default; update"); +} + +hcut(m, where, howmuch: int) +{ + if((m = whichmenu(m)) < 0) panic("hcut: whichmenu"); + t := ctxt.menus[m].text; + if (t == nil) panic("hcut -- no text"); + +# sctdump(t.sects, "Hcut, before"); + t.nrunes -= howmuch; + t.sects = sctdelete(t.sects, where, howmuch); +# sctdump(t.sects, "Hcut, after"); + for (fls := t.flayers; fls != nil; fls = tl fls) { + fl := hd fls; + if (where < fl.scope.first) { + if (where + howmuch <= fl.scope.first) + fl.scope.first -= howmuch; + else + fl.scope.first = where; + } + if (where < fl.scope.last) { + if (where + howmuch <= fl.scope.last) + fl.scope.last -= howmuch; + else + fl.scope.last = where; + } + } +} + +hgrow(tag, l1, l2: int) +{ + if((m := whichmenu(tag)) < 0) panic("hgrow: whichmenu"); + t := ctxt.menus[m].text; + grow(t, l1, l2); +} + +hdata(m, l: int, s: string) +{ + nr: list of (int, int); + + if((m = whichmenu(m)) < 0) panic("hdata: whichmenu"); + t := ctxt.menus[m].text; + if (t == nil) panic("hdata -- no text"); + if (s != "") { + t.sects = sctput(t.sects, l, s); + updatefls(t, l, s); + } + for (nr = nil; requested != nil; requested = tl requested) { + (r1, r2) := hd requested; + if (r1 != m || r2 != l) + nr = (r1, r2) :: nr; + } + requested = nr; + clrlock(); +} + +hgrowdata(tag, l1, l2: int, s: string) +{ + if((m := whichmenu(tag)) < 0) panic("hgrow: whichmenu"); + t := ctxt.menus[m].text; + if (t == nil) panic("hdata -- no text"); + grow(t, l1, l2); + t.sects = sctput(t.sects, l1, s); + updatefls(t, l1, s); +} + +hsetdot(m, l1, l2: int) +{ + if((m = whichmenu(m)) < 0) panic("hsetdot: whichmenu"); + t := ctxt.menus[m].text; + if (t == nil || t.flayers == nil) panic("hsetdot -- no text"); + samtk->setdot(hd t.flayers, l1, l2); +} + +hcurrent(tag: int) +{ + if ((i := whichmenu(tag)) < 0) panic("hcurrent: whichmenu"); + if (ctxt.menus[i].text == nil) { + n := startfile(tag); + ctxt.menus[i].text = ctxt.texts[n]; + if (ctxt.menus[i].name != nil) + samtk->settitle(ctxt.texts[n], ctxt.menus[i].name); + } + ctxt.work = hd ctxt.menus[i].text.flayers; +} + +hmoveto(m, l: int) +{ + if((m = whichmenu(m)) < 0) panic("hmoveto: whichmenu"); + t := ctxt.menus[m].text; + fl := hd t.flayers; + if (fl.scope.first <= l && + (l < fl.scope.last || fl.scope.last == fl.scope.first)) + return; + (n, p) := sctrevcnt(t.sects, l, fl.lines/2); +# fprint(ctxt.logfd, "hmoveto: (n, p) = (%d, %d)\n", n, p); + if (n < 0) { + outTsll(Torigin, t.tag, l, fl.lines/2); + setlock(); + return; + } + scrollto(fl, p); +} + +startcmdfile() +{ + t := ctxt.tag++; + n := newtext(t, 1); + ctxt.cmd = ctxt.texts[n]; + outTv(Tstartcmdfile, big t); +} + +startnewfile() +{ + t := ctxt.tag++; + n := newtext(t, 0); + outTv(Tstartnewfile, big t); +} + +startfile(tag: int): int +{ + n := newtext(tag, 0); + outTv(Tstartfile, big tag); + setlock(); + return n; +} + +horigin(m, l: int) +{ + if((m = whichmenu(m)) < 0) panic("hmoveto: whichmenu"); + t := ctxt.menus[m].text; + fl := hd t.flayers; + scrollto(fl, l); + clrlock(); +} + +scrollto(fl: ref Flayer, where: int) +{ + s: string; + n: int; + + tag := fl.tag; + if ((i := whichtext(tag)) < 0) panic("scrollto: whichtext"); + t := ctxt.texts[i]; + + samtk->flclear(fl); + (n, s) = sctgetlines(t.sects, where, fl.lines); + fl.scope.first = where; + fl.scope.last = where + len s; + if (s != "") + samtk->flinsert(fl, where, s); + if (n == 0) { + samtk->setscrollbar(t, fl); + } else { + (h, l) := scthole(t, fl.scope.last); + fl.scope.last = h; + if (l > 0) + outrequest(tag, h, l); + else + if (fl.scope.first > t.nrunes) { + fl.scope.first = t.nrunes; + fl.scope.last = t.nrunes; + samtk->setscrollbar(t, fl); + } + } +} + +scthole(t: ref Text, f: int): (int, int) +{ + p := 0; + h := -1; + l := 0; + for (scts := t.sects; scts != nil; scts = tl scts) { + sct := hd scts; + nr := sct.nrunes; + nt := len sct.text; + if (h >= 0) { + if (sct.text == "") { + l += nr; + if (l >= 512) return (h,512); + } else + return (h,l); + } + if (h < 0 && f < nr) { + if (nt < nr) { + if (f < nt) { + h = p + nt; + l = nr - nt; + } else { + h = p + f; + l = nr - f; + } + if (l >= 512) return (h,512); + } + } + p += sct.nrunes; + f -= sct.nrunes; + } + if (h == -1) return (p, 0); + return (h, l); +} + +# return (x, p): x = -1: p -> hole; x = 0: p -> line n; x > 0: p -> eof +sctlinecount(t: ref Text, pos, n: int): (int, int) +{ + i: int; + + p := 0; + for (scts := t.sects; scts != nil; scts = tl scts) { + sct := hd scts; + nr := sct.nrunes; + nt := len sct.text; + if (pos < nr) { + if (pos > 0) i = pos; else i = 0; + while (i < nt) { + if (sct.text[i++] == '\n') n--; + if (n == 0) return (0, p + i); + } + if (nt < nr) return (-1, p + nt); + } + p += sct.nrunes; + pos -= sct.nrunes; + } + return (n, p); +} + +sctrevcnt(scts: list of ref Section, pos, n: int): (int, int) +{ + if (scts == nil) return (n, 0); + sct := hd scts; + scts = tl scts; + nt := len sct.text; + nr := sct.nrunes; + if (pos >= nr) { + (n, pos) = sctrevcnt(scts, pos - nr, n); + pos += nr; + } + if (n > 0) { + if (nt < nr && pos > nt) + return(-1, pos); + for (i := pos-1; i >= 0; i--) { + if (sct.text[i] == '\n') n--; + if (n == 0) break; + } + return (n, i + 1); + } + return (n, pos); +} + +insertfls(t: ref Text, l: int, s: string) +{ + for (fls := t.flayers; fls != nil; fls = tl fls) { + fl := hd fls; + if (l < fl.scope.first || l > fl.scope.last) continue; + samtk->flinsert(fl, l, s); + samtk->setscrollbar(t, fl); + fl.scope.last += len s; + } +} + +updatefls(t: ref Text, l: int, s: string) +{ + for (fls := t.flayers; fls != nil; fls = tl fls) { + fl := hd fls; + if (l < fl.scope.first || l > fl.scope.last) continue; + samtk->flinsert(fl, l, s); + (x, p) := sctlinecount(t, fl.scope.first, fl.lines); + fl.scope.last = p; + if (x >= 0) { + if (p > l + len s) { + samtk->flinsert(fl, l + len s, + sctget(t.sects, l + len s, p)); + } + if (x == 0) + samtk->fldelexcess(fl); + } else { + (h1, h2) := scthole(t, l); + fl.scope.last = h1; + if (h2 > 0) { + outrequest(t.tag, h1, h2); + continue; + } else { + panic("Can't happen ??"); + } + } + samtk->setscrollbar(t, fl); + } +} + +outrequest(tag, h1, h2: int) { + for (l := requested; l != nil; l = tl l) { + (r1, r2) := hd l; + if (r1 == tag && r2 == h1) return; + } + outTsls(Trequest, tag, h1, h2); + requested = (tag, h1) :: requested; + setlock(); +} + +deletefls(t: ref Text, pos, nbytes: int) +{ + for (fls := t.flayers; fls != nil; fls = tl fls) { + fl := hd fls; + if (pos >= fl.scope.last) continue; + if (pos + nbytes <= fl.scope.first || pos >= fl.scope.last) { + fl.scope.first -= nbytes; + fl.scope.last -= nbytes; + continue; + } + samtk->fldelete(fl, pos, pos + nbytes); + (x, p) := sctlinecount(t, fl.scope.first, fl.lines); + if (x >= 0 && p > fl.scope.last) { + samtk->flinsert(fl, fl.scope.last, + sctget(t.sects, fl.scope.last, p)); + fl.scope.last = p; + } else { + fl.scope.last = p; + (h1, h2) := scthole(t, fl.scope.last); + if (h2 > 0) + outrequest(t.tag, h1, h2); + } + samtk->setscrollbar(t, fl); + } +} + +contract(s: string): string +{ + if (len s < 32) + cs := s; + else + cs = s[0:16] + " ... " + s[len s - 16:]; + for (i := 0; i < len cs; i++) + if (cs[i] == '\n') cs[i] = '\u008a'; + return cs; +} + +cleanout() +{ + if ((fl := ctxt.which) == nil) return; + if ((i := whichtext(fl.tag)) < 0) panic("cleanout: whichtext"); + t := ctxt.texts[i]; + + if (fl.typepoint >= 0 && fl.dot.first > fl.typepoint) { + s := sctget(t.sects, fl.typepoint, fl.dot.first); + outTslS(Samstub->Ttype, fl.tag, fl.typepoint, s); + t.state &= ~Samterm->LDirty; + } + fl.typepoint = -1; +} + +newtext(tag, tp: int): int +{ + n := len ctxt.texts; + t := ref Text( + tag, # tag + 0, # lock + samtk->newflayer(tag, tp) :: nil, # flayers + 0, # nrunes + nil, # sects + 0 # state + ); + texts := array [n + 1] of ref Text; + texts[0:] = ctxt.texts; + texts[n] = t; + ctxt.texts = texts; + samtk->newcur(t, hd t.flayers); + return n; +} + +keypress(key: string) +{ + # Find text and flayer + fl := ctxt.which; + tag := fl.tag; + if ((i := whichtext(tag)) < 0) panic("keypress: whichtext"); + t := ctxt.texts[i]; + + if (fl.dot.last != fl.dot.first) { + cut(t, fl); + } + + case (key) { + "\b" => + if (t.nrunes == 0 || fl.dot.first == 0) + return; + fl.dot.first--; + if (fl.typepoint >= 0 && fl.dot.first >= fl.typepoint) { + t.nrunes -= fl.dot.last - fl.dot.first; + t.sects = sctdelete(t.sects, fl.dot.first, fl.dot.last - fl.dot.first); + deletefls(t, fl.dot.first, fl.dot.last - fl.dot.first); + if (fl.dot.first == fl.typepoint) { + fl.typepoint = -1; + t.state &= ~Samterm->LDirty; + if ((i = whichmenu(tag)) < 0) + panic("keypress: whichmenu"); + samtk->settitle(t, ctxt.menus[i].name); + } + } else { + cut(t, fl); + } + * => + if (fl.typepoint < 0) { + fl.typepoint = fl.dot.first; + t.state |= Samterm->LDirty; + if ((i = whichmenu(tag)) < 0) + panic("keypress: whichmenu"); + samtk->settitle(t, ctxt.menus[i].name); + } + if (fl.dot.first > t.nrunes) + panic("keypress -- cursor > file len"); + t.sects = sctmakeroom(t.sects, fl.dot.first, len key); + t.nrunes += len key; + t.sects = sctput(t.sects, fl.dot.first, key); + insertfls(t, fl.dot.first, key); + f := fl.dot.first + len key; + samtk->setdot(fl, f, f); + if (key == "\n") { + if (f >= fl.scope.last) { + (n, p) := sctrevcnt(t.sects, f-1, 2*fl.lines/3); + if (n < 0) { + outTsll(Torigin, t.tag, f-1, 2*fl.lines/3); + setlock(); + } else { + scrollto(fl, p); + } + } + if (t == ctxt.cmd && fl.dot.last == t.nrunes) { + outcmd(); + setlock(); + } + cleanout(); + } + } + return; +} + +cut(t: ref Text, fl: ref Flayer) +{ + if (fl.typepoint >= 0) panic("cut: typepoint"); + outTsll(Tcut, fl.tag, fl.dot.first, fl.dot.last); + t.nrunes -= fl.dot.last - fl.dot.first; + t.sects = sctdelete(t.sects, fl.dot.first, fl.dot.last - fl.dot.first); + deletefls(t, fl.dot.first, fl.dot.last - fl.dot.first); +} + +paste(t: ref Text, fl: ref Flayer) +{ + if (fl.typepoint >= 0) panic("paste: typepoint"); + if (snarflen == 0) return; + if (fl.dot.first < fl.dot.last) cut(t, fl); + outTsl(Tpaste, fl.tag, fl.dot.first); +} + +snarf(nil: ref Text, fl: ref Flayer) +{ + if (fl.typepoint >= 0) panic("snarf: typepoint"); + if (fl.dot.first == fl.dot.last) return; + snarflen = fl.dot.last - fl.dot.first; + outTsll(Tsnarf, fl.tag, fl.dot.first, fl.dot.last); +} + +look(nil: ref Text, fl: ref Flayer) +{ + if (fl.typepoint >= 0) panic("look: typepoint"); + outTsll(Tlook, fl.tag, fl.dot.first, fl.dot.last); + setlock(); +} + +send(nil: ref Text, fl: ref Flayer) +{ + if (fl.typepoint >= 0) panic("send: typepoint"); + outcmd(); + outTsll(Tsend, fl.tag, fl.dot.first, fl.dot.last); + setlock(); +} + +search(nil: ref Text, fl: ref Flayer) +{ + if (fl.typepoint >= 0) panic("search: typepoint"); + outcmd(); + outT0(Tsearch); + setlock(); +} + +zerox(t: ref Text) +{ + fl := samtk->newflayer(t.tag, ctxt.cmd == t); + t.flayers = fl :: t.flayers; + m := whichmenu(t.tag); + samtk->settitle(t, ctxt.menus[m].name); + samtk->newcur(t, fl); + scrollto(fl, 0); +} + +sctget(scts: list of ref Section, p1, p2: int): string +{ + while (scts != nil) { + sct := hd scts; scts = tl scts; + ln := len sct.text; + if (p1 < sct.nrunes) { + if (ln < sct.nrunes && p2 > ln) { + sctdump(scts, "panic"); + panic("sctget - asking for a hole"); + } + if (p2 > sct.nrunes) { + s := sct.text[p1:]; + return s + sctget(scts, 0, p2 - ln); + } + return sct.text[p1:p2]; + } + p1 -= sct.nrunes; + p2 -= sct.nrunes; + } + return ""; +} + +sctgetlines(scts: list of ref Section, p, n: int): (int, string) +{ + s := ""; + while (scts != nil) { + sct := hd scts; scts = tl scts; + ln := len sct.text; + if (p < sct.nrunes) { + if (p > ln) return (n, s); + if (p > 0) b := p; else b = 0; + for (i := b; i < ln && n > 0; ) { + if (sct.text[i++] == '\n') n--; + } + if ( i > b) + s = s + sct.text[b:i]; + if (n == 0 || ln < sct.nrunes) return (n, s); + } + p -= sct.nrunes; + } + return (n, s); +} + +sctput(scts: list of ref Section, pos: int, s: string): list of ref Section +{ + # There should be a hole to receive text + if (scts == nil && s != "") panic("sctput: scts is nil\n"); + sct := hd scts; + l := len sct.text; + if (sct.nrunes <= pos) { + return sct :: sctput(tl scts, pos-sct.nrunes, s); + } + if (pos < l) { + sctdump(scts, "panic"); + panic("sctput: overwriting"); + } + if (pos == l) { + if (sct.nrunes < l + len s) { + sct.text += s[:sct.nrunes-l]; + return sct :: sctput(tl scts, 0, s[sct.nrunes-l:]); + } + sct.text += s; + return sct :: tl scts; + } + nrunes := sct.nrunes; + sct.nrunes = pos; + if (nrunes < pos + len s) + return sct :: + ref Section(nrunes-pos, s[:nrunes-pos]) :: + sctput(tl scts, 0, s[nrunes-pos:]); + return sct :: ref Section(nrunes-pos, s) :: tl scts; +} + +sctmakeroom(scts: list of ref Section, pos: int, l: int): list of ref Section +{ + if (scts == nil) { + if (pos) panic("sctmakeroom: beyond end of sections"); + return ref Section(l, nil) :: nil; + } + sct := hd scts; + if (sct.nrunes < pos) + return sct :: sctmakeroom(tl scts, pos-sct.nrunes, l); + if (len sct.text <= pos) { + # just add to the hole at end of section + sct.nrunes += l; + return sct :: tl scts; + } + if (pos == 0) { + # text is non-nil! + bsct := ref Section(l, nil); + return bsct :: scts; + } + bsct := ref Section(pos + l, sct.text[0:pos]); + esct := ref Section(sct.nrunes-pos, sct.text[pos:]); + return bsct :: esct :: tl scts; +} + +sctdelete(scts: list of ref Section, start, nbytes: int): list of ref Section +{ + if (nbytes == 0) return scts; + if (scts == nil) panic("sctdelete: at eof"); + sct := hd scts; + scts = tl scts; + nrunes := sct.nrunes; + if (start + nbytes < len sct.text) { + sct.text = sct.text[0:start] + sct.text[start+nbytes:]; + sct.nrunes -= nbytes; + return sct :: scts; + } + if (start < nrunes) { + if (start > 0) { + if (start < len sct.text) + sct.text = sct.text[0:start]; + if (start + nbytes <= nrunes) { + sct.nrunes -= nbytes; + return sct :: scts; + } + sct.nrunes = start; + return sct :: sctdelete(scts, 0, nbytes-nrunes+start); + } + if (nbytes < nrunes) { + sct.text = ""; + sct.nrunes -= nbytes; + return sct :: scts; + } + return sctdelete(scts, 0, nbytes - nrunes); + } + return sct :: sctdelete(scts, start - nrunes, nbytes); +} + +grow(t: ref Text, at, l: int) +{ +# sctdump(t.sects, "grow, before"); + t.sects = sctmakeroom(t.sects, at, l); + t.nrunes += l; +# sctdump(t.sects, "grow, after"); + for (fls := t.flayers; fls != nil; fls = tl fls) { + fl := hd fls; + if (at < fl.scope.first) fl.scope.first += l; + if (at < fl.scope.last) fl.scope.last += l; + } +} + +findhole(t: ref Text): (int, int) +{ + for (fls := t.flayers; fls != nil; fls = tl fls) { + (h, l) := scthole(t, (hd fls).scope.first); + if (l > 0) return (h, l); + } + return (0, 0); +} + +sctdump(scts: list of ref Section, s: string) +{ + fprint(ctxt.logfd, "Sctdump: %s\n", s); + p := 0; + while (scts != nil) { + sct := hd scts; scts = tl scts; + fprint(ctxt.logfd, "\tsct@%4d len=%4d len txt=%4d: %s\n", + p, sct.nrunes, len sct.text, contract(sct.text)); + p += sct.nrunes; + } + fprint(ctxt.logfd, "\tend@%4d\n", p); +} diff --git a/appl/wm/samstub.m b/appl/wm/samstub.m new file mode 100644 index 00000000..5bde16d8 --- /dev/null +++ b/appl/wm/samstub.m @@ -0,0 +1,132 @@ +Samstub: module +{ + PATH: con "/dis/wm/samstub.dis"; + SAM: con "sam -R"; + + VERSION: con 0; + UTFmax: con 3; + + TBLOCKSIZE: con 512; # largest piece of text sent to terminal ... + DATASIZE: con (UTFmax*TBLOCKSIZE+30); + # ... including protocol header stuff + SNARFSIZE: con 4096; # maximum length of exchanged snarf buffer + + # Message types + Error, Status, Debug: con iota; + + Sammsg: adt { + mtype: int; + mcount: int; + mdata: array of byte; + + inshort: fn(h: self ref Sammsg, n: int): int; + inlong: fn(h: self ref Sammsg, n: int): int; + invlong: fn(h: self ref Sammsg, n: int): big; + outcopy: fn(h: self ref Sammsg, pos: int, data: array of byte); + outshort: fn(h: self ref Sammsg, pos: int, s: int); + outlong: fn(h: self ref Sammsg, pos: int, s: int); + outvlong: fn(h: self ref Sammsg, pos: int, s: big); + }; + + Samio: adt { + ctl: ref Sys->FD; # /cmd/nnn/ctl + data: ref Sys->FD; # /cmd/nnn/data + buffer: array of byte; # buffered data read from sam + index: int; + count: int; # pointers into buffer + + }; + + init: fn(ctxt: ref Context); + + start: fn(): (ref Samio, chan of ref Sammsg); + sender: fn(s: ref Samio, c: chan of ref Sammsg); + receiver: fn(s: ref Samio, c: chan of ref Sammsg); + + outTs: fn(t, s: int); + outTv: fn(t: int, i: big); + outT0: fn(t: int); + outTsl: fn(t, m, l: int); + outTslS: fn(t, s1, l1: int, s: string); + outTsll: fn(t, m, l1, l2: int); + + cleanout: fn(); + close: fn(win, tag: int); + cut: fn(t: ref Text, fl: ref Flayer); + findhole: fn(t: ref Text): (int, int); + grow: fn(t: ref Text, l1, l2: int); + horigin: fn(m, l: int); + inmesg: fn(h: ref Sammsg): int; + keypress: fn(key: string); + look: fn(t: ref Text, fl: ref Flayer); + menuins: fn(p: int, s: string, t: ref Text, tg: int); + newtext: fn(tag, tp: int): int; + paste: fn(t: ref Text, fl: ref Flayer); + scrollto: fn(fl: ref Flayer, where: int); + sctget: fn(scts: list of ref Section, p1, p2: int): string; + sctgetlines: fn(scts: list of ref Section, p, n: int): + (int, string); + scthole: fn(t: ref Text, f: int): (int, int); + sctput: fn(scts: list of ref Section, pos: int, s: string): + list of ref Section; + search: fn(t: ref Text, fl: ref Flayer); + send: fn(t: ref Text, fl: ref Flayer); + setlock: fn(); + snarf: fn(t: ref Text, fl: ref Flayer); + startcmdfile: fn(); + startfile: fn(tag: int): int; + startnewfile: fn(); + updatefls: fn(t: ref Text, l: int, s: string); + zerox: fn(t: ref Text); + + Tversion, # version + Tstartcmdfile, # terminal just opened command frame + Tcheck, # ask host to poke with Hcheck + Trequest, # request data to fill a hole + Torigin, # gimme an Horigin near here + Tstartfile, # terminal just opened a file's frame + Tworkfile, # set file to which commands apply + Ttype, # add some characters, but terminal already knows + Tcut, + Tpaste, + Tsnarf, + Tstartnewfile, # terminal just opened a new frame + Twrite, # write file + Tclose, # terminal requests file close; check mod. status + Tlook, # search for literal current text + Tsearch, # search for last regular expression + Tsend, # pretend he typed stuff + Tdclick, # double click + Tstartsnarf, # initiate snarf buffer exchange + Tsetsnarf, # remember string in snarf buffer + Tack, # acknowledge Hack + Texit, # exit + TMAX: con iota; + + Hversion, # version + Hbindname, # attach name[0] to text in terminal + Hcurrent, # make named file the typing file + Hnewname, # create "" name in menu + Hmovname, # move file name in menu + Hgrow, # insert space in rasp + Hcheck0, # see below + Hcheck, # ask terminal to check whether it needs more data + Hunlock, # command is finished; user can do things + Hdata, # store this data in previously allocated space + Horigin, # set origin of file/frame in terminal + Hunlockfile, # unlock file in terminal + Hsetdot, # set dot in terminal + Hgrowdata, # Hgrow + Hdata folded together + Hmoveto, # scrolling, context search, etc. + Hclean, # named file is now 'clean' + Hdirty, # named file is now 'dirty' + Hcut, # remove space from rasp + Hsetpat, # set remembered regular expression + Hdelname, # delete file name from menu + Hclose, # close file and remove from menu + Hsetsnarf, # remember string in snarf buffer + Hsnarflen, # report length of implicit snarf + Hack, # request acknowledgement + Hexit, + HMAX: con iota; +}; diff --git a/appl/wm/samterm.m b/appl/wm/samterm.m new file mode 100644 index 00000000..34c82095 --- /dev/null +++ b/appl/wm/samterm.m @@ -0,0 +1,75 @@ +include "tk.m"; +include "wmlib.m"; + +Samterm: module +{ + + PATH: con "/dis/wm/sam.dis"; + + Section: adt + { + nrunes: int; + text: string; # if null, we haven't got it + }; + + Range: adt { + first, last: int; + }; + + Flayer: adt { + tag: int; + t: ref Tk->Toplevel; + tkwin: string; # tk window name + scope: Range; # part of file in range + dot: Range; # cursor position wrt file, not scope + width: int; # window width (not used yet) + lineheigth: int; # height of a single line (for resize) + lines: int; # window height in lines + scrollbar: Range; # current position of scrollbar + typepoint: int; # -1, or pos of first unsent char typed + }; + + Text: adt { + tag: int; + lock: int; + flayers: list of ref Flayer; # hd flayers is current + nrunes: int; + sects: list of ref Section; + state: int; + }; + + Dirty: con 1; + LDirty: con 2; + + Menu: adt { + tag: int; + name: string; + text: ref Text; + }; + + Context: adt { + ctxt: ref Draw->Context; + tag: int; # globally unique tag generator + lock: int; # global lock + + keysel: array of chan of string; + scrollsel: array of chan of string; + buttonsel: array of chan of string; + menu2sel: array of chan of string; + menu3sel: array of chan of string; + titlesel: array of chan of string; + flayers: array of ref Flayer; + + menus: array of ref Menu; + texts: array of ref Text; + + cmd: ref Text; # sam command window + which: ref Flayer; # current flayer (sam or work) + work: ref Flayer; # current work flayer + + pgrp: int; # process group + logfd: ref FD; + }; + + init: fn(ctxt: ref Draw->Context, args: list of string); +}; diff --git a/appl/wm/samtk.b b/appl/wm/samtk.b new file mode 100644 index 00000000..31eea9fb --- /dev/null +++ b/appl/wm/samtk.b @@ -0,0 +1,688 @@ +implement Samtk; + +include "sys.m"; +sys: Sys; +sprint, FD: import sys; + +include "draw.m"; +draw: Draw; + +include "samterm.m"; +Context, Flayer, Text, Section: import Samterm; + +include "tkclient.m"; + +include "samtk.m"; + +ctxt: ref Context; + +tk: Tk; +tkclient: Tkclient; + +tksam1 := array[] of { + "frame .w", + "scrollbar .w.s -command {send scroll}", + "text .w.t -width 80w -height 8h", + "pack .w.s -side left -fill y", + "pack .w.t -fill both -expand 1", + "pack .Wm_t -fill x", + "pack .w -fill both -expand 1", + "pack propagate . 0", +}; + +tkwork1 := array[] of { + "frame .w", + "scrollbar .w.s -command {send scroll}", + "text .w.t -width 80w -height 20h", + "pack .w.s -side left -fill y", + "pack .w.t -fill both -expand 1", + "pack .Wm_t -fill x", + "pack .w -fill both -expand 1", + "pack propagate . 0", +}; + +tkcmdlist := array[] of { + "bind .w.t <Key> {send keys {%A}}", + "bind .w.t <Key-\b> {send keys {%A}}", + "bind .w.s <ButtonRelease-1> +{send scroll %s %b %y}", + "bind .w.t <ButtonPress-1> +{send button1 %s %b %x %y}", + "bind .w.t <ButtonRelease-1> +{send button1 %s %b %x %y}", + "bind .w.t <Double-ButtonPress-1> {send button1 2 %b %x %y}", + "bind .w.t <Double-ButtonRelease-1> {send button1 3 %b %x %y}", + "bind .w.t <ButtonPress-2> {.m2 post %x %y; grab set .m2}", + "bind .w.t <ButtonPress-3> {.m3 post %x %y; grab set .m3}", + "bind . <Configure> {send titlesel resize}", + "focus .w.t", + "update" +}; + +menuidx := array[2] of {"0","0"}; + +init(c: ref Context) +{ + ctxt = c; + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + + tkclient = load Tkclient Tkclient->PATH; + tkclient->init(); + + scrollpos = scrolllines = 0; +} + +x := 10; +y := 10; + +newflayer(tag, tp: int): ref Flayer +{ + if (ctxt.which != nil) { + tk->cmd(ctxt.which.t, + ".Wm_t.title configure -background blue; update"); + } + (t, cmdc) := tkclient->toplevel(ctxt.ctxt.screen, "-borderwidth 1 -relief raised", "SamTerm", Tkclient->Appl); + tk->cmd(t, ". configure -x "+string x+" -y "+string y+"; update"); + + if (x == 10 && y == 10) { + y = 200; + } else { + x += 40; + y += 40; + } + + n := chanadd(); + ctxt.titlesel[n] = cmdc; + tk->namechan(t, ctxt.menu3sel[n], "menu3"); + tk->namechan(t, ctxt.menu2sel[n], "menu2"); + tk->namechan(t, ctxt.buttonsel[n], "button1"); + tk->namechan(t, ctxt.keysel[n], "keys"); + tk->namechan(t, ctxt.scrollsel[n], "scroll"); + tk->namechan(t, ctxt.titlesel[n], "titlesel"); + + lines: int; + if (tp) { + lines = 8; + tkclient->tkcmds(t, tksam1); + mkmenu2c(t); + } else { + lines = 20; + tkclient->tkcmds(t, tkwork1); + mkmenu2(t); + } + mkmenu3(t); + tkclient->tkcmds(t, tkcmdlist); + + f := ref Flayer( + tag, # tag + t, # t + "SamTerm", # tkwin + (0, 0), # scope + (0, 0), # dot + int tk->cmd(t, ".w.t cget actwidth"), # screen width + int tk->cmd(t, ".w.t cget actheight") / lines, # lineheigth + lines, # lines + (0, 1), # scrollbar + -1 # typepoint + ); + ctxt.flayers[n] = f; + return f; +} + +menu2str := array [] of { + "cut", + "paste", + "snarf", + "look", +# "exch", + "send", # storage for last pattern +}; + +menu3str := array [] of { + "new", + "zerox", + "close", + "write", +}; + +mkmenu2c(t: ref Tk->Toplevel) +{ + menus := array [NMENU2+1] of string; + + menus[0] = "menu .m2"; + for (i := 0; i < NMENU2; i++) { + menus[i+1] = addmenuitem(2, "menu2", menu2str[i]); + } + tkclient->tkcmds(t, menus); +} + +mkmenu2(t: ref Tk->Toplevel) +{ + menus := array [NMENU2+1] of string; + + menus[0] = "menu .m2"; + for (i := 0; i < NMENU2-1; i++) { + menus[i+1] = addmenuitem(2, "menu2", menu2str[i]); + } + menus[NMENU2] = addmenuitem(2, "edit", "/"); + tkclient->tkcmds(t, menus); +} + +mkmenu3(t: ref Tk->Toplevel) +{ + menus := array [NMENU3+len ctxt.menus+1] of string; + + menus[0] = "menu .m3"; + for (i := 0; i < NMENU3; i++) { + menus[i+1] = addmenuitem(3, "menu3", menu3str[i]); + } + for (i = 0; i < len ctxt.menus; i++) { + menus[i+NMENU3+1] = addmenuitem(3, "menu3", ctxt.menus[i].name); + } + tkclient->tkcmds(t, menus); +} + +addmenuitem(d: int, m, s: string): string +{ + return sprint(".m%d add command -text %s -command {send %s %s}", + d, s, m, s); +} + +menuins(pos: int, s: string) +{ + for (i := 0; i < len ctxt.flayers; i++) + tk->cmd(ctxt.flayers[i].t, + sprint(".m3 insert %d command -text %s -command {send menu3 %s}", + pos + NMENU3, s, s)); +} + +menudel(pos: int) +{ + for (i := 0; i < len ctxt.flayers; i++) + tk->cmd(ctxt.flayers[i].t, sprint(".m3 delete %d", pos + NMENU3)); +} + +hsetpat(s: string) +{ + for (i := 0; i < len ctxt.flayers; i++) { + fl := ctxt.flayers[i]; + if (fl.tag != ctxt.cmd.tag) { + tk->cmd(fl.t, ".m2 entryconfigure " + + string Search + + " -command {send menu2 search} -text '/" + s); + } + } +} + +lastsearchstring := "//"; + +setmenu(num : int,c : string){ + fl := ctxt.flayers[num]; + (nil, l) := sys->tokenize(c, " "); + x1 := int hd l - 50; + y1 := int hd tl l - int tk->cmd(fl.t, ".m"+string num+" yposition "+menuidx[num-2]) + - 10; + tk->cmd(fl.t, ".m"+string num+" activate "+menuidx[num-2]+ + "; .m"+string num+" post "+string x1+" "+string y1+ + "; grab set .m"+string num+"; update"); +} + +titlectl(win: int, menu: string) +{ + tkclient->wmctl(ctxt.flayers[win].t, menu); +} + +flraise(t: ref Text, fl: ref Flayer) +{ + nfls: list of ref Flayer; + + nfls = nil; + t.flayers = fl :: dellist(t.flayers, fl); + tk->cmd(fl.t, "raise .; focus .w.t; update"); +} + +dellist(fls: list of ref Flayer, fl: ref Flayer): list of ref Flayer +{ + if (fls == nil) return nil; + if (hd fls == fl) return dellist(tl fls, fl); + return hd fls :: dellist(tl fls, fl); +} + +append(fls: list of ref Flayer, fl: ref Flayer): list of ref Flayer +{ + if (fls == nil) return fl :: nil; + return hd fls :: append(tl fls, fl); +} + +focus(fl: ref Flayer) +{ + tk->cmd(fl.t, "focus .w.t; update"); +} + +newcur(t: ref Text, fl: ref Flayer) +{ + if (ctxt.which == fl) return; + flraise(t, fl); + ctxt.which = fl; + if (t != ctxt.cmd) + ctxt.work = fl; +} + +settitle(t: ref Text, s: string) +{ + sd := ""; + sz := ""; + if (t.state & Samterm->Dirty) sd = " (Dirty)"; + if (t != ctxt.cmd && (t.state & Samterm->LDirty)) sd = " (Modified)"; + if (len t.flayers > 1) sz = " (Zeroxed)"; + for (fls := t.flayers; fls != nil; fls = tl fls) { + fl := hd fls; + fl.tkwin = s; + tkclient->settitle(fl.t, s + sd + sz); + tk->cmd(fl.t, "update"); + } +} + +resize(fl: ref Flayer) +{ + fl.lines = int tk->cmd(fl.t, ".w.t cget actheight") / fl.lineheigth; +} + +allflayers(s: string) +{ + for (i := 0; i < len ctxt.texts; i++) + for (fls := ctxt.texts[i].flayers; fls != nil; fls = tl fls) { + fl := hd fls; + tk->cmd(fl.t, s); + } +} + +setdot(fl: ref Flayer, l1, l2: int) +{ + tk->cmd(fl.t, ".w.t tag remove sel 0.0 end"); + + fl.dot.first = l1; + fl.dot.last = l2; + if (l2 <= fl.scope.first) + tk->cmd(fl.t, ".w.t mark set insert 0.0"); + else if (fl.scope.last <= l1) + tk->cmd(fl.t, ".w.t mark set insert end"); + else { + tk->cmd(fl.t, sprint(".w.t mark set insert 0.0+%dchars", + l1-fl.scope.first)); + if (l1 != l2) + tk->cmd(fl.t, sprint(".w.t tag add sel 0.0+%dchars 0.0+%dchars", + l1-fl.scope.first, + l2-fl.scope.first)); + } + tk->cmd(fl.t, "update"); +} + +panic(s: string) +{ + stderr := sys->fildes(2); + sys->fprint(stderr, "Panic: %s\n", s); + f := sys->sprint("#p/%d/ctl", ctxt.pgrp); + if ((fd := sys->open(f, sys->OWRITE)) != nil) + sys->write(fd, array of byte "killgrp\n", 8); + exit; +} + +whichmenu(tag: int): int +{ + for (i := 0; i < len ctxt.menus; i++) + if (ctxt.menus[i].tag == tag) + return i; + return -1; +} + +whichtext(tag: int): int +{ + for (i := 0; i < len ctxt.texts; i++) + if (ctxt.texts[i].tag == tag) + return i; + return -1; +} + +setscrollbar(t: ref Text, fl: ref Flayer) +{ + ll := real t.nrunes; + f1 := 0.0; f2 := 1.0; + if (ll != 0.0) { + f1 = real fl.scope.first / ll; + if (fl.scope.last > t.nrunes) + f2 = 1.0; + else + f2 = real fl.scope.last / ll; + } + fl.scrollbar = fl.scope; + tk->cmd(fl.t, sprint(".w.s set %f %f; update", f1, f2)); +} + +buttonselect(fl: ref Flayer, s: string): int +{ + tag := fl.tag; + if ((i := whichtext(tag)) < 0) panic("buttonselect: whichtext"); + t := ctxt.texts[i]; + + (n, l) := sys->tokenize(s, " "); + if (n != 4) panic("buttonselect"); + + # ignore mouse down -- wait for mouse up + if (hd l == "1" || hd l == "3") return 0; + + if (ctxt.which != fl) { + if (ctxt.menus[i].text != ctxt.cmd) + ctxt.work = fl; + newcur(t, fl); +# setdot(fl, fl.dot.first, fl.dot.first); + return 0; + } + + if (hd l == "2") { + # Double click + l = tl tl l; + s = tk->cmd(fl.t, ".w.t index @" + hd l + "," + hd tl l); + fl.dot.first = fl.dot.last = coord2pos(t, fl, s); + return 1; + } + + rg := tk->cmd(fl.t, ".w.t tag ranges sel"); + if (rg == "") { + # Nothing selected, find insertion point + l = tl tl l; + s = tk->cmd(fl.t, ".w.t index @" + hd l + "," + hd tl l); + fl.dot.first = fl.dot.last = coord2pos(t, fl, s); + } else { + (n, l) = sys->tokenize(rg, " "); + #if (n == 4 && hd tl l == hd tl tl l) + # lst := hd tl tl tl l; + #else if (n != 2) panic("buttonselect: tag ranges"); + #else lst = hd tl l; + # We only have one contiguous selection, so, take the + # first as dot.first and the last as dot.last + fst:=hd l; + lst:=fst; + while(l!=nil){ + lst=hd l; + l = tl l; + } + fl.dot.first = coord2pos(t, fl, fst); + fl.dot.last = coord2pos(t, fl, lst); + tk->cmd(fl.t, ".w.t mark set insert " + fst); + tk->cmd(fl.t, "update"); + } + return 0; +} + +coord2pos(t: ref Text, fl: ref Flayer, s: string): int +{ + x, y: int; + + (n, l) := sys->tokenize(s, "."); + if (n != 2) panic("coord2pos"); + y = (int hd l) - 1; + x = int hd tl l; + if (x == 0 && y == 0) return fl.scope.first; + first := fl.scope.first; + for (scts := t.sects; scts != nil; scts = tl scts) { + sct := hd scts; + if (first >= sct.nrunes) { + first -= sct.nrunes; + continue; + } + if (first > 0) i := first; else i = 0; + while (i < len sct.text) { + if (y) { + if (sct.text[i++] == '\n') y--; + } else { + if (x <= 1) + return fl.scope.first - first + i + x; + if (sct.text[i++] == '\n') panic("coord2pos"); + x--; + } + } + if (len sct.text < sct.nrunes) panic("coord2pos: hole"); + first -= sct.nrunes; + } + if (x <= 0 && y == 0) return t.nrunes; + panic("coord2pos: can't find"); + return(-1); +} + +scrollpos, scrolllines: int; + +scroll(fl: ref Flayer, s: string): (int, int) +{ + tag := fl.tag; + if ((i := whichtext(tag)) < 0) panic("scroll: whichtext"); + t := ctxt.texts[i]; + (n, l) := sys->tokenize(s, " "); + height := fl.scrollbar.last - fl.scrollbar.first; + length := t.nrunes; + case (hd l) { + "0" => + if (n != 3) panic("scroll: format"); + return (scrollpos, scrolllines); + "moveto" => + if (n != 2) panic("scroll: format"); + f := real hd tl l; + if (f < 0.0) f = 0.0; + if (f > 1.0) f = 1.0; + scrollpos = int (f * real length) - height/2; + scrolllines = 1; + "scroll" => + if (n != 3) panic("scroll: format"); + l = tl l; + n = int hd l; + case(hd tl l) { + "page" => + if (n < 0) { + scrollpos = fl.scrollbar.first; + scrolllines = fl.lines; + break; + } + scrollpos = fl.scrollbar.last; + scrolllines = 0; + "unit" => + if (n < 0) { + scrollpos = fl.scrollbar.first - 1; + scrolllines = 1; + break; + } + (p, q) := rasplines(t.sects, fl.scrollbar.first, 1); + if (p > 0) { + scrollpos = p; + scrolllines = 0; + } else { + scrollpos = fl.scrollbar.first; + scrolllines = 0; + } + } + * => + panic("scroll: input"); + } + if (scrollpos > length) + scrollpos = length; + if (scrollpos < 0) { + scrollpos = 0; + scrolllines = 0; + } + if (length != 0) + tk->cmd(fl.t, sprint(".w.s set %f %f", + real scrollpos / real length, + real (scrollpos + height) / real length)); + else + tk->cmd(fl.t, ".w.s set 0.0 1.0"); + tk->cmd(fl.t, "update"); + return (-1, -1); +} + +flclear(fl: ref Flayer) +{ + tk->cmd(fl.t, ".w.t delete 0.0 end"); + tk->cmd(fl.t, "update"); +} + +flinsert(fl: ref Flayer, l: int, s: string) +{ + offset := l-fl.scope.first; + tk->cmd(fl.t, ".w.t insert 0.0+" + string offset + "chars '" + s); + setdot(fl, fl.dot.first, fl.dot.last); +} + +fldelexcess(fl: ref Flayer) +{ + tk->cmd(fl.t, ".w.t delete " + string (fl.lines+1) + ".0 end"); +} + +fldelete(fl: ref Flayer, l1, l2: int) +{ + s: string; + if (l1 <= fl.scope.first) { + if (l2 >= fl.scope.last) { + s = sprint(".w.t delete 0.0 end"); + fl.scope.first = fl.scope.last = l1; + } else { + s = sprint(".w.t delete 0.0 0.0+%dchars", + l2 - fl.scope.first); + fl.scope.last -= l2 - l1; + fl.scope.first = l1; + } + } else { + if (l2 >= fl.scope.last) { + s = sprint(".w.t delete 0.0+%dchars end", + l1 - fl.scope.first); + fl.scope.last = l1; + } else { + s = sprint(".w.t delete 0.0+%dchars 0.0+%dchars", + l1 - fl.scope.first, l2 - fl.scope.first); + fl.scope.last -= l2 - l1; + } + } + if (fl.dot.first >= l2) fl.dot.first -= l2-l1; + else if (fl.dot.first > l1) fl.dot.first = l1; + if (fl.dot.last >= l2) fl.dot.last -= l2-l1; + else if (fl.dot.last > l1) fl.dot.last = l1; + tk->cmd(fl.t, s); + setdot(fl, fl.dot.first, fl.dot.last); + tk->cmd(fl.t, "update"); +} + +# Calculate position forward or backward nlines lines from pos. +# If lines > 0 count forward, if lines < 0 count backward.\ +# Returns a pair, (position, nlines). Nlines is the remaining +# number of lines to be found. If non-zero, beginning or end of +# rasp was encountered while still counting, or a hole was +# encountered. In the former case, position will be 0 or nrunes, +# in the latter case, position will be set to -1. +# To search to the beginning of the current line, set nlines to -1; + +rasplines(scts: list of ref Section, pos, nlines: int): (int, int) +{ + p, i: int; + if (nlines < 0) { + if (scts != nil) { + sct := hd scts; scts = tl scts; + if (pos > sct.nrunes) { + (p, nlines) = + rasplines(scts, pos - sct.nrunes, nlines); + if (p < 0) return (p, nlines); + pos = p + sct.nrunes; + if (nlines == 0) return (pos, 0); + } + if (pos > len sct.text) return (-1, nlines); + for (p = pos-1; p >= 0; p--) { + if (sct.text[p] == '\n') nlines++; + if (nlines == 0) return (p+1, 0); + } + } + return (0, nlines); + } else { + p = 0; + while (scts != nil) { + sct := hd scts; scts = tl scts; + if (pos < sct.nrunes) { + for (i = pos; i < len sct.text; i++) { + if (sct.text[i] == '\n') nlines--; + if (nlines == 0) return (p+i+1, 0); + } + if (i < sct.nrunes) return (-1, nlines); + } + pos -= sct.nrunes; + if (pos < 0) pos = 0; + p += sct.nrunes; + } + return (p, nlines); + } +} + +chanadd(): int +{ + l := len ctxt.flayers; + + keysel := array [l+1] of chan of string; + keysel[0:] = ctxt.keysel; + keysel[l] = chan of string; + ctxt.keysel = keysel; + scrollsel := array [l+1] of chan of string; + scrollsel[0:] = ctxt.scrollsel; + scrollsel[l] = chan of string; + ctxt.scrollsel = scrollsel; + buttonsel := array [l+1] of chan of string; + buttonsel[0:] = ctxt.buttonsel; + buttonsel[l] = chan of string; + ctxt.buttonsel = buttonsel; + menu2sel := array [l+1] of chan of string; + menu2sel[0:] = ctxt.menu2sel; + menu2sel[l] = chan of string; + ctxt.menu2sel = menu2sel; + menu3sel := array [l+1] of chan of string; + menu3sel[0:] = ctxt.menu3sel; + menu3sel[l] = chan of string; + ctxt.menu3sel = menu3sel; + titlesel := array [l+1] of chan of string; + titlesel[0:] = ctxt.titlesel; + titlesel[l] = chan of string; + ctxt.titlesel = titlesel; + flayers := array [l+1] of ref Flayer; + flayers[0:] = ctxt.flayers; + flayers[l] = nil; + ctxt.flayers = flayers; + return l; +} + +chandel(n: int) +{ + l := len ctxt.flayers; + if (n >= l) + panic("chandel"); + + keysel := array [l-1] of chan of string; + keysel[0:] = ctxt.keysel[0:n]; + keysel[n:] = ctxt.keysel[n+1:]; + ctxt.keysel = keysel; + scrollsel := array [l-1] of chan of string; + scrollsel[0:] = ctxt.scrollsel[0:n]; + scrollsel[n:] = ctxt.scrollsel[n+1:]; + ctxt.scrollsel = scrollsel; + buttonsel := array [l-1] of chan of string; + buttonsel[0:] = ctxt.buttonsel[0:n]; + buttonsel[n:] = ctxt.buttonsel[n+1:]; + ctxt.buttonsel = buttonsel; + menu2sel := array [l-1] of chan of string; + menu2sel[0:] = ctxt.menu2sel[0:n]; + menu2sel[n:] = ctxt.menu2sel[n+1:]; + ctxt.menu2sel = menu2sel; + menu3sel := array [l-1] of chan of string; + menu3sel[0:] = ctxt.menu3sel[0:n]; + menu3sel[n:] = ctxt.menu3sel[n+1:]; + ctxt.menu3sel = menu3sel; + titlesel := array [l-1] of chan of string; + titlesel[0:] = ctxt.titlesel[0:n]; + titlesel[n:] = ctxt.titlesel[n+1:]; + ctxt.titlesel = titlesel; + flayers := array [l-1] of ref Flayer; + flayers[0:] = ctxt.flayers[0:n]; + flayers[n:] = ctxt.flayers[n+1:]; + ctxt.flayers = flayers; +} diff --git a/appl/wm/samtk.m b/appl/wm/samtk.m new file mode 100644 index 00000000..cc3efe18 --- /dev/null +++ b/appl/wm/samtk.m @@ -0,0 +1,54 @@ +Samtk: module +{ + + PATH: con "/dis/wm/samtk.dis"; + + Cut, + Paste, + Snarf, + Look, +# Exch, + Send, + NMENU2: con iota; + Search: con Send; + + New, + Zerox, + Close, + Write, + NMENU3: con iota; + + None, + Some, + All: con iota; # visibility in flayer (`some' may not be used) + + init: fn(ctxt: ref Context); + + allflayers: fn(s: string); + append: fn(fls: list of ref Flayer, fl: ref Flayer): + list of ref Flayer; + buttonselect: fn(fl: ref Flayer, s: string): int; + chanadd: fn(): int; + chandel: fn(n: int); + coord2pos: fn(t: ref Text, fl: ref Flayer, s: string): int; + flclear: fn(fl: ref Flayer); + fldelete: fn(fl: ref Flayer, l1, l2: int); + fldelexcess: fn(fl: ref Flayer); + flinsert: fn(fl: ref Flayer, l: int, s: string); + flraise: fn(t: ref Text, fl: ref Flayer); + focus: fn(fl: ref Flayer); + hsetpat: fn(s: string); + menudel: fn(pos: int); + menuins: fn(pos: int, s: string); + newcur: fn(t: ref Text, fl: ref Flayer); + newflayer: fn(tag, tp: int): ref Flayer; + panic: fn(s: string); + resize: fn(fl: ref Flayer); + scroll: fn(fl: ref Flayer, s: string): (int, int); + setdot: fn(fl: ref Flayer, l1, l2: int); + setscrollbar: fn(t: ref Text, fl: ref Flayer); + settitle: fn(t: ref Text, s: string); + titlectl: fn(win: int, menu: string); + whichmenu: fn(tag: int): int; + whichtext: fn(tag: int): int; +}; diff --git a/appl/wm/sendmail.b b/appl/wm/sendmail.b new file mode 100644 index 00000000..da28eca2 --- /dev/null +++ b/appl/wm/sendmail.b @@ -0,0 +1,652 @@ +implement WmSendmail; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + Context: import draw; + +include "tk.m"; + tk: Tk; + Toplevel: import tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "dialog.m"; + dialog: Dialog; + +include "selectfile.m"; + selectfile: Selectfile; + +WmSendmail: module +{ + init: fn(ctxt: ref Draw->Context, args: list of string); +}; + +srv: Sys->Connection; +main: ref Toplevel; +ctxt: ref Context; +username: string; + +mail_cfg := array[] of { + "frame .top", + "label .top.l -bitmap email.bit", + "frame .top.con", + "frame .top.con.b", + "button .top.con.b.con -bitmap mailcon -command {send msg connect}", + "bind .top.con.b.con <Enter> +{.top.status configure -text {connect/disconnect to mail server}}", + "button .top.con.b.send -bitmap maildeliver -command {send msg send}", + "bind .top.con.b.send <Enter> +{.top.status configure -text {deliver mail}}", + + "button .top.con.b.nocc -bitmap mailnocc -command {.hdr.e.cc delete 0 end}", + "bind .top.con.b.nocc <Enter> +{.top.status configure -text {no carbon copy}}", + + "button .top.con.b.new -bitmap mailnew -command {send msg new}", + "bind .top.con.b.new <Enter> +{.top.status configure -text {start a new message}}", + "button .top.con.b.save -bitmap mailsave -command {send msg save}", + "bind .top.con.b.save <Enter> +{.top.status configure -text {save message}}", + "pack .top.con.b.con .top.con.b.send .top.con.b.nocc .top.con.b.new .top.con.b.save -padx 2 -side left", + "label .top.status -text {not connected ...} -anchor w", + "pack .top.l -side left", + "pack .top.con -side left -padx 10", + "pack .top.con.b .top.status -in .top.con -fill x -expand 1", + "frame .hdr", + "frame .hdr.l", + "frame .hdr.e", + "label .hdr.l.mt -text {Mail To:}", + "label .hdr.l.cc -text {Mail CC:}", + "label .hdr.l.sb -text {Subject:}", + "pack .hdr.l.mt .hdr.l.cc .hdr.l.sb -fill y -expand 1", + "entry .hdr.e.mt -bg white", + "entry .hdr.e.cc -bg white", + "entry .hdr.e.sb -bg white", + "bind .hdr.e.mt <Key-\n> {}", + "bind .hdr.e.cc <Key-\n> {}", + "bind .hdr.e.sb <Key-\n> {}", + "pack .hdr.e.mt .hdr.e.cc .hdr.e.sb -fill x -expand 1", + "pack .hdr.l -side left -fill y", + "pack .hdr.e -side left -fill x -expand 1", + "frame .body", + "scrollbar .body.scroll -command {.body.t yview}", + "text .body.t -width 15c -height 7c -yscrollcommand {.body.scroll set} -bg white", + "pack .body.t -side left -expand 1 -fill both", + "pack .body.scroll -side left -fill y", + "pack .top -anchor w -padx 5", + "pack .hdr -fill x -anchor w -padx 5 -pady 5", + "pack .body -expand 1 -fill both -padx 5 -pady 5", + "pack .b -padx 5 -pady 5 -fill x", + "pack propagate . 0", + "update" +}; + +con_cfg := array[] of { + "frame .b", + "button .b.ok -text {Connect} -command {send cmd ok}", + "button .b.can -text {Cancel} -command {send cmd can}", + "pack .b.ok .b.can -side left -fill x -padx 10 -pady 10 -expand 1", + "frame .l", + "label .l.h -text {Mail Server:} -anchor w", + "label .l.u -text {User Name:} -anchor w", + "pack .l.h .l.u -fill both -expand 1", + "frame .e", + "entry .e.h -width 30w", + "entry .e.u -width 30w", + "pack .e.h .e.u -fill x", + "frame .f -borderwidth 2 -relief raised", + "pack .l .e -fill both -expand 1 -side left -in .f", + "bind .e.h <Key-\n> {send cmd ok}", + "bind .e.u <Key-\n> {send cmd ok}", +}; + +con_pack := array[] of { + "pack .f", + "pack .b -fill x -expand 1", + "focus .e.u", + "update", +}; + +new_cmd := array[] of { + ".hdr.e.mt delete 0 end", + ".hdr.e.cc delete 0 end", + ".hdr.e.sb delete 0 end", + ".body.t delete 1.0 end", + ".body.t see 1.0", + "update" +}; + +init(xctxt: ref Context, argv: list of string) +{ + sys = load Sys Sys->PATH; + if (xctxt == nil) { + sys->fprint(sys->fildes(2), "sendmail: no window context\n"); + raise "fail:bad context"; + } + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + dialog = load Dialog Dialog->PATH; + selectfile = load Selectfile Selectfile->PATH; + + ctxt = xctxt; + + tkclient->init(); + dialog->init(); + selectfile->init(); + + tkargs := ""; + argv = tl argv; + if(argv != nil) { + tkargs = hd argv; + argv = tl argv; + } + + titlectl: chan of string; + (main, titlectl) = tkclient->toplevel(ctxt, tkargs, + "MailStop: Sender", Tkclient->Appl); + + msg := chan of string; + tk->namechan(main, msg, "msg"); + + for (c:=0; c<len mail_cfg; c++) + tk->cmd(main, mail_cfg[c]); + tkclient->onscreen(main, nil); + tkclient->startinput(main, "kbd"::"ptr"::nil); + + if(argv != nil) + fromreadmail(hd argv); + + for(;;) alt { + s := <-main.ctxt.kbd => + tk->keyboard(main, s); + s := <-main.ctxt.ptr => + tk->pointer(main, *s); + s := <-main.ctxt.ctl or + s = <-main.wreq or + s = <-titlectl => + if(s == "exit") { + if(srv.dfd == nil) + return; + status("Closing connection..."); + smtpcmd("QUIT"); + return; + } + tkclient->wmctl(main, s); + cmd := <-msg => + case cmd { + "connect" => + if(srv.dfd == nil) { + connect(main, 1); + fixbutton(); + break; + } + disconnect(); + "save" => + save(); + "send" => + sendmail(); + "new" => + for (c=0; c<len new_cmd; c++) + tk->cmd(main, new_cmd[c]); + } + } +} + +fixbutton() +{ + s := "Connect"; + if(srv.dfd != nil) + s = "Disconnect"; + + tk->cmd(main, ".top.con configure -text "+s+"; update"); +} + +sendmail() +{ + if(srv.dfd == nil) { + dialog->prompt(ctxt, main.image, "error -fg red", "Send", + "You must be connected to deliver mail", + 0, "Continue"::nil); + return; + } + + mto := tk->cmd(main, ".hdr.e.mt get"); + if(mto == "") { + dialog->prompt(ctxt, main.image, "error -fg red", "Send", + "You must fill in the \"Mail To\" entry", + 0, "Continue (nothing sent)"::nil); + return; + } + + if(tk->cmd(main, ".body.t index end") == "1.0") { + opt := "Cancel" :: "Send anyway" :: nil; + if(dialog->prompt(ctxt, main.image, "warning -fg yellow", "Send", + "The body of the mail is empty", 0, opt) == 0) + return; + } + + (err, s) := smtpcmd("MAIL FROM:<"+username+">"); + if(err != nil) { + dialog->prompt(ctxt, main.image, "error -fg red", "Send", + "Failed to specify FROM correctly:\n"+err, + 0, "Continue (nothing sent)"::nil); + return; + } + status(s); + (err, s) = smtpcmd("RCPT TO:<"+mto+">"); + if(err != nil) { + dialog->prompt(ctxt, main.image, "error -fg red", "Send", + "Failed to specify TO correctly:\n"+err, + 0, "Continue (nothing sent)"::nil); + return; + } + status(s); + cc := tk->cmd(main, ".hdr.e.cc get"); + if(cc != nil) { + (nil, l) := sys->tokenize(cc, "\t ,"); + while(l != nil) { + copy := hd l; + (err, s) = smtpcmd("RCPT TO:<"+copy+">"); + if(err != nil) { + dialog->prompt(ctxt, main.image, "error -fg red", "Send", + "Carbon copy to "+copy+"failed:\n"+err, + 0, "Continue (nothing sent)"::nil); + } + } + } + (err, s) = smtpcmd("DATA"); + if(err != nil) { + dialog->prompt(ctxt, main.image, "error -fg red", "Send", + "Failed to enter DATA mode:\n"+err, + 0, "Continue (nothing sent)"::nil); + return; + } + + sub := tk->cmd(main, ".hdr.e.sb get"); + if(sub != nil) + sys->fprint(srv.dfd, "Subject: %s\n", sub); + + b := array of byte tk->cmd(main, ".body.t get 1.0 end"); + n := sys->write(srv.dfd, b, len b); + b = nil; + if(n < 0) { + dialog->prompt(ctxt, main.image, "error -fg red", "Send", + "Error writing server:\n"+sys->sprint("%r"), + 0, "Abort (partial send)"::nil); + return; + } + (err, s) = smtpcmd("\r\n."); + if(err != nil) { + dialog->prompt(ctxt, main.image, "error -fg red", "Send", + "Failed to terminate message:\n"+err, + 0, "Abort (partial send)"::nil); + return; + } + status(s); +} + +save() +{ + mto := tk->cmd(main, ".hdr.e.to get"); + if(mto == "") { + dialog->prompt(ctxt, main.image, "error -fg red", "Save", + "No message to save", + 0, "Dismiss"::nil); + return; + } + + pat := list of { + "*.letter (Saved mail)", + "* (All files)" + }; + + fname: string; + fd: ref Sys->FD; + + for(;;) { + fname = selectfile->filename(ctxt, main.image, "Save in Mailbox", pat, + "/usr/"+rf("/dev/user")+"/mail"); + if(fname == nil) + return; + + fd = sys->create(fname, sys->OWRITE, 8r660); + if(fd != nil) + break; + r := dialog->prompt(ctxt, main.image, "error -fg red", "Save", + "Failed to create "+sys->sprint("%s\n%r", fname), + 0, "Retry"::"Cancel"::nil); + if(r > 0) + return; + } + + r := sys->fprint(srv.dfd, "Mail To: %s\n", mto); + cc := tk->cmd(main, ".hdr.e.cc get"); + if(cc != nil) + r += sys->fprint(srv.dfd, "Mail CC: %s\n", cc); + sb := tk->cmd(main, ".hdr.e.sb get"); + if(sb != nil) + r += sys->fprint(srv.dfd, "Subject: %s\n\n", sb); + + s := tk->cmd(main, ".body.t get 1.0 end"); + b := array of byte s; + n := sys->write(fd, b, len b); + if(n < 0) { + dialog->prompt(ctxt, main.image, "error -fg red", "Save", + "Error writing file "+sys->sprint("%s\n%r", fname), + 0, "Continue"::nil); + return; + } + status("wrote "+string(n+r)+" bytes."); +} + +status(msg: string) +{ + tk->cmd(main, ".top.status configure -text {"+msg+"}; update"); +} + +disconnect() +{ + (err, s) := smtpcmd("QUIT"); + srv.dfd = nil; + fixbutton(); + if(err != nil) { + dialog->prompt(ctxt, main.image, "error -fg red", "Disconnect", + "Server problem:\n"+err, + 0, "Dismiss"::nil); + return; + } + status(s); +} + +connect(parent: ref Toplevel, interactive: int) +{ + (t, conctl) := tkclient->toplevel(ctxt, postposn(parent), + "Connection Parameters", 0); + + cmd := chan of string; + tk->namechan(t, cmd, "cmd"); + + for (c:=0; c<len con_cfg; c++) + tk->cmd(t, con_cfg[c]); + + username = rf("/dev/user"); + s := rf("/usr/"+username+"/mail/smtpserver"); + if(s != "") + tk->cmd(t, ".e.h insert 0 '"+s); + + s = rf("/usr/"+username+"/mail/domain"); + if(s != nil) + username += "@"+s; + + u := tk->cmd(t, ".e.u get"); + if(u == "") + tk->cmd(t, ".e.u insert 0 '"+username); + + if(interactive == 0 && checkthendial(t) != 0) + return; + + for (c=0; c<len con_pack; c++) + tk->cmd(t, con_pack[c]); + tkclient->onscreen(t, nil); + tkclient->startinput(t, "kbd"::"ptr"::nil); + + for(;;) alt { + ss := <-t.ctxt.kbd => + tk->keyboard(t, ss); + ss := <-t.ctxt.ptr => + tk->pointer(t, *ss); + ss := <-t.ctxt.ctl or + ss = <-t.wreq or + ss = <-conctl => + if (ss == "exit") + return; + tkclient->wmctl(t, ss); + s = <-cmd => + if(s == "can") + return; + if(checkthendial(t) != 0) + return; + status("not connected"); + } + srv.dfd = nil; +} + +checkthendial(t: ref Toplevel): int +{ + server := tk->cmd(t, ".e.h get"); + if(server == "") { + dialog->prompt(ctxt, t.image, "error -fg red", "Connect", + "You must supply a server address", + 0, "Continue"::nil); + return 0; + } + user := tk->cmd(t, ".e.u get"); + if(user == "") { + dialog->prompt(ctxt, t.image, "error -fg red", "Connect", + "You must supply a user name", + 0, "Continue"::nil); + return 0; + } + if(dom(user) == "") { + dialog->prompt(ctxt, t.image, "error -fg red", "Connect", + "The user name must contain an '@'", + 0, "Continue"::nil); + return 0; + } + return dialer(t, server, user); +} + +dialer(t: ref Toplevel, server, user: string): int +{ + ok: int; + + status("dialing server..."); + (ok, srv) = sys->dial(netmkaddr(server, nil, "25"), nil); + if(ok < 0) { + dialog->prompt(ctxt, t.image, "error -fg red", "Connect", + "The following error occurred while\n"+ + "dialing the server: "+sys->sprint("%r"), + 0, "Continue"::nil); + return 0; + } + status("connected..."); + (err, s) := smtpresp(); + if(err != nil) { + dialog->prompt(ctxt, t.image, "error -fg red", "Connect", + "An error occurred during sign on.\n"+err, + 0, "Continue"::nil); + return 0; + } + status(s); + (err, s) = smtpcmd("HELO "+dom(user)); + if(err != nil) { + dialog->prompt(ctxt, t.image, "error -fg red", "Connect", + "An error occurred during login.\n"+err, + 0, "Continue"::nil); + return 0; + } + status("ready to send..."); + return 1; +} + +rf(file: string): string +{ + fd := sys->open(file, sys->OREAD); + if(fd == nil) + return ""; + + buf := array[128] of byte; + n := sys->read(fd, buf, len buf); + if(n < 0) + return ""; + + return string buf[0:n]; +} + +postposn(parent: ref Toplevel): string +{ + x := int tk->cmd(parent, ".top.con cget -actx"); + y := int tk->cmd(parent, ".top.con cget -acty"); + h := int tk->cmd(parent, ".top.con cget -height"); + + return "-x "+string(x-2)+" -y "+string(y+h+2); +} + +dom(name: string): string +{ + for(i := 0; i < len name; i++) + if(name[i] == '@') + return name[i+1:]; + return nil; +} + +fromreadmail(hdr: string) +{ + (nil, l) := sys->tokenize(hdr, "\n"); + while(l != nil) { + s := hd l; + l = tl l; + n := match(s, "subject: "); + if(n != nil) { + tk->cmd(main, ".hdr.e.sb insert end '"+n); + continue; + } + n = match(s, "cc: "); + if(n != nil) { + tk->cmd(main, ".hdr.e.cc insert end '"+n); + continue; + } + n = match(s, "from: "); + if(n != nil) { + n = extract(n); + tk->cmd(main, ".hdr.e.mt insert end '"+n); + } + } + connect(main, 0); +} + +extract(name: string): string +{ + for(i := 0; i < len name; i++) { + if(name[i] == '<') { + for(j := i+1; j < len name; j++) + if(name[j] == '>') + break; + return name[i+1:j]; + } + } + for(i = 0; i < len name; i++) + if(name[i] == ' ') + break; + return name[0:i]; +} + +lower(c: int): int +{ + if(c >= 'A' && c <= 'Z') + c = 'a' + (c - 'A'); + return c; +} + +match(text, pat: string): string +{ + for(i := 0; i < len pat; i++) { + c := text[i]; + p := pat[i]; + if(c != p && lower(c) != p) + return ""; + } + return text[i:]; +} + +# +# Talk SMTP +# +smtpcmd(cmd: string): (string, string) +{ + cmd += "\r\n"; +# sys->print("->%s", cmd); + b := array of byte cmd; + l := len b; + n := sys->write(srv.dfd, b, l); + if(n != l) + return ("send to server:"+sys->sprint("%r"), nil); + + return smtpresp(); +} + +smtpresp(): (string, string) +{ + s := ""; + i := 0; + lastc := 0; + for(;;) { + c := smtpgetc(); + if(c == -1) + return ("read from server:"+sys->sprint("%r"), nil); + if(lastc == '\r' && c == '\n') + break; + s[i++] = c; + lastc = c; + } +# sys->print("<-%s\n", s); + if(i < 3) + return ("short read from server", nil); + s = s[0:i-1]; + case s[0] { + '1' or '2' or '3' => + i = 3; + while(s[i] == ' ' && i < len s) + i++; + return (nil, s[i:]); + '4'or '5' => + i = 3; + while(s[i] == ' ' && i < len s) + i++; + return (s[i:], nil); + * => + return ("invalid server response", nil); + } +} + +Iob: adt +{ + nbyte: int; + posn: int; + buf: array of byte; +}; +smtpbuf: Iob; + +smtpgetc(): int +{ + if(smtpbuf.nbyte > 0) { + smtpbuf.nbyte--; + return int smtpbuf.buf[smtpbuf.posn++]; + } + if(smtpbuf.buf == nil) + smtpbuf.buf = array[512] of byte; + + smtpbuf.posn = 0; + n := sys->read(srv.dfd, smtpbuf.buf, len smtpbuf.buf); + if(n < 0) + return -1; + + smtpbuf.nbyte = n-1; + return int smtpbuf.buf[smtpbuf.posn++]; +} + +netmkaddr(addr, net, svc: string): string +{ + if(net == nil) + net = "net"; + (n, l) := sys->tokenize(addr, "!"); + if(n <= 1){ + if(svc== nil) + return sys->sprint("%s!%s", net, addr); + return sys->sprint("%s!%s!%s", net, addr, svc); + } + if(svc == nil || n > 2) + return addr; + return sys->sprint("%s!%s", addr, svc); +} diff --git a/appl/wm/sh.b b/appl/wm/sh.b new file mode 100644 index 00000000..159ce6bc --- /dev/null +++ b/appl/wm/sh.b @@ -0,0 +1,851 @@ +implement WmSh; + +include "sys.m"; + sys: Sys; + FileIO: import sys; + +include "draw.m"; + draw: Draw; + Context, Rect: import draw; + +include "tk.m"; + tk: Tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "plumbmsg.m"; + plumbmsg: Plumbmsg; + Msg: import plumbmsg; + +include "workdir.m"; + +include "string.m"; + str: String; + +include "arg.m"; + +WmSh: module +{ + init: fn(ctxt: ref Draw->Context, args: list of string); +}; + +Command: type WmSh; + +BSW: con 23; # ^w bacspace word +BSL: con 21; # ^u backspace line +EOT: con 4; # ^d end of file +ESC: con 27; # hold mode + +# XXX line-based limits are inadequate - memory is still +# blown if a client writes a very long line. +HIWAT: con 2000; # maximum number of lines in transcript +LOWAT: con 1500; # amount to reduce to after high water + +Name: con "Shell"; + +Rdreq: adt +{ + off: int; + nbytes: int; + fid: int; + rc: chan of (array of byte, string); +}; + +shwin_cfg := array[] of { + "menu .m", + ".m add command -text noscroll -command {send edit noscroll}", + ".m add command -text cut -command {send edit cut}", + ".m add command -text paste -command {send edit paste}", + ".m add command -text snarf -command {send edit snarf}", + ".m add command -text send -command {send edit send}", + "frame .b -bd 1 -relief ridge", + "frame .ft -bd 0", + "scrollbar .ft.scroll -command {send scroll t}", + "text .ft.t -bd 1 -relief flat -yscrollcommand {send scroll s} -bg white -selectforeground black -selectbackground #CCCCCC", + ".ft.t tag configure sel -relief flat", + "pack .ft.scroll -side left -fill y", + "pack .ft.t -fill both -expand 1", + "pack .Wm_t -fill x", + "pack .b -anchor w -fill x", + "pack .ft -fill both -expand 1", + "focus .ft.t", + "bind .ft.t <Key> {send keys {%A}}", + "bind .ft.t <Control-d> {send keys {%A}}", + "bind .ft.t <Control-h> {send keys {%A}}", + "bind .ft.t <Control-w> {send keys {%A}}", + "bind .ft.t <Control-u> {send keys {%A}}", + "bind .ft.t <Button-1> +{send but1 pressed}", + "bind .ft.t <Double-Button-1> +{send but1 pressed}", + "bind .ft.t <ButtonRelease-1> +{send but1 released}", + "bind .ft.t <ButtonPress-2> {send but2 %X %Y}", + "bind .ft.t <Motion-Button-2-Button-1> {}", + "bind .ft.t <Motion-ButtonPress-2> {}", + "bind .ft.t <ButtonPress-3> {send but3 pressed}", + "bind .ft.t <ButtonRelease-3> {send but3 released %x %y}", + "bind .ft.t <Motion-Button-3> {}", + "bind .ft.t <Motion-Button-3-Button-1> {}", + "bind .ft.t <Double-Button-3> {}", + "bind .ft.t <Double-ButtonRelease-3> {}", +}; + +rdreq: list of Rdreq; +menuindex := "0"; +holding := 0; +plumbed := 0; +rawon := 0; +rawinput := ""; +scrolling := 1; +partialread: array of byte; +cwd := ""; +width, height, font: string; + +events: list of string; +evrdreq: list of Rdreq; +winname: string; + +badmod(p: string) +{ + sys->print("wm/sh: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init(ctxt: ref Context, argv: list of string) +{ + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + + tkclient = load Tkclient Tkclient->PATH; + if (tkclient == nil) + badmod(Tkclient->PATH); + + str = load String String->PATH; + if (str == nil) + badmod(String->PATH); + + arg := load Arg Arg->PATH; + if (arg == nil) + badmod(Arg->PATH); + arg->init(argv); + + plumbmsg = load Plumbmsg Plumbmsg->PATH; + + sys->pctl(Sys->FORKNS | Sys->NEWPGRP | Sys->FORKENV, nil); + + tkclient->init(); + if (ctxt == nil) + ctxt = tkclient->makedrawcontext(); + if(ctxt == nil){ + sys->fprint(sys->fildes(2), "sh: no window context\n"); + raise "fail:bad context"; + } + + if(plumbmsg != nil && plumbmsg->init(1, nil, 0) >= 0){ + plumbed = 1; + workdir := load Workdir Workdir->PATH; + cwd = workdir->init(); + } + + shargs: list of string; + while ((opt := arg->opt()) != 0) { + case opt { + 'w' => + width = arg->arg(); + 'h' => + height = arg->arg(); + 'f' => + font = arg->arg(); + 'c' => + a := arg->arg(); + if (a == nil) { + sys->print("usage: wm/sh [-ilxvn] [-w width] [-h height] [-f font] [-c command] [file [args...]\n"); + raise "fail:usage"; + } + shargs = a :: "-c" :: shargs; + 'i' or 'l' or 'x' or 'v' or 'n' => + shargs = sys->sprint("-%c", opt) :: shargs; + } + } + argv = arg->argv(); + for (; shargs != nil; shargs = tl shargs) + argv = hd shargs :: argv; + + winname = Name + " " + cwd; + + spawn main(ctxt, argv); +} + +task(t: ref Tk->Toplevel) +{ + tkclient->wmctl(t, "task"); +} + +atend(t: ref Tk->Toplevel, w: string): int +{ + s := cmd(t, w+" yview"); + for(i := 0; i < len s; i++) + if(s[i] == ' ') + break; + return i == len s - 2 && s[i+1] == '1'; +} + +main(ctxt: ref Draw->Context, argv: list of string) +{ + (t, titlectl) := tkclient->toplevel(ctxt, "", winname, Tkclient->Appl); + wm := t.ctxt; + + edit := chan of string; + tk->namechan(t, edit, "edit"); + + keys := chan of string; + tk->namechan(t, keys, "keys"); + + butcmd := chan of string; + tk->namechan(t, butcmd, "button"); + + event := chan of string; + tk->namechan(t, event, "action"); + + scroll := chan of string; + tk->namechan(t, scroll, "scroll"); + + but1 := chan of string; + tk->namechan(t, but1, "but1"); + but2 := chan of string; + tk->namechan(t, but2, "but2"); + but3 := chan of string; + tk->namechan(t, but3, "but3"); + button1 := 0; + button3 := 0; + + for (i := 0; i < len shwin_cfg; i++) + cmd(t, shwin_cfg[i]); + (menuw, nil) := itemsize(t, ".m"); + if (font != nil) { + if (font[0] != '/' && (len font == 1 || font[0:2] != "./")) + font = "/fonts/" + font; + cmd(t, ".ft.t configure -font " + font); + } + cmd(t, ".ft.t configure -width 65w -height 20h"); + cmd(t, "pack propagate . 0"); + if(width != nil) + cmd(t, ". configure -width " + width); + if(height != nil) + cmd(t, ". configure -height " + height); + tkclient->onscreen(t, nil); + tkclient->startinput(t, "ptr" :: "kbd" :: nil); + + ioc := chan of (int, ref FileIO, ref FileIO, string, ref FileIO); + spawn newsh(ctxt, ioc, argv); + + (nil, file, filectl, consfile, shctl) := <-ioc; + if(file == nil || filectl == nil || shctl == nil) { + sys->print("newsh: shell cons creation failed\n"); + return; + } + dummyfwrite := chan of (int, array of byte, int, Sys->Rwrite); + fwrite := file.write; + + rdrpc: Rdreq; + + # outpoint is place in text to insert characters printed by programs + cmd(t, ".ft.t mark set outpoint 1.0; .ft.t mark gravity outpoint left"); + + for(;;) alt { + c := <-wm.kbd => + tk->keyboard(t, c); + m := <-wm.ptr => + tk->pointer(t, *m); + c := <-wm.ctl or + c = <-t.wreq or + c = <-titlectl => + tkclient->wmctl(t, c); + ecmd := <-edit => + editor(t, ecmd); + sendinput(t); + + c := <-keys => + cut(t, 1); + char := c[1]; + if(char == '\\') + char = c[2]; + if(rawon){ + if(int cmd(t, ".ft.t compare insert >= outpoint")){ + rawinput[len rawinput] = char; + sendinput(t); + break; + } + } + case char { + * => + cmd(t, ".ft.t insert insert "+c); + '\n' or + EOT => + cmd(t, ".ft.t insert insert "+c); + sendinput(t); + '\b' => + cmd(t, ".ft.t tkTextDelIns -c"); + BSL => + cmd(t, ".ft.t tkTextDelIns -l"); + BSW => + cmd(t, ".ft.t tkTextDelIns -w"); + ESC => + setholding(t, !holding); + } + cmd(t, ".ft.t see insert;update"); + + c := <-but1 => + button1 = (c == "pressed"); + button3 = 0; # abort any pending button 3 action + + c := <-but2 => + if(button1){ + cut(t, 1); + cmd(t, "update"); + break; + } + (nil, l) := sys->tokenize(c, " "); + x := int hd l - menuw/2; + y := int hd tl l - int cmd(t, ".m yposition "+menuindex) - 10; + cmd(t, ".m activate "+menuindex+"; .m post "+string x+" "+string y+ + "; update"); + button3 = 0; # abort any pending button 3 action + + c := <-but3 => + if(c == "pressed"){ + button3 = 1; + if(button1){ + paste(t); + sendinput(t); + cmd(t, "update"); + } + break; + } + if(plumbed == 0 || button3 == 0 || button1 != 0) + break; + button3 = 0; + # plumb message triggered by release of button 3 + (nil, l) := sys->tokenize(c, " "); + x := int hd tl l; + y := int hd tl tl l; + index := cmd(t, ".ft.t index @"+string x+","+string y); + selindex := cmd(t, ".ft.t tag ranges sel"); + if(selindex != "") + insel := cmd(t, ".ft.t compare sel.first <= "+index)=="1" && + cmd(t, ".ft.t compare sel.last >= "+index)=="1"; + else + insel = 0; + attr := ""; + if(insel) + text := tk->cmd(t, ".ft.t get sel.first sel.last"); + else{ + # have line with text in it + # now extract whitespace-bounded string around click + (nil, w) := sys->tokenize(index, "."); + charno := int hd tl w; + left := cmd(t, ".ft.t index {"+index+" linestart}"); + right := cmd(t, ".ft.t index {"+index+" lineend}"); + line := tk->cmd(t, ".ft.t get "+left+" "+right); + for(i=charno; i>0; --i) + if(line[i-1]==' ' || line[i-1]=='\t') + break; + for(j:=charno; j<len line; j++) + if(line[j]==' ' || line[j]=='\t') + break; + text = line[i:j]; + attr = "click="+string (charno-i); + } + msg := ref Msg( + "WmSh", + "", + cwd, + "text", + attr, + array of byte text); + if(msg.send() < 0) + sys->fprint(sys->fildes(2), "sh: plumbing write error: %r\n"); + c := <-butcmd => + simulatetype(t, tkunquote(c)); + sendinput(t); + cmd(t, "update"); + c := <-event => + events = str->append(tkunquote(c), events); + if (evrdreq != nil) { + rc := (hd evrdreq).rc; + rc <-= (array of byte hd events, nil); + evrdreq = tl evrdreq; + events = tl events; + } + rdrpc = <-shctl.read => + if(rdrpc.rc == nil) + continue; + if (events != nil) { + rdrpc.rc <-= (array of byte hd events, nil); + events = tl events; + } else + evrdreq = rdrpc :: evrdreq; + (nil, data, nil, wc) := <-shctl.write => + if (wc == nil) + break; + if ((err := shctlcmd(t, string data)) != nil) + wc <-= (0, err); + else + wc <-= (len data, nil); + rdrpc = <-filectl.read => + if(rdrpc.rc == nil) + continue; + rdrpc.rc <-= (nil, "not allowed"); + (nil, data, nil, wc) := <-filectl.write => + if(wc == nil) { + # consctl closed - revert to cooked mode + # XXX should revert only on *last* close? + rawon = 0; + continue; + } + (nc, cmdlst) := sys->tokenize(string data, " \n"); + if(nc == 1) { + case hd cmdlst { + "rawon" => + rawon = 1; + rawinput = ""; + # discard previous input + advance := string (len tk->cmd(t, ".ft.t get outpoint end") +1); + cmd(t, ".ft.t mark set outpoint outpoint+" + advance + "chars"); + partialread = nil; + "rawoff" => + rawon = 0; + partialread = nil; + "holdon" => + setholding(t, 1); + cmd(t, "update"); + "holdoff" => + setholding(t, 0); + cmd(t, "update"); + * => + wc <-= (0, "unknown consctl request"); + continue; + } + wc <-= (len data, nil); + continue; + } + wc <-= (0, "unknown consctl request"); + + rdrpc = <-file.read => + if(rdrpc.rc == nil) { + (ok, nil) := sys->stat(consfile); + if (ok < 0) + return; + continue; + } + append(rdrpc); + sendinput(t); + + c := <-scroll => + if(c[0] == 't'){ + cmd(t, ".ft.t yview "+c[1:]+";update"); + if(scrolling) + fwrite = file.write; + else if(atend(t, ".ft.t")) + fwrite = file.write; + else + fwrite = dummyfwrite; + }else{ + cmd(t, ".ft.scroll set "+c[1:]+";update"); + if(atend(t, ".ft.t") && fwrite == dummyfwrite) + fwrite = file.write; + } + (nil, data, nil, wc) := <-fwrite => + if(wc == nil) { + (ok, nil) := sys->stat(consfile); + if (ok < 0) + return; + continue; + } + needscroll := atend(t, ".ft.t"); + cdata := cursorcontrol(t, string data); + ncdata := string len cdata + "chars;"; + cmd(t, ".ft.t insert outpoint '"+ cdata); + wc <-= (len data, nil); + data = nil; + s := ".ft.t mark set outpoint outpoint+" + ncdata; + if(!atend(t, ".ft.t") && scrolling == 0) + fwrite = dummyfwrite; + else if(needscroll) + s += ".ft.t see outpoint;"; + s += "update"; + cmd(t, s); + nlines := int cmd(t, ".ft.t index end"); + if(nlines > HIWAT){ + s = ".ft.t delete 1.0 "+ string (nlines-LOWAT) +".0;update"; + cmd(t, s); + } + } +} + +setholding(t: ref Tk->Toplevel, hold: int) +{ + if(hold == holding) + return; + holding = hold; + color := "blue"; + if(!holding){ + color = "black"; + tkclient->settitle(t, winname); + sendinput(t); + }else + tkclient->settitle(t, winname+" (holding)"); + cmd(t, ".ft.t configure -foreground "+color); +} + +tkunquote(s: string): string +{ + if (s == nil) + return nil; + t: string; + if (s[0] != '{' || s[len s - 1] != '}') + return s; + for (i := 1; i < len s - 1; i++) { + if (s[i] == '\\') + i++; + t[len t] = s[i]; + } + return t; +} + +buttonid := 0; +shctlcmd(win: ref Tk->Toplevel, c: string): string +{ + toks := str->unquoted(c); + if (toks == nil) + return "null command"; + n := len toks; + case hd toks { + "button" or + "action"=> + # (button|action) title sendtext + if (n != 3) + return "bad usage"; + id := ".b.b" + string buttonid++; + cmd(win, "button " + id + " -text " + tk->quote(hd tl toks) + + " -command 'send " + hd toks + " " + tk->quote(hd tl tl toks)); + cmd(win, "pack " + id + " -side left"); + cmd(win, "pack propagate .b 0"); + "clear" => + cmd(win, "pack propagate .b 1"); + for (i := 0; i < buttonid; i++) + cmd(win, "destroy .b.b" + string i); + buttonid = 0; + "cwd" => + if (n != 2) + return "bad usage"; + cwd = hd tl toks; + winname = Name + " " + cwd; + tkclient->settitle(win, winname); + * => + return "bad command"; + } + cmd(win, "update"); + return nil; +} + + +RPCread: type (int, int, int, chan of (array of byte, string)); + +append(r: RPCread) +{ + t := r :: nil; + while(rdreq != nil) { + t = hd rdreq :: t; + rdreq = tl rdreq; + } + rdreq = t; +} + +insat(t: ref Tk->Toplevel, mark: string): int +{ + return cmd(t, ".ft.t compare insert == "+mark) == "1"; +} + +insininput(t: ref Tk->Toplevel): int +{ + if(cmd(t, ".ft.t compare insert >= outpoint") != "1") + return 0; + return cmd(t, ".ft.t compare {insert linestart} == {outpoint linestart}") == "1"; +} + +isalnum(s: string): int +{ + if(s == "") + return 0; + c := s[0]; + if('a' <= c && c <= 'z') + return 1; + if('A' <= c && c <= 'Z') + return 1; + if('0' <= c && c <= '9') + return 1; + if(c == '_') + return 1; + if(c > 16rA0) + return 1; + return 0; +} + +cursorcontrol(t: ref Tk->Toplevel, s: string): string +{ + l := len s; + for(i := 0; i < l; i++) { + case s[i] { + '\b' => + pre := ""; + rem := ""; + if(i + 1 < l) + rem = s[i+1:]; + if(i == 0) { # erase existing character in line + if(tk->cmd(t, ".ft.t get " + + "{outpoint linestart} outpoint") != "") + cmd(t, ".ft.t delete outpoint-1char"); + } else { + if(s[i-1] != '\n') # don't erase newlines + i--; + if(i) + pre = s[:i]; + } + s = pre + rem; + l = len s; + i = len pre - 1; + '\r' => + s[i] = '\n'; + if(i + 1 < l && s[i+1] == '\n') # \r\n + s = s[:i] + s[i+1:]; + else if(i > 0 && s[i-1] == '\n') # \n\r + s = s[:i-1] + s[i:]; + l = len s; + '\0' => + s[i] = Sys->UTFerror; + } + } + return s; +} + +editor(t: ref Tk->Toplevel, ecmd: string) +{ + s, snarf: string; + + case ecmd { + "scroll" => + menuindex = "0"; + scrolling = 1; + cmd(t, ".m entryconfigure 0 -text noscroll -command {send edit noscroll}"); + "noscroll" => + menuindex = "0"; + scrolling = 0; + cmd(t, ".m entryconfigure 0 -text scroll -command {send edit scroll}"); + "cut" => + menuindex = "1"; + cut(t, 1); + "paste" => + menuindex = "2"; + paste(t); + "snarf" => + menuindex = "3"; + if(cmd(t, ".ft.t tag ranges sel") == "") + break; + snarf = tk->cmd(t, ".ft.t get sel.first sel.last"); + tkclient->snarfput(snarf); + "send" => + menuindex = "4"; + if(cmd(t, ".ft.t tag ranges sel") != ""){ + snarf = tk->cmd(t, ".ft.t get sel.first sel.last"); + tkclient->snarfput(snarf); + }else{ + snarf = tkclient->snarfget(); + } + if(snarf != "") + s = snarf; + else + return; + if(s[len s-1] != '\n' && s[len s-1] != EOT) + s[len s] = '\n'; + simulatetype(t, s); + } + cmd(t, "update"); +} + +simulatetype(t: ref Tk->Toplevel, s: string) +{ + if(rawon){ + rawinput += s; + }else{ + cmd(t, ".ft.t see end; .ft.t insert end '"+s); + cmd(t, ".ft.t mark set insert end"); + tk->cmd(t, ".ft.t tag remove sel sel.first sel.last"); + } +} + +cut(t: ref Tk->Toplevel, snarfit: int) +{ + if(cmd(t, ".ft.t tag ranges sel") == "") + return; + if(snarfit) + tkclient->snarfput(tk->cmd(t, ".ft.t get sel.first sel.last")); + cmd(t, ".ft.t delete sel.first sel.last"); +} + +paste(t: ref Tk->Toplevel) +{ + snarf := tkclient->snarfget(); + if(snarf == "") + return; + cut(t, 0); + if(rawon && int cmd(t, ".ft.t compare insert >= outpoint")){ + rawinput += snarf; + }else{ + cmd(t, ".ft.t insert insert '"+snarf); + cmd(t, ".ft.t tag add sel insert-"+string len snarf+"chars insert"); + } +} + +sendinput(t: ref Tk->Toplevel) +{ + input: string; + if(rawon) + input = rawinput; + else + input = tk->cmd(t, ".ft.t get outpoint end"); + if(rdreq == nil || (input == nil && len partialread == 0)) + return; + r := hd rdreq; + (chars, bytes, partial) := triminput(r.nbytes, input, partialread); + if(bytes == nil) + return; # no terminator yet + rdreq = tl rdreq; + + alt { + r.rc <-= (bytes, nil) => + # check that it really was sent + alt { + r.rc <-= (nil, nil) => + ; + * => + return; + } + * => + return; # requester has disappeared; ignore his request and try another + } + if(rawon) + rawinput = rawinput[chars:]; + else + cmd(t, ".ft.t mark set outpoint outpoint+" + string chars + "chars"); + partialread = partial; +} + +# read at most nr bytes from the input string, returning the number of characters +# consumed, the bytes to be read, and any remaining bytes from a partially +# read multibyte UTF character. +triminput(nr: int, input: string, partial: array of byte): (int, array of byte, array of byte) +{ + if(nr <= len partial) + return (0, partial[0:nr], partial[nr:]); + if(holding) + return (0, nil, partial); + + # keep the array bounds within sensible limits + if(nr > len input*Sys->UTFmax) + nr = len input*Sys->UTFmax; + buf := array[nr+Sys->UTFmax] of byte; + t := len partial; + buf[0:] = partial; + + hold := !rawon; + i := 0; + while(i < len input){ + c := input[i++]; + # special case for ^D - don't read the actual ^D character + if(!rawon && c == EOT){ + hold = 0; + break; + } + + t += sys->char2byte(c, buf, t); + if(c == '\n' && !rawon){ + hold = 0; + break; + } + if(t >= nr) + break; + } + if(hold){ + for(j := i; j < len input; j++){ + c := input[j]; + if(c == '\n' || c == EOT) + break; + } + if(j == len input) + return (0, nil, partial); + # strip ^D when next read would read it, otherwise + # we'll give premature EOF. + if(i == j && input[i] == EOT) + i++; + } + partial = nil; + if(t > nr){ + partial = buf[nr:t]; + t = nr; + } + return (i, buf[0:t], partial); +} + +newsh(ctxt: ref Context, ioc: chan of (int, ref FileIO, ref FileIO, string, ref FileIO), + args: list of string) +{ + pid := sys->pctl(sys->NEWFD, nil); + + sh := load Command "/dis/sh.dis"; + if(sh == nil) { + ioc <-= (0, nil, nil, nil, nil); + return; + } + + tty := "cons."+string pid; + + sys->bind("#s","/chan",sys->MBEFORE); + fio := sys->file2chan("/chan", tty); + fioctl := sys->file2chan("/chan", tty + "ctl"); + shctl := sys->file2chan("/chan", "shctl"); + ioc <-= (pid, fio, fioctl, "/chan/"+tty, shctl); + if(fio == nil || fioctl == nil || shctl == nil) + return; + + sys->bind("/chan/"+tty, "/dev/cons", sys->MREPL); + sys->bind("/chan/"+tty+"ctl", "/dev/consctl", sys->MREPL); + + fd0 := sys->open("/dev/cons", sys->OREAD|sys->ORCLOSE); + fd1 := sys->open("/dev/cons", sys->OWRITE); + fd2 := sys->open("/dev/cons", sys->OWRITE); + + { + sh->init(ctxt, "sh" :: "-n" :: args); + }exception{ + "fail:*" => + exit; + } +} + +cmd(top: ref Tk->Toplevel, c: string): string +{ + s:= tk->cmd(top, c); +# sys->print("* %s\n", c); + if (s != nil && s[0] == '!') + sys->fprint(sys->fildes(2), "wmsh: tk error on '%s': %s\n", c, s); + return s; +} + +itemsize(top: ref Tk->Toplevel, item: string): (int, int) +{ + w := int tk->cmd(top, item + " cget -actwidth"); + h := int tk->cmd(top, item + " cget -actheight"); + b := int tk->cmd(top, item + " cget -borderwidth"); + return (w+b, h+b); +} diff --git a/appl/wm/smenu.b b/appl/wm/smenu.b new file mode 100644 index 00000000..6b06754f --- /dev/null +++ b/appl/wm/smenu.b @@ -0,0 +1,204 @@ +implement Smenu; + +include "sys.m"; + sys: Sys; +include "draw.m"; +include "tk.m"; + tk: Tk; +include "smenu.m"; + +Scrollmenu.new(t: ref Tk->Toplevel, name: string, labs: array of string, e: int, o: int): ref Scrollmenu +{ + if(sys == nil) + sys = load Sys Sys->PATH; + if(tk == nil) + tk = load Tk Tk->PATH; + m := ref Scrollmenu; + n := len labs; + if(n < e) + e = n; + if(o > n-e) + o = n-e; + l := 0; + for(i := 0; i < n; i++){ + if(len labs[i] > l) + l = len labs[i]; + i++; + } + nlabs := array[n] of string; + sp := string array[l] of { * => byte ' ' }; + for(i = 0; i < n; i++) + nlabs[i] = labs[i] + sp[0: l - len labs[i]]; + sch := cname(name); + cmd(t, "menu " + name); + for(i = 0; i < e; i++){ + cmd(t, name + " add command -label {" + nlabs[o+i] + "} -command {send " + sch + " " + string i + "}"); + } + # cmd(t, "bind " + name + " <ButtonPress-1> +{send " + sch + " b}"); + # cmd(t, "bind " + name + " <ButtonRelease-1> +{send " + sch + " b}"); + cmd(t, "bind " + name + " <Motion> +{send " + sch + " M %x %y}"); + cmd(t, "bind " + name + " <Map> +{send " + sch + " m}"); + cmd(t, "bind " + name + " <Unmap> +{send " + sch + " u}"); + cmd(t, "update"); + m.name = name; + m.labs = nlabs; + m.c = nil; + m.t = t; + m.m = e; + m.n = n; + m.o = o; + m.timer = 1; + return m; +} + +Scrollmenu.post(m: self ref Scrollmenu, x: int, y: int, resc: chan of string, prefix: string) +{ + sync := chan of int; + spawn listen(m, sync, resc, prefix); + <- sync; + cmd(m.t, m.name + " post " + string x + " " + string y); + cmd(m.t, "update"); +} + +Scrollmenu.destroy(m: self ref Scrollmenu) +{ + if(m.c != nil){ + m.c <-= "u"; # fake unmap message + m.c = nil; + } + m.name = nil; + m.labs = nil; + m.t = nil; +} + +timer(t: int, sync: chan of int, c: chan of int) +{ + sync <-= 0; + for(;;){ + alt{ + c <-= 0 => + sys->sleep(t); + <- sync => + exit; + } + } +} + +TINT: con 100; +SEC: con 1000/TINT; + +listen(m: ref Scrollmenu, sync: chan of int, resc: chan of string, prefix: string) +{ + timerc := chan of int; + cmdc := chan of string; + m.c = cmdc; + tk->namechan(m.t, cmdc, cname(m.name)); + sync <-= 0; + x := y := ly := w := h := -1; + for(;;){ + alt{ + <- timerc => + if(x > 0 && x < w){ + if(y < 0 && y > -h/m.m) + menudir(m, -1); + else if(y > 0+h && y < h+h/m.m) + menudir(m, 1); + } + s := <- cmdc => + (nil, toks) := sys->tokenize(s, " "); + case hd toks{ + "M" => + x = int hd tl toks; + y = int hd tl tl toks; + if(!m.timer && x > 0 && x < w){ + mv := 0; + if(y < ly && y < 0) + mv = y/(h/m.m)-1; + else if(y > ly && y > h) + mv = (y-h)/(h/m.m)+1; + if(mv != 0) + menudirs(m, mv); + ly = y; + } + "m" => + w = int cmd(m.t, m.name + " cget -actwidth"); + h = int cmd(m.t, m.name + " cget -actheight"); + ly = -1; + if(m.timer){ + spawn timer(TINT, sync, timerc); + <- sync; + } + "u" => + if(m.timer) + sync <-= 0; + m.c = nil; + exit; + * => + # do not block + res := prefix + string (int hd toks + m.o); + for(t := 0; t < SEC; ){ + if(m.timer) + alt{ + resc <-= res => + t = SEC; + <- timerc => + t++; + } + else + alt{ + resc <-= res => + t = SEC; + * => + sys->sleep(TINT); + t++; + } + } + } + } + } +} + +menudirs(sm: ref Scrollmenu, n: int) +{ + if(n < 0) + (a, d) := (-n, -1); + else + (a, d) = (n, 1); + for(i := 0; i < a; i++) + menudir(sm, d); +} + +menudir(sm: ref Scrollmenu, d: int) +{ + o := sm.o; + n := sm.n; + m := sm.m; + if(d == -1){ + if(o == 0) + return; + for(i := 0; i < m; i++) + cmd(sm.t, sm.name + " entryconfigure " + string i + " -label {" + sm.labs[o-1+i] + "}"); + sm.o = o-1; + } + else{ + if(o+m == n) + return; + for(i := 0; i < m; i++) + cmd(sm.t, sm.name + " entryconfigure " + string i + " -label {" + sm.labs[o+1+i] + "}"); + sm.o = o+1; + } + cmd(sm.t, "update"); +} + +cname(s: string): string +{ + return "sm_" + s + "_sm"; +} + +cmd(top: ref Tk->Toplevel, s: string): string +{ + e := tk->cmd(top, s); + if (e != nil && e[0] == '!') + sys->fprint(sys->fildes(2), "Smenu: tk error on '%s': %s\n", s, e); + return e; +} diff --git a/appl/wm/smenu.m b/appl/wm/smenu.m new file mode 100644 index 00000000..6002bde0 --- /dev/null +++ b/appl/wm/smenu.m @@ -0,0 +1,18 @@ +Smenu: module +{ + PATH: con "/dis/wm/smenu.dis"; + + Scrollmenu: adt{ + # private data + m, n, o: int; + timer: int; + name: string; + labs: array of string; + c: chan of string; + t: ref Tk->Toplevel; + + new: fn(t: ref Tk->Toplevel, name: string, labs: array of string, entries: int, origin: int): ref Scrollmenu; + post: fn(m: self ref Scrollmenu, x: int, y: int, resc: chan of string, prefix: string); + destroy: fn(m: self ref Scrollmenu); + }; +};
\ No newline at end of file diff --git a/appl/wm/snake.b b/appl/wm/snake.b new file mode 100644 index 00000000..a3c8c6a2 --- /dev/null +++ b/appl/wm/snake.b @@ -0,0 +1,373 @@ +implement Snake; +include "sys.m"; + sys: Sys; +include "draw.m"; + draw: Draw; + Display, Point, Screen, Image, Rect: import draw; +include "tk.m"; + tk: Tk; +include "tkclient.m"; + tkclient: Tkclient; +include "keyboard.m"; +include "rand.m"; + rand: Rand; +include "scoretable.m"; + scoretable: Scoretable; + +Snake: module{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +Tick: adt{ + dt: int; +}; + +DX: con 30; +DY: con 30; +Size: int; + +EMPTY, SNAKE, FOOD, CRASH: con iota; +HIGHSCOREFILE: con "/lib/scores/snake"; + +board: array of array of int; +win: ref Tk->Toplevel; + +init(ctxt: ref Draw->Context, argv: list of string) +{ + sys = load Sys Sys->PATH; + if (ctxt == nil) { + sys->fprint(sys->fildes(2), "snake: no window context\n"); + raise "fail:bad context"; + } + draw = load Draw Draw->PATH; + tkclient = load Tkclient Tkclient->PATH; + if(tkclient == nil){ + sys->print("sys->fildes(2), couldn't load %s: %r\n", Tkclient->PATH); + raise "fail:bad module"; + } + tkclient->init(); + tk = load Tk Tk->PATH; + rand = load Rand Rand->PATH; + if(rand == nil){ + sys->fprint(sys->fildes(2), "snake: cannot load %s: %r\n", Rand->PATH); + raise "fail:bad module"; + } + scoretable = load Scoretable Scoretable->PATH; + if (scoretable != nil) { + (ok, err) := scoretable->init(-1, readfile("/dev/user"), "snake", HIGHSCOREFILE); + if (ok == -1) { + sys->fprint(sys->fildes(2), "snake: cannot init scoretable: %s\n", err); + scoretable = nil; + } + } + + sys->pctl(Sys->NEWPGRP, nil); + ctlchan: chan of string; + (win, ctlchan) = tkclient->toplevel(ctxt, nil, "Snake", Tkclient->Hide); + + tk->namechan(win, kch := chan of string, "kch"); + + cmd(win, "canvas .c -bd 2 -relief ridge"); + cmd(win, "label .scoret -text Score:"); + cmd(win, "label .score -text 0"); + cmd(win, "frame .f"); + if (scoretable != nil) { + cmd(win, "label .hight -text High:"); + cmd(win, "label .high -text 0"); + cmd(win, "pack .hight .high -in .f -side left"); + } + cmd(win, "pack .score .scoret -in .f -side right"); + cmd(win, "pack .f -side top -fill x"); + cmd(win, "pack .c"); + cmd(win, "bind .c <Key> {send kch %s}"); + cmd(win, "bind . <ButtonRelease-1> {focus .c}"); + cmd(win, "bind .Wm_t <ButtonRelease-1> +{focus .c}"); + cmd(win, "focus .c"); + + Size = int cmd(win, ".c cget -actheight") / DY; + cmd(win, ".c configure -width " + string (Size * DX) + " -height " + string (Size * DY)); + + tkclient->onscreen(win, nil); + tkclient->startinput(win, "kbd"::"ptr"::nil); + + spawn winctl(ctlchan); + if (len argv > 1) + game(kch, hd tl argv); + + for(;;){ + game(kch, nil); + cmd(win, ".c delete all"); + } +} + +winctl(ctlchan: chan of string) +{ + for(;;) alt { + s := <-win.ctxt.kbd => + tk->keyboard(win, s); + s := <-win.ctxt.ptr => + tk->pointer(win, *s); + s := <-win.ctxt.ctl or + s = <-win.wreq or + s = <-ctlchan => + tkclient->wmctl(win, s); + } +} + +board2s(board: array of array of int): string +{ + s := string DX + "." + string DY + "."; + for (y := 0; y < DY; y++) + for (x := 0; x < DX; x++) + s[len s] = board[x][y] + '0'; + return s; +} + +replayproc(replay: string, kch: chan of string, tick: chan of int, nil: ref Tick) +{ + i := 0; + while(i < len replay){ + n := 0; + while(i < len replay && replay[i] >= '0' && replay[i] <= '9') { + n = n*10 + replay[i] - '0'; + i++; + } + for (t := 0; t < n; t++) { + tick <-= 1; + sys->sleep(0); + } + if (i == len replay) + break; + kch <-= string replay[i]; + i++; + } + tick <-= 1; + tick <-= 0; +} + +game(realkch: chan of string, replay: string) +{ + scores := scoretable->scores(); + if (scores != nil) + cmd(win, ".high configure -text " + string (hd scores).score); + cmd(win, ".score configure -text {0}"); + board = array[DX] of { * => array[DY] of{* => EMPTY}}; + + seed := rand->rand(16r7fffffff); + if (replay != nil) { + seed = int replay; + for (i := 0; i < len replay; i++) + if (replay[i] == '.') + break; + if (i<len replay) + replay = replay[i+1:]; + } + rand->init(seed); + p := Point(DX/2, DY/2); + dir := Point(1, 0); + lkey := 'r'; + snake := array[5] of Point; + for(i := 0; i < len snake; i++){ + snake[i] = p.add(dir.mul(i)); + make(snake[i]); + } + placefood(); + p = p.add(dir.mul(i)); + ticki := ref Tick(100); + realtick := chan of int; + + userkch: chan of string; + if(replay != nil) { + (userkch, realkch) = (realkch, chan of string); + spawn replayproc(replay, realkch, realtick, ticki); + } else { + userkch = chan of string; + spawn ticker(realtick, ticki); + } + cmd(win, "update"); + + score := 0; + leaveit := 0; + paused := 0; + + log := ""; + nticks := 0; + odir := dir; + + dummykch := chan of string; + kch := realkch; + + dummytick := chan of int; + tick := realtick; + for(;;){ + alt{ + c := <-kch => + if(paused){ + paused = 0; + tick = realtick; + } + kch = dummykch; + ndir := dir; + case int c{ + Keyboard->Up => + ndir = (0, -1); + Keyboard->Down => + ndir = (0, 1); + Keyboard->Left => + ndir = (-1, 0); + Keyboard->Right => + ndir = (1, 0); + 'q' => + tkclient->wmctl(win, "exit"); + 'p' => + paused = 1; + tick = dummytick; + kch = realkch; + } + if (!ndir.eq(dir) && !ndir.eq(dir.mul(-1))) { # don't allow 180° turn. + lkey = int c; + dir = ndir; + } + <-tick => + if(!odir.eq(dir)) { + log += string nticks; + log[len log] = lkey; + nticks = 0; + odir = dir; + } + nticks++; + if(leaveit){ + ns := array[len snake + 1] of Point; + ns[0:] = snake; + snake = ns; + leaveit = 0; + } else{ + destroy(snake[0]); + snake[0:] = snake[1:]; + } + np := snake[len snake - 2].add(dir); + np.x = (np.x + DX) % DX; + np.y = (np.y + DY) % DY; + snake[len snake - 1] = np; + wasfood := board[np.x][np.y] == FOOD; + if(!make(np)){ + cmd(win, ".c create oval " + r2s(square(np).inset(-5)) + " -fill yellow"); + cmd(win, "update"); + if (scoretable != nil && replay == nil) { + board[np.x][np.y] = CRASH; + log += string nticks; + sys->print("%d.%s\n", seed, log); + scoretable->setscore(score, string seed + "." + log + " " + board2s(board)); + } + ticki.dt = -1; + while(<-tick) + ; + sys->sleep(750); + absorb(realkch); + if(int <-realkch == 'q') + tkclient->wmctl(win, "exit"); + return; + } + if(wasfood){ + score++; + #if(score % 10 == 0){ + # if(ticki.dt > 0) + # ticki.dt -= 5; + #} + cmd(win, ".score configure -text " + string score); + leaveit = 1; + placefood(); + } + cmd(win, "update"); + kch = realkch; + } + } +} + +placefood() +{ + for(;;) + if(makefood((rand->rand(DX), rand->rand(DY)))) + return; +} + +make(p: Point): int +{ + # b := board[p.x][p.y]; + if(board[p.x][p.y] == SNAKE) + return 0; + cmd(win, ".c create rectangle " + r2s(square(p)) + + " -fill blue -outline {} -tags b." + string p.x + "." + string p.y); + board[p.x][p.y] = SNAKE; + return 1; +} + +makefood(p: Point): int +{ + b := board[p.x][p.y]; + if(b == SNAKE) + return 0; + cmd(win, ".c create oval " + r2s(square(p).inset(-2)) + + " -fill red -tags b." + string p.x + "." + string p.y); + board[p.x][p.y] = FOOD; + return 1; +} + +destroy(p: Point) +{ + board[p.x][p.y] = 0; + cmd(win, ".c delete b." + string p.x + "." + string p.y); +} + +square(p: Point): Rect +{ + p = p.mul(Size); + return (p, p.add((Size, Size))); +} + +ticker(tick: chan of int, ticki: ref Tick) +{ + while((dt := ticki.dt) >= 0){ + sys->sleep(dt); + tick <-= 1; + } + tick <-= 0; +} + +absorb(c: chan of string) +{ + for(;;){ + alt{ + <-c => + ; + * => + return; + } + } +} + +r2s(r: Rect): string +{ + return sys->sprint("%d %d %d %d", r.min.x, r.min.y, r.max.x, r.max.y); +} + +readfile(f: string): string +{ + fd := sys->open(f, Sys->OREAD); + if (fd == nil) + return nil; + buf := array[Sys->ATOMICIO] of byte; + n := sys->read(fd, buf, len buf); + if (n <= 0) + return nil; + return string buf[0:n]; +} + +cmd(win: ref Tk->Toplevel, s: string): string +{ + r := tk->cmd(win, s); + if(len r > 0 && r[0] == '!'){ + sys->print("error executing '%s': %s\n", s, r[1:]); + } + return r; +} diff --git a/appl/wm/stopwatch.b b/appl/wm/stopwatch.b new file mode 100644 index 00000000..7748bbe0 --- /dev/null +++ b/appl/wm/stopwatch.b @@ -0,0 +1,184 @@ +implement WmStopWatch; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + +include "tk.m"; + tk: Tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "daytime.m"; + daytime: Daytime; + + +WmStopWatch: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +t: ref Tk->Toplevel; +cmd: chan of string; + +tpid: int; + +hr, +min, +sec: int; + +sw_cfg := array[] of { + "frame .f", + "button .f.b1 -text Start -command {send cmd start}", + "button .f.b2 -text Stop -command {send cmd stop}", + "button .f.b3 -text Reset -command {send cmd reset}", + "pack .f.b1 .f.b2 .f.b3 -side left -fill x -expand 1", + + "frame .ft", + "label .ft.d -label {0:00:00}", + "pack .ft.d -expand 1", + + "frame .fs1", + "button .fs1.s -text Time1 -command {send cmd s1}", + "label .fs1.l -label {0:00:00}", + "pack .fs1.s .fs1.l -side left -expand 1", + + "frame .fs2", + "button .fs2.s -text Time2 -command {send cmd s2}", + "label .fs2.l -label {0:00:00}", + "pack .fs2.s .fs2.l -side left -expand 1", + + "frame .fs3", + "button .fs3.s -text Time3 -command {send cmd s3}", + "label .fs3.l -label {0:00:00}", + "pack .fs3.s .fs3.l -side left -expand 1", + + "pack .Wm_t -fill x", + "pack .f .ft .fs1 .fs2 .fs3", + "pack propagate . 0", + "update", +}; + +init(ctxt: ref Draw->Context, nil: list of string) +{ + sys = load Sys Sys->PATH; + if (ctxt == nil) { + sys->fprint(sys->fildes(2), "stopwatch: no window context\n"); + raise "fail:bad context"; + } + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient= load Tkclient Tkclient->PATH; + daytime = load Daytime Daytime->PATH; + + if(draw==nil || tk==nil || tkclient==nil || daytime==nil){ + sys->fprint(sys->fildes(2), "stopwatch: couldn't load modules\n"); + return; + } + + tkclient->init(); + + menubut := chan of string; + (t, menubut) = tkclient->toplevel(ctxt, "-borderwidth 2 -relief raised", "StopWatch", Tkclient->Appl); + + hr = 0; + min = 0; + sec = 0; + + cmd = chan of string; + tk->namechan(t, cmd, "cmd"); + for (c:=0; c<len sw_cfg; c++) + tk->cmd(t, sw_cfg[c]); + tkclient->onscreen(t, nil); + tkclient->startinput(t, "kbd"::"ptr"::nil); + tpid = 0; + + # keep the timerloop in a separate thread, + # so that wm events don't hold up the ticker + # i.e., titlebar click&hold would otherwise + # 'pause' the timer since the tick would not + # be processed. + + pid := chan of int; + spawn timerloop(pid); + looppid := <- pid; + + for(;;) alt { + s := <-t.ctxt.kbd => + tk->keyboard(t, s); + s := <-t.ctxt.ptr => + tk->pointer(t, *s); + s := <-t.ctxt.ctl or + s = <-t.wreq => + tkclient->wmctl(t, s); + menu := <-menubut => + if(menu == "exit") { + if(tpid) + kill(tpid); + kill(looppid); + return; + } + tkclient->wmctl(t, menu); + } +} + +timerloop(pid: chan of int) +{ + pid <- = sys->pctl(0, nil); + + tick := chan of int; + s: string; + + for(;;) alt { + c := <-cmd => + if(c == "stop"){ + if(tpid != 0){ + kill(tpid); + tpid = 0; + } + } else if(c == "reset"){ + hr = min = sec = 0; + s = sys->sprint("%d:%2.2d:%2.2d", hr, min, sec); + tk->cmd(t, ".ft.d configure -label {"+s+"};update"); + } else if(c == "start"){ + if(tpid == 0){ + spawn timer(tick); + tpid = <- tick; + } + } else if(c == "s1" || c == "s2" || c == "s3"){ + s = sys->sprint("%d:%2.2d:%2.2d", hr, min, sec); + tk->cmd(t, ".f"+c+".l configure -label {"+s+"};update"); + } + <-tick => + sec++; + if(sec>=60){ + sec = 0; + min++; + if(min>=60){ + min = 0; + hr++; + } + } + s = sys->sprint("%d:%2.2d:%2.2d", hr, min, sec); + tk->cmd(t, ".ft.d configure -label {"+s+"};update"); + } +} + +timer(c: chan of int) +{ + pid := sys->pctl(0, nil); + for(;;) { + c <-= pid; + sys->sleep(1000); + } +} + +kill(pid: int) +{ + fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE); + if(fd != nil) + sys->fprint(fd, "kill"); +} diff --git a/appl/wm/sweeper.b b/appl/wm/sweeper.b new file mode 100644 index 00000000..f721ee9a --- /dev/null +++ b/appl/wm/sweeper.b @@ -0,0 +1,330 @@ +implement Sweeper; + +# +# michael@vitanuova.com +# +# Copyright © 2000 Vita Nuova Limited. All rights reserved. +# Copyright © 2001 Vita Nuova Holdings Limited. All rights reserved. +# + +include "sys.m"; + sys: Sys; +include "draw.m"; + draw: Draw; + Point, Rect, Image, Font, Context, Screen, Display: import draw; +include "tk.m"; + tk: Tk; + Toplevel: import tk; +include "tkclient.m"; + tkclient: Tkclient; +include "daytime.m"; + daytime: Daytime; +include "rand.m"; + rand: Rand; + +stderr: ref Sys->FD; + +Sweeper: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +mainwin: ref Toplevel; +score: int; +mines: int; + +WIDTH: con 220; +HEIGHT: con 220; + +EASY: con 20; +SZB: con 10; +SZI: con SZB+2; # internal board is 2 larger than visible board + +Cell: adt { + mine, state: int; +}; + +board: array of array of Cell; + +UNSELECTED, SELECTED, MARKED: con (1<<iota); + + +init(ctxt: ref Draw->Context, nil: list of string) +{ + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + daytime = load Daytime Daytime->PATH; + rand = load Rand Rand->PATH; + + stderr = sys->fildes(2); + rand->init(daytime->now()); + daytime = nil; + + tkclient->init(); + if(ctxt == nil) + ctxt = tkclient->makedrawcontext(); + + (win, wmcmd) := tkclient->toplevel(ctxt, "", "Mine Sweeper", Tkclient->Hide); + mainwin = win; + sys->pctl(Sys->NEWPGRP, nil); + cmdch := chan of string; + tk->namechan(win, cmdch, "cmd"); + display_board(); + pid := -1; + finished := 0; + init_board(); + tkclient->onscreen(win, nil); + tkclient->startinput(win, "kbd"::"ptr"::nil); + for (;;) { + alt { + s := <-win.ctxt.kbd => + tk->keyboard(win, s); + s := <-win.ctxt.ptr => + tk->pointer(win, *s); + c := <-win.ctxt.ctl or + c = <-win.wreq or + c = <- wmcmd => # wm commands + case c { + "exit" => + if(pid != -1) + kill(pid); + exit; + * => + tkclient->wmctl(win, c); + } + c := <- cmdch => # tk commands + (nil, toks) := sys->tokenize(c, " "); + case hd toks { + "b" => + x := int hd tl toks; + y := int hd tl tl toks; + i := board_check(x, y); + case i { + -1 => + display_mines(); + display_lost(); + finished = 1; + 0 to 8 => + if (finished) + break; + score++; + board[x][y].state = SELECTED; + display_square(x, y, sys->sprint("%d", i), "olive"); + if (i == 0) { # check all adjacent zeros + display_zeros(x, y); + } + display_score(); + if (score+mines == SZB*SZB) { + display_mines(); + display_win(); + finished = 1; + } + * => + ; + } + cmd(mainwin, "update"); + "b3" => + x := int hd tl toks; + y := int hd tl tl toks; + mark_square(x, y); + cmd(mainwin, "update"); + "restart" => + init_board(); + display_score(); + reset_display(); + finished = 0; + * => + sys->fprint(stderr, "%s\n", c); + } + } + } +} + +display_board() { + i, j: int; + pack: string; + + for(i = 0; i < len win_config; i++) + cmd(mainwin, win_config[i]); + + for (i = 1; i <= SZB; i++) { + cmd(mainwin, sys->sprint("frame .f%d", i)); + pack = ""; + for (j = 1; j <= SZB; j++) { + pack += sys->sprint(" .f%d.b%dx%d", i, i, j); + cmd(mainwin, sys->sprint("button .f%d.b%dx%d -text { } -width 14 -command {send cmd b %d %d}", i, i, j, i, j)); + cmd(mainwin, sys->sprint("bind .f%d.b%dx%d <ButtonRelease-3> {send cmd b3 %d %d}", i, i, j, i, j)); + } + cmd(mainwin, sys->sprint("pack %s -side left", pack)); + cmd(mainwin, sys->sprint("pack .f%d -side top -fill x", i)); + } + + for (i = 0; i < len win_config2; i++) + cmd (mainwin, win_config2[i]); +} + +reset_display() +{ + for (i := 1; i <= SZB; i++) { + for (j := 1; j <= SZB; j++) { + s := sys->sprint(".f%d.b%dx%d configure -text { } -bg #dddddd -activebackground #eeeeee", i, i, j); + cmd(mainwin, s); + } + } + cmd(mainwin, "update"); +} + + +init_board() +{ + i, j: int; + + score = 0; + mines = 0; + board = array[SZI] of array of Cell; + for (i = 0; i < SZI; i++) + board[i] = array[SZI] of Cell; + + # initialize board + for (i = 0; i < SZI; i++) + for (j =0; j < SZI; j++) { + board[i][j].mine = 0; + board[i][j].state = UNSELECTED; + } + + # place mines + for (i = 0; i < EASY; i++) { + j = rand->rand(SZB*SZB); + if (board[(j/SZB)+1][(j%SZB)+1].mine == 0) { # rand could yield same result twice + board[(j/SZB)+1][(j%SZB)+1].mine = 1; + mines++; + } + } + cmd(mainwin, "update"); +} + +display_score() +{ + cmd(mainwin, ".f.l configure -text {Score: "+ sys->sprint("%d", score)+ "}"); +} + +display_win() +{ + cmd(mainwin, ".f.l configure -text {You have Won}"); +} + +display_lost() +{ + cmd(mainwin, ".f.l configure -text {You have Lost}"); +} + +display_mines() +{ + for (i := 1; i <= SZB; i++) + for (j := 1; j <= SZB; j++) + if (board[i][j].mine == 1) + display_square(i, j, "M", "red"); +} + +display_square(i, j: int, v: string, c: string) { + cmd(mainwin, sys->sprint(".f%d.b%dx%d configure -text {%s} -bg %s -activebackground %s", i, i, j, v, c, c)); + cmd(mainwin, "update"); +} + +mark_square(i, j: int) { + case board[i][j].state { + UNSELECTED => + board[i][j].state = MARKED; + display_square(i, j, "?", "orange"); + MARKED => + board[i][j].state = UNSELECTED; + display_square(i, j, " ", "#dddddd"); + } +} + +board_check(i, j: int) : int +{ + if (board[i][j].mine == 1) + return -1; + if (board[i][j].state&(SELECTED|MARKED)) + return -2; + c := 0; + for (x := i-1; x <= i+1; x++) + for (y := j-1; y <= j+1; y++) + if (board[x][y].mine == 1) + c++; + return c; +} + +display_zeros(i, j: int) +{ + for (x := i-1; x <= i+1; x++) { + for (y := j-1; y <= j+1; y++) { + if (x <1 || x>SZB || y<1 || y>SZB) + continue; + if (board_check(x, y) == 0) { + score++; + board[x][y].state = SELECTED; + display_square(x, y, "0", "olive"); + display_zeros(x, y); + } + } + } +} + +fatal(s: string) +{ + sys->fprint(stderr, "%s\n", s); + exit; +} + +sleep(t: int) +{ + sys->sleep(t); +} + +kill(pid: int): int +{ + fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE); + if(fd == nil) + return -1; + if(sys->write(fd, array of byte "kill", 4) != 4) + return -1; + return 0; +} + +cmd(top: ref Toplevel, s: string): string +{ + e := tk->cmd(top, s); + if (e != nil && e[0] == '!') + sys->fprint(stderr, "sweeper: tk error on '%s': %s\n", s, e); + return e; +} + +win_config := array[] of { + "frame .f -width 220 -height 220", + + "menubutton .f.sz -text Options -menu .f.sz.sm", + "menu .f.sz.sm", + ".f.sz.sm add command -label restart -command { send cmd restart }", + "pack .f.sz -side left", + + "label .f.l -text {Score: }", + "pack .f.l -side right", + + "frame .ft", + "label .ft.l -text { }", + "pack .ft.l -side left", + + "pack .f -side top -fill x", + "pack .ft -side top -fill x", + +}; + +win_config2 := array[] of { + + "pack propagate . 0", + "update", +};
\ No newline at end of file diff --git a/appl/wm/task.b b/appl/wm/task.b new file mode 100644 index 00000000..762d1262 --- /dev/null +++ b/appl/wm/task.b @@ -0,0 +1,240 @@ +implement WmTask; + +include "sys.m"; + sys: Sys; + Dir: import sys; + +include "draw.m"; + draw: Draw; + +include "tk.m"; + tk: Tk; + Toplevel: import tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "dialog.m"; + dialog: Dialog; + +Prog: adt +{ + pid: int; + pgrp: int; + size: int; + state: string; + mod: string; +}; + +WmTask: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +Wm: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +task_cfg := array[] of { + "frame .fl", + "scrollbar .fl.scroll -command {.fl.l yview}", + "listbox .fl.l -width 40w -yscrollcommand {.fl.scroll set}", + "frame .b", + "button .b.ref -text Refresh -command {send cmd r}", + "button .b.deb -text Debug -command {send cmd d}", + "button .b.files -text Files -command {send cmd f}", + "button .b.kill -text Kill -command {send cmd k}", + "button .b.killg -text {Kill Group} -command {send cmd kg}", + "pack .b.ref .b.deb .b.files .b.kill .b.killg -side left -padx 2 -pady 2", + "pack .b -fill x", + "pack .fl.scroll -side left -fill y", + "pack .fl.l -fill both -expand 1", + "pack .fl -fill both -expand 1", + "pack propagate . 0", +}; + +init(ctxt: ref Draw->Context, nil: list of string) +{ + sys = load Sys Sys->PATH; + if (ctxt == nil) { + sys->fprint(sys->fildes(2), "task: no window context\n"); + raise "fail:bad context"; + } + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient= load Tkclient Tkclient->PATH; + dialog = load Dialog Dialog->PATH; + + tkclient->init(); + dialog->init(); + + sysnam := sysname(); + + (t, wmctl) := tkclient->toplevel(ctxt, "", sysnam, Tkclient->Appl); + if(t == nil) + return; + + cmd := chan of string; + tk->namechan(t, cmd, "cmd"); + + for (c:=0; c<len task_cfg; c++) + tk->cmd(t, task_cfg[c]); + + readprog(t); + + tk->cmd(t, ".fl.l see end;update"); + tkclient->onscreen(t, nil); + tkclient->startinput(t, "kbd"::"ptr"::nil); + + for(;;) alt { + s := <-t.ctxt.kbd => + tk->keyboard(t, s); + s := <-t.ctxt.ptr => + tk->pointer(t, *s); + s := <-t.ctxt.ctl or + s = <-t.wreq => + tkclient->wmctl(t, s); + menu := <-wmctl => + case menu { + "exit" => + return; + "task" => + tkclient->wmctl(t, menu); + tk->cmd(t, ".fl.l delete 0 end"); + readprog(t); + tk->cmd(t, ".fl.l see end;update"); + * => + tkclient->wmctl(t, menu); + } + bcmd := <-cmd => + case bcmd { + "d" => + sel := tk->cmd(t, ".fl.l curselection"); + if(sel == "") + break; + pid := int tk->cmd(t, ".fl.l get "+sel); + stk := load Wm "/dis/wm/deb.dis"; + if(stk == nil) + break; + spawn stk->init(ctxt, "wm/deb" :: "-p "+string pid :: nil); + stk = nil; + "k" or "kg" => + sel := tk->cmd(t, ".fl.l curselection"); + if(sel == "") + break; + pid := int tk->cmd(t, ".fl.l get "+sel); + what := "opening ctl file"; + cfile := "/prog/"+string pid+"/ctl"; + cfd := sys->open(cfile, sys->OWRITE); + if(cfd != nil) { + if(bcmd == "kg"){ + if(sys->fprint(cfd, "killgrp") > 0){ + cfd = nil; + refresh(t); + break; + } + }else if(sys->fprint(cfd, "kill") > 0){ + tk->cmd(t, ".fl.l delete "+sel); + cfd = nil; + break; + } + cfd = nil; + what = "sending kill request"; + } + if(bcmd == "k" && sys->sprint("%r") == "file does not exist") { + refresh(t); + break; + } + dialog->prompt(ctxt, t.image, "error -fg red", "Kill", + "Error "+what+"\n"+ + "System: "+sys->sprint("%r"), + 0, "OK" :: nil); + "r" => + refresh(t); + "f" => + sel := tk->cmd(t, ".fl.l curselection"); + if(sel == "") + break; + pid := int tk->cmd(t, ".fl.l get "+sel); + fi := load Wm "/dis/wm/edit.dis"; + if(fi == nil) + break; + spawn fi->init(ctxt, + "edit" :: + "/prog/"+string pid+"/fd" :: nil); + fi = nil; + } + } +} + +refresh(t: ref Tk->Toplevel) +{ + tk->cmd(t, ".fl.l delete 0 end"); + readprog(t); + tk->cmd(t, ".fl.l see end;update"); +} + +mkprog(file: string): ref Prog +{ + fd := sys->open("/prog/"+file+"/status", sys->OREAD); + if(fd == nil) + return nil; + + buf := array[256] of byte; + n := sys->read(fd, buf, len buf); + if(n <= 0) + return nil; + + (v, l) := sys->tokenize(string buf[0:n], " "); + if(v < 6) + return nil; + + prg := ref Prog; + prg.pid = int hd l; + l = tl l; + prg.pgrp = int hd l; + l = tl l; + l = tl l; + # eat blanks in user name + while(len l > 3) + l = tl l; + prg.state = hd l; + l = tl l; + prg.size = int hd l; + l = tl l; + prg.mod = hd l; + + return prg; +} + +readprog(t: ref Toplevel) +{ + fd := sys->open("/prog", sys->OREAD); + if(fd == nil) + return; + for(;;) { + (n, d) := sys->dirread(fd); + if(n <= 0) + break; + for(i := 0; i < n; i++) { + p := mkprog(d[i].name); + if(p != nil){ + l := sys->sprint("%4d %4d %3dK %-7s %s", p.pid, p.pgrp, p.size, p.state, p.mod); + tk->cmd(t, ".fl.l insert end '"+l); + } + } + } +} + +sysname(): string +{ + fd := sys->open("#c/sysname", sys->OREAD); + if(fd == nil) + return "Anon"; + buf := array[128] of byte; + n := sys->read(fd, buf, len buf); + if(n < 0) + return "Anon"; + return string buf[0:n]; +} diff --git a/appl/wm/telnet.b b/appl/wm/telnet.b new file mode 100644 index 00000000..077dd6aa --- /dev/null +++ b/appl/wm/telnet.b @@ -0,0 +1,820 @@ +implement WmTelnet; + +include "sys.m"; + sys: Sys; + Connection: import sys; + +include "draw.m"; + draw: Draw; + Context: import draw; + +include "tk.m"; + tk: Tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "dialog.m"; + dialog: Dialog; + +WmTelnet: module +{ + init: fn(ctxt: ref Draw->Context, args: list of string); +}; + +Iob: adt +{ + fd: ref Sys->FD; + t: ref Tk->Toplevel; + out: cyclic ref Iob; + buf: array of byte; + ptr: int; + nbyte: int; +}; + +BS: con 8; # ^h backspace character +BSW: con 23; # ^w bacspace word +BSL: con 21; # ^u backspace line +EOT: con 4; # ^d end of file +ESC: con 27; # hold mode + +HIWAT: con 2000; # maximum number of lines in transcript +LOWAT: con 1500; # amount to reduce to after high water + +Name: con "Telnet"; +ctxt: ref Context; +cmds: chan of string; +net: Connection; +stderr: ref Sys->FD; +mcrlf: int; +netinp: ref Iob; + +# control characters +Se: con 240; # end subnegotiation +NOP: con 241; +Mark: con 242; # data mark +Break: con 243; +Interrupt: con 244; +Abort: con 245; # TENEX ^O +AreYouThere: con 246; +Erasechar: con 247; # erase last character +Eraseline: con 248; # erase line +GoAhead: con 249; # half duplex clear to send +Sb: con 250; # start subnegotiation +Will: con 251; +Wont: con 252; +Do: con 253; +Dont: con 254; +Iac: con 255; + +# options +Binary, Echo, SGA, Stat, Timing, +Det, Term, EOR, Uid, Outmark, +Ttyloc, M3270, Padx3, Window, Speed, +Flow, Line, Xloc, Extend: con iota; + +Opt: adt +{ + name: string; + code: int; + noway: int; + remote: int; # remote value + local: int; # local value +}; + +opt := array[] of +{ + Binary => Opt("binary", 0, 0, 0, 0), + Echo => Opt("echo", 1, 0, 0, 0), + SGA => Opt("suppress Go Ahead", 3, 0, 0, 0), + Stat => Opt("status", 5, 1, 0, 0), + Timing => Opt("timing", 6, 1, 0, 0), + Det => Opt("det", 20, 1, 0, 0), + Term => Opt("terminal", 24, 0, 0, 0), + EOR => Opt("end of record", 25, 1, 0, 0), + Uid => Opt("uid", 26, 1, 0, 0), + Outmark => Opt("outmark", 27, 1, 0, 0), + Ttyloc => Opt("ttyloc", 28, 1, 0, 0), + M3270 => Opt("3270 mode", 29, 1, 0, 0), + Padx3 => Opt("pad x.3", 30, 1, 0, 0), + Window => Opt("window size", 31, 1, 0, 0), + Speed => Opt("speed", 32, 1, 0, 0), + Flow => Opt("flow control", 33, 1, 0, 0), + Line => Opt("line mode", 34, 0, 0, 0), + Xloc => Opt("X display loc", 35, 1, 0, 0), + Extend => Opt("Extended", 255, 1, 0, 0), +}; + +shwin_cfg := array[] of { + "menu .m", + ".m add command -text Cut -command {send edit cut}", + ".m add command -text Paste -command {send edit paste}", + ".m add command -text Snarf -command {send edit snarf}", + ".m add command -text Send -command {send edit send}", + "frame .ft", + "scrollbar .ft.scroll -command {.ft.t yview}", + "text .ft.t -width 70w -height 25h -yscrollcommand {.ft.scroll set}", + "frame .mb", + "menubutton .mb.c -text Connect -menu .mbc", + "menubutton .mb.t -text Terminal -menu .mbt", + "menu .mbc", + ".mbc add command -text {Remote System} -command {send cmd con}", + ".mbc add command -text {Disconnect} -state disabled -command {send cmd dis}", + ".mbc add command -text {Exit} -command {send cmd exit}", + ".mbc add separator", + "menu .mbt", + ".mbt add checkbutton -text {Line Mode} -command {send cmd line}", + ".mbt add checkbutton -text {Map CR to LF} -command {send cmd crlf}", + "pack .mb.c .mb.t -side left", + "pack .ft.scroll -side left -fill y", + "pack .ft.t -fill both -expand 1", + "pack .mb -fill x", + "pack .ft -fill both -expand 1", + "pack propagate . 0", + "focus .ft.t", + "bind .ft.t <Key> {send keys {%A}}", + "bind .ft.t <Control-d> {send keys {%A}}", + "bind .ft.t <Control-h> {send keys {%A}}", + "bind .ft.t <ButtonPress-3> {send but3 %X %Y}", + "bind .ft.t <ButtonRelease-3> {}", + "bind .ft.t <DoubleButton-3> {}", + "bind .ft.t <Double-ButtonRelease-3> {}", + "bind .ft.t <ButtonPress-2> {}", + "bind .ft.t <ButtonRelease-2> {}", + "update" +}; + +connect_cfg := array[] of { + "frame .fl", + "label .fl.h -text Host", + "label .fl.p -text Port", + "pack .fl.h .fl.p", + "frame .el", + "entry .el.h", + "entry .el.p", + ".el.p insert end 'telnet", + "pack .el.h .el.p", + "pack .Wm_t -fill x", + "pack .fl .el -side left", + "focus .el.h", + "bind .el.h <Key-\n> {send cmd ok}", + "bind .el.p <key-\n> {send cmd ok}", + "update" +}; + +connected_cfg := array[] of { + "focus .ft.t", + ".mbc entryconfigure 0 -state disabled", + ".mbc entryconfigure 1 -state normal" +}; + +menuindex := "0"; +holding := 0; + +init(C: ref Context, argv: list of string) +{ + sys = load Sys Sys->PATH; + if (C == nil) { + sys->fprint(sys->fildes(2), "telnet: no window context\n"); + raise "fail:bad context"; + } + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + dialog = load Dialog Dialog->PATH; + + ctxt = C; + tkclient->init(); + dialog->init(); + + sys->pctl(Sys->NEWPGRP, nil); + stderr = sys->fildes(2); + + tkargs := ""; + argv = tl argv; + if(argv != nil) { + tkargs = hd argv; + argv = tl argv; + } + (t, titlectl) := tkclient->toplevel(ctxt, tkargs, Name, Tkclient->Appl); + + edit := chan of string; + tk->namechan(t, edit, "edit"); + for (cc:=0; cc<len shwin_cfg; cc++) + tk->cmd(t, shwin_cfg[cc]); + + keys := chan of string; + tk->namechan(t, keys, "keys"); + + but3 := chan of string; + tk->namechan(t, but3, "but3"); + + cmds = chan of string; + tk->namechan(t, cmds, "cmd"); + + # outpoint is place in text to insert characters printed by programs + tk->cmd(t, ".ft.t mark set outpoint end; .ft.t mark gravity outpoint left"); + tkclient->onscreen(t, nil); + tkclient->startinput(t, "kbd"::"ptr"::nil); + + for(;;) alt { + s := <-t.ctxt.kbd => + tk->keyboard(t, s); + s := <-t.ctxt.ptr => + tk->pointer(t, *s); + s := <-t.ctxt.ctl or + s = <-t.wreq or + s = <-titlectl => + if(s == "exit") { + kill(); + return; + } + tkclient->wmctl(t, s); + ecmd := <-edit => + editor(t, ecmd); + sendinput(t); + + c := <-keys => + if(opt[Echo].local == 0) { + sys->fprint(net.dfd, "%c", c[1]); + break; + } + cut(t, 1); + char := c[1]; + if(char == '\\') + char = c[2]; + update := ";.ft.t see insert;update"; + case char{ + * => + tk->cmd(t, ".ft.t insert insert "+c+update); + '\n' or EOT => + tk->cmd(t, ".ft.t insert insert "+c+update); + sendinput(t); + BS => + if(!insat(t, "outpoint")) + tk->cmd(t, ".ft.t delete insert-1chars"+update); + ESC => + holding ^= 1; + color := "blue"; + if(!holding){ + color = "black"; + tkclient->settitle(t, Name); + sendinput(t); + }else + tkclient->settitle(t, Name+" (holding)"); + tk->cmd(t, ".ft.t configure -foreground "+color+update); + BSL => + if(insininput(t)) + tk->cmd(t, ".ft.t delete outpoint insert"+update); + else + tk->cmd(t, ".ft.t delete {insert linestart} insert"+update); + BSW => + if(insat(t, "outpoint")) + break; + a0 := isalnum(tk->cmd(t, ".ft.t get insert-1chars")); + a1 := isalnum(tk->cmd(t, ".ft.t get insert")); + start: string; + if(a0 && a1) # middle of word + start = "{insert wordstart}"; + else if(a0) # end of word + start = "{insert-1chars wordstart}"; + else{ # beginning or not in word; must search + s: string; + for(n:=1; ;){ + s = tk->cmd(t, ".ft.t get insert-"+ string n +"chars"); + if(s=="" || s=="\n"){ + start = "insert-"+ string n+"chars"; + break; + } + n++; + if(isalnum(s)){ + start = "{insert-"+ string n+"chars wordstart}"; + break; + } + } + + } + # don't ^w across outpoint + if(tk->cmd(t, ".ft.t compare insert >= outpoint") == "1" + && tk->cmd(t, ".ft.t compare "+start+" < outpoint") == "1") + start = "outpoint"; + tk->cmd(t, ".ft.t delete " + start + " insert"+update); + } + + c := <-but3 => + (nil, l) := sys->tokenize(c, " "); + x := int hd l - 50; + y := int hd tl l - int tk->cmd(t, ".m yposition "+menuindex) - 10; + tk->cmd(t, ".m activate "+menuindex+"; .m post "+string x+" "+string y+ + "; grab set .m; update"); + + c := <-cmds => + case c { + "con" => + tk->cmd(t, ".mb.c configure -state disabled"); + connect(t); + tk->cmd(t, ".mb.c configure -state normal; update"); + "dis" => + tkclient->settitle(t, "Telnet"); + tk->cmd(t, ".mbc entryconfigure 0 -state normal"); + tk->cmd(t, ".mbc entryconfigure 1 -state disabled"); + net.cfd = nil; + net.dfd = nil; + kill(); + "exit" => + kill(); + return; + "crlf" => + mcrlf = !mcrlf; + break; + "line" => + if(opt[Line].local == 0) + send3(netinp, Iac, Will, opt[Line].code); + else + send3(netinp, Iac, Wont, opt[Line].code); + } + } +} + +insat(t: ref Tk->Toplevel, mark: string): int +{ + return tk->cmd(t, ".ft.t compare insert == "+mark) == "1"; +} + +insininput(t: ref Tk->Toplevel): int +{ + if(tk->cmd(t, ".ft.t compare insert >= outpoint") != "1") + return 0; + return tk->cmd(t, ".ft.t compare {insert linestart} == {outpoint linestart}") == "1"; +} + +isalnum(s: string): int +{ + if(s == "") + return 0; + c := s[0]; + if('a' <= c && c <= 'z') + return 1; + if('A' <= c && c <= 'Z') + return 1; + if('0' <= c && c <= '9') + return 1; + if(c == '_') + return 1; + if(c > 16rA0) + return 1; + return 0; +} + +editor(t: ref Tk->Toplevel, ecmd: string) +{ + s, snarf: string; + + case ecmd { + "cut" => + menuindex = "0"; + cut(t, 1); + + "paste" => + menuindex = "1"; + snarf = tkclient->snarfget(); + if(snarf == "") + break; + cut(t, 0); + tk->cmd(t, ".ft.t insert insert '"+snarf); + sendinput(t); + + "snarf" => + menuindex = "2"; + if(tk->cmd(t, ".ft.t tag ranges sel") == "") + break; + snarf = tk->cmd(t, ".ft.t get sel.first sel.last"); + tkclient->snarfput(snarf); + + "send" => + menuindex = "3"; + if(tk->cmd(t, ".ft.t tag ranges sel") != ""){ + snarf = tk->cmd(t, ".ft.t get sel.first sel.last"); + tkclient->snarfput(snarf); + }else + snarf = tkclient->snarfget(); + if(snarf != "") + s = snarf; + else + return; + if(s[len s-1] != '\n' && s[len s-1] != EOT) + s[len s] = '\n'; + tk->cmd(t, ".ft.t see end; .ft.t insert end '"+s); + tk->cmd(t, ".ft.t mark set insert end"); + tk->cmd(t, ".ft.t tag remove sel sel.first sel.last"); + } + tk->cmd(t, "update"); +} + +cut(t: ref Tk->Toplevel, snarfit: int) +{ + if(tk->cmd(t, ".ft.t tag ranges sel") == "") + return; + if(snarfit) + tkclient->snarfput(tk->cmd(t, ".ft.t get sel.first sel.last")); + tk->cmd(t, ".ft.t delete sel.first sel.last"); +} + +sendinput(t: ref Tk->Toplevel) +{ + if(holding) + return; + input := tk->cmd(t, ".ft.t get outpoint end"); + slen := len input; + if(slen == 0) + return; + + for(i := 0; i < slen; i++) + if(input[i] == '\n' || input[i] == EOT) + break; + + if(i >= slen) + return; + + advance := string (i+1); + if(input[i] == EOT) + input = input[0:i]; + else + input = input[0:i+1]; + + sys->fprint(net.dfd, "%s", input); + tk->cmd(t, ".ft.t mark set outpoint outpoint+" + advance + "chars"); +} + +kill() +{ + path := sys->sprint("#p/%d/ctl", sys->pctl(0, nil)); + fd := sys->open(path, sys->OWRITE); + if(fd != nil) + sys->fprint(fd, "killgrp"); +} + +connect(t: ref Tk->Toplevel) +{ + (b, titlectl) := tkclient->toplevel(ctxt, nil, "Connect", 0); + for (c:=0; c<len connect_cfg; c++) + tk->cmd(b, connect_cfg[c]); + + cmd := chan of string; + tk->namechan(b, cmd, "cmd"); + tkclient->onscreen(b, nil); + tkclient->startinput(b, "kbd"::"ptr"::nil); + +loop: for(;;) alt { + s := <-b.ctxt.kbd => + tk->keyboard(b, s); + s := <-b.ctxt.ptr => + tk->pointer(b, *s); + s := <-b.ctxt.ctl or + s = <-b.wreq or + s = <-titlectl => + if(s == "exit") + return; + tkclient->wmctl(b, s); + <-cmd => + break loop; + } + + addr := sys->sprint("tcp!%s!%s", + tk->cmd(b, ".el.h get"), + tk->cmd(b, ".el.p get")); + + tkclient->settitle(b, "Dialing"); + tk->cmd(b, "update"); + + ok: int; + (ok, net) = sys->dial(addr, nil); + if(ok < 0) { + dialog->prompt(ctxt, b.image, "error -fg red", + "Connect", "Connection to host failed\n"+sys->sprint("%r"), + 0, "Stop connect" :: nil); + return; + } + + tkclient->settitle(t, "Telnet - "+addr); + for (c=0; c<len connected_cfg; c++) + tk->cmd(b, connected_cfg[c]); + + spawn fromnet(t); +} + +flush(t: ref Tk->Toplevel, data: array of byte) +{ + cdata := string data; + ncdata := string len cdata + "chars;"; + moveins := insat(t, "outpoint"); + tk->cmd(t, ".ft.t insert outpoint '"+ cdata); + s := ".ft.t mark set outpoint outpoint+" + ncdata; + s += ".ft.t see outpoint;"; + if(moveins) + s += ".ft.t mark set insert insert+" + ncdata; + s += "update"; + tk->cmd(t, s); + nlines := int tk->cmd(t, ".ft.t index end"); + if(nlines > HIWAT){ + s = ".ft.t delete 1.0 "+ string (nlines-LOWAT) +".0;update"; + tk->cmd(t, s); + } +} + +iobnew(fd: ref Sys->FD, t: ref Tk->Toplevel, out: ref Iob, size: int): ref Iob +{ + iob := ref Iob; + iob.fd = fd; + iob.t = t; + iob.out = out; + iob.buf = array[size] of byte; + iob.nbyte = 0; + iob.ptr = 0; + return iob; +} + +iobget(iob: ref Iob): int +{ + if(iob.nbyte == 0) { + if(iob.out != nil) + iobflush(iob.out); + iob.nbyte = sys->read(iob.fd, iob.buf, len iob.buf); + if(iob.nbyte <= 0) + return iob.nbyte; + iob.ptr = 0; + } + iob.nbyte--; + return int iob.buf[iob.ptr++]; +} + +iobput(iob: ref Iob, c: int) +{ + iob.buf[iob.ptr++] = byte c; + if(iob.ptr == len iob.buf) + iobflush(iob); +} + +iobflush(iob: ref Iob) +{ + if(iob.fd == nil) { + flush(iob.t, iob.buf[0:iob.ptr]); + iob.ptr = 0; + } +} + +fromnet(t: ref Tk->Toplevel) +{ + conout := iobnew(nil, t, nil, 2048); + netinp = iobnew(net.dfd, nil, conout, 2048); + + crnls := 0; + freenl := 0; + +loop: for(;;) { + c := iobget(netinp); + case c { + -1 => + cmds <-= "dis"; + return; + '\n' => # skip nl after string of cr's */ + if(!opt[Binary].local && !mcrlf) { + crnls++; + if(freenl == 0) + break; + freenl = 0; + continue loop; + } + '\r' => + if(!opt[Binary].local && !mcrlf) { + if(crnls++ == 0){ + freenl = 1; + c = '\n'; + break; + } + continue loop; + } + Iac => + c = iobget(netinp); + if(c == Iac) + break; + iobflush(conout); + if(control(netinp, c) < 0) + return; + + continue loop; + } + iobput(conout, c); + } +} + +control(bp: ref Iob, c: int): int +{ + case c { + AreYouThere => + sys->fprint(net.dfd, "Inferno telnet V1.0\r\n"); + Sb => + return sub(bp); + Will => + return will(bp); + Wont => + return wont(bp); + Do => + return doit(bp); + Dont => + return dont(bp); + Se => + sys->fprint(stderr, "telnet: SE without an SB\n"); + -1 => + return -1; + * => + break; + } + return 0; +} + +sub(bp: ref Iob): int +{ + subneg: string; + i := 0; + for(;;){ + c := iobget(bp); + if(c == Iac) { + c = iobget(bp); + if(c == Se) + break; + subneg[i++] = Iac; + } + if(c < 0) + return -1; + subneg[i++] = c; + } + if(i == 0) + return 0; + + sys->fprint(stderr, "sub %d %d n = %d\n", subneg[0], subneg[1], i); + + for(i = 0; i < len opt; i++) + if(opt[i].code == subneg[0]) + break; + + if(i >= len opt) + return 0; + + case i { + Term => + sbsend(opt[Term].code, array of byte "dumb"); + } + + return 0; +} + +sbsend(code: int, data: array of byte): int +{ + buf := array[4+len data+2] of byte; + o := 4+len data; + + buf[0] = byte Iac; + buf[1] = byte Sb; + buf[2] = byte code; + buf[3] = byte 0; + buf[4:] = data; + buf[o] = byte Iac; + o++; + buf[o] = byte Se; + + return sys->write(net.dfd, buf, len buf); +} + +will(bp: ref Iob): int +{ + c := iobget(bp); + if(c < 0) + return -1; + + sys->fprint(stderr, "will %d\n", c); + + for(i := 0; i < len opt; i++) + if(opt[i].code == c) + break; + + if(i >= len opt) { + send3(bp, Iac, Dont, c); + return 0; + } + + rv := 0; + if(opt[i].noway) + send3(bp, Iac, Dont, c); + else + if(opt[i].remote == 0) + rv |= send3(bp, Iac, Do, c); + + if(opt[i].remote == 0) + rv |= change(bp, i, Will); + opt[i].remote = 1; + return rv; +} + +wont(bp: ref Iob): int +{ + c := iobget(bp); + if(c < 0) + return -1; + + sys->fprint(stderr, "wont %d\n", c); + + for(i := 0; i < len opt; i++) + if(opt[i].code == c) + break; + + if(i >= len opt) + return 0; + + rv := 0; + if(opt[i].remote) { + rv |= change(bp, i, Wont); + rv |= send3(bp, Iac, Dont, c); + } + opt[i].remote = 0; + return rv; +} + +doit(bp: ref Iob): int +{ + c := iobget(bp); + if(c < 0) + return -1; + + sys->fprint(stderr, "do %d\n", c); + + for(i := 0; i < len opt; i++) + if(opt[i].code == c) + break; + + if(i >= len opt || opt[i].noway) { + send3(bp, Iac, Wont, c); + return 0; + } + rv := 0; + if(opt[i].local == 0) { + rv |= change(bp, i, Do); + rv |= send3(bp, Iac, Will, c); + } + opt[i].local = 1; + return rv; +} + +dont(bp: ref Iob): int +{ + c := iobget(bp); + if(c < 0) + return -1; + + sys->fprint(stderr, "dont %d\n", c); + + for(i := 0; i < len opt; i++) + if(opt[i].code == c) + break; + + if(i >= len opt || opt[i].noway) + return 0; + + rv := 0; + if(opt[i].local){ + opt[i].local = 0; + rv |= change(bp, i, Dont); + rv |= send3(bp, Iac, Wont, c); + } + opt[i].local = 0; + return rv; +} + +change(nil: ref Iob, nil: int, nil: int): int +{ + return 0; +} + +send3(bp: ref Iob, c0: int, c1: int, c2: int): int +{ + buf := array[3] of byte; + + buf[0] = byte c0; + buf[1] = byte c1; + buf[2] = byte c2; + + t: string; + case c0 { + Will => t = "Will"; + Wont => t = "Wont"; + Do => t = "Do"; + Dont => t = "Dont"; + } + if(t != nil) + sys->fprint(stderr, "r %s %d\n", t, c1); + + r := sys->write(bp.fd, buf, 3); + if(r != 3) + return -1; + return 0; +} diff --git a/appl/wm/tetris.b b/appl/wm/tetris.b new file mode 100644 index 00000000..ddde1a17 --- /dev/null +++ b/appl/wm/tetris.b @@ -0,0 +1,806 @@ +# Copyright © 1999 Roger Peppe. All rights reserved. +implement Tetris; + +include "sys.m"; + sys: Sys; + stderr: ref Sys->FD; +include "draw.m"; + draw: Draw; + Point, Rect: import draw; +include "tk.m"; + tk: Tk; +include "tkclient.m"; + tkclient: Tkclient; +include "rand.m"; + rand: Rand; +include "scoretable.m"; + scoretab: Scoretable; +include "arg.m"; +include "keyboard.m"; + Up, Down, Right, Left: import Keyboard; + +include "keyring.m"; +include "security.m"; # for random seed + +Tetris: module { + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +SCORETABLE: con "/lib/scores/tetris"; +LOCKPORT: con 18343; + +# number of pieces across and down board. +BOARDWIDTH: con 10; +BOARDHEIGHT: con 22; + +awaitingscore := 1; + +Row: adt { + tag: string; + delete: int; +}; + +Board: adt { + new: fn(top: ref Tk->Toplevel, w: string, + blocksize: int, maxsize: Point): ref Board; + makeblock: fn(bd: self ref Board, colour: string, p: Point): string; + moveblock: fn(bd: self ref Board, b: string, p: Point); + movecurr: fn(bd: self ref Board, delta: Point); + delrows: fn(bd: self ref Board, rows: list of int); + landedblock: fn(bd: self ref Board, b: string, p: Point); + setnextshape: fn(bd: self ref Board, colour: string, spec: array of Point); + setscore: fn(bd: self ref Board, score: int); + setlevel: fn(bd: self ref Board, level: int); + setnrows: fn(bd: self ref Board, level: int); + gameover: fn(bd: self ref Board); + update: fn(bd: self ref Board); + + state: array of array of byte; + w: string; + dx: int; + win: ref Tk->Toplevel; + rows: array of Row; + maxid: int; +}; + +Piece: adt { + shape: int; + rot: int; +}; + +Shape: adt { + coords: array of array of Point; + colour: string; + score: array of int; +}; + +Game: adt { + new: fn(bd: ref Board): ref Game; + move: fn(g: self ref Game, dx: int); + rotate: fn(g: self ref Game, clockwise: int); + tick: fn(g: self ref Game): int; + drop: fn(g: self ref Game); + + bd: ref Board; + level: int; + delay: int; + score: int; + nrows: int; + pieceids: array of string; + pos: Point; + next, + curr: Piece; +}; + +badmod(path: string) +{ + sys->fprint(stderr, "tetris: cannot load %s: %r\n", path); + raise "fail: bad module"; +} + +usage() +{ + sys->fprint(stderr, "usage: tetris [-b blocksize]\n"); + raise "fail:usage"; +} + +init(ctxt: ref Draw->Context, argv: list of string) +{ + sys = load Sys Sys->PATH; + stderr = sys->fildes(2); + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + if (tk == nil) + badmod(Tk->PATH); + tkclient = load Tkclient Tkclient->PATH; + if (tkclient == nil) + badmod(Tkclient->PATH); + tkclient->init(); + rand = load Rand Rand->PATH; + if (rand == nil) + badmod(Rand->PATH); + arg := load Arg Arg->PATH; + if (arg == nil) + badmod(Arg->PATH); + if (ctxt == nil) + ctxt = tkclient->makedrawcontext(); + blocksize := 17; # preferred block size + arg->init(argv); + while ((opt := arg->opt()) != 0) { + case opt { + 'b' => + if ((b := arg->arg()) == nil || int b <= 0) + usage(); + blocksize = int b; + * => + usage(); + } + } + if (arg->argv() != nil) + usage(); + + sys->pctl(Sys->NEWPGRP|Sys->FORKNS, nil); + scoretab = load Scoretable Scoretable->PATH; + scorech := chan of int; + spawn scoresrvwait(scorech); + (win, winctl) := tkclient->toplevel(ctxt, "", "Tetris",Tkclient->Hide); + seedrand(); + fromuser := chan of string; + tk->namechan(win, fromuser, "user"); + cmd(win, "bind . <Key> {send user k %s}"); + cmd(win, "bind . <ButtonRelease-1> {focus .}"); + cmd(win, "bind .Wm_t <ButtonRelease-1> +{focus .}"); + cmd(win, "focus ."); + + maxsize := Point(10000, 10000); + if (ctxt.display.image != nil) { + img := ctxt.display.image; + wsz := wsize(win, "."); + maxsize.y = img.r.dy() - wsz.y; + maxsize.x = img.r.dx(); + } + + tkclient->onscreen(win, nil); + tkclient->startinput(win, "kbd"::"ptr"::nil); + for (;;) { + bd := Board.new(win, ".f", blocksize, maxsize); + if (bd == nil) { + sys->fprint(stderr, "tetris: couldn't make board\n"); + return; + } + cmd(win, "bind .f.c <ButtonRelease-1> {send user m %x %y}"); + cmd(win, "pack .f -side top"); + cmd(win, "update"); + g := Game.new(bd); + (finished, rank) := rungame(g, win, fromuser, winctl, scorech); + if (finished) + break; + cmd(win, "pack propagate . 0"); + if (scoretab != nil) { + cmd(win, "destroy .f"); + if (showhighscores(win, fromuser, winctl, rank) == 0) + break; + } else + cmd(win, "destroy .f"); + } +} + +wsize(win: ref Tk->Toplevel, w: string): Point +{ + bd := int cmd(win, w + " cget -bd"); + return (int cmd(win, w + " cget -width") + bd * 2, + int cmd(win, w + " cget -height") + bd * 2); +} + +rungame(g: ref Game, win: ref Tk->Toplevel, fromuser: chan of string, winctl: chan of string, scorech: chan of int): (int, int) +{ + tickchan := chan of int; + spawn ticker(g, tickchan); + paused := 0; + tch := chan of int; + + gameover := 0; + rank := -1; + bdsize := wsize(win, ".f.c"); + boundy := bdsize.y * 2 / 3; + id := cmd(win, ".f.c create line " + p2s((0, boundy)) + " " + p2s((bdsize.x, boundy)) + + " -fill white"); + cmd(win, ".f.c lower " + id); + for (;;) alt { + s := <-win.ctxt.kbd => + tk->keyboard(win, s); + s := <-win.ctxt.ptr => + tk->pointer(win, *s); + s := <-fromuser => + key: int; + if (s[0] == 'm') { + (nil, toks) := sys->tokenize(s, " "); + p := Point(int hd tl toks, int hd tl tl toks); + if (p.y > boundy) + key = ' '; + else { + x := p.x / (bdsize.x / 3); + case x { + 0 => + key = '7'; + 1 => + key = '8'; + 2 => + key = '9'; + * => + break; + } + } + } else if (s[0] == 'k') + key = int s[1:]; + else + sys->print("oops (%s)\n", s); + if (gameover) + return (key == 'q', rank); + if (paused) { + paused = 0; + (tickchan, tch) = (tch, tickchan); + if (key != 'q') + continue; + } + case key { + '9' or 'c' or Right => + g.move(1); + '7' or 'z' or Left => + g.move(-1); + '8' or 'x' or Up => + g.rotate(0); + ' ' or Down => + g.drop(); + 'p' => + paused = 1; + (tickchan, tch) = (tch, tickchan); + 'q' => + g.delay = -1; + while (<-tickchan) + ; + return (1, rank); + } + s := <-win.ctxt.ctl or + s = <-win.wreq or + s = <-winctl => + tkclient->wmctl(win, s); + n := <-tickchan => + if (g.tick() == -1) { + while (n) + n = <-tickchan; + if (awaitingscore && !<-scorech) { + awaitingscore = 0; + scoretab = nil; + } + if (scoretab != nil) + rank = scoretab->setscore(g.score, sys->sprint("%d %d %bd", g.nrows, g.level, + big readfile("/dev/time") / big 1000000)); + gameover = 1; + } + ok := <-scorech => + awaitingscore = 0; + if (!ok) + scoretab = nil; + } +} + +tablerow(win: ref Tk->Toplevel, w, bg: string, relief: string, vals: array of string, widths: array of string) +{ + cmd(win, "frame " + w + " -bd 2 -relief " + relief); + for (i := 0; i < len vals; i++) { + cw := cmd(win, "label " + w + "." + string i + " -text " + tk->quote(vals[i]) + " -width " + widths[i] + bg); + cmd(win, "pack " + cw + " -side left -anchor w"); + } + cmd(win, "pack " + w + " -side top"); +} + +showhighscores(win: ref Tk->Toplevel, fromuser: chan of string, winctl: chan of string, rank: int): int +{ + widths := array[] of {"10w", "7w", "7w", "5w"}; # user, score, level, rows + cmd(win, "frame .f -bd 4 -relief raised"); + cmd(win, "label .f.title -text {High Scores}"); + cmd(win, "pack .f.title -side top -anchor n"); + tablerow(win, ".f.h", nil, "raised", array[] of {"User", "Score", "Level", "Rows"}, widths); + sl := scoretab->scores(); + n := 0; + while (sl != nil) { + s := hd sl; + bg := ""; + if (n == rank) + bg = " -bg white"; + f := ".f.f" + string n++; + nrows := level := ""; + (nil, toks) := sys->tokenize(s.other, " "); + if (toks != nil) + (nrows, toks) = (hd toks, tl toks); + if (toks != nil) + level = hd toks; + tablerow(win, f, bg, "sunken", array[] of {s.user, string s.score, level, nrows}, widths); + sl = tl sl; + } + cmd(win, "button .f.b -text {New game} -command {send user s}"); + cmd(win, "pack .f.b -side top"); + cmd(win, "pack .f -side top"); + cmd(win, "update"); + for (;;) alt { + s := <-win.ctxt.kbd => + tk->keyboard(win, s); + s := <-win.ctxt.ptr => + tk->pointer(win, *s); + s := <-fromuser => + if (s[0] == 'k') { + cmd(win, "destroy .f"); + return int s[1:] != 'q'; + } else if (s[0] == 's') { + cmd(win, "destroy .f"); + return 1; + } + s := <-win.ctxt.ctl or + s = <-win.wreq or + s = <-winctl => + tkclient->wmctl(win, s); + } +} + +scoresrvwait(ch: chan of int) +{ + if (scoretab == nil) { + ch <-= 0; + return; + } + (ok, err) := scoretab->init(LOCKPORT, readfile("/dev/user"), "tetris", SCORETABLE); + if (ok != -1) + ch <-= 1; + else { + if (err != "timeout") + sys->fprint(stderr, "tetris: scoretable error: %s\n", err); + else + sys->fprint(stderr, "tetris: timed out trying to connect to score server\n"); + ch <-= 0; + } +} + +readfile(f: string): string +{ + fd := sys->open(f, Sys->OREAD); + if (fd == nil) + return nil; + buf := array[Sys->ATOMICIO] of byte; + n := sys->read(fd, buf, len buf); + if (n <= 0) + return nil; + return string buf[0:n]; +} + +ticker(g: ref Game, c: chan of int) +{ + c <-= 1; + while (g.delay >= 0) { + sys->sleep(g.delay); + c <-= 1; + } + c <-= 0; +} + +seedrand() +{ + random := load Random Random->PATH; + if (random == nil) { + sys->fprint(stderr, "tetris: cannot load %s: %r\n", Random->PATH); + return; + } + seed := random->randomint(Random->ReallyRandom); + rand->init(seed); +} + +Game.new(bd: ref Board): ref Game +{ + g := ref Game; + g.bd = bd; + g.level = 0; + g.pieceids = array[4] of string; + g.score = 0; + g.delay = delays[g.level]; + g.nrows = 0; + g.next = randompiece(); + newpiece(g); + bd.update(); + return g; +} + +randompiece(): Piece +{ + p: Piece; + p.shape = rand->rand(len shapes); + p.rot = rand->rand(len shapes[p.shape].coords); + return p; +} + +Game.move(g: self ref Game, dx: int) +{ + np := g.pos.add((dx, 0)); + if (canmove(g, g.curr, np)) { + g.bd.movecurr((dx, 0)); + g.bd.update(); + g.pos = np; + } +} + +Game.rotate(g: self ref Game, clockwise: int) +{ + inc := 1; + if (!clockwise) + inc = -1; + npiece := g.curr; + coords := shapes[npiece.shape].coords; + nrots := len coords; + npiece.rot = (npiece.rot + inc + nrots) % nrots; + if (canmove(g, npiece, g.pos)) { + c := coords[npiece.rot]; + for (i := 0; i < len c; i++) { + np := g.pos.add(c[i]); + g.bd.moveblock(g.pieceids[i], g.pos.add(c[i])); + } + g.curr = npiece; + g.bd.update(); + } +} + +Game.tick(g: self ref Game): int +{ + if (canmove(g, g.curr, g.pos.add((0, 1)))) { + g.bd.movecurr((0, 1)); + g.pos.y++; + } else { + c := shapes[g.curr.shape].coords[g.curr.rot]; + max := g.pos.y; + min := g.pos.y + 4; + for (i := 0; i < len c; i++) { + p := g.pos.add(c[i]); + if (p.y < 0) { + g.delay = -1; + g.bd.gameover(); + g.bd.update(); + return -1; + } + if (p.y > max) + max = p.y; + if (p.y < min) + min = p.y; + g.bd.landedblock(g.pieceids[i], p); + } + full: list of int; + for (i = min; i <= max; i++) { + for (x := 0; x < BOARDWIDTH; x++) + if (g.bd.state[i][x] == byte 0) + break; + if (x == BOARDWIDTH) + full = i :: full; + } + if (full != nil) { + g.bd.delrows(full); + g.nrows += len full; + g.bd.setnrows(g.nrows); + level := g.nrows / 10; + if (level != g.level) { + g.bd.setlevel(level); + g.level = level; + if (level >= len delays) + level = len delays - 1; + g.delay = delays[level]; + } + } + g.score += shapes[g.curr.shape].score[g.curr.rot]; + g.bd.setscore(g.score); + newpiece(g); + } + g.bd.update(); + return 0; +} + +Game.drop(g: self ref Game) +{ + p := g.pos.add((0, 1)); + while (canmove(g, g.curr, p)) + p.y++; + p.y--; + g.bd.movecurr((0, p.y - g.pos.y)); + g.pos = p; + g.bd.update(); +} + +canmove(g: ref Game, piece: Piece, p: Point): int +{ + c := shapes[piece.shape].coords[piece.rot]; + for (i := 0; i < len c; i++) { + q := p.add(c[i]); + if (q.x < 0 || q.x >= BOARDWIDTH || q.y >= BOARDHEIGHT) + return 0; + if (q.y >= 0 && int g.bd.state[q.y][q.x]) + return 0; + } + return 1; +} + +newpiece(g: ref Game) +{ + piece := g.curr = g.next; + g.next = randompiece(); + g.bd.setnextshape(shapes[g.next.shape].colour, shapes[g.next.shape].coords[g.next.rot]); + shape := shapes[g.curr.shape]; + coords := shape.coords[g.curr.rot]; + g.pos = (3, -4); + for (i := 0; i < len coords; i++) + g.pieceids[i] = g.bd.makeblock(shape.colour, g.pos.add(coords[i])); +} + +p2s(p: Point): string +{ + return string p.x + " " + string p.y; +} + +Board.new(top: ref Tk->Toplevel, w: string, blocksize: int, maxsize: Point): ref Board +{ + cmd(top, "frame " + w); + cmd(top, "canvas " + w + ".c -borderwidth 2 -relief sunken -width 1 -height 1"); + cmd(top, "frame " + w + ".f"); + cmd(top, "canvas " + w + ".f.ns -width 1 -height 1"); + makescorewidget(top, w + ".f.scoref", "Score"); + makescorewidget(top, w + ".f.levelf", "Level"); + makescorewidget(top, w + ".f.rowsf", "Rows"); + cmd(top, "pack " + w + ".c -side left"); + cmd(top, "pack " + w + ".f -side top"); + cmd(top, "pack " + w + ".f.ns -side top"); + cmd(top, "pack " + w + ".f.scoref -side top -fill x"); + cmd(top, "pack " + w + ".f.levelf -side top -fill x"); + cmd(top, "pack " + w + ".f.rowsf -side top -fill x"); + + sz := wsize(top, w); + avail := Point(maxsize.x - sz.x, maxsize.y); + avail.x /= BOARDWIDTH; + avail.y /= BOARDHEIGHT; + dx := avail.x; + if (avail.y < avail.x) + dx = avail.y; + if (dx <= 0) + return nil; + if (dx > blocksize) + dx = blocksize; + cmd(top, w + ".f.ns configure -width " + string(4 * dx + 1 - 2*2) + + " -height " + string(4 * dx + 1 - 2*2)); + cmd(top, w + ".c configure -width " + string(dx * BOARDWIDTH + 1) + + " -height " + string(dx * BOARDHEIGHT + 1)); + bd := ref Board(array[BOARDHEIGHT] + of {* => array[BOARDWIDTH] of {* => byte 0}}, + w, dx, top, array[BOARDHEIGHT] of {* => Row(nil, 0)}, 1); + return bd; +} + +makescorewidget(top: ref Tk->Toplevel, w, title: string) +{ + cmd(top, "frame " + w); + cmd(top, "label " + w + ".title -text " + tk->quote(title)); + cmd(top, "label " + w + + ".val -bd 3 -relief sunken -width 5w -text 0 -anchor e"); + cmd(top, "pack " + w + ".title -side left -anchor w"); + cmd(top, "pack " + w + ".val -side right -anchor e"); +} + +blockrect(bd: ref Board, p: Point): string +{ + p = p.mul(bd.dx); + q := p.add((bd.dx, bd.dx)); + return string p.x + " " + string p.y + " " + string q.x + " " + string q.y; +} + +Board.makeblock(bd: self ref Board, colour: string, p: Point): string +{ + tag := cmd(bd.win, bd.w + ".c create rectangle " + blockrect(bd, p) + " -fill " + colour + " -tags curr"); + if (tag != nil && tag[0] == '!') + return nil; + return tag; +} + +Board.moveblock(bd: self ref Board, b: string, p: Point) +{ + cmd(bd.win, bd.w + ".c coords " + b + " " + blockrect(bd, p)); +} + +Board.movecurr(bd: self ref Board, delta: Point) +{ + delta = delta.mul(bd.dx); + cmd(bd.win, bd.w + ".c move curr " + string delta.x + " " + string delta.y); +} + +Board.landedblock(bd: self ref Board, b: string, p: Point) +{ + cmd(bd.win, bd.w + ".c dtag " + b + " curr"); + rs := cmd(bd.win, bd.w + ".c coords " + b); + if (rs != nil && rs[0] == '!') + return; + (n, toks) := sys->tokenize(rs, " "); + if (len toks != 4) { + sys->fprint(stderr, "bad coords for block %s\n", b); + return; + } + y := int hd tl toks / bd.dx; + if (y < 0) + return; + if (y >= BOARDHEIGHT) { + sys->fprint(stderr, "block '%s' too far down (coords %s)\n", b, rs); + return; + } + rtag := bd.rows[y].tag; + if (rtag == nil) + rtag = bd.rows[y].tag = "r" + string bd.maxid++; + cmd(bd.win, bd.w + ".c addtag " + rtag + " withtag " + b); + if (p.y >= 0) + bd.state[p.y][p.x] = byte 1; +} + +Board.delrows(bd: self ref Board, rows: list of int) +{ + while (rows != nil) { + r := hd rows; + bd.rows[r].delete = 1; + rows = tl rows; + } + j := BOARDHEIGHT - 1; + for (i := BOARDHEIGHT - 1; i >= 0; i--) { + if (bd.rows[i].delete) { + cmd(bd.win, bd.w + ".c delete " + bd.rows[i].tag); + bd.rows[i] = (nil, 0); + bd.state[i] = nil; + } else { + if (i != j && bd.rows[i].tag != nil) { + dy := (j - i) * bd.dx; + cmd(bd.win, bd.w + ".c move " + bd.rows[i].tag + " 0 " + string dy); + bd.rows[j] = bd.rows[i]; + bd.rows[i] = (nil, 0); + bd.state[j] = bd.state[i]; + bd.state[i] = nil; + } + j--; + } + } + for (i = 0; i < BOARDHEIGHT; i++) + if (bd.state[i] == nil) + bd.state[i] = array[BOARDWIDTH] of {* => byte 0}; +} + +Board.update(bd: self ref Board) +{ + cmd(bd.win, "update"); +} + +Board.setnextshape(bd: self ref Board, colour: string, spec: array of Point) +{ + cmd(bd.win, bd.w + ".f.ns delete all"); + min := Point(4,4); + max := Point(0,0); + for (i := 0; i < len spec; i++) { + if (spec[i].x > max.x) max.x = spec[i].x; + if (spec[i].x < min.x) min.x = spec[i].x; + if (spec[i].y > max.y) max.y = spec[i].y; + if (spec[i].y < min.y) min.y = spec[i].y; + } + o: Point; + o.x = (4 - (max.x - min.x + 1)) * bd.dx / 2 - min.x * bd.dx; + o.y = (4 - (max.y - min.y + 1)) * bd.dx / 2 - min.y * bd.dx; + for (i = 0; i < len spec; i++) { + br := Rect(o.add(spec[i].mul(bd.dx)), o.add(spec[i].add((1,1)).mul(bd.dx))); + cmd(bd.win, bd.w + ".f.ns create rectangle " + + string br.min.x + " " + string br.min.y + " " + string br.max.x + " " + string br.max.y + + " -fill " + colour); + } +} + +Board.setscore(bd: self ref Board, score: int) +{ + cmd(bd.win, bd.w + ".f.scoref.val configure -text " + string score); +} + +Board.setlevel(bd: self ref Board, level: int) +{ + cmd(bd.win, bd.w + ".f.levelf.val configure -text " + string level); +} + +Board.setnrows(bd: self ref Board, nrows: int) +{ + cmd(bd.win, bd.w + ".f.rowsf.val configure -text " + string nrows); +} + +Board.gameover(bd: self ref Board) +{ + cmd(bd.win, "label " + bd.w + ".gameover -text {Game over} -bd 4 -relief ridge"); + p := Point(BOARDWIDTH * bd.dx / 2, BOARDHEIGHT * bd.dx / 3); + cmd(bd.win, bd.w + ".c create window " + string p.x + " " + string p.y + " -window " + bd.w + ".gameover"); +} + +cmd(top: ref Tk->Toplevel, s: string): string +{ + e := tk->cmd(top, s); +# sys->print("%s\n", s); + if (e != nil && e[0] == '!') + sys->fprint(stderr, "tetris: tk error on '%s': %s\n", s, e); + return e; +} + +VIOLET: con "#ffaaff"; +CYAN: con "#93ddf1"; + +delays := array[] of {300, 250, 200, 150, 100, 80}; + +shapes := array[] of { +Shape( + # #### + array[] of { + array[] of {Point(0,1), Point(1,1), Point(2,1), Point(3,1)}, + array[] of {Point(1,0), Point(1,1), Point(1,2), Point(1,3)}, + }, + "red", + array[] of {5, 8}), +Shape( + # ## + # ## + array[] of { + array[] of {Point(0,0), Point(0,1), Point(1,0), Point(1,1)}, + }, + "orange", + array[] of {6}), +Shape( + # # + # ## + # # + array[] of { + array[] of {Point(1,0), Point(0,1), Point(1,1), Point(2,1)}, + array[] of {Point(1,0), Point(1,1), Point(2,1), Point(1,2)}, + array[] of {Point(0,1), Point(1,1), Point(2,1), Point(1,2)}, + array[] of {Point(1,0), Point(0,1), Point(1,1), Point(1,2)}, + }, + "yellow", + array[] of {5,5,6,5}), +Shape( + # ## + # ## + array[] of { + array[] of {Point(0,0), Point(1,0), Point(1,1), Point(2,1)}, + array[] of {Point(1,0), Point(0,1), Point(1,1), Point(0,2)}, + }, + "green", + array[] of {6,7}), +Shape( + # ## + # ## + array[] of { + array[] of {Point(1,0), Point(2,0), Point(0,1), Point(1,1)}, + array[] of {Point(0,0), Point(0,1), Point(1,1), Point(1,2)}, + }, + "blue", + array[] of {6,7}), +Shape( + # ### + # # + array[] of { + array[] of {Point(2,0), Point(0,1), Point(1,1), Point(2,1)}, + array[] of {Point(0,0), Point(0,1), Point(0,2), Point(1,2)}, + array[] of {Point(0,0), Point(1,0), Point(2,0), Point(0,1)}, + array[] of {Point(0,0), Point(1,0), Point(1,1), Point(1,2)}, + }, + CYAN, + array[] of {6,7,6,7}), +Shape( + # # + # ### + array[] of { + array[] of {Point(0,0), Point(1,0), Point(2,0), Point(2,1)}, + array[] of {Point(1,0), Point(1,1), Point(0,2), Point(1,2)}, + array[] of {Point(0,0), Point(0,1), Point(1,1), Point(2,1)}, + array[] of {Point(0,0), Point(1,0), Point(0,1), Point(0,2)}, + }, + VIOLET, + array[] of {6,7,6,7} +), +}; + diff --git a/appl/wm/toolbar.b b/appl/wm/toolbar.b new file mode 100644 index 00000000..a96f5ba4 --- /dev/null +++ b/appl/wm/toolbar.b @@ -0,0 +1,566 @@ +implement Toolbar; +include "sys.m"; + sys: Sys; +include "draw.m"; + draw: Draw; + Screen, Display, Image, Rect, Point, Wmcontext, Pointer: import draw; +include "tk.m"; + tk: Tk; +include "tkclient.m"; + tkclient: Tkclient; +include "sh.m"; + shell: Sh; + Listnode, Context: import shell; +include "string.m"; + str: String; +include "arg.m"; + +myselfbuiltin: Shellbuiltin; + +Toolbar: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); + initbuiltin: fn(c: ref Context, sh: Sh): string; + runbuiltin: fn(c: ref Context, sh: Sh, + cmd: list of ref Listnode, last: int): string; + runsbuiltin: fn(c: ref Context, sh: Sh, + cmd: list of ref Listnode): list of ref Listnode; + whatis: fn(c: ref Sh->Context, sh: Sh, name: string, wtype: int): string; + getself: fn(): Shellbuiltin; +}; + +MAXCONSOLELINES: con 1024; + +# execute this if no menu items have been created +# by the init script. +defaultscript := + "{menu shell " + + "{{autoload=std; load $autoload; pctl newpgrp; wm/sh}&}}"; + +tbtop: ref Tk->Toplevel; +screenr: Rect; + +badmodule(p: string) +{ + sys->fprint(stderr(), "toolbar: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init(ctxt: ref Draw->Context, argv: list of string) +{ + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + if(draw == nil) + badmodule(Draw->PATH); + tk = load Tk Tk->PATH; + if(tk == nil) + badmodule(Tk->PATH); + + str = load String String->PATH; + if(str == nil) + badmodule(String->PATH); + + tkclient = load Tkclient Tkclient->PATH; + if(tkclient == nil) + badmodule(Tkclient->PATH); + tkclient->init(); + + shell = load Sh Sh->PATH; + if (shell == nil) + badmodule(Sh->PATH); + arg := load Arg Arg->PATH; + if (arg == nil) + badmodule(Arg->PATH); + + myselfbuiltin = load Shellbuiltin "$self"; + if (myselfbuiltin == nil) + badmodule("$self(Shellbuiltin)"); + + sys->pctl(Sys->NEWPGRP|Sys->FORKNS, nil); + + sys->bind("#p", "/prog", sys->MREPL); + sys->bind("#s", "/chan", sys->MBEFORE); + + arg->init(argv); + arg->setusage("toolbar [-s]"); + startmenu := 1; + while((c := arg->opt()) != 0){ + case c { + 's' => + startmenu = 0; + * => + arg->usage(); + } + } + argv = arg->argv(); + arg = nil; + + if (ctxt == nil){ + sys->fprint(sys->fildes(2), "toolbar: must run under a window manager\n"); + raise "fail:no wm"; + } + + exec := chan of string; + task := chan of string; + + tbtop = toolbar(ctxt, startmenu, exec, task); + tkclient->startinput(tbtop, "ptr" :: "control" :: nil); + layout(tbtop); + + shctxt := Context.new(ctxt); + shctxt.addmodule("wm", myselfbuiltin); + + snarfIO := sys->file2chan("/chan", "snarf"); + if(snarfIO == nil) + fatal(sys->sprint("cannot make /chan/snarf: %r")); + sync := chan of string; + spawn consoleproc(ctxt, sync); + if ((err := <-sync) != nil) + fatal(err); + + setupfinished := chan of int; + donesetup := 0; + spawn setup(shctxt, setupfinished); + + snarf: array of byte; +# write("/prog/"+string sys->pctl(0, nil)+"/ctl", "restricted"); # for testing + for(;;) alt{ + s := <-tbtop.ctxt.kbd => + tk->keyboard(tbtop, c); + m := <-tbtop.ctxt.ptr => + tk->pointer(tbtop, *m); + s := <-tbtop.ctxt.ctl or + s = <-tbtop.wreq => + wmctl(tbtop, s); + s := <-exec => + # guard against parallel access to the shctxt environment + if (donesetup){ + { + shctxt.run(ref Listnode(nil, s) :: nil, 0); + } exception e {"fail:*" =>;} + } + detask := <-task => + deiconify(detask); + (off, data, fid, wc) := <-snarfIO.write => + if(wc == nil) + break; + if (off == 0) # write at zero truncates + snarf = data; + else { + if (off + len data > len snarf) { + nsnarf := array[off + len data] of byte; + nsnarf[0:] = snarf; + snarf = nsnarf; + } + snarf[off:] = data; + } + wc <-= (len data, ""); + (off, nbytes, nil, rc) := <-snarfIO.read => + if(rc == nil) + break; + if (off >= len snarf) { + rc <-= (nil, ""); # XXX alt + break; + } + e := off + nbytes; + if (e > len snarf) + e = len snarf; + rc <-= (snarf[off:e], ""); # XXX alt + donesetup = <-setupfinished => + ; + } +} + +wmctl(top: ref Tk->Toplevel, c: string) +{ + args := str->unquoted(c); + if(args == nil) + return; + n := len args; + + case hd args{ + "request" => + # request clientid args... + if(n < 3) + return; + args = tl args; + clientid := hd args; + args = tl args; + err := handlerequest(clientid, args); + if(err != nil) + sys->fprint(sys->fildes(2), "toolbar: bad wmctl request %#q: %s\n", c, err); + "newclient" => + # newclient id + ; + "delclient" => + # delclient id + deiconify(hd tl args); + "rect" => + tkclient->wmctl(top, c); + layout(top); + * => + tkclient->wmctl(top, c); + } +} + +handlerequest(clientid: string, args: list of string): string +{ + n := len args; + case hd args { + "task" => + # task name + if(n != 2) + return "no task label given"; + iconify(clientid, hd tl args); + "untask" or + "unhide" => + deiconify(clientid); + * => + return "unknown request"; + } + return nil; +} + +iconify(id, label: string) +{ + label = condenselabel(label); + e := tk->cmd(tbtop, "button .toolbar." +id+" -command {send task "+id+"} -takefocus 0"); + cmd(tbtop, ".toolbar." +id+" configure -text '" + label); + if(e[0] != '!') + cmd(tbtop, "pack .toolbar."+id+" -side left -fill y"); + cmd(tbtop, "update"); +} + +deiconify(id: string) +{ + e := tk->cmd(tbtop, "destroy .toolbar."+id); + if(e == nil){ + tkclient->wmctl(tbtop, sys->sprint("ctl %q untask", id)); + tkclient->wmctl(tbtop, sys->sprint("ctl %q kbdfocus 1", id)); + } + cmd(tbtop, "update"); +} + +layout(top: ref Tk->Toplevel) +{ + r := top.screenr; + h := 32; + if(r.dy() < 480) + h = tk->rect(top, ".b", Tk->Border|Tk->Required).dy(); + cmd(top, ". configure -x " + string r.min.x + + " -y " + string (r.max.y - h) + + " -width " + string r.dx() + + " -height " + string h); + cmd(top, "update"); + tkclient->onscreen(tbtop, "exact"); +} + +toolbar(ctxt: ref Draw->Context, startmenu: int, + exec, task: chan of string): ref Tk->Toplevel +{ + (tbtop, nil) = tkclient->toplevel(ctxt, nil, nil, Tkclient->Plain); + screenr = tbtop.screenr; + + cmd(tbtop, "button .b -text {XXX}"); + cmd(tbtop, "pack propagate . 0"); + + tk->namechan(tbtop, exec, "exec"); + tk->namechan(tbtop, task, "task"); + cmd(tbtop, "frame .toolbar"); + if (startmenu) { + cmd(tbtop, "menubutton .toolbar.start -menu .m -borderwidth 0 -bitmap vitasmall.bit"); + cmd(tbtop, "pack .toolbar.start -side left"); + } + cmd(tbtop, "pack .toolbar -fill x"); + cmd(tbtop, "menu .m"); + return tbtop; +} + +setup(shctxt: ref Context, finished: chan of int) +{ + ctxt := shctxt.copy(0); + ctxt.run(shell->stringlist2list("run"::"/lib/wmsetup"::nil), 0); + # if no items in menu, then create some. + if (tk->cmd(tbtop, ".m type 0")[0] == '!') + ctxt.run(shell->stringlist2list(defaultscript::nil), 0); + cmd(tbtop, "update"); + finished <-= 1; +} + +condenselabel(label: string): string +{ + if(len label > 15){ + new := ""; + l := 0; + while(len label > 15 && l < 3) { + new += label[0:15]+"\n"; + label = label[15:]; + for(v := 0; v < len label; v++) + if(label[v] != ' ') + break; + label = label[v:]; + l++; + } + label = new + label; + } + return label; +} + +initbuiltin(ctxt: ref Context, nil: Sh): string +{ + if (tbtop == nil) { + sys = load Sys Sys->PATH; + sys->fprint(sys->fildes(2), "wm: cannot load wm as a builtin\n"); + raise "fail:usage"; + } + ctxt.addbuiltin("menu", myselfbuiltin); + ctxt.addbuiltin("delmenu", myselfbuiltin); + ctxt.addbuiltin("error", myselfbuiltin); + return nil; +} + +whatis(nil: ref Sh->Context, nil: Sh, nil: string, nil: int): string +{ + return nil; +} + +runbuiltin(c: ref Context, sh: Sh, + cmd: list of ref Listnode, nil: int): string +{ + case (hd cmd).word { + "menu" => return builtin_menu(c, sh, cmd); + "delmenu" => return builtin_delmenu(c, sh, cmd); + } + return nil; +} + +runsbuiltin(nil: ref Context, nil: Sh, + nil: list of ref Listnode): list of ref Listnode +{ + return nil; +} + +stderr(): ref Sys->FD +{ + return sys->fildes(2); +} + +word(ln: ref Listnode): string +{ + if (ln.word != nil) + return ln.word; + if (ln.cmd != nil) + return shell->cmd2string(ln.cmd); + return nil; +} + +menupath(title: string): string +{ + mpath := ".m."+title; + for(j := 0; j < len mpath; j++) + if(mpath[j] == ' ') + mpath[j] = '_'; + return mpath; +} + +builtin_menu(nil: ref Context, nil: Sh, argv: list of ref Listnode): string +{ + n := len argv; + if (n < 3 || n > 4) { + sys->fprint(stderr(), "usage: menu topmenu [ secondmenu ] command\n"); + raise "fail:usage"; + } + primary := (hd tl argv).word; + argv = tl tl argv; + + if (n == 3) { + w := word(hd argv); + if (len w == 0) + cmd(tbtop, ".m insert 0 separator"); + else + cmd(tbtop, ".m insert 0 command -label " + tk->quote(primary) + + " -command {send exec " + w + "}"); + } else { + secondary := (hd argv).word; + argv = tl argv; + + mpath := menupath(primary); + e := tk->cmd(tbtop, mpath+" cget -width"); + if(e[0] == '!') { + cmd(tbtop, "menu "+mpath); + cmd(tbtop, ".m insert 0 cascade -label "+tk->quote(primary)+" -menu "+mpath); + } + w := word(hd argv); + if (len w == 0) + cmd(tbtop, mpath + " insert 0 separator"); + else + cmd(tbtop, mpath+" insert 0 command -label "+tk->quote(secondary)+ + " -command {send exec "+w+"}"); + } + return nil; +} + +builtin_delmenu(nil: ref Context, nil: Sh, nil: list of ref Listnode): string +{ + delmenu(".m"); + cmd(tbtop, "menu .m"); + return nil; +} + +delmenu(m: string) +{ + for (i := int cmd(tbtop, m + " index end"); i >= 0; i--) + if (cmd(tbtop, m + " type " + string i) == "cascade") + delmenu(cmd(tbtop, m + " entrycget " + string i + " -menu")); + cmd(tbtop, "destroy " + m); +} + +getself(): Shellbuiltin +{ + return myselfbuiltin; +} + +cmd(top: ref Tk->Toplevel, c: string): string +{ + s := tk->cmd(top, c); + if (s != nil && s[0] == '!') + sys->fprint(stderr(), "tk error on %#q: %s\n", c, s); + return s; +} + +kill(pid: int, note: string): int +{ + fd := sys->open("/prog/"+string pid+"/ctl", Sys->OWRITE); + if(fd == nil || sys->fprint(fd, "%s", note) < 0) + return -1; + return 0; +} + +fatal(s: string) +{ + sys->fprint(sys->fildes(2), "wm: %s\n", s); + kill(sys->pctl(0, nil), "killgrp"); + raise "fail:error"; +} + +bufferproc(in, out: chan of string) +{ + h, t: list of string; + dummyout := chan of string; + for(;;){ + outc := dummyout; + s: string; + if(h != nil || t != nil){ + outc = out; + if(h == nil) + for(; t != nil; t = tl t) + h = hd t :: h; + s = hd h; + } + alt{ + x := <-in => + t = x :: t; + outc <-= s => + h = tl h; + } + } +} + +con_cfg := array[] of +{ + "frame .cons", + "scrollbar .cons.scroll -command {.cons.t yview}", + "text .cons.t -width 60w -height 15w -bg white "+ + "-fg black -font /fonts/misc/latin1.6x13.font "+ + "-yscrollcommand {.cons.scroll set}", + "pack .cons.scroll -side left -fill y", + "pack .cons.t -fill both -expand 1", + "pack .cons -expand 1 -fill both", + "pack propagate . 0", + "update" +}; +nlines := 0; # transcript length + +consoleproc(ctxt: ref Draw->Context, sync: chan of string) +{ + iostdout := sys->file2chan("/chan", "wmstdout"); + if(iostdout == nil){ + sync <-= sys->sprint("cannot make /chan/wmstdout: %r"); + return; + } + iostderr := sys->file2chan("/chan", "wmstderr"); + if(iostderr == nil){ + sync <-= sys->sprint("cannot make /chan/wmstdout: %r"); + return; + } + + sync <-= nil; + + (top, titlectl) := tkclient->toplevel(ctxt, "", "Log", tkclient->Appl); + for(i := 0; i < len con_cfg; i++) + cmd(top, con_cfg[i]); + + r := tk->rect(top, ".", Tk->Border|Tk->Required); + cmd(top, ". configure -x " + string ((top.screenr.dx() - r.dx()) / 2 + top.screenr.min.x) + + " -y " + string (r.dy() / 3 + top.screenr.min.y)); + + tkclient->startinput(top, "ptr"::"kbd"::nil); + tkclient->onscreen(top, "onscreen"); + tkclient->wmctl(top, "task"); + + for(;;) alt { + c := <-titlectl or + c = <-top.wreq or + c = <-top.ctxt.ctl => + if(c == "exit") + c = "task"; + tkclient->wmctl(top, c); + c := <-top.ctxt.kbd => + tk->keyboard(top, c); + p := <-top.ctxt.ptr => + tk->pointer(top, *p); + (off, nbytes, fid, rc) := <-iostdout.read => + if(rc == nil) + break; + alt{ + rc <-= (nil, "inappropriate use of file") =>; + * =>; + } + (off, nbytes, fid, rc) := <-iostderr.read => + if(rc == nil) + break; + alt{ + rc <-= (nil, "inappropriate use of file") =>; + * =>; + } + (off, data, fid, wc) := <-iostdout.write => + conout(top, data, wc); + (off, data, fid, wc) := <-iostderr.write => + conout(top, data, wc); + if(wc != nil) + tkclient->wmctl(top, "untask"); + } +} + +conout(top: ref Tk->Toplevel, data: array of byte, wc: Sys->Rwrite) +{ + if(wc == nil) + return; + + s := string data; + tk->cmd(top, ".cons.t insert end '"+ s); + alt{ + wc <-= (len data, nil) =>; + * =>; + } + + for(i := 0; i < len s; i++) + if(s[i] == '\n') + nlines++; + if(nlines > MAXCONSOLELINES){ + cmd(top, ".cons.t delete 1.0 " + string (nlines/4) + ".0; update"); + nlines -= nlines / 4; + } + + tk->cmd(top, ".cons.t see end; update"); +} diff --git a/appl/wm/unibrowse.b b/appl/wm/unibrowse.b new file mode 100644 index 00000000..50eefb40 --- /dev/null +++ b/appl/wm/unibrowse.b @@ -0,0 +1,966 @@ +implement Unibrowse; + +# unicode browser for inferno. +# roger peppe (rog@ohm.york.ac.uk) + +include "sys.m"; + sys: Sys; + stderr: ref Sys->FD; +include "draw.m"; + draw: Draw; +include "tk.m"; + tk: Tk; +include "tkclient.m"; + tkclient: Tkclient; +include "dialog.m"; + dialog: Dialog; +include "selectfile.m"; + selectfile: Selectfile; +include "string.m"; + str: String; +include "bufio.m"; + bio: Bufio; + +Unibrowse: module +{ + init: fn(ctxt: ref Draw->Context, nil: list of string); +}; + +Widgetstack: adt { + stk: list of string; # list of widget names; bottom of list is left-most widget + name: string; + + # init returns the widget name for the widgetstack; + # wn is the name of the frame holding the widget stack + new: fn(wn: string): ref Widgetstack; + + push: fn(ws: self ref Widgetstack, w: string); + pop: fn(ws: self ref Widgetstack): string; + top: fn(ws: self ref Widgetstack): string; +}; + +Defaultwidth: con 30; +Defaultheight: con 1; + +Tablerows: con 3; +Tablecols: con 8; + +Element: adt { + name: string; + cmd: chan of string; + cmdname: string; + config: array of string; + doneinit: int; +}; + +# columns in unidata file +ud_VAL, ud_CHARNAME, ud_CATEG, ud_COMBINE, ud_BIDIRECT, +ud_DECOMP, ud_DECDIGIT, ud_DIGIT, ud_NUMERICVAL, ud_MIRRORED, +ud_OLDNAME, ud_COMMENT, ud_UPCASE, ud_LOWCASE, ud_TITLECASE: con iota; + +# default font configurations within the application +DEFAULTFONT: con ""; +UNICODEFONT: con "lucm/unicode.9"; +TITLEFONT: con "misc/latin1.8x13"; +DATAFONT: con "misc/latin1.8x13"; +BUTTONFONT: con "misc/latin1.8x13"; + +currfont := "/fonts/" + UNICODEFONT + ".font"; + +MAINMENU, BYSEARCH, BYNUMBER, BYCATEGORY, BYFONT, TABLE: con iota; +elements := array[] of { +MAINMENU => Element(".main", nil, "maincmd", array[] of { + "frame .main", + "$listbox data .main.menu -height 6h", + "$button button .main.insp -text {Inspector} -command {send maincmd inspect}", + "$button button .main.font -text {Font} -command {send maincmd font}", + "$label unicode .fontlabel", # .fontlabel's font is currently chosen font + "pack .main.menu -side top", + "pack .main.insp .main.font -side left", + "bind .main.menu <ButtonRelease-1> +{send maincmd newselect}" + }, 0), +BYNUMBER => Element(".numfield", nil, "numcmd", array[] of { + "frame .numfield", + "$entry data .numfield.f -width 8w", + "bind .numfield.f <Key-\n> {send numcmd shownum}", + "$label title .numfield.l -text 'Hex unicode value", + "pack .numfield.l .numfield.f -side left" + }, 0), +TABLE => Element(".tbl", nil, "tblcmd", array[] of { + "frame .tbl", + "frame .tbl.tf", + "frame .tbl.buts", + "$button button .tbl.buts.forw -text {Next} -command {send tblcmd forw}", + "$button button .tbl.buts.backw -text {Prev} -command {send tblcmd backw}", + "pack .tbl.buts.forw .tbl.buts.backw -side left", + "pack .tbl.tf -side top", + "pack .tbl.buts -side left" + }, 0), +BYCATEGORY => Element(".cat", nil, "catcmd", array[] of { + "frame .cat", + "$listbox data .cat.menu -width 43w -height 130 -yscrollcommand {.cat.yscroll set}", + "scrollbar .cat.yscroll -width 18 -command {.cat.menu yview}", + "pack .cat.yscroll .cat.menu -side left -fill y", + "bind .cat.menu <ButtonRelease-1> +{send catcmd newselect}" + }, 0), +BYSEARCH => Element(".srch", nil, "searchcmd", array[] of { + "frame .srch", + "$listbox data .srch.menu -width 43w -height 130 -yscrollcommand {.srch.yscroll set}", + "scrollbar .srch.yscroll -width 18 -command {.srch.menu yview}", + "pack .srch.yscroll .srch.menu -side left -fill y", + "bind .srch.menu <ButtonRelease-1> +{send searchcmd search}" + }, 0), +BYFONT => Element(".font", nil, "fontcmd", array[] of { + "frame .font", + "$listbox data .font.menu -width 43w -height 130 -yscrollcommand {.font.yscroll set}", + "scrollbar .font.yscroll -width 18 -command {.font.menu yview}", + "pack .font.yscroll .font.menu -side left -fill y", + "bind .font.menu <ButtonRelease-1> +{send fontcmd newselect}" + }, 0), +}; + +entries := array[] of { +("By Category", BYCATEGORY), +("By number", BYNUMBER), +("Symbol wordsearch", BYSEARCH), +("Font information", BYFONT) +}; + +toplevelconfig := array[] of { +"pack .Wm_t .display -side top -fill x", +"image create bitmap waiting -file cursor.wait" +}; + +wmchan: chan of string; # from main window +inspchan: chan of string; # to inspector + +ctxt: ref Draw->Context; +displ: ref Widgetstack; +top: ref Tk->Toplevel; +unidata: ref bio->Iobuf; + +UNIDATA: con "/lib/unidata/unidata2.txt"; +UNIINDEX: con "/lib/unidata/index2.txt"; +UNIBLOCKS: con "/lib/unidata/blocks.txt"; + +notice(msg: string) +{ + dialog->prompt(ctxt, top.image, "bomb.bit", "Notice", msg, 0, "OK"::nil); +} + +init(drawctxt: ref Draw->Context, nil: list of string) +{ + entrychan := chan of string; + + ctxt = drawctxt; + config(); + if ((unidata = bio->open(UNIDATA, bio->OREAD)) == nil) { + notice("Couldn't open unicode data file"); + inspchan <-= "exit"; + exit; + } + + push(MAINMENU); + tkclient->onscreen(top, nil); + tkclient->startinput(top, "kbd"::"ptr"::nil); + currpos := 0; + + for (;;) alt { + c := <-top.ctxt.kbd => + tk->keyboard(top, c); + p := <-top.ctxt.ptr => + tk->pointer(top, *p); + c := <-top.ctxt.ctl or + c = <-top.wreq or + c = <-wmchan => + tkclient->wmctl(top, c); + c := <-elements[MAINMENU].cmd => + case c { + "font" => + font := choosefont(ctxt); + if (font != nil) { + currfont = font; + updatefont(); + update(top); + } + "newselect" => + sel := int cmd(top, ".main.menu curselection"); + (nil, el) := entries[sel]; + if (el == BYSEARCH) { + spawn sendentry(top, "Enter search string", entrychan); + break; + } + pop(MAINMENU); + push(el); + update(top); + + "inspect" => + inspchan <-= "raise"; + } + c := <-entrychan => + if (c != nil) { + pop(MAINMENU); + push(BYSEARCH); + update(top); + keywordsearch(c); + } + + c := <-elements[BYNUMBER].cmd => + txt := cmd(top, ".numfield.f get"); + (n, nil) := str->toint(txt, 16); + + pop(BYNUMBER); + push(TABLE); + setchar(n); + currpos = filltable(n); + update(top); + + c := <-elements[BYCATEGORY].cmd => + sel := cmd(top, ".cat.menu curselection"); + (currpos, nil) = str->toint(cmd(top, ".cat.menu get "+sel), 16); + pop(BYCATEGORY); + push(TABLE); + currpos = filltable(currpos); + update(top); + + c := <-elements[TABLE].cmd => + case c { + "forw" => currpos = filltable(currpos + Tablerows * Tablecols); + update(top); + + "backw" => currpos = filltable(currpos - Tablerows * Tablecols); + update(top); + + * => # must be set <col> <row> + (nil, args) := sys->tokenize(c, " "); + setchar(currpos + int hd tl args + + int hd tl tl args * Tablecols); + } + + c := <-elements[BYSEARCH].cmd => + sel := cmd(top, ".srch.menu curselection"); + (n, nil) := str->toint(cmd(top, ".srch.menu get "+sel), 16); + + pop(BYSEARCH); + push(TABLE); + setchar(n); + currpos = filltable(n); + update(top); + + c := <-elements[BYFONT].cmd => + sel := cmd(top, ".font.menu curselection"); + (currpos, nil) = str->toint(cmd(top, ".font.menu get "+sel), 16); + pop(BYFONT); + push(TABLE); + currpos = filltable(currpos); + update(top); + } + inspchan <-= "exit"; +} + +sendentry(t: ref Tk->Toplevel, msg: string, where: chan of string) +{ + where <-= dialog->getstring(ctxt, t.image, msg); + exit; +} + +setchar(c: int) +{ + s := ""; s[0] = c; + inspchan <-= s; +} + + +charconfig := array[] of { +"frame .chdata -borderwidth 5 -relief ridge", +"frame .chdata.f1", +"frame .chdata.f2", +"frame .chdata.chf -borderwidth 4 -relief raised", +"frame .chdata.chcf -borderwidth 3 -relief ridge", +"$label title .chdata.chf.title -text 'Glyph: ", +"$label unicode .chdata.ch", +"$label data .chdata.val -anchor e", +"$label title .chdata.name -anchor w", +"$label data .chdata.cat -anchor w", +"$label data .chdata.comm -anchor w", +"$button button .chdata.snarfbut -text {Snarf} -command {send charcmd snarf}", +"$button button .chdata.pastebut -text {Paste} -command {send charcmd paste}", +"pack .chdata.chf.title .chdata.chcf -in .chdata.chf -side left", +"pack .chdata.ch -in .chdata.chcf", +"pack .chdata.chf -in .chdata.f1 -side left -padx 1 -pady 1", +"pack .chdata.val -in .chdata.f1 -side right", +"pack .chdata.snarfbut .chdata.pastebut -in .chdata.f2 -side right", +"pack .chdata.f1 .chdata.name .chdata.cat .chdata.comm .chdata.f2 -fill x -side top", +"pack .Wm_t .chdata -side top -fill x", +}; + +inspector(ctxt: ref Draw->Context, cmdch: chan of string) +{ + chtop: ref Tk->Toplevel; + + kbd := chan of int; + ptr := chan of ref Draw->Pointer; + wreq := chan of string; + iwmchan := chan of string; + ctl := chan of string; + + charcmd := chan of string; + currc := 'A'; + + for (;;) alt { + c := <-kbd => + tk->keyboard(chtop, c); + p := <-ptr => + tk->pointer(chtop, *p); + c := <-ctl or + c = <-wreq or + c = <-iwmchan => + if (c != "exit" && chtop != nil) + tkclient->wmctl(chtop, c); + else + chtop = nil; + c := <-cmdch => + case c { + "raise" => + if (chtop != nil) { + cmd(chtop, "raise ."); + break; + } + org := winorg(top); + org.y += int cmd(top, ". cget -actheight"); + (chtop, iwmchan) = tkclient->toplevel(ctxt, + "-x "+string org.x+" -y "+string org.y, + "Character inspector", 0); + tk->namechan(chtop, charcmd, "charcmd"); + + runconfig(chtop, charconfig); + inspector_setchar(chtop, currc); + tkclient->onscreen(chtop, "onscreen"); + tkclient->startinput(chtop, "ptr"::nil); + kbd = chtop.ctxt.kbd; + ptr = chtop.ctxt.ptr; + ctl = chtop.ctxt.ctl; + wreq = chtop.wreq; + "font" => + if (chtop != nil) { + cmd(chtop, ".chdata.ch configure -font "+currfont); + update(chtop); + } + "exit" => + exit; + * => + if (len c == 1) { + currc = c[0]; + inspector_setchar(chtop, currc); + } else { + sys->fprint(stderr, "unknown inspector cmd: '%s'\n", c); + } + } + c := <-charcmd => + case c { + "snarf" => + tkclient->snarfput(cmd(chtop, ".chdata.ch cget -text")); + "paste" => + buf := tkclient->snarfget(); + if (len buf > 0) + inspector_setchar(chtop, buf[0]); + } + } +} + +inspector_setchar(t: ref Tk->Toplevel, c: int) +{ + line := look(unidata, ';', sys->sprint("%4.4X", c)); + labelset(t, ".chdata.ch", sys->sprint("%c", c)); + labelset(t, ".chdata.val", sys->sprint("%4.4X", c)); + if (line == nil) { + labelset(t, ".chdata.name", "No entry found in unicode table"); + labelset(t, ".chdata.cat", ""); + labelset(t, ".chdata.comm", ""); + } else { + flds := fields(line, ';'); + labelset(t, ".chdata.name", fieldindex(flds, ud_CHARNAME)); + labelset(t, ".chdata.cat", categname(fieldindex(flds, ud_CATEG))); + labelset(t, ".chdata.comm", fieldindex(flds, ud_OLDNAME)); + } + update(t); +} + +keywordsearch(key: string): int +{ + + data := bio->open(UNIINDEX, Sys->OREAD); + + key = str->tolower(key); + + busy(); + cmd(top, ".srch.menu delete 0 end"); + count := 0; + while ((l := bio->data.gets('\n')) != nil) { + l = str->tolower(l); + if (str->prefix(key, l)) { + if (len l > 1 && l[len l - 2] == '\r') + l = l[0:len l - 2]; + else + l = l[0:len l - 1]; + flds := fields(l, '\t'); + cmd(top, ".srch.menu insert end '" + +fieldindex(flds, 1)+": "+fieldindex(flds, 0)); + update(top); + count++; + } + } + notbusy(); + if (count == 0) { + notice("No match"); + return 0; + } + return 1; +} + +nomodule(s: string) +{ + sys->fprint(stderr, "couldn't load modules %s: %r\n", s); + raise "could not load modules"; +} + +config() +{ + sys = load Sys Sys->PATH; + if(ctxt == nil){ + sys->fprint(stderr, "unibrowse: window manager required\n"); + raise "no wm"; + } + sys->pctl(Sys->NEWPGRP, nil); + stderr = sys->fildes(2); + + draw = load Draw Draw->PATH; + if (draw == nil) nomodule(Draw->PATH); + + tk = load Tk Tk->PATH; + if (tk == nil) nomodule(Tk->PATH); + + tkclient = load Tkclient Tkclient->PATH; + if (tkclient == nil) nomodule(Tkclient->PATH); + + dialog = load Dialog Dialog->PATH; + if (dialog == nil) nomodule(Dialog->PATH); + + selectfile = load Selectfile Selectfile->PATH; + if (selectfile == nil) nomodule(Selectfile->PATH); + + str = load String String->PATH; + if (str == nil) nomodule(String->PATH); + + bio = load Bufio Bufio->PATH; + if (bio == nil) nomodule(Bufio->PATH); + + tkclient->init(); + dialog->init(); + selectfile->init(); + + ctxt = ctxt; + + (top, wmchan) = tkclient->toplevel(ctxt, nil, "Unicode browser", Tkclient->Hide); + + displ = Widgetstack.new(".display"); + cmd(top, "pack .display"); + + for (i := 0; i < len elements; i++) { + elements[i].cmd = tkchan(elements[i].cmdname); + runconfig(top, elements[i].config); + } + + runconfig(top, toplevelconfig); + + inspchan = chan of string; + spawn inspector(ctxt, inspchan); +} + +runconfig(top: ref Tk->Toplevel, cmds: array of string) +{ + for (i := 0; i < len cmds; i++) { + ent := tkexpand(cmds[i]); + if (ent != nil) { + err := cmd(top, ent); + if (len err > 0 && err[0] == '!') + sys->fprint(stderr, "config err: %s on '%s'\n", err, ent); + } + } +} + +update(top: ref Tk->Toplevel) +{ cmd(top, "update"); } + +busy() +{ cmd(top, "cursor -image waiting"); } + +notbusy() +{ cmd(top, "cursor -default"); } + +initelement(el: int): int +# returns non-zero on success +{ + if (!elements[el].doneinit) { + elements[el].doneinit = 1; + case el { + MAINMENU => + for (e := entries; len e > 0; e = e[1:]) { + (text, nil) := e[0]; + cmd(top, ".main.menu insert end '" + text); + } + + BYCATEGORY => + cats := getcategories(); + if (cats == nil) { + notice("No categories found"); + elements[el].doneinit = 0; + return 0; + } + while (cats != nil) { + cmd(top, ".cat.menu insert 0 '" + hd cats); + cats = tl cats; + } + BYFONT => + elements[el].doneinit = 0; # do it each time + fonts := getfonts(currfont); + if (fonts == nil) { + notice("Can't find font information file"); + return 0; + } + + cmd(top, ".font.menu delete 0 end"); + while (fonts != nil) { + cmd(top, ".font.menu insert 0 '" + hd fonts); + fonts = tl fonts; + } + TABLE => + inittable(); + } + + } + return 1; +} + +tablecharpath(col, row: int): string +{ + return ".tbl.tf.c"+string row+"_"+string col; +} + +inittable() +{ + i: int; + for (i = 0; i < Tablerows; i++) { + cmd(top, tkexpand("$label title .tbl.tf.num" + string i)); + cmd(top, sys->sprint("grid .tbl.tf.num%d -row %d", i, i)); + + # >>> could put entry here + for (j := 0; j < Tablecols; j++) { + cname := ".tbl.tf.c" + string i +"_" +string j; + cmd(top, tkexpand("$label unicode "+cname + +" -borderwidth 1 -relief raised")); + cmd(top, "bind "+cname+" <ButtonRelease-1>" + +" {send tblcmd set "+string j +" "+string i+"}"); + cmd(top, "grid "+cname+" -row "+string i+" -column "+string (j+1) + + " -sticky ews"); + } + } +} + +# fill table starting at n. +# return actual starting value. +filltable(n: int): int +{ + if (n < 0) + n = 0; + if (n + Tablerows * Tablecols > 16rffff) + n = 16rffff - Tablerows * Tablecols; + n -= n % Tablecols; + for (i := 0; i < Tablerows; i++) { + cmd(top, ".tbl.tf.num" + string i +" configure -text '" + + sys->sprint("%4.4X",n+i*Tablecols)); + for (j := 0; j < Tablecols; j++) { + cname := tablecharpath(j, i); + cmd(top, cname + " configure -text '" + +sys->sprint("%c", n + i * Tablecols + j)); + } + } + return n; +} + +cnumtoint(s: string): int +{ + if (len s == 0) + return 0; + if (s[0] == '0' && len s > 1) { + n: int; + if (s[1] == 'x' || s[1] == 'X') { + if (len s < 3) + return 0; + (n, nil) = str->toint(s[2:], 16); + } else + (n, nil) = str->toint(s, 8); + return n; + } + return int s; +} + +getfonts(font: string): list of string +{ + f := bio->open(font, bio->OREAD); + if (f == nil) + return nil; + + # ignore header + if (bio->f.gets('\n') == nil) + return nil; + + ret: list of string; + while ((s := bio->f.gets('\n')) != nil) { + (count, wds) := sys->tokenize(s, " \t"); + if (count < 3 || count > 4) + continue; # ignore malformed lines + first := cnumtoint(hd wds); + wds = tl wds; + last := cnumtoint(hd wds); + wds = tl wds; + if (tl wds != nil) # if optional third field exists + wds = tl wds; # ignore it + name := hd wds; + if (name != "" && name[len name - 1] == '\n') + name = name[0:len name - 1]; + ret = sys->sprint("%.4X-%.4X: %s", first, last, name) :: ret; + } + return ret; +} + +getcategories(): list of string +{ + f := bio->open(UNIBLOCKS, bio->OREAD); + if (f == nil) + return nil; + + ret: list of string; + while ((s := bio->f.gets('\n')) != nil) { + if (s[0] == '#') + continue; + (s, nil) = str->splitr(s, "^\n\r"); + if (len s > 0) { + start, end: string; + (start, s) = str->splitl(s, ";"); + s = str->drop(s, "; "); + (end, s) = str->splitl(s, ";"); + s = str->drop(s, "; "); + + ret = start+"-"+end+": "+s :: ret; + } + } + return ret; +} + + +tkexpand(s: string): string +{ + if (len s == 0 || s[0] != '$') + return s; + + cmd, tp, name: string; + (cmd, s) = str->splitl(s, " \t"); + cmd = cmd[1:]; + + s = str->drop(s, " \t"); + (tp, s) = str->splitl(s, " \t"); + s = str->drop(s, " \t"); + + (name, s) = str->splitl(s, " \t"); + s = str->drop(s, " \t"); + + font := ""; + case tp { + "deflt" => font = DEFAULTFONT; + "title" => font = TITLEFONT; + "data" => font = DATAFONT; + "button" => font = BUTTONFONT; + "unicode" => font = currfont; + } + if (font != nil) { + if (font[0] != '/') + font = "/fonts/"+font+".font"; + font = "-font "+font; + } + + + ret := cmd+" "+name+" "+font+" "+s; + return ret; +} + +categname(s: string): string +{ + r := "Unknown category"; + case s { + "Mn" => r = "Mark, Non-Spacing "; + "Mc" => r = "Mark, Combining"; + "Nd" => r = "Number, Decimal Digit"; + "No" => r = "Number, Other"; + "Zs" => r = "Separator, Space"; + "Zl" => r = "Separator, Line"; + "Zp" => r = "Separator, Paragraph"; + "Cc" => r = "Other, Control or Format"; + "Co" => r = "Other, Private Use"; + "Cn" => r = "Other, Not Assigned"; + "Lu" => r = "Letter, Uppercase"; + "Ll" => r = "Letter, Lowercase"; + "Lt" => r = "Letter, Titlecase "; + "Lm" => r = "Letter, Modifier"; + "Lo" => r = "Letter, Other "; + "Pd" => r = "Punctuation, Dash"; + "Ps" => r = "Punctuation, Open"; + "Pe" => r = "Punctuation, Close"; + "Po" => r = "Punctuation, Other"; + "Sm" => r = "Symbol, Math"; + "Sc" => r = "Symbol, Currency"; + "So" => r = "Symbol, Other"; + } + return r; +} + + +fields(s: string, sep: int): list of string +# seperator can't be '^' (see string(2)) +{ + cl := ""; cl[0] = sep; + ret: list of string; + do { + (l, r) := str->splitr(s, cl); + ret = r :: ret; + if (len l > 0) + s = l[0:len l - 1]; + else + s = nil; + } while (s != nil); + return ret; +} + +fieldindex(sl: list of string, n: int): string +{ + for (; sl != nil; sl = tl sl) { + if (n == 0) + return hd sl; + n--; + } + return nil; +} + +push(el: int) +{ + if (initelement(el)) { + displ.push(elements[el].name); + } +} + +pop(el: int) +# pop elements until we encounter one matching el. +{ + while (displ.top() != elements[el].name) + displ.pop(); +} + +tkchan(nm: string): chan of string +{ + c := chan of string; + tk->namechan(top, c, nm); + return c; +} + +cmd(top: ref Tk->Toplevel, s: string): string +{ + # sys->print("%s\n", s); + e := tk->cmd(top, s); + if (e != nil && e[0] == '!') + sys->fprint(sys->fildes(2), "tk error on '%s': %s\n", s, e); + return e; +} + +labelset(t: ref Tk->Toplevel, name: string, val: string) +{ + cmd(t, name+" configure -text '"+val); +} + + +choosefont(ctxt: ref Draw->Context): string +{ + font := selectfile->filename(ctxt, top.image, "Select a font", "*.font" :: nil, "/fonts"); + if (font != nil) { + ret := cmd(top, ".fontlabel configure"+" -font "+font); + if (len ret > 0 && ret[0] == '!') { + font = nil; + notice("Bad font: "+ret[1:]); + } + } + return font; +} + +updatefont() +{ + if (elements[TABLE].doneinit) # only if table is being displayed + for (i := 0; i < Tablerows; i++) + for (j := 0; j < Tablecols; j++) + cmd(top, tablecharpath(j, i) + " configure -font "+currfont); + # update the font display table if it's being displayed + for (el := displ.stk; el != nil; el = tl el) { + if (hd el == elements[BYFONT].name) { + initelement(BYFONT); + } + } + inspchan <-= "font"; +} + + +winorg(t: ref Tk->Toplevel): Draw->Point +{ + return Draw->Point(int cmd(t, ". cget -x"), int cmd(t, ". cget -y")); +} + +Widgetstack.new(wn: string): ref Widgetstack +{ + cmd(top, "frame "+wn+" -borderwidth 4 -relief ridge"); + + return ref Widgetstack(nil, wn); +} + +Widgetstack.push(ws: self ref Widgetstack, w: string) +{ + if (w == nil) + return; + opts: con " -fill y -side left"; + + if (ws.stk == nil) { + cmd(top, "pack "+w+" -in "+ws.name+" "+opts); + } else { + cmd(top, "pack "+w+" -after "+hd ws.stk+" "+opts); + } + + ws.stk = w :: ws.stk; +} + +Widgetstack.pop(ws: self ref Widgetstack): string +{ + if (ws.stk == nil) { + sys->fprint(stderr, "widget stack underflow!\n"); + exit; + } + old := hd ws.stk; + ws.stk = tl ws.stk; + cmd(top, "pack forget "+old); + return old; +} + +Widgetstack.top(ws: self ref Widgetstack): string +{ + if (ws.stk == nil) + return nil; + return hd ws.stk; +} + +# binary search for key in f. +# code converted from bsd source without permission. +look(f: ref bio->Iobuf, sep: int, key: string): string +{ + bot := mid := big 0; + ktop := bio->f.seek(big 0, Sys->SEEKEND); + key = canon(key, sep); + + for (;;) { + mid = (ktop + bot) / big 2; + bio->f.seek(mid, Sys->SEEKSTART); + c: int; + do { + c = bio->f.getb(); + mid++; + } while (c != bio->EOF && c != bio->ERROR && c != '\n'); + (entry, eof) := getword(f); + if (entry == nil && eof) + break; + entry = canon(entry, sep); + case comparewords(key, entry) { + -2 or -1 or 0 => + if (ktop <= mid) + break; + ktop = mid; + continue; + 1 or 2 => + bot = mid; + continue; + } + break; + } + bio->f.seek(bot, Sys->SEEKSTART); + while (bio->f.seek(big 0, Sys->SEEKRELA) < ktop) { + (entry, eof) := getword(f); + if (entry == nil && eof) + return nil; + word := canon(entry, sep); + case comparewords(key, word) { + -2 => + return nil; + -1 or 0 => + return entry; + 1 or 2 => + continue; + } + break; + } + for (;;) { + (entry, eof) := getword(f); + if (entry == nil && eof) + return nil; + word := canon(entry, sep); + case comparewords(key, word) { + -1 or 0 => + return entry; + } + break; + } + return nil; +} + +comparewords(s, t: string): int +{ + if (s == t) + return 0; + i := 0; + for (; i < len s && i < len t && s[i] == t[i]; i++) + ; + if (i >= len s) + return -1; + if (i >= len t) + return 1; + if (s[i] < t[i]) + return -2; + return 2; +} + +getword(f: ref bio->Iobuf): (string, int) +{ + ret := ""; + for (;;) { + c := bio->f.getc(); + if (c == bio->EOF || c == bio->ERROR) + return (ret, 0); + if (c == '\n') + break; + ret[len ret] = c; + } + return (ret, 1); +} + +canon(s: string, sep: int): string +{ + if (sep < 0) + return s; + i := 0; + for (; i < len s; i++) + if (s[i] == sep) + break; + return s[0:i]; +} diff --git a/appl/wm/view.b b/appl/wm/view.b new file mode 100644 index 00000000..c96ef87d --- /dev/null +++ b/appl/wm/view.b @@ -0,0 +1,484 @@ +implement View; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + Context, Rect, Point, Display, Screen, Image: import draw; + +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; + +include "imagefile.m"; + imageremap: Imageremap; + readgif: RImagefile; + readjpg: RImagefile; + readxbitmap: RImagefile; + readpng: RImagefile; + +include "tk.m"; + tk: Tk; + Toplevel: import tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "selectfile.m"; + selectfile: Selectfile; + +include "arg.m"; + +include "plumbmsg.m"; + plumbmsg: Plumbmsg; + Msg: import plumbmsg; + +stderr: ref Sys->FD; +display: ref Display; +x := 25; +y := 25; +img_patterns: list of string; +plumbed := 0; +background: ref Image; + +View: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +init(ctxt: ref Draw->Context, argv: list of string) +{ + spawn realinit(ctxt, argv); +} + +realinit(ctxt: ref Draw->Context, argv: list of string) +{ + sys = load Sys Sys->PATH; + if (ctxt == nil) { + sys->fprint(sys->fildes(2), "view: no window context\n"); + raise "fail:bad context"; + } + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + selectfile = load Selectfile Selectfile->PATH; + + sys->pctl(Sys->NEWPGRP, nil); + tkclient->init(); + selectfile->init(); + + stderr = sys->fildes(2); + display = ctxt.display; + background = display.color(16r222222ff); + + arg := load Arg Arg->PATH; + if(arg == nil) + badload(Arg->PATH); + + img_patterns = list of { + "*.bit (Compressed image files)", + "*.gif (GIF image files)", + "*.jpg (JPEG image files)", + "*.jpeg (JPEG image files)", + "*.png (PNG image files)", + "*.xbm (X Bitmap image files)" + }; + + imageremap = load Imageremap Imageremap->PATH; + if(imageremap == nil) + badload(Imageremap->PATH); + + bufio = load Bufio Bufio->PATH; + if(bufio == nil) + badload(Bufio->PATH); + + + arg->init(argv); + errdiff := 1; + while((c := arg->opt()) != 0) + case c { + 'f' => + errdiff = 0; + 'i' => + if(!plumbed){ + plumbmsg = load Plumbmsg Plumbmsg->PATH; + if(plumbmsg != nil && plumbmsg->init(1, "view", 1000) >= 0) + plumbed = 1; + } + } + argv = arg->argv(); + arg = nil; + if(argv == nil && !plumbed){ + f := selectfile->filename(ctxt, nil, "View file name", img_patterns, nil); + if(f == "") { + #spawn view(nil, nil, ""); + return; + } + argv = f :: nil; + } + + + for(;;){ + file: string; + if(argv != nil){ + file = hd argv; + argv = tl argv; + if(file == "-f"){ + errdiff = 0; + continue; + } + }else if(plumbed){ + file = plumbfile(); + if(file == nil) + break; + errdiff = 1; # set this from attributes? + }else + break; + + (ims, masks, err) := readimages(file, errdiff); + + if(ims == nil) + sys->fprint(stderr, "view: can't read %s: %s\n", file, err); + else + spawn view(ctxt, ims, masks, file); + } +} + +badload(s: string) +{ + sys->fprint(stderr, "view: can't load %s: %r\n", s); + raise "fail:load"; +} + +readimages(file: string, errdiff: int) : (array of ref Image, array of ref Image, string) +{ + im := display.open(file); + + if(im != nil) + return (array[1] of {im}, array[1] of ref Image, nil); + + fd := bufio->open(file, Sys->OREAD); + if(fd == nil) + return (nil, nil, sys->sprint("%r")); + + (mod, err1) := filetype(file, fd); + if(mod == nil) + return (nil, nil, err1); + + (ai, err2) := mod->readmulti(fd); + if(ai == nil) + return (nil, nil, err2); + if(err2 != "") + sys->fprint(stderr, "view: %s: %s\n", file, err2); + ims := array[len ai] of ref Image; + masks := array[len ai] of ref Image; + for(i := 0; i < len ai; i++){ + masks[i] = transparency(ai[i], file); + + # if transparency is enabled, errdiff==1 is probably a mistake, + # but there's no easy solution. + (ims[i], err2) = imageremap->remap(ai[i], display, errdiff); + if(ims[i] == nil) + return(nil, nil, err2); + } + return (ims, masks, nil); +} + +viewcfg := array[] of { + "panel .p", + "menu .m", + ".m add command -label Open -command {send cmd open}", + ".m add command -label Grab -command {send cmd grab} -state disabled", + ".m add command -label Save -command {send cmd save}", + "pack .p -side bottom -fill both -expand 1", + "bind .p <Button-3> {send cmd but3 %X %Y}", + "bind .p <Motion-Button-3> {}", + "bind .p <ButtonRelease-3> {}", + "bind .p <Button-1> {send but1 %X %Y}", +}; + +DT: con 250; + +timer(dt: int, ticks, pidc: chan of int) +{ + pidc <-= sys->pctl(0, nil); + for(;;){ + sys->sleep(dt); + ticks <-= 1; + } +} + +view(ctxt: ref Context, ims, masks: array of ref Image, file: string) +{ + file = lastcomponent(file); + (t, titlechan) := tkclient->toplevel(ctxt, "", "view: "+file, Tkclient->Hide); + + cmd := chan of string; + tk->namechan(t, cmd, "cmd"); + but1 := chan of string; + tk->namechan(t, but1, "but1"); + + for (c:=0; c<len viewcfg; c++) + tk->cmd(t, viewcfg[c]); + tk->cmd(t, "update"); + + image := display.newimage(ims[0].r, ims[0].chans, 0, Draw->White); + if (image == nil) { + sys->fprint(stderr, "view: can't create image: %r\n"); + return; + } + imconfig(t, image); + image.draw(image.r, ims[0], masks[0], ims[0].r.min); + tk->putimage(t, ".p", image, nil); + tk->cmd(t, "update"); + + pid := -1; + ticks := chan of int; + if(len ims > 1){ + pidc := chan of int; + spawn timer(DT, ticks, pidc); + pid = <-pidc; + } + imno := 0; + grabbing := 0; + tkclient->onscreen(t, nil); + tkclient->startinput(t, "kbd"::"ptr"::nil); + + + for(;;) alt{ + s := <-t.ctxt.kbd => + tk->keyboard(t, s); + s := <-t.ctxt.ptr => + tk->pointer(t, *s); + s := <-t.ctxt.ctl or + s = <-t.wreq or + s = <-titlechan => + tkclient->wmctl(t, s); + + <-ticks => + if(masks[imno] != nil) + paneldraw(t, image, image.r, background, nil, image.r.min); + ++imno; + if(imno >= len ims) + imno = 0; + paneldraw(t, image, ims[imno].r, ims[imno], masks[imno], ims[imno].r.min); + tk->cmd(t, "update"); + + s := <-cmd => + (nil, l) := sys->tokenize(s, " "); + case (hd l) { + "open" => + spawn open(ctxt, t); + "grab" => + tk->cmd(t, "cursor -bitmap cursor.drag; grab set .p"); + grabbing = 1; + "save" => + patterns := list of { + "*.bit (Inferno image files)", + "*.gif (GIF image files)", + "*.jpg (JPEG image files)", + "* (All files)" + }; + f := selectfile->filename(ctxt, t.image, "Save file name", + patterns, nil); + if(f != "") { + fd := sys->create(f, Sys->OWRITE, 8r664); + if(fd != nil) + display.writeimage(fd, ims[0]); + } + "but3" => + if(!grabbing) { + xx := int hd tl l - 50; + yy := int hd tl tl l - int tk->cmd(t, ".m yposition 0") - 10; + tk->cmd(t, ".m activate 0; .m post "+string xx+" "+string yy+ + "; grab set .m; update"); + } + } + s := <- but1 => + if(grabbing) { + (nil, l) := sys->tokenize(s, " "); + xx := int hd l; + yy := int hd tl l; +# grabtop := tk->intop(ctxt.screen, xx, yy); +# if(grabtop != nil) { +# cim := grabtop.image; +# imr := Rect((0,0), (cim.r.dx(), cim.r.dy())); +# image = display.newimage(imr, cim.chans, 0, draw->White); +# if(image == nil){ +# sys->fprint(stderr, "view: can't allocate image\n"); +# exit; +# } +# image.draw(imr, cim, nil, cim.r.min); +# tk->cmd(t, ".Wm_t.title configure -text {View: grabbed}"); +# imconfig(t, image); +# tk->putimage(t, ".p", image, nil); +# tk->cmd(t, "update"); +# # Would be nicer if this could be spun off cleanly +# ims = array[1] of {image}; +# masks = array[1] of ref Image; +# imno = 0; +# grabtop = nil; +# cim = nil; +# } + tk->cmd(t, "cursor -default; grab release .p"); + grabbing = 0; + } + } +} + +open(ctxt: ref Context, t: ref tk->Toplevel) +{ + f := selectfile->filename(ctxt, t.image, "View file name", img_patterns, nil); + t = nil; + if(f != "") { + (ims, masks, err) := readimages(f, 1); + if(ims == nil) + sys->fprint(stderr, "view: can't read %s: %s\n", f, err); + else + view(ctxt, ims, masks, f); + } +} + +lastcomponent(path: string) : string +{ + for(k:=len path-2; k>=0; k--) + if(path[k] == '/'){ + path = path[k+1:]; + break; + } + return path; +} + +imconfig(t: ref Toplevel, im: ref Draw->Image) +{ + width := im.r.dx(); + height := im.r.dy(); + tk->cmd(t, ".p configure -width " + string width + + " -height " + string height + "; update"); +} + +plumbfile(): string +{ + if(!plumbed) + return nil; + for(;;){ + msg := Msg.recv(); + if(msg == nil){ + sys->print("view: can't read /chan/plumb.view: %r\n"); + return nil; + } + if(msg.kind != "text"){ + sys->print("view: can't interpret '%s' kind of message\n", msg.kind); + continue; + } + file := string msg.data; + if(len file>0 && file[0]!='/' && len msg.dir>0){ + if(msg.dir[len msg.dir-1] == '/') + file = msg.dir+file; + else + file = msg.dir+"/"+file; + } + return file; + } +} + +Tab: adt +{ + suf: string; + path: string; + mod: RImagefile; +}; + +GIF, JPG, PIC, PNG, XBM: con iota; + +tab := array[] of +{ + GIF => Tab(".gif", RImagefile->READGIFPATH, nil), + JPG => Tab(".jpg", RImagefile->READJPGPATH, nil), + PIC => Tab(".pic", RImagefile->READPICPATH, nil), + XBM => Tab(".xbm", RImagefile->READXBMPATH, nil), + PNG => Tab(".png", RImagefile->READPNGPATH, nil), +}; + +filetype(file: string, fd: ref Iobuf): (RImagefile, string) +{ + for(i:=0; i<len tab; i++){ + n := len tab[i].suf; + if(len file>n && file[len file-n:]==tab[i].suf) + return loadmod(i); + } + + # sniff the header looking for a magic number + buf := array[20] of byte; + if(fd.read(buf, len buf) != len buf) + return (nil, sys->sprint("%r")); + fd.seek(big 0, 0); + if(string buf[0:6]=="GIF87a" || string buf[0:6]=="GIF89a") + return loadmod(GIF); + if(string buf[0:5] == "TYPE=") + return loadmod(PIC); + jpmagic := array[] of {byte 16rFF, byte 16rD8, byte 16rFF, byte 16rE0, + byte 0, byte 0, byte 'J', byte 'F', byte 'I', byte 'F', byte 0}; + if(eqbytes(buf, jpmagic)) + return loadmod(JPG); + pngmagic := array[] of {byte 137, byte 80, byte 78, byte 71, byte 13, byte 10, byte 26, byte 10}; + if(eqbytes(buf, pngmagic)) + return loadmod(PNG); + if(string buf[0:7] == "#define") + return loadmod(XBM); + return (nil, "can't recognize file type"); +} + +eqbytes(buf, magic: array of byte): int +{ + for(i:=0; i<len magic; i++) + if(magic[i]>byte 0 && buf[i]!=magic[i]) + return 0; + return i == len magic; +} + +loadmod(i: int): (RImagefile, string) +{ + if(tab[i].mod == nil){ + tab[i].mod = load RImagefile tab[i].path; + if(tab[i].mod == nil) + sys->fprint(stderr, "view: can't find %s reader: %r\n", tab[i].suf); + else + tab[i].mod->init(bufio); + } + return (tab[i].mod, nil); +} + +transparency(r: ref RImagefile->Rawimage, file: string): ref Image +{ + if(r.transp == 0) + return nil; + if(r.nchans != 1){ + sys->fprint(stderr, "view: can't do transparency for multi-channel image %s\n", file); + return nil; + } + i := display.newimage(r.r, display.image.chans, 0, 0); + if(i == nil){ + sys->fprint(stderr, "view: can't allocate mask for %s: %r\n", file); + exit; + } + pic := r.chans[0]; + npic := len pic; + mpic := array[npic] of byte; + index := r.trindex; + for(j:=0; j<npic; j++) + if(pic[j] == index) + mpic[j] = byte 0; + else + mpic[j] = byte 16rFF; + i.writepixels(i.r, mpic); + return i; +} + +paneldraw(t: ref Tk->Toplevel, dst: ref Image, r: Rect, src, mask: ref Image, p: Point) +{ + dst.draw(r, src, mask, p); + s := sys->sprint(".p dirty %d %d %d %d", r.min.x, r.min.y, r.max.x, r.max.y); + tk->cmd(t, s); +} diff --git a/appl/wm/vt.b b/appl/wm/vt.b new file mode 100644 index 00000000..6813e226 --- /dev/null +++ b/appl/wm/vt.b @@ -0,0 +1,1007 @@ +implement WmVt; + +# note: this code was hacked together in a hurry from some decade-old C code +# of mine, so don't expect it to be pretty... +# Also, don't expect it to be finished... I had to rush to check this +# in... it's just been worked on as a side-project from time to time +# But it's good enough to be useful most of the time + +include "sys.m"; + sys: Sys; + sprint: import sys; +include "draw.m"; + draw: Draw; + Display, Font, Black, Rect, Image, Point, Endsquare, Enddisc: import draw; +include "tk.m"; + tk: Tk; + Toplevel: import tk; +include "tkclient.m"; + tkclient: Tkclient; +include "sh.m"; + +CON_Maxnpts: con 1000; +Maxnhits: con 5; + + +WmVt: module { + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + + + +VT_MAXPARAM: con 8; + + +Vt: adt { + y1, y2: int; + mode: int; # misc mode parameters + qmode: int; # extended mode parameters + attr: int; # display attributes + fg: int; # foreground color + bg: int; # background color + + # saved values: + save_x, save_y: int; + save_attr: int; + save_fg, save_bg: int; + save_mode: int; + save_qmode: int; + + # escape code parsing: + esc: int; # escape mode + pcount: int; # parameter count + etype: int; # escape code type + ptype: int; # current parameter type + value: int; # current value + param: array of int; + + # display info: + wid, hgt: int; + x, y: int; + dx, dy: int; + nlcr: int; + ccc: int; + scr: array of string; + cc: array of string; +}; + + +display: ref Display; +t: ref Toplevel; +canvas: ref Image; +canvrect: Rect; +org: Point; +font: ref Font; +stderr: ref Sys->FD; +vt: ref Vt; +pad: string; +vtc := array[16] of ref Image; +raw := 0; +echo := 1; +reverse := 0; +sq := ""; + +inpchan: chan of string; + + +shwin_cfg := array[] of { + "frame .f", + "pack .c .f -side top -fill x", + "pack propagate . 0", + "focus .f", + "bind .f <Key> {send keys {%A}}", + "bind . <Configure> {send cmd resize}", + "update" +}; + + +titlebar() +{ + tk->cmd(t, "destroy .Wm_t.S"); + tk->cmd(t, "button .Wm_t.S -bg #aaaaaa -fg white -text {" + + sprint("%d x %d", vt.wid, vt.hgt) + "}; " + + "pack .Wm_t.S -side right"); + c := "green"; + if(raw) + c = "red"; + tk->cmd(t, "destroy .Wm_t.k"); + tk->cmd(t, "button .Wm_t.k -bitmap keyboard.bit"+ + " -background "+c+" -command {send wm_title raw}; " + + "pack .Wm_t.k -side right"); + c = "red"; + if(echo) + c = "green"; + tk->cmd(t, "destroy .Wm_t.d"); + tk->cmd(t, "button .Wm_t.d -bitmap display.bit"+ + " -background "+c+" -command {send wm_title echo}; " + + "pack .Wm_t.d -side right"); + c = "white"; + if(reverse) + c = "black"; + tk->cmd(t, "destroy .Wm_t.r"); + tk->cmd(t, "button .Wm_t.r -width 24 -height 24 "+ + " -background "+c+" -command {send wm_title reverse}; " + + "pack .Wm_t.r -side right"); + tk->cmd(t, "update"); +} + +init(ctxt: ref Draw->Context, nil: list of string) +{ + sys = load Sys Sys->PATH; + if (ctxt == nil) { + sys->fprint(sys->fildes(2), "vt: no window context\n"); + raise "fail:bad context"; + } + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + + stderr = sys->fildes(2); + + sys->pctl(Sys->FORKNS, nil); + sys->pctl(Sys->NEWPGRP, nil); + + menubut: chan of string; + tkclient->init(); + (t, menubut) = tkclient->toplevel(ctxt, "", "WmVt", Tkclient->Appl); + + display = ctxt.display; + font = Font.open(display, "*default*"); + + vt = ref Vt; + vt.hgt = 24; + vt.wid = 80; + vt.scr = array[vt.hgt] of string; + vt.cc = array[vt.hgt] of string; + vt_init(vt); + + pad = ""; + for(i:=0; i<vt.wid; i++) + pad[i] = ' '; + + cmd := chan of string; + tk->namechan(t, cmd, "cmd"); + tk->cmd(t, "canvas .c -height " + + string (vt.hgt*font.height) + + + " -width " + string (vt.wid*font.width("0")) + + " -background red"); + tkcmds(t, shwin_cfg); + tkclient->onscreen(t, nil); + tkclient->startinput(t, "kbd"::"ptr"::nil); + titlebar(); + + keys := chan of string; + tk->namechan(t, keys, "keys"); + + canvas = t.image; + canvrect = canvposn(t); + org = canvrect.min; + + npts := 0; + WasUp := 1; + + for(i=0; i<16; i++) { + r := 0; + g := 0; + b := 0; + v := 192; + if(i&8) + v = 255; + if(i&1) + r = v; + if(i&2) + g = v; + if(i&4) + b = v; + vtc[i] = display.newimage(((0,0),(1,1)), t.image.chans, + 1, display.rgb2cmap(r, g, b)); + if (vtc[i] == nil) { + sys->fprint(sys->fildes(2), "Failed to allocate image\n"); + exit; + } + } + + vt_write(vt, "\u001b[2J"); + + ioc := chan of (int, ref Sys->FileIO, ref Sys->FileIO); + spawn newsh(ctxt, ioc); + + (pid, file, filectl) := <- ioc; + if((file == nil) || (filectl == nil)) { + sys->print("newsh: %r\n"); + return; + } + + # XXX - need to kill this later + ic := chan of string; + spawn consinp(ic, file.read); + + inpchan = ic; # hack + + for(;;) alt { + s := <-t.ctxt.kbd => + tk->keyboard(t, s); + s := <-t.ctxt.ptr => + tk->pointer(t, *s); + s := <-t.ctxt.ctl or + s = <-t.wreq => + tkclient->wmctl(t, s); + menu := <- menubut => + if(menu == "exit") { + kill(pid); + return; + } + else if(menu == "raw") { + raw = !raw; + titlebar(); + redraw(); + } + else if(menu == "echo") { + echo = !echo; + titlebar(); + redraw(); + } + else if(menu == "reverse") { + reverse = !reverse; + tmp := vtc[0]; + vtc[0] = vtc[7]; + vtc[7] = tmp; + titlebar(); + redraw(); + } else + tkclient->wmctl(t, menu); + tk->cmd(t, "focus .f"); + + s := <- cmd => + (n, cmdstr) := sys->tokenize(s, " \t\n"); + case hd cmdstr { + "quit" => + exit; + "resize" => + # sys->print("resize\n"); + canvas = t.image; + canvrect = canvposn(t); + org = canvrect.min; + # sys->print("%d,%d %d,%d\n", canvrect.max.x, canvrect.min.x, + # canvas.r.max.x, canvas.r.min.x); + resize((canvrect.max.x-canvrect.min.x)/font.width("0"), + (canvrect.max.y-canvrect.min.y)/font.height); + titlebar(); + redraw(); + } + + c := <- keys => + ic <-= c[1:2]; + if(echo) + scwrite(c[1:2]); + + (off, data, fid, wc) := <- file.write => + if(wc == nil) + return; + if(echo && !raw && sq != "") { + s := ""; + for(i=0; i<len sq; i++) + s += "\b \b"; + scwrite(s); + } + scwrite(string data); + if(echo && !raw && sq != "") + scwrite(sq); + wc <-= (len data, nil); + (off, data, fid, wc) := <- filectl.write => + if(string data == "rawon") { + raw = 1; + echo = 0; + titlebar(); + redraw(); + } + if(string data == "rawoff") { + raw = 0; + echo = 1; + titlebar(); + redraw(); + } + wc <-= (len data, nil); + } +} + +resize(wid,hgt: int) +{ + scr := array[hgt] of string; + cc := array[hgt] of string; + for(y :=0; y<hgt; y++) { + oy := y + hgt - vt.hgt; + if(oy < vt.hgt && oy >= 0) { + scr[y] = vt.scr[oy]; + cc[y] = vt.cc[oy]; + } else { + scr[y] = ""; + cc[y] = ""; + } + } + vt.x += wid - vt.wid; + vt.y += hgt - vt.hgt; + if(vt.x < 0) + vt.x = 0; + if(vt.x >= wid) + vt.x = wid; + if(vt.y < 0) + vt.y = 0; + if(vt.y >= hgt) + vt.y = hgt; + vt.wid = wid; + vt.hgt = hgt; + vt.scr = scr; + vt.cc = cc; +} + + +fixdx := 0; +fixdy := 0; + +canvposn(t: ref Toplevel): Rect +{ + r: Rect; + + r.min.x = int tk->cmd(t, ".c cget -actx") + int tk->cmd(t, ".dx get"); + r.min.y = int tk->cmd(t, ".c cget -acty") + int tk->cmd(t, ".dy get"); + r.max.x = r.min.x + int tk->cmd(t, ".c cget -width") + int tk->cmd(t, ".dw get"); + r.max.y = r.min.y + int tk->cmd(t, ".c cget -height") + int tk->cmd(t, ".dh get"); + + # correction for Tk bug (width/height not correct): + dx := (t.image.r.max.x - t.image.r.min.x) - (r.max.x - r.min.x); + dy := (t.image.r.max.y - t.image.r.min.y) - (r.max.y - r.min.y); + if(fixdx == 0) { + fixdx = dx; + fixdy = dy; + } else { + r.max.x += dx-fixdx; + r.max.y += dy-fixdy; + } + return r; +} + + +redraw() +{ + # sys->print("redraw\n"); + for(y:=0; y<vt.hgt; y++) { + xp := canvrect.min.x; + yp := canvrect.max.y-(vt.hgt-y)*font.height; + f := 0; + for(x:=0; x<=len vt.cc[y]; x++) { + if(x == len vt.cc[y] || (vt.cc[y][x]>>4) != (vt.cc[y][f]>>4)) { + if(x == len vt.cc[y]) + w := canvrect.max.x-xp; + else + w = font.width(vt.scr[y][f:x]); + if(len vt.cc[y] == 0) + ccc := 7; + else + ccc = vt.cc[y][f]; + canvas.draw(((xp,yp),(xp+w,yp+font.height)), + vtc[ccc>>4], nil, (0, 0)); + xp += w; + f = x; + } + } + xp = canvrect.min.x; + f = 0; + for(x=1; x<=len vt.scr[y]; x++) { + if(x == len vt.scr[y] || (vt.cc[y][x]&15) != (vt.cc[y][f]&15)) { + canvas.text((xp,yp), vtc[vt.cc[y][f]&15], + (0, 0), font, vt.scr[y][f:x]); + xp += font.width(vt.scr[y][f:x]); + f = x; + } + } + } +} + + + +scwrite(s: string) +{ + putchar(vt.x, vt.y, vtscr(vt.y, vt.x), vtcc(vt.y, vt.x)); + vt_write(vt, s); + putchar(vt.x, vt.y, vtscr(vt.y, vt.x), vtcc(vt.y, vt.x) ^ 16rff); +} + +putchar(x,y: int, ch: int, ccc: int) +{ + if(len vt.scr[y] < x) { + vt.scr[y] += pad[0:x-len vt.scr[y]]; + vt.cc[y] += pad[0:x-len vt.cc[y]]; + } + xp := canvrect.min.x+font.width(vt.scr[y][0:x]); + yp := canvrect.max.y-(vt.hgt-y)*font.height; + s: string; + s[0] = ch; + canvas.draw(((xp,yp),(xp+font.width(s),yp+font.height)), + vtc[ccc>>4], nil, (0, 0)); + canvas.text((xp,yp), vtc[ccc&15], (0, 0), font, s); +} + +VT_PUTCHAR(vt: ref Vt, x,y: int, ch: int) +{ + if(len vt.scr[y] < x) { + vt.scr[y] += pad[0:x-len vt.scr[y]]; + vt.cc[y] += pad[0:x-len vt.cc[y]]; + } + vt.scr[y][x] = ch; + vt.cc[y][x] = vt.ccc; + putchar(x, y, ch, int vt.ccc); +} + +VT_SCROLL_UP(vt: ref Vt, x1,y1,x2,y2,n: int) +{ + # XXX - needs to handle vertical slices + for(i:=y1; i<=y2-n; i++) { + vt.scr[i] = vt.scr[i+n]; + vt.cc[i] = vt.cc[i+n]; + } + r: Rect; + r.min.x = canvrect.min.x; + r.max.x = r.min.x+(x2-x1+1)*font.width(" "); + r.min.y = canvrect.max.y-(vt.hgt-y1)*font.height; + r.max.y = r.min.y+(y2-y1-n+1)*font.height; + canvas.draw(r, canvas, nil, (r.min.x, r.min.y+font.height*n)); + VT_CLEAR(vt, x1,y2-n+1,x2,y2); +} + +VT_SCROLL_DOWN(vt: ref Vt, x1,y1,x2,y2,n: int) +{ + # XXX - needs to handle vertical slices + for(i:=y2; i>=y1+n; i--) { + vt.scr[i] = vt.scr[i-n]; + vt.cc[i] = vt.cc[i-n]; + } + VT_CLEAR(vt, x1,y1,x2,y1+n-1); + redraw(); +} + +VT_SCROLL_LEFT(vt: ref Vt, x1,y1,x2,y2,n: int) +{ + # XXX - shouldn't always scroll whole line + for(y:=y1; y<=y2; y++) { + if(len vt.scr[y] > n) { + vt.scr[y] = vt.scr[y][n:]; + vt.cc[y] = vt.cc[y][n:]; + } else { + vt.scr[y] = ""; + vt.cc[y] = ""; + } + } + redraw(); +} + +VT_SCROLL_RIGHT(vt: ref Vt, x1,y1,x2,y2,n: int) +{ + # XXX - shouldn't always scroll whole line + for(y:=y1; y<=y2; y++) { + vt.scr[y] = pad[0:n] + vt.scr[y]; + vt.cc[y] = pad[0:n] + vt.cc[y]; + } + redraw(); +} + +VT_CLEAR(vt: ref Vt, x1,y1,x2,y2: int) +{ + # XXX - needs to handle vertical slices + for(y:=y1; y<=y2; y++) { + vt.scr[y] = ""; + vt.cc[y] = ""; + } + r: Rect; + r.min.x = canvrect.min.x; + r.max.x = r.min.x + (x2-x1+1)*font.width(" "); + r.min.y = canvrect.max.y-(vt.hgt-y1)*font.height; + r.max.y = r.min.y + (y2-y1+1)*font.height; + canvas.draw(r, vtc[vt.ccc>>4], nil, (0, 0)); +} + +VT_SET_COLOR(vt: ref Vt) +{ + if(vt.attr & (1<<7)) + vt.ccc = ((vt.fg<<4) | vt.bg); + else + vt.ccc = ((vt.bg<<4) | vt.fg); + if(vt.attr & (1<<1)) + vt.ccc ^= (1<<3); +} + +vtscr(y,x: int): int +{ + if(vt.scr[y] == nil) + return ' '; + if(x >= len vt.scr[y]) + return ' '; + return vt.scr[y][x]; +} + +vtcc(y,x: int): int +{ + if(vt.cc[y] == nil) + return 7; + if(x >= len vt.cc[y]) + return 7; + return vt.cc[y][x]; +} + +VT_SET_CURSOR(nil: ref Vt, x,y: int) +{ +} + +VT_BEEP(nil: ref Vt) +{ + redraw(); +} + +# function for simulated typing (for returning status) +VT_TYPE(vt: ref Vt, b: string) +{ + inpchan <-= b; +} + + +############################################################################# + + +vt_save_state(vt: ref Vt) +{ + vt.save_x = vt.x; + vt.save_y = vt.y; + vt.save_attr = vt.attr; + vt.save_fg = vt.fg; + vt.save_bg = vt.bg; + vt.save_mode = vt.mode; + vt.save_qmode = vt.qmode; +} + +vt_restore_state(vt: ref Vt) +{ + vt.x = vt.save_x; + vt.y = vt.save_y; + vt.attr = vt.save_attr; + vt.fg = vt.save_fg; + vt.bg = vt.save_bg; + vt.mode = vt.save_mode; + vt.qmode = vt.save_qmode; + VT_SET_COLOR(vt); +} + + + +# expects vt.wid, vt.hgt and implementation +# variables to be initialized first: + +vt_init(vt: ref Vt) +{ + vt.fg = 7; + vt.bg = 0; + vt.attr = 0; + vt.mode = 0; + vt.qmode = (1<<7); + vt.y1 = 0; + vt.y2 = vt.hgt-1; + vt.x = 0; + vt.y = 0; + vt.dx = 1; + vt.dy = 1; + vt.esc = 0; + vt.pcount = 0; + vt.param = array[VT_MAXPARAM] of int; + vt_save_state(vt); + VT_SET_COLOR(vt); +} + + +vt_checkscroll(vt: ref Vt, s: string) +{ + i := 0; + n: int; + if (vt.y == vt.y2+1 || vt.y >= vt.hgt) { + n = 1; + while(i < len s && n < (vt.y2-vt.y1)) { + c := s[i++]; + if(c == 27 || c > 126 || c < 0) + break; + if(c == '\n') + n++; + } + vt.y = vt.y2-n+1; + VT_SCROLL_UP(vt,0,vt.y1,vt.wid-1,vt.y2,n); + } else if (vt.y == vt.y1-1) { + vt.y = vt.y1; + VT_SCROLL_DOWN(vt,0,vt.y1,vt.wid-1,vt.y2,1); + } else if (vt.y < 0) + vt.y = 0; +} + +vt_write(vt: ref Vt, s: string) +{ + ch: int; + check_scroll: int; + n: int; + i := 0; + + while(i < len s) { + check_scroll = 0; + ch = s[i++]; + case vt.esc { + 1 => + if(ch == '[') { + vt.etype = ch; + vt.esc++; + vt.value = 0; + vt.pcount = 0; + vt.ptype = 1; + for(n=0; n<VT_MAXPARAM; n++) + vt.param[n] = 0; + } else { + check_scroll = vt_call_ncsi(vt, ch); + vt.esc = 0; + } + 2 => + if(ch >= '0' && ch <= '9') + vt.value=(vt.value)*10+(ch-'0'); + else if(ch == '?') + vt.ptype = -1; + else { + vt.param[vt.pcount++] = vt.value*vt.ptype; + if(ch == ';') { + if(vt.pcount >= VT_MAXPARAM) + vt.pcount = VT_MAXPARAM-1; + vt.value = 0; + } else { + check_scroll = vt_call_csi(vt, ch); + vt.esc = 0; + } + } + * => + case ch { + '\n' => + vt.y += vt.dy; + check_scroll = 1; + if(vt.nlcr) + vt.x = 0; + '\r' => + vt.x = 0; + '\b' => + if (vt.x > 0) + vt.x -= vt.dx; + '\t' => + n = (vt.x & ~7)+8; + if(vt.mode & (1<<4)) + VT_SCROLL_RIGHT(vt, vt.x,vt.y, + vt.wid-1,vt.y, n - vt.x); + vt.x = n; + if(vt.x > vt.wid) { + vt.x = 0; + vt.y++; + check_scroll = 1; + } + 7 => + VT_BEEP(vt); + 11 => + vt.x = 0; + vt.y = vt.y1; + 12 => + VT_CLEAR(vt,0,vt.y1,vt.wid-1,vt.y2); + 27 => + vt.esc++; + 133 => + vt.x = 0; + vt.y++; + check_scroll = 1; + 132 => + vt.y++; + check_scroll = 1; + 136 => # XXX - set a tabstop + ; + 141 => + vt.y--; + check_scroll = 1; + 142 => # XXX -- map G2 into GL for next char only + ; + 143 => # XXX -- map G3 into GL for next char + ; + 144 => # XXX -- device control string + ; + 145 => # XXX -- start of string - ignored + ; + 146 => # XXX -- device attribute request + ; + 147 => + vt.esc = 2; + vt.etype = '['; + vt.esc++; + vt.value = 0; + vt.pcount = 0; + vt.ptype = 1; + for(n=0; n<VT_MAXPARAM; n++) + vt.param[n] = 0; + * => + if(vt.mode & (1<<4)) + VT_SCROLL_RIGHT(vt,vt.x,vt.y, + vt.wid-1,vt.y,1); + if(ch>=32 || ch <=126) { + if(vt.qmode & (1<<15)) { + if(vt.x >= vt.wid-1 && (vt.qmode & (1<<7))) { + vt.x = 0; + vt.y += vt.dy; + vt_checkscroll(vt, s[i:]); + } + vt.qmode &= ~(1<<15); + } + VT_PUTCHAR(vt,vt.x,vt.y,ch); + if((vt.x += vt.dx) >= vt.wid) { + vt.x = vt.wid-1; + vt.qmode |= (1<<15); + } + } + } + } + if(check_scroll) + vt_checkscroll(vt, s[i:]); + if(vt.x < 0) + vt.x = 0; + else if(vt.x >= vt.wid) + vt.x = vt.wid-1; + if(vt.y < 0) + vt.y = 0; + else if(vt.y >= vt.hgt) + vt.y = vt.hgt-1; + } + VT_SET_CURSOR(vt, vt.x, vt.y); +} + + + + +vt_call_csi(vt: ref Vt, ch: int): int +{ + i, n: int; + case ch { + 'A' => + vt.y -= vt_param(vt, 1,1,1,vt.hgt); + 'B' => + vt.y += vt_param(vt, 1,1,1,vt.hgt); + 'C' => + vt.x += vt_param(vt, 1,1,1,vt.wid); + 'D' => + vt.x -= vt_param(vt, 1,1,1,vt.wid); + 'f' or 'H' => + vt.y = vt_param(vt, 0,1,1,vt.hgt)-1; + vt.x = vt_param(vt, 1,1,1,vt.wid)-1; + 'J' => + case vt.param[0] { + 0 => VT_CLEAR(vt,vt.x,vt.y,vt.wid-1,vt.y); + VT_CLEAR(vt,0,vt.y+1,vt.wid-1,vt.y2); + 1 => VT_CLEAR(vt,0,0,vt.wid-1,vt.y-1); + VT_CLEAR(vt,0,vt.y,vt.x,vt.y); + 2 => VT_CLEAR(vt,0,vt.y1,vt.wid-1,vt.y2); + } + 'K' => + case vt.param[0] { + 0 => VT_CLEAR(vt,vt.x,vt.y,vt.wid-1,vt.y); + 1 => VT_CLEAR(vt,0,vt.y,vt.x,vt.y); + 2 => VT_CLEAR(vt,0,vt.y,vt.wid-1,vt.y); + } + 'L' => + n = vt_param(vt, 0,1,1,vt.hgt); + VT_SCROLL_DOWN(vt,0,vt.y,vt.wid-1,vt.y2,n); + 'M' => + n = vt_param(vt,0,1,1,vt.hgt); + VT_SCROLL_UP(vt,0,vt.y,vt.wid-1,vt.y2,n); + '@' => + n = vt_param(vt,0,1,1,vt.wid-1-vt.x); + VT_SCROLL_RIGHT(vt,vt.x,vt.y,vt.wid-1,vt.y,n); + 'P' => + n = vt_param(vt,0,1,1,vt.wid-1-vt.x); + VT_SCROLL_LEFT(vt,vt.x,vt.y,vt.wid-1,vt.y,n); + 'X' => + n = vt_param(vt,0,1,1,vt.wid-1-vt.x); + VT_CLEAR(vt,vt.x,vt.y,vt.x+n-1,vt.y); + 'm' => + if(vt.pcount == 0) + vt.pcount++; + for(i=0; i<vt.pcount; i++) { + n = vt.param[i]; + if(!n) { + vt.attr = 0; + vt.fg = 7; + vt.bg = 0; + } else if (n < 16) + vt.attr |= (1<<n); + else if (n < 28) + vt.attr &= ~(1<<(n-20)); + else if (n < 38) + vt.fg = n-30; + else if (n < 48) + vt.bg = n-40; + else if (n < 58) + vt.fg = n-50+8; + else if (n < 68) + vt.bg = n-60+8; + } + VT_SET_COLOR(vt); + 'c' => + if(vt.wid >= 132) + VT_TYPE(vt, "\u001b[?61;1;6c"); + else + VT_TYPE(vt, "\u001b[?61;6c"); + 'n' => + n = vt_param(vt, 0,0,0,9); + if(n == 5) + VT_TYPE(vt, "\u001b[0n"); + if(n == 5 || n == 6) + VT_TYPE(vt, sprint("\u001b[%d;%dR",vt.y+1,vt.x+1)); + 'r' => + vt.y1 = vt_param(vt, 0,1,1,vt.hgt)-1; + vt.y2 = vt_param(vt, 1,vt.hgt,1,vt.hgt)-1; + 's' => + vt_save_state(vt); + 'u' => + vt_restore_state(vt); + 'h' => + for(i=0; i<vt.pcount; i++) { + n = vt.param[i]; + if(n >= 0) + vt.mode |= (1<<n); + else + vt.qmode |= (1<<(-n)); + } + 'l' => + for(i=0; i<vt.pcount; i++) { + n = vt.param[i]; + if(n >= 0) + vt.mode &= ~(1<<n); + else + vt.qmode &= ~(1<<(-n)); + } + } + + if(vt.y < 0) + vt.y = 0; + if(vt.y >= vt.hgt) + vt.y = vt.hgt-1; + if(vt.x < 0) + vt.x = 0; + if(vt.x >= vt.wid) + vt.x = vt.wid-1; + return 0; +} + +vt_call_ncsi(vt: ref Vt, ch: int): int +{ + case ch { + 'E' => + vt.x = 0; + '9' => + ; + 'D' => + vt.y++; + return 1; + 'H' => # XXX -- horizontal tab set + ; + '6' => + ; + 'M' => + vt.y--; + return 1; + '7' => + vt_save_state(vt); + '8' => + vt_restore_state(vt); + '=' => + ; + '>' => + ; + '#' => + ; + '(' => + ; + ')' => + ; + } + return 0; +} + + +vt_param(vt: ref Vt, n: int, def: int, min, max: int): int +{ + param := vt.param[n]; + if(param == 0) + param = def; + if(param < min) + param = min; + if(param > max) + param = max; + return param; +} + +############################################################################# + + +consinp(cs: chan of string, cr: chan of (int, int, int, Sys->Rread)) +{ + for(;;) { + alt { + sq += <- cs => ; + + (nil, nbytes, nil, rc) := <- cr => + p := 0; + for(;;) { + if(raw) + p = len sq; + else + forloop: + for(i := 0; i < len sq; i++) { + case sq[i] { + '\b' => + if(i > 0) { + sq = sq[0:i-1] + sq[i+1:]; + --i; + } + '\n' => + p = i+1; + break forloop; + } + } + if(p > 0) + break; + sq += <- cs; + } + if(nbytes > p) + nbytes = p; + alt { + rc <-= (array of byte sq[0:nbytes], "") => + sq = sq[nbytes:]; + * => ; + } + } + } +} + +newsh(ctxt: ref Draw->Context, ioc: chan of (int, ref Sys->FileIO, ref Sys->FileIO)) +{ + pid := sys->pctl(sys->NEWFD, nil); + + sh := load Command "/dis/sh.dis"; + if(sh == nil) { + ioc <-= (0, nil, nil); + return; + } + + tty := "cons."+string pid; + + sys->bind("#s","/chan",sys->MBEFORE); + fio := sys->file2chan("/chan", tty); + fioctl := sys->file2chan("/chan", tty + "ctl"); + ioc <-= (pid, fio, fioctl); + if ((fio == nil) || (fioctl == nil)) + return; + + sys->bind("/chan/"+tty, "/dev/cons", sys->MREPL); + sys->bind("/chan/"+tty+"ctl", "/dev/consctl", sys->MREPL); + + fd0 := sys->open("/dev/cons", sys->OREAD|sys->ORCLOSE); + fd1 := sys->open("/dev/cons", sys->OWRITE); + fd2 := sys->open("/dev/cons", sys->OWRITE); + + sh->init(ctxt, "sh" :: "-n" :: nil); +} + +kill(pid: int) +{ + fd := sys->open("#p/"+string pid+"/ctl", sys->OWRITE); + if(fd != nil) + sys->fprint(fd, "killgrp"); +} + +tkcmds(t: ref Tk->Toplevel, cfg: array of string) +{ + for(i := 0; i < len cfg; i++) + tk->cmd(t, cfg[i]); +} diff --git a/appl/wm/wish.b b/appl/wm/wish.b new file mode 100644 index 00000000..d5d2f353 --- /dev/null +++ b/appl/wm/wish.b @@ -0,0 +1,165 @@ +implement wish; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + +include "tk.m"; + tk: Tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "bufio.m"; + bufmod : Bufio; +Iobuf : import bufmod; + +include "../lib/tcl.m"; + tcl : Tcl_Core; + +wish : module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + + + +menubut : chan of string; +keyboard,mypid : int; + +Wwsh : ref Tk->Toplevel; + +init(ctxt: ref Draw->Context, argv: list of string) { + sys = load Sys Sys->PATH; + if (ctxt == nil) { + sys->fprint(sys->fildes(2), "wish: no window context\n"); + raise "fail:bad context"; + } + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient= load Tkclient Tkclient->PATH; + bufmod = load Bufio Bufio->PATH; + if (tk==nil || tkclient==nil || bufmod==nil){ + sys->print("Load Error: %r\n"); + exit; + } + tcl=load Tcl_Core Tcl_Core->PATH; + if (tcl==nil){ + sys->print("Cannot load Tcl (%r)\n"); + exit; + } + keyboard=1; + argv = tl argv; + if (argv!=nil) + file:=parse_args(argv); + geom:=""; + mypid=sys->pctl(sys->NEWPGRP, nil); + tkclient->init(); + Wshinit(ctxt, geom); + tcl->init(ctxt,argv); + tcl->set_top(Wwsh); + shellit(file); +} + + + + + +parse_args(argv : list of string) : string { + while (argv!=nil){ + case (hd argv){ + "-k" => + keyboard=0; + "-f" => + argv = tl argv; + return hd argv; + * => + return nil; + } + argv = tl argv; + } + return nil; +} + +shellit(file:string){ + drag:=chan of string; + tk->namechan(Wwsh, drag, "Wm_drag"); + lines:=chan of string; + Tcl_Chan:=chan of string; + tk->namechan(Wwsh, lines, "lines"); + tk->namechan(Wwsh, Tcl_Chan, "Tcl_Chan"); + new_inp:="wish%"; + unfin:="wish>"; + line : string; + loadfile(file); + quiet:=0; + if (keyboard) + spawn tcl->grab_lines(new_inp,unfin,lines); + for(;;){ + alt{ + s := <-drag => + if(len s < 6 || s[0:5] != "path=") + break; + loadfile(s[5:]); + sys->print("%s ",new_inp); + line = <-lines => + line = tcl->prepass(line); + msg:= tcl->evalcmd(line,0); + if (msg!=nil) + sys->print("%s\n",msg); + sys->print("%s ", new_inp); + tcl->clear_error(); + rline := <-Tcl_Chan => + rline = tcl->prepass(rline); + msg:= tcl->evalcmd(rline,0); + if (msg!=nil) + sys->print("%s\n",msg); + tcl->clear_error(); + menu := <-menubut => + if(menu == "exit"){ + kfd := sys->open("#p/"+string mypid+"/ctl", sys->OWRITE); + if(kfd == nil) + sys->print("error opening pid %d (%r)\n",mypid); + sys->fprint(kfd, "killgrp"); + exit; + } + tkclient->wmctl(Wwsh, menu); + } + } +} + + + +loadfile(file :string) { + iob : ref Iobuf; + line,input : string; + line = ""; + if (file==nil) + return; + iob = bufmod->open(file,bufmod->OREAD); + if (iob==nil){ + sys->print("File %s cannot be opened for reading",file); + return; + } + while((input=iob.gets('\n'))!=nil){ + line+=input; + if (tcl->finished(line,0)){ + line = tcl->prepass(line); + msg:= tcl->evalcmd(line,0); + if (msg!=nil) + sys->print("%s\n",msg); + tcl->clear_error(); + line=nil; + } + } +} + +Wshinit(ctxt: ref Draw->Context, geom: string) { + (Wwsh, menubut) = tkclient->toplevel(ctxt, geom, + "WishPad",Tkclient->Appl); + cmd := chan of string; + tk->namechan(Wwsh, cmd, "wsh"); + tk->cmd(Wwsh, "update"); +} diff --git a/appl/wm/wm.b b/appl/wm/wm.b new file mode 100644 index 00000000..d8232b0b --- /dev/null +++ b/appl/wm/wm.b @@ -0,0 +1,678 @@ +implement Wm; +include "sys.m"; + sys: Sys; +include "draw.m"; + draw: Draw; + Screen, Display, Image, Rect, Point, Wmcontext, Pointer: import draw; +include "wmsrv.m"; + wmsrv: Wmsrv; + Window, Client: import wmsrv; +include "tk.m"; +include "wmclient.m"; + wmclient: Wmclient; +include "string.m"; + str: String; +include "sh.m"; +include "winplace.m"; + winplace: Winplace; + +Wm: module { + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +Ptrstarted, Kbdstarted, Controlstarted, Controller, Fixedorigin: con 1<<iota; +Bdwidth: con 3; +Sminx, Sminy, Smaxx, Smaxy: con iota; +Minx, Miny, Maxx, Maxy: con 1<<iota; +Background: con int 16r777777FF; + +screen: ref Screen; +display: ref Display; +ptrfocus: ref Client; +kbdfocus: ref Client; +controller: ref Client; +allowcontrol := 1; +fakekbd: chan of string; +fakekbdin: chan of string; +buttons := 0; + +badmodule(p: string) +{ + sys->fprint(sys->fildes(2), "wm: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init(ctxt: ref Draw->Context, argv: list of string) +{ + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + if(draw == nil) + badmodule(Draw->PATH); + + str = load String String->PATH; + if(str == nil) + badmodule(String->PATH); + + wmsrv = load Wmsrv Wmsrv->PATH; + if(wmsrv == nil) + badmodule(Wmsrv->PATH); + + wmclient = load Wmclient Wmclient->PATH; + if(wmclient == nil) + badmodule(Wmclient->PATH); + wmclient->init(); + + winplace = load Winplace Winplace->PATH; + if(winplace == nil) + badmodule(Winplace->PATH); + winplace->init(); + + sys->pctl(Sys->NEWPGRP|Sys->FORKNS, nil); + if (ctxt == nil) + ctxt = wmclient->makedrawcontext(); + display = ctxt.display; + + buts := Wmclient->Appl; + if(ctxt.wm == nil) + buts = Wmclient->Plain; + win := wmclient->window(ctxt, "Wm", buts); + wmclient->win.reshape(((0, 0), (100, 100))); + wmclient->win.onscreen("place"); + if(win.image == nil){ + sys->fprint(sys->fildes(2), "wm: cannot get image to draw on\n"); + raise "fail:no image"; + } + wmclient->win.startinput("kbd" :: "ptr" :: nil); + + wmctxt := win.ctxt; + screen = makescreen(win.image); + + (clientwm, join, req) := wmsrv->init(); + clientctxt := ref Draw->Context(ctxt.display, nil, clientwm); + + wmrectIO := sys->file2chan("/chan", "wmrect"); + if(wmrectIO == nil) + fatal(sys->sprint("cannot make /chan/wmrect: %r")); + + sync := chan of string; + argv = tl argv; + if(argv == nil) + argv = "wm/toolbar" :: nil; + spawn command(clientctxt, argv, sync); + if((e := <-sync) != nil) + fatal("cannot run command: " + e); + + fakekbd = chan of string; + for(;;) alt { + c := <-win.ctl or + c = <-wmctxt.ctl => + # XXX could implement "pleaseexit" in order that + # applications can raise a warning message before + # they're unceremoniously dumped. + if(c == "exit") + for(z := wmsrv->top(); z != nil; z = z.znext) + z.ctl <-= "exit"; + + wmclient->win.wmctl(c); + if(win.image != screen.image) + reshaped(win); + c := <-wmctxt.kbd or + c = int <-fakekbd => + if(kbdfocus != nil) + kbdfocus.kbd <-= c; + p := <-wmctxt.ptr => + if(wmclient->win.pointer(*p)) + break; + if(p.buttons && (ptrfocus == nil || buttons == 0)){ + c := wmsrv->find(p.xy); + if(c != nil){ + ptrfocus = c; + c.ctl <-= "raise"; + setfocus(win, c); + } + } + if(ptrfocus != nil && (ptrfocus.flags & Ptrstarted) != 0){ + # inside currently selected client or it had button down last time (might have come up) + buttons = p.buttons; + ptrfocus.ptr <-= p; + break; + } + buttons = 0; + (c, rc) := <-join => + rc <-= nil; + # new client; inform it of the available screen rectangle. + # XXX do we need to do this now we've got wmrect? + c.ctl <-= "rect " + r2s(screen.image.r); + if(allowcontrol){ + controller = c; + c.flags |= Controller; + allowcontrol = 0; + }else + controlevent("newclient " + string c.id); + c.cursor = "cursor"; + (c, data, rc) := <-req => + # if client leaving + if(rc == nil){ + c.remove(); + if(c == ptrfocus) + ptrfocus = nil; + if(c == kbdfocus) + kbdfocus = nil; + if(c == controller) + controller = nil; + controlevent("delclient " + string c.id); + for(z := wmsrv->top(); z != nil; z = z.znext) + if(z.flags & Kbdstarted) + break; + setfocus(win, z); + c.stop <-= 1; + break; + } + err := handlerequest(win, wmctxt, c, string data); + n := len data; + if(err != nil) + n = -1; + alt{ + rc <-= (n, err) =>; + * =>; + } + (nil, nil, nil, wc) := <-wmrectIO.write => + if(wc == nil) + break; + alt{ + wc <-= (0, "cannot write") =>; + * =>; + } + (off, nil, nil, rc) := <-wmrectIO.read => + if(rc == nil) + break; + d := array of byte r2s(screen.image.r); + if(off > len d) + off = len d; + alt{ + rc <-= (d[off:], nil) =>; + * =>; + } + } +} + +handlerequest(win: ref Wmclient->Window, wmctxt: ref Wmcontext, c: ref Client, req: string): string +{ +#sys->print("%d: %s\n", c.id, req); + args := str->unquoted(req); + if(args == nil) + return "no request"; + n := len args; + if(req[0] == '!' && n < 3) + return "bad arg count"; + case hd args { + "key" => + # XXX should we restrict this capability to certain clients only? + if(n != 2) + return "bad arg count"; + if(fakekbdin == nil){ + fakekbdin = chan of string; + spawn bufferproc(fakekbdin, fakekbd); + } + fakekbdin <-= hd tl args; + "ptr" => + # ptr x y + if(n != 3) + return "bad arg count"; + if(ptrfocus != c) + return "cannot move pointer"; + e := wmclient->win.wmctl(req); + if(e == nil){ + c.ptr <-= nil; # flush queue + c.ptr <-= ref Pointer(buttons, (int hd tl args, int hd tl tl args), sys->millisec()); + } + "cursor" => + # cursor hotx hoty dx dy data + if(n != 6 && n != 1) + return "bad arg count"; + c.cursor = req; + if(ptrfocus == c || kbdfocus == c) + return wmclient->win.wmctl(c.cursor); + "start" => + if(n != 2) + return "bad arg count"; + case hd tl args { + "mouse" or + "ptr" => + c.flags |= Ptrstarted; + "kbd" => + c.flags |= Kbdstarted; + # XXX this means that any new window grabs the focus from the current + # application, but usually you want this to happen... how can we distinguish + # the two cases? + setfocus(win, c); + "control" => + if((c.flags & Controller) == 0) + return "control not available"; + c.flags |= Controlstarted; + * => + return "unknown input source"; + } + "!reshape" => + # reshape tag reqid rect [how] + # XXX allow "how" to specify that the origin of the window is never + # changed - a new window will be created instead. + if(n < 7) + return "bad arg count"; + args = tl args; + tag := hd args; args = tl args; + args = tl args; # skip reqid + r: Rect; + r.min.x = int hd args; args = tl args; + r.min.y = int hd args; args = tl args; + r.max.x = int hd args; args = tl args; + r.max.y = int hd args; args = tl args; + if(args != nil){ + case hd args{ + "onscreen" => + r = fitrect(r, screen.image.r); + "place" => + r = fitrect(r, screen.image.r); + r = newrect(r, screen.image.r); + "exact" => + ; + "max" => + r = screen.image.r; # XXX don't obscure toolbar? + * => + return "unkown placement method"; + } + } + return reshape(c, tag, r); + "delete" => + # delete tag + if(tl args == nil) + return "tag required"; + c.setimage(hd tl args, nil); + if(c.wins == nil && c == kbdfocus) + setfocus(win, nil); + "raise" => + c.top(); + "lower" => + c.bottom(); + "!move" or + "!size" => + # !move tag reqid startx starty + # !size tag reqid mindx mindy + ismove := hd args == "!move"; + if(n < 3) + return "bad arg count"; + args = tl args; + tag := hd args; args = tl args; + args = tl args; # skip reqid + w := c.window(tag); + if(w == nil) + return "no such tag"; + if(ismove){ + if(n != 5) + return "bad arg count"; + return dragwin(wmctxt.ptr, c, w, Point(int hd args, int hd tl args).sub(w.r.min)); + }else{ + if(n != 5) + return "bad arg count"; + sizewin(wmctxt.ptr, c, w, Point(int hd args, int hd tl args)); + } + "fixedorigin" => + c.flags |= Fixedorigin; + "rect" => + ; + "kbdfocus" => + if(n != 2) + return "bad arg count"; + if(int hd tl args) + setfocus(win, c); + else if(c == kbdfocus) + setfocus(win, nil); + # controller specific messages: + "request" => # can be used to test for control. + if((c.flags & Controller) == 0) + return "you are not in control"; + "ctl" => + # ctl id msg + if((c.flags & Controlstarted) == 0) + return "invalid request"; + if(n < 3) + return "bad arg count"; + id := int hd tl args; + for(z := wmsrv->top(); z != nil; z = z.znext) + if(z.id == id) + break; + if(z == nil) + return "no such client"; + z.ctl <-= str->quoted(tl tl args); + "endcontrol" => + if(c != controller) + return "invalid request"; + controller = nil; + allowcontrol = 1; + c.flags &= ~(Controlstarted | Controller); + * => + if(c == controller || controller == nil || (controller.flags & Controlstarted) == 0) + return "unknown control request"; + controller.ctl <-= "request " + string c.id + " " + req; + } + return nil; +} + +Fix: con 1000; +# the window manager window has been reshaped; +# allocate a new screen, and move all the +reshaped(win: ref Wmclient->Window) +{ + oldr := screen.image.r; + newr := win.image.r; + mx := Fix; + if(oldr.dx() > 0) + mx = newr.dx() * Fix / oldr.dx(); + my := Fix; + if(oldr.dy() > 0) + my = newr.dy() * Fix / oldr.dy(); + screen = makescreen(win.image); + for(z := wmsrv->top(); z != nil; z = z.znext){ + for(wl := z.wins; wl != nil; wl = tl wl){ + w := hd wl; + w.img = nil; + nr := w.r.subpt(oldr.min); + nr.min.x = nr.min.x * mx / Fix; + nr.min.y = nr.min.y * my / Fix; + nr.max.x = nr.max.x * mx / Fix; + nr.max.y = nr.max.y * my / Fix; + nr = nr.addpt(newr.min); + w.img = screen.newwindow(nr, Draw->Refbackup, Draw->Nofill); + # XXX check for creation failure + w.r = nr; + z.ctl <-= sys->sprint("!reshape %q -1 %s", w.tag, r2s(nr)); + z.ctl <-= "rect " + r2s(newr); + } + } +} + +controlevent(e: string) +{ + if(controller != nil && (controller.flags & Controlstarted)) + controller.ctl <-= e; +} + +dragwin(ptr: chan of ref Pointer, c: ref Client, w: ref Window, off: Point): string +{ + if(buttons == 0) + return "too late"; + p: ref Pointer; + do{ + p = <-ptr; + w.img.origin(w.img.r.min, p.xy.sub(off)); + } while (p.buttons != 0); + c.ptr <-= p; + buttons = 0; + r: Rect; + r.min = p.xy.sub(off); + r.max = r.min.add(w.r.size()); + if(r.eq(w.r)) + return "not moved"; + reshape(c, w.tag, r); + return nil; +} + +sizewin(ptrc: chan of ref Pointer, c: ref Client, w: ref Window, minsize: Point): string +{ + borders := array[4] of ref Image; + showborders(borders, w.r, Minx|Maxx|Miny|Maxy); + screen.image.flush(Draw->Flushnow); + while((ptr := <-ptrc).buttons == 0) + ; + xy := ptr.xy; + move, show: int; + offset := Point(0, 0); + r := w.r; + show = Minx|Miny|Maxx|Maxy; + if(xy.in(w.r) == 0){ + r = (xy, xy); + move = Maxx|Maxy; + }else { + if(xy.x < (r.min.x+r.max.x)/2){ + move=Minx; + offset.x = xy.x - r.min.x; + }else{ + move=Maxx; + offset.x = xy.x - r.max.x; + } + if(xy.y < (r.min.y+r.max.y)/2){ + move |= Miny; + offset.y = xy.y - r.min.y; + }else{ + move |= Maxy; + offset.y = xy.y - r.max.y; + } + } + return reshape(c, w.tag, sweep(ptrc, r, offset, borders, move, show, minsize)); +} + +reshape(c: ref Client, tag: string, r: Rect): string +{ + w := c.window(tag); + # if window hasn't changed size, then just change its origin and use the same image. + if((c.flags & Fixedorigin) == 0 && w != nil && w.r.size().eq(r.size())){ + c.setorigin(tag, r.min); + } else { + img := screen.newwindow(r, Draw->Refbackup, Draw->Nofill); + if(img == nil) + return sys->sprint("window creation failed: %r"); + if(c.setimage(tag, img) == -1) + return "can't do two at once"; + } + c.top(); + return nil; +} + +sweep(ptr: chan of ref Pointer, r: Rect, offset: Point, borders: array of ref Image, move, show: int, min: Point): Rect +{ + while((p := <-ptr).buttons != 0){ + xy := p.xy.sub(offset); + if(move&Minx) + r.min.x = xy.x; + if(move&Miny) + r.min.y = xy.y; + if(move&Maxx) + r.max.x = xy.x; + if(move&Maxy) + r.max.y = xy.y; + showborders(borders, r, show); + } + r = r.canon(); + if(r.min.y < screen.image.r.min.y){ + r.min.y = screen.image.r.min.y; + r = r.canon(); + } + if(r.dx() < min.x){ + if(move & Maxx) + r.max.x = r.min.x + min.x; + else + r.min.x = r.max.x - min.x; + } + if(r.dy() < min.y){ + if(move & Maxy) + r.max.y = r.min.y + min.y; + else { + r.min.y = r.max.y - min.y; + if(r.min.y < screen.image.r.min.y){ + r.min.y = screen.image.r.min.y; + r.max.y = r.min.y + min.y; + } + } + } + return r; +} + +showborders(b: array of ref Image, r: Rect, show: int) +{ + r = r.canon(); + b[Sminx] = showborder(b[Sminx], show&Minx, + (r.min, (r.min.x+Bdwidth, r.max.y))); + b[Sminy] = showborder(b[Sminy], show&Miny, + ((r.min.x+Bdwidth, r.min.y), (r.max.x-Bdwidth, r.min.y+Bdwidth))); + b[Smaxx] = showborder(b[Smaxx], show&Maxx, + ((r.max.x-Bdwidth, r.min.y), (r.max.x, r.max.y))); + b[Smaxy] = showborder(b[Smaxy], show&Maxy, + ((r.min.x+Bdwidth, r.max.y-Bdwidth), (r.max.x-Bdwidth, r.max.y))); +} + +showborder(b: ref Image, show: int, r: Rect): ref Image +{ + if(!show) + return nil; + if(b != nil && b.r.size().eq(r.size())) + b.origin(r.min, r.min); + else + b = screen.newwindow(r, Draw->Refbackup, Draw->Red); + return b; +} + +r2s(r: Rect): string +{ + return string r.min.x + " " + string r.min.y + " " + + string r.max.x + " " + string r.max.y; +} + +# XXX for consideration: +# do not allow applications to grab the keyboard focus +# unless there is currently no keyboard focus... +# but what about launching a new app from the taskbar: +# surely we should allow that to grab the focus? +setfocus(win: ref Wmclient->Window, new: ref Client) +{ + old := kbdfocus; + if(old == new) + return; + if(new == nil) + wmclient->win.wmctl("cursor"); + else if(old == nil || old.cursor != new.cursor) + wmclient->win.wmctl(new.cursor); + if(new != nil && (new.flags & Kbdstarted) == 0) + return; + if(old != nil) + old.ctl <-= "haskbdfocus 0"; + + if(new != nil){ + new.ctl <-= "raise"; + new.ctl <-= "haskbdfocus 1"; + kbdfocus = new; + } else + kbdfocus = nil; +} + +makescreen(img: ref Image): ref Screen +{ + screen = Screen.allocate(img, img.display.color(Background), 0); + img.draw(img.r, screen.fill, nil, screen.fill.r.min); + return screen; +} + +kill(pid: int, note: string): int +{ + fd := sys->open("/prog/"+string pid+"/ctl", Sys->OWRITE); + if(fd == nil || sys->fprint(fd, "%s", note) < 0) + return -1; + return 0; +} + +fatal(s: string) +{ + sys->fprint(sys->fildes(2), "wm: %s\n", s); + kill(sys->pctl(0, nil), "killgrp"); + raise "fail:error"; +} + +# fit a window rectangle to the available space. +# try to preserve requested location if possible. +# make sure that the window is no bigger than +# the screen, and that its top and left-hand edges +# will be visible at least. +fitrect(w, r: Rect): Rect +{ + if(w.dx() > r.dx()) + w.max.x = w.min.x + r.dx(); + if(w.dy() > r.dy()) + w.max.y = w.min.y + r.dy(); + size := w.size(); + if (w.max.x > r.max.x) + (w.min.x, w.max.x) = (r.min.x - size.x, r.max.x - size.x); + if (w.max.y > r.max.y) + (w.min.y, w.max.y) = (r.min.y - size.y, r.max.y - size.y); + if (w.min.x < r.min.x) + (w.min.x, w.max.x) = (r.min.x, r.min.x + size.x); + if (w.min.y < r.min.y) + (w.min.y, w.max.y) = (r.min.y, r.min.y + size.y); + return w; +} + +lastrect: Rect; +# find an suitable area for a window +newrect(w, r: Rect): Rect +{ + rl: list of Rect; + for(z := wmsrv->top(); z != nil; z = z.znext) + for(wl := z.wins; wl != nil; wl = tl wl) + rl = (hd wl).r :: rl; + lastrect = winplace->place(rl, r, lastrect, w.size()); + return lastrect; +} + +bufferproc(in, out: chan of string) +{ + h, t: list of string; + dummyout := chan of string; + for(;;){ + outc := dummyout; + s: string; + if(h != nil || t != nil){ + outc = out; + if(h == nil) + for(; t != nil; t = tl t) + h = hd t :: h; + s = hd h; + } + alt{ + x := <-in => + t = x :: t; + outc <-= s => + h = tl h; + } + } +} + +command(ctxt: ref Draw->Context, args: list of string, sync: chan of string) +{ + if((sh := load Sh Sh->PATH) != nil){ + sh->run(ctxt, "{$*&}" :: args); + sync <-= nil; + return; + } + fds := list of {0, 1, 2}; + sys->pctl(sys->NEWFD, fds); + + cmd := hd args; + file := cmd; + + if(len file<4 || file[len file-4:]!=".dis") + file += ".dis"; + + c := load Wm file; + if(c == nil) { + err := sys->sprint("%r"); + if(err != "permission denied" && err != "access permission denied" && file[0]!='/' && file[0:2]!="./"){ + c = load Wm "/dis/"+file; + if(c == nil) + err = sys->sprint("%r"); + } + if(c == nil){ + sync <-= sys->sprint("%s: %s\n", cmd, err); + exit; + } + } + sync <-= nil; + c->init(ctxt, args); +} diff --git a/appl/wm/wmdeb.m b/appl/wm/wmdeb.m new file mode 100644 index 00000000..378740e9 --- /dev/null +++ b/appl/wm/wmdeb.m @@ -0,0 +1,82 @@ +Diss: module {}; + +DebSrc: module +{ + PATH: con "/dis/wm/debsrc.dis"; + + Mod: adt + { + src: string; # .b path + tk: string; # text widget + dis: string; # .dis path + sym: ref Sym; # debugger symbol table + srcask: int; # look for src file? + symask: int; # look for symbol file? + }; + + loadsrc: fn(src: string, addpath: int): ref Mod; + showstrsrc: fn(src: string); + search: fn(s: string): int; + snarf: fn(): string; + getsel: fn(): (ref Mod, int); + attachdis: fn(m: ref Mod): int; + attachsym: fn(m: ref Mod); + showmodsrc: fn(m: ref Mod, src: ref Src); + findmod: fn(m: ref Module): ref Mod; + + init: fn(ctxt: ref Draw->Context, t: ref Tk->Toplevel, + tkclient: Tkclient, selectfile: Selectfile, dialog: Dialog, + str: String, debug: Debug, xscroll: int, remcr: int); + reinit: fn(xscroll: int, remcr: int); + + packed: ref Mod; + searchpath: array of string; + opendir: string; +}; + +DebData: module +{ + PATH: con "/dis/wm/debdata.dis"; + + Datum: adt + { + tkid: string; + parent: string; # tkid of parent + vtk: string; # root tk name + e: ref Exp; + val: string; # value displayed on screen + canwalk: int; # can the variable be expanded? + kids: cyclic array of ref Datum; # list of expanded kids + + expand: fn(d: self ref Datum, okids: array of ref Datum, who: string): ref Datum; + contract: fn(d: self ref Datum, who: string): ref Datum; + destroy: fn(d: self ref Datum); + showsrc: fn(d: self ref Datum); + }; + + Vars: adt + { + tk: string; # root tk widget + xbar: int; # x coord of var/val dividing line + d: array of ref Datum; # displayed variables + + create: fn(): ref Vars; + delete: fn(v: self ref Vars); + show: fn(v: self ref Vars); + refresh: fn(v: self ref Vars, e: array of ref Debug->Exp); + + expand: fn(v: self ref Vars, kid: string); + contract: fn(v: self ref Vars, kid: string); + showsrc: fn(v: self ref Vars, kid: string); + update: fn(v: self ref Vars); + scrolly: fn(v: self ref Vars, s: string); + }; + + ctl: fn(s: string); + wmctl: fn(s: string); + init: fn(ctxt: ref Draw->Context, geom: string, + debsrc: DebSrc, + str: String, debug: Debug): + (ref Tk->Toplevel, chan of string, chan of string); + raisex: fn(); +}; diff --git a/appl/wm/wmplay.b b/appl/wm/wmplay.b new file mode 100644 index 00000000..83becacf --- /dev/null +++ b/appl/wm/wmplay.b @@ -0,0 +1,176 @@ +implement WmPlay; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + Context: import draw; + gctxt: ref Context; + +include "tk.m"; + tk: Tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "dialog.m"; + dialog: Dialog; + +include "selectfile.m"; + selectfile: Selectfile; + +tpid: int; +ppid: int; +Magic: con "rate"; +data: con "/dev/audio"; +ctl: con "/dev/audioctl"; +buffz: con Sys->ATOMICIO; +top: ref Tk->Toplevel; + +WmPlay: module +{ + init: fn(ctxt: ref Context, argv: list of string); +}; + +notecmd := array[] of { + "frame .f", + "label .f.l -bitmap error -foreground red", + "button .b -text Continue -command {send cmd done}", + "focus .f", + "bind .f <Key-\n> {send cmd done}", + "pack .f.l .f.m -side left -expand 1 -padx 10 -pady 10", + "pack .f .b -padx 10 -pady 10", + "update; cursor -default" +}; + +notice(message: string) +{ + dialog->prompt(gctxt, top.image, "error -fg red", "Error", message, 0, "OK"::nil); +} + +play(f: string) +{ + ppid = sys->pctl(0, nil); + buff := array[buffz] of byte; + inf := sys->open(f, Sys->OREAD); + if (inf == nil) { + notice(sys->sprint("could not open %s: %r", f)); + return; + } + n := sys->read(inf, buff, buffz); + if (n < 0) { + notice(sys->sprint("could not read %s: %r", f)); + return; + } + if (n < 10 || string buff[0:4] != Magic) { + notice(sys->sprint("%s: not an audio file", f)); + return; + } + i := 0; + for (;;) { + if (i == n) { + notice(sys->sprint("%s: bad header", f)); + return; + } + if (buff[i] == byte '\n') { + i++; + if (i == n) { + notice(sys->sprint("%s: bad header", f)); + return; + } + if (buff[i] == byte '\n') { + i++; + if ((i % 4) != 0) { + notice(sys->sprint("%s: unpadded header", f)); + return; + } + break; + } + } + else + i++; + } + df := sys->open(data, Sys->OWRITE); + if (df == nil) { + notice(sys->sprint("could not open %s: %r", data)); + return; + } + cf := sys->open(ctl, Sys->OWRITE); + if (cf == nil) { + notice(sys->sprint("could not open %s: %r", ctl)); + return; + } + if (sys->write(cf, buff, i - 1) < 0) { + notice(sys->sprint("could not write %s: %r", ctl)); + return; + } + if (n > i && sys->write(df, buff[i:n], n - i) < 0) { + notice(sys->sprint("could not write %s: %r", data)); + return; + } + if (sys->stream(inf, df, Sys->ATOMICIO) < 0) { + notice(sys->sprint("could not stream %s: %r", data)); + return; + } +} + +doplay(f: string) +{ + play(f); + kill(tpid); +} + +init(ctxt: ref Context, argv: list of string) +{ + sys = load Sys Sys->PATH; + if (ctxt == nil) { + sys->fprint(sys->fildes(2), "wmplay: no window context\n"); + raise "fail:bad context"; + } + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + dialog = load Dialog Dialog->PATH; + selectfile = load Selectfile Selectfile->PATH; + + gctxt = ctxt; + sys->pctl(Sys->NEWPGRP, nil); + tkclient->init(); + dialog->init(); + selectfile->init(); + + file: string; + argv = tl argv; + if (argv != nil) + file = hd argv; + else { + file = selectfile->filename(ctxt, nil, "Locate Audio File", "*.iaf"::"*.wav"::nil, ""); + if (file == "") + exit; + } + + (t, menubut) := tkclient->toplevel(ctxt, "-borderwidth 2 -relief raised", "Play", 0); + tk->cmd(t, "label .d -label {" + file + "}"); + tk->cmd(t, "pack .Wm_t -fill x; pack .d; pack propagate . 0"); + tk->cmd(t, "update"); + top = t; + tpid = sys->pctl(0, nil); + spawn doplay(file); + + for(;;) { + menu := <- menubut; + if(menu == "exit") { + kill(ppid); + return; + } + tkclient->wmctl(t, menu); + } +} + +kill(pid: int) +{ + fd := sys->open("/prog/" + string pid + "/ctl", sys->OWRITE); + if (fd != nil) + sys->fprint(fd, "kill"); +} |
