ref: d916a4c3823f55227ffae35738c2497256e307b5
dir: /appl/cmd/sh/tk.b/
implement Shellbuiltin; include "sys.m"; sys: Sys; include "draw.m"; include "tk.m"; tk: Tk; include "tkclient.m"; tkclient: Tkclient; include "sh.m"; sh: Sh; Listnode, Context: import sh; myself: Shellbuiltin; tklock: chan of int; chans := array[23] of list of (string, chan of string); wins := array[16] of list of (int, ref Tk->Toplevel); winid := 0; badmodule(ctxt: ref Context, p: string) { ctxt.fail("bad module", sys->sprint("tk: cannot load %s: %r", p)); } initbuiltin(ctxt: ref Context, shmod: Sh): string { sys = load Sys Sys->PATH; sh = shmod; myself = load Shellbuiltin "$self"; if (myself == nil) badmodule(ctxt, "self"); tk = load Tk Tk->PATH; if (tk == nil) badmodule(ctxt, Tk->PATH); tkclient = load Tkclient Tkclient->PATH; if (tkclient == nil) badmodule(ctxt, Tkclient->PATH); tkclient->init(); tklock = chan[1] of int; ctxt.addbuiltin("tk", myself); ctxt.addbuiltin("chan", myself); ctxt.addbuiltin("send", myself); ctxt.addsbuiltin("tk", myself); ctxt.addsbuiltin("recv", myself); ctxt.addsbuiltin("alt", myself); ctxt.addsbuiltin("tkquote", myself); return nil; } whatis(nil: ref Sh->Context, nil: Sh, nil: string, nil: int): string { return nil; } getself(): Shellbuiltin { return myself; } runbuiltin(ctxt: ref Context, nil: Sh, cmd: list of ref Listnode, nil: int): string { case (hd cmd).word { "tk" => return builtin_tk(ctxt, cmd); "chan" => return builtin_chan(ctxt, cmd); "send" => return builtin_send(ctxt, cmd); } return nil; } runsbuiltin(ctxt: ref Context, nil: Sh, cmd: list of ref Listnode): list of ref Listnode { case (hd cmd).word { "tk" => return sbuiltin_tk(ctxt, cmd); "recv" => return sbuiltin_recv(ctxt, cmd); "alt" => return sbuiltin_alt(ctxt, cmd); "tkquote" => return sbuiltin_tkquote(ctxt, cmd); } return nil; } builtin_tk(ctxt: ref Context, argv: list of ref Listnode): string { # usage: tk window _title_ _options_ # tk wintitle _winid_ _title_ # tk _winid_ _cmd_ if (tl argv == nil) ctxt.fail("usage", "usage: tk (<winid>|window|onscreen|winctlwintitle|del|namechan) args..."); argv = tl argv; w := (hd argv).word; case w { "window" => remark(ctxt, string makewin(ctxt, tl argv)); "wintitle" => argv = tl argv; # change the title of a window if (len argv != 2 || !isnum((hd argv).word)) ctxt.fail("usage", "usage: tk wintitle winid title"); tkclient->settitle(egetwin(ctxt, hd argv), word(hd tl argv)); "winctl" => argv = tl argv; if (len argv != 2 || !isnum((hd argv).word)) ctxt.fail("usage", "usage: tk winctl winid cmd"); wid := (hd argv).word; win := egetwin(ctxt, hd argv); rq := word(hd tl argv); if (rq == "exit") { delwin(int wid); delchan(wid); } tkclient->wmctl(win, rq); "onscreen" => argv = tl argv; if (len argv < 1 || !isnum((hd argv).word)) ctxt.fail("usage", "usage: tk onscreen winid [how]"); how := ""; if(tl argv != nil) how = word(hd tl argv); win := egetwin(ctxt, hd argv); tkclient->startinput(win, "ptr" :: "kbd" :: nil); tkclient->onscreen(win, how); "namechan" => argv = tl argv; n := len argv; if (n < 2 || n > 3 || !isnum((hd argv).word)) ctxt.fail("usage", "usage: tk namechan winid chan [name]"); name: string; if (n == 3) name = word(hd tl tl argv); else name = word(hd tl argv); tk->namechan(egetwin(ctxt, hd argv), egetchan(ctxt, hd tl argv), name); "del" => if (len argv < 2) ctxt.fail("usage", "usage: tk del id..."); for (argv = tl argv; argv != nil; argv = tl argv) { id := (hd argv).word; if (isnum(id)) delwin(int id); delchan(id); } * => e := tkcmd(ctxt, argv); if (e != nil) remark(ctxt, e); if (e != nil && e[0] == '!') return e; } return nil; } remark(ctxt: ref Context, s: string) { if (ctxt.options() & ctxt.INTERACTIVE) sys->print("%s\n", s); } # create a new window (and its associated channel) makewin(ctxt: ref Context, argv: list of ref Listnode): int { if (argv == nil) ctxt.fail("usage", "usage: tk window title options"); if (ctxt.drawcontext == nil) ctxt.fail("no draw context", sys->sprint("tk: no graphics context available")); (title, options) := (word(hd argv), concat(tl argv)); (top, topchan) := tkclient->toplevel(ctxt.drawcontext, options, title, Tkclient->Appl); newid := addwin(top); addchan(string newid, topchan); return newid; } builtin_chan(ctxt: ref Context, argv: list of ref Listnode): string { # create a new channel argv = tl argv; if (argv == nil) ctxt.fail("usage", "usage: chan name...."); for (; argv != nil; argv = tl argv) { name := (hd argv).word; if (name == nil || isnum(name)) ctxt.fail("bad chan", "tk: bad channel name "+q(name)); if (addchan(name, chan of string) == nil) ctxt.fail("bad chan", "tk: channel "+q(name)+" already exists"); } return nil; } builtin_send(ctxt: ref Context, argv: list of ref Listnode): string { if (len argv != 3) ctxt.fail("usage", "usage: send chan arg"); argv = tl argv; c := egetchan(ctxt, hd argv); c <-= word(hd tl argv); return nil; } sbuiltin_tk(ctxt: ref Context, argv: list of ref Listnode): list of ref Listnode { # usage: tk _winid_ _command_ # tk window _title_ _options_ argv = tl argv; if (argv == nil) ctxt.fail("usage", "tk (window|wid) args"); case (hd argv).word { "window" => return ref Listnode(nil, string makewin(ctxt, tl argv)) :: nil; "winids" => ret: list of ref Listnode; for (i := 0; i < len wins; i++) for (wl := wins[i]; wl != nil; wl = tl wl) ret = ref Listnode(nil, string (hd wl).t0) :: ret; return ret; * => return ref Listnode(nil, tkcmd(ctxt, argv)) :: nil; } } sbuiltin_alt(ctxt: ref Context, argv: list of ref Listnode): list of ref Listnode { # usage: alt chan ... argv = tl argv; if (argv == nil) ctxt.fail("usage", "usage: alt chan..."); nc := len argv; kbd := array[nc] of chan of int; ptr := array[nc] of chan of ref Draw->Pointer; ca := array[nc * 3] of chan of string; win := array[nc] of ref Tk->Toplevel; cname := array[nc] of string; i := 0; for (; argv != nil; argv = tl argv) { w := (hd argv).word; ca[i*3] = egetchan(ctxt, hd argv); cname[i] = w; if(isnum(w)){ win[i] = egetwin(ctxt, hd argv); ca[i*3+1] = win[i].ctxt.ctl; ca[i*3+2] = win[i].wreq; ptr[i] = win[i].ctxt.ptr; kbd[i] = win[i].ctxt.kbd; } i++; } for(;;) alt{ (n, key) := <-kbd => tk->keyboard(win[n], key); (n, p) := <-ptr => tk->pointer(win[n], *p); (n, v) := <-ca => return ref Listnode(nil, cname[n/3]) :: ref Listnode(nil, v) :: nil; } } sbuiltin_recv(ctxt: ref Context, argv: list of ref Listnode): list of ref Listnode { # usage: recv chan if (len argv != 2) ctxt.fail("usage", "usage: recv chan"); ch := hd tl argv; c := egetchan(ctxt, ch); if(!isnum(ch.word)) return ref Listnode(nil, <-c) :: nil; win := egetwin(ctxt, ch); for(;;)alt{ key := <-win.ctxt.kbd => tk->keyboard(win, key); p := <-win.ctxt.ptr => tk->pointer(win, *p); s := <-win.ctxt.ctl or s = <-win.wreq or s = <-c => return ref Listnode(nil, s) :: nil; } } sbuiltin_tkquote(ctxt: ref Context, argv: list of ref Listnode): list of ref Listnode { if (len argv != 2) ctxt.fail("usage", "usage: tkquote arg"); return ref Listnode(nil, tk->quote(word(hd tl argv))) :: nil; } tkcmd(ctxt: ref Context, argv: list of ref Listnode): string { if (argv == nil || !isnum((hd argv).word)) ctxt.fail("usage", "usage: tk winid command"); return tk->cmd(egetwin(ctxt, hd argv), concat(tl argv)); } hashfn(s: string, n: int): int { h := 0; m := len s; for(i:=0; i<m; i++){ h = 65599*h+s[i]; } return (h & 16r7fffffff) % n; } q(s: string): string { return "'" + s + "'"; } egetchan(ctxt: ref Context, n: ref Listnode): chan of string { if ((c := getchan(n.word)) == nil) ctxt.fail("bad chan", "tk: bad channel name "+ q(n.word)); return c; } # assumes that n.word has been checked and found to be numeric. egetwin(ctxt: ref Context, n: ref Listnode): ref Tk->Toplevel { wid := int n.word; if (wid < 0 || (top := getwin(wid)) == nil) ctxt.fail("bad win", "tk: unknown window id " + q(n.word)); return top; } getchan(name: string): chan of string { n := hashfn(name, len chans); for (cl := chans[n]; cl != nil; cl = tl cl) { (cname, c) := hd cl; if (cname == name) return c; } return nil; } addchan(name: string, c: chan of string): chan of string { n := hashfn(name, len chans); tklock <-= 1; if (getchan(name) == nil) chans[n] = (name, c) :: chans[n]; <-tklock; return c; } delchan(name: string) { n := hashfn(name, len chans); tklock <-= 1; ncl: list of (string, chan of string); for (cl := chans[n]; cl != nil; cl = tl cl) { (cname, nil) := hd cl; if (cname != name) ncl = hd cl :: ncl; } chans[n] = ncl; <-tklock; } addwin(top: ref Tk->Toplevel): int { tklock <-= 1; id := winid++; slot := id % len wins; wins[slot] = (id, top) :: wins[slot]; <-tklock; return id; } delwin(id: int) { tklock <-= 1; slot := id % len wins; nwl: list of (int, ref Tk->Toplevel); for (wl := wins[slot]; wl != nil; wl = tl wl) { (wid, nil) := hd wl; if (wid != id) nwl = hd wl :: nwl; } wins[slot] = nwl; <-tklock; } getwin(id: int): ref Tk->Toplevel { slot := id % len wins; for (wl := wins[slot]; wl != nil; wl = tl wl) { (wid, top) := hd wl; if (wid == id) return top; } return nil; } word(n: ref Listnode): string { if (n.word != nil) return n.word; if (n.cmd != nil) n.word = sh->cmd2string(n.cmd); return n.word; } isnum(s: string): int { for (i := 0; i < len s; i++) if (s[i] > '9' || s[i] < '0') return 0; return 1; } concat(argv: list of ref Listnode): string { if (argv == nil) return nil; s := word(hd argv); for (argv = tl argv; argv != nil; argv = tl argv) s += " " + word(hd argv); return s; } lockproc(c: chan of int) { sys->pctl(Sys->NEWFD|Sys->NEWNS, nil); for(;;){ c <-= 1; <-c; } }