implement Feedkey; # # Copyright © 2004 Vita Nuova Holdings Limited # include "sys.m"; sys: Sys; include "draw.m"; include "tk.m"; tk: Tk; include "tkclient.m"; tkclient: Tkclient; include "string.m"; str: String; Feedkey: module { init: fn(nil: ref Draw->Context, nil: list of string); }; config := array[] of { "frame .f", "button .f.done -command {send cmd done} -text {Done}", "frame .f.key -bg white", "pack .f.key .f.done .f", "update" }; Debug: con 0; init(ctxt: ref Draw->Context, nil: list of string) { sys = load Sys Sys->PATH; tk = load Tk Tk->PATH; tkclient = load Tkclient Tkclient->PATH; str = load String String->PATH; needfile := "/mnt/factotum/needkey"; if(Debug) needfile = "/dev/null"; needs := chan of list of ref Attr; acks := chan of int; sys->pctl(Sys->NEWPGRP|Sys->NEWFD, list of {0, 1, 2}); fd := sys->open(needfile, Sys->ORDWR); if(fd == nil) err(sys->sprint("can't open %s: %r", needfile)); spawn needy(fd, needs, acks); fd = nil; ctlfile := "/mnt/factotum/ctl"; keyfd := sys->open(ctlfile, Sys->ORDWR); if(keyfd == nil) err(sys->sprint("can't open %s: %r", ctlfile)); tkclient->init(); spawn feedkey(ctxt, keyfd, needs, acks); } feedkey(ctxt: ref Draw->Context, keyfd: ref Sys->FD, needs: chan of list of ref Attr, acks: chan of int) { (top, tkctl) := tkclient->toplevel(ctxt, nil, "Need key", Tkclient->Appl); cmd := chan of string; tk->namechan(top, cmd, "cmd"); for(i := 0; i < len config; i++) tkcmd(top, config[i]); tkclient->startinput(top, "ptr" :: nil); tkclient->onscreen(top, nil); if(!Debug) tkclient->wmctl(top, "task"); attrs: list of ref Attr; for(;;) alt{ s :=<-tkctl or s = <-top.ctxt.ctl or s = <-top.wreq => tkclient->wmctl(top, s); p := <-top.ctxt.ptr => tk->pointer(top, *p); c := <-top.ctxt.kbd => tk->keyboard(top, c); s := <-cmd => case s { "done" => result := extract(top, ".f.key", attrs); if(Debug) sys->print("result: %s\n", attrtext(result)); if(sys->fprint(keyfd, "key %s", attrtext(result)) < 0) sys->fprint(sys->fildes(2), "feedkey: can't install key %q: %r\n", attrtext(result)); acks <-= 0; tkclient->wmctl(top, "task"); tk->cmd(top, "pack forget .f.key"); * => sys->fprint(sys->fildes(2), "feedkey: odd command: %q\n", s); } attrs = <-needs => if(attrs == nil) exit; tkclient->startinput(top, "kbd" :: nil); tkcmd(top, "destroy .f.key"); tkcmd(top, "frame .f.key -bg white"); populate(top, ".f.key", attrs); tkcmd(top, "pack forget .f.done"); tkcmd(top, "pack .f.key .f.done .f"); tkcmd(top, "update"); tkclient->wmctl(top, "unhide"); } } err(s: string) { sys->fprint(sys->fildes(2), "feedkey: %s\n", s); raise "fail:error"; } user(): string { fd := sys->open("/dev/user", Sys->OREAD); if(fd == nil) return nil; b := array[Sys->NAMEMAX] of byte; n := sys->read(fd, b, len b); if(n <= 0) return nil; return string b[0:n]; } tkcmd(top: ref Tk->Toplevel, cmd: string): string { if(0) sys->print("tk: %q\n", cmd); r := tk->cmd(top, cmd); if(r != nil && r[0] == '!') sys->fprint(sys->fildes(2), "feedkey: tk: %q on %q\n", r, cmd); return r; } populate(top: ref Tk->Toplevel, tag: string, attrs: list of ref Attr) { c := 0; for(al := attrs; al != nil; al = tl al){ a := hd al; if(a.name == nil) tkcmd(top, sys->sprint("entry %s.n%d -bg yellow", tag, c)); else tkcmd(top, sys->sprint("label %s.n%d -bg white -text '%s", tag, c, a.name)); tkcmd(top, sys->sprint("label %s.e%d -bg white -text ' = ", tag, c)); case a.tag { Aquery => show := ""; if(a.name != nil && a.name[0] == '!') show = " -show {•}"; tkcmd(top, sys->sprint("entry %s.v%d%s -bg yellow", tag, c, show)); if(a.val == nil && a.name == "user") a.val = user(); tkcmd(top, sys->sprint("%s.v%d insert 0 '%s", tag, c, a.val)); tkcmd(top, sys->sprint("grid %s.n%d %s.e%d %s.v%d -in %s -sticky w -pady 1", tag, c, tag, c, tag, c, tag)); Aval => if(a.name != nil){ val := a.val; if(a.name[0] == '!') val = "..."; # just in case tkcmd(top, sys->sprint("label %s.v%d -bg white -text %s", tag, c, val)); }else tkcmd(top, sys->sprint("entry %s.v%d -bg yellow", tag, c)); tkcmd(top, sys->sprint("grid %s.n%d %s.e%d %s.v%d -in %s -sticky w -pady 1", tag, c, tag, c, tag, c, tag)); Aattr => tkcmd(top, sys->sprint("grid %s.n%d x x -in %s -sticky w -pady 1", tag, c, tag)); } c++; } } extract(top: ref Tk->Toplevel, tag: string, attrs: list of ref Attr): list of ref Attr { c := 0; nl: list of ref Attr; for(al := attrs; al != nil; al = tl al){ a := ref *hd al; if(a.tag == Aquery){ a.val = tkcmd(top, sys->sprint("%s.v%d get", tag, c)); if(a.name == nil) a.name = tk->cmd(top, sys->sprint("%s.n%d get", tag, c)); # name might start with `!' if(a.name != nil){ a.tag = Aval; nl = a :: nl; } }else nl = a :: nl; c++; } return nl; } reverse[T](l: list of T): list of T { rl: list of T; for(; l != nil; l = tl l) rl = hd l :: rl; return rl; } needy(fd: ref Sys->FD, needs: chan of list of ref Attr, acks: chan of int) { if(Debug){ for(;;){ needs <-= parseline("proto=pass user? server=fred.com service=ftp confirm !password?"); <-acks; } } buf := array[512] of byte; while((n := sys->read(fd, buf, len buf)) > 0){ s := string buf[0:n]; for(i := 0; i < len s; i++) if(s[i] == ' ') break; if(i >= len s) continue; attrs := parseline(s[i+1:]); nl: list of ref Attr; tag: ref Attr; for(; attrs != nil; attrs = tl attrs){ a := hd attrs; if(a.name == "tag") tag = a; else nl = a :: nl; } if(nl == nil) continue; attrs = reverse(ref Attr(Aquery, nil, nil) :: ref Attr(Aquery, nil, nil) :: nl); # add a few blank if(attrs != nil && tag != nil && tag.val != nil){ needs <-= attrs; <-acks; sys->fprint(fd, "tag=%d", int tag.val); } } if(n < 0) sys->fprint(sys->fildes(2), "feedkey: error reading needkey: %r\n"); needs <-= nil; } # need a library module Aattr, Aval, Aquery: con iota; Attr: adt { tag: int; name: string; val: string; text: fn(a: self ref Attr): string; }; parseline(s: string): list of ref Attr { fld := str->unquoted(s); rfld := fld; for(fld = nil; rfld != nil; rfld = tl rfld) fld = (hd rfld) :: fld; attrs: list of ref Attr; for(; fld != nil; fld = tl fld){ n := hd fld; a := ""; tag := Aattr; for(i:=0; i return a.name; Aval => return sys->sprint("%q=%q", a.name, a.val); Aquery => return a.name+"?"; * => return "??"; } } attrtext(attrs: list of ref Attr): string { s := ""; sp := 0; for(; attrs != nil; attrs = tl attrs){ if(sp) s[len s] = ' '; sp = 1; s += (hd attrs).text(); } return s; }