summaryrefslogtreecommitdiff
path: root/appl/cmd/rioimport.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/rioimport.b')
-rw-r--r--appl/cmd/rioimport.b620
1 files changed, 620 insertions, 0 deletions
diff --git a/appl/cmd/rioimport.b b/appl/cmd/rioimport.b
new file mode 100644
index 00000000..49980000
--- /dev/null
+++ b/appl/cmd/rioimport.b
@@ -0,0 +1,620 @@
+implement Rioimport;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+ draw: Draw;
+ Image, Point, Rect, Display, Screen: import draw;
+include "wmsrv.m";
+ wmsrv: Wmsrv;
+include "sh.m";
+ sh: Sh;
+include "string.m";
+ str: String;
+
+Rioimport: module{
+ init: fn(nil: ref Draw->Context, argv: list of string);
+};
+
+Client: adt{
+ ptrstarted: int;
+ kbdstarted: int;
+ state: int; # Hidden|Current
+ req: chan of (array of byte, Sys->Rwrite);
+ resize: chan of ref Riowin;
+ ptr: chan of ref Draw->Pointer;
+ riowctl: chan of (ref Riowin, int);
+ wins: list of ref Riowin;
+ winfd: ref Sys->FD;
+ sc: ref Wmsrv->Client;
+};
+
+Riowin: adt {
+ tag: string;
+ img: ref Image;
+ dir: string;
+ state: int;
+ ptrpid: int;
+ kbdpid: int;
+ ctlpid: int;
+ ptrfd: ref Sys->FD;
+ ctlfd: ref Sys->FD;
+};
+
+Hidden, Current: con 1<<iota;
+Ptrsize: con 1+4*12; # 'm' plus 4 12-byte decimal integers
+P9PATH: con "/n/local";
+Borderwidth: con 4; # defined in /sys/include/draw.h
+
+display: ref Display;
+wsysseq := 0;
+screenr := Rect((0, 0), (640, 480)); # no way of getting this reliably from rio
+
+Minwinsize: con Point(100, 42);
+
+init(nil: ref Draw->Context, argv: list of string)
+{
+ sys = load Sys Sys->PATH;
+ draw = load Draw Draw->PATH;
+ sh = load Sh Sh->PATH;
+ sh->initialise();
+ str = load String String->PATH;
+ wmsrv = load Wmsrv Wmsrv->PATH;
+
+ wc := chan of (ref Draw->Context, string);
+ spawn rioproxy(wc);
+ (ctxt, err) := <-wc;
+ if(err != nil){
+ sys->fprint(sys->fildes(2), "rioimport: %s\n", err);
+ raise "fail:no display";
+ }
+ sh->run(ctxt, tl argv);
+}
+
+ebind(a, b: string, flag: int)
+{
+ if(sys->bind(a, b, flag) == -1){
+ sys->fprint(sys->fildes(2), "rioimport: cannot bind %q onto %q: %r\n", a, b);
+ raise "fail:error";
+ }
+}
+
+rioproxy(wc: chan of (ref Draw->Context, string))
+{
+ {
+ rioproxy1(wc);
+ } exception e {
+ "fail:*" =>
+ wc <-= (nil, e[5:]);
+ }
+}
+
+rioproxy1(wc: chan of (ref Draw->Context, string))
+{
+ sys->pctl(Sys->NEWFD, 0 :: 1 :: 2 :: nil);
+
+ ebind("#U*", P9PATH, Sys->MREPL);
+ display = Display.allocate(P9PATH + "/dev");
+ if(display == nil)
+ raise sys->sprint("fail:cannot allocate display: %r");
+
+
+ (wm, join, req) := wmsrv->init();
+ if(wm == nil){
+ wc <-= (nil, sys->sprint("%r"));
+ return;
+ }
+ readscreenr();
+ wc <-= (ref Draw->Context(display, nil, wm), nil);
+
+ sys->pctl(Sys->FORKNS, nil);
+ ebind("#₪", "/srv", Sys->MREPL|Sys->MCREATE);
+ if(sys->bind(P9PATH+"/dev/draw", "/dev/draw", Sys->MREPL) == -1)
+ ebind(P9PATH+"/dev", "/dev", Sys->MAFTER);
+ sh->run(nil, "mount" :: "{mntgen}" :: "/mnt" :: nil);
+
+ clients: array of ref Client;
+ nc := 0;
+ for(;;) alt{
+ (sc, rc) := <-join =>
+ if(nc != 0)
+ rc <-= "only one client available";
+ sync := chan of (ref Client, string);
+ spawn clientproc(sc,sync);
+ (c, err) := <-sync;
+ rc <-= err;
+ if(c != nil){
+ if(sc.id >= len clients)
+ clients = (array[sc.id + 1] of ref Client)[0:] = clients;
+ clients[sc.id] = c;
+ }
+ (sc, data, rc) := <-req =>
+ clients[sc.id].req <-= (data, rc);
+ if(rc == nil)
+ clients[sc.id] = nil;
+ }
+}
+zclient: Client;
+clientproc(sc: ref Wmsrv->Client, rc: chan of (ref Client, string))
+{
+ c := ref zclient;
+ c.req = chan of (array of byte, Sys->Rwrite);
+ c.resize = chan of ref Riowin;
+ c.ptr = chan of ref Draw->Pointer;
+ c.riowctl = chan of (ref Riowin, int);
+ c.sc = sc;
+ rc <-= (c, nil);
+
+loop:
+ for(;;) alt{
+ (data, drc) := <-c.req =>
+ if(drc == nil)
+ break loop;
+ err := handlerequest(c, data);
+ n := len data;
+ if(err != nil)
+ n = -1;
+ alt{
+ drc <-= (n, err) =>;
+ * =>;
+ }
+ p := <-c.ptr =>
+ sc.ptr <-= p;
+ w := <-c.resize =>
+ if((c.state & Hidden) == 0)
+ sc.ctl <-= sys->sprint("!reshape %q -1 0 0 0 0 getwin", w.tag);
+ (w, state) := <-c.riowctl =>
+ if((c.state^state)&Current)
+ sc.ctl <-= "haskbdfocus " + string ((state & Current)!=0);
+ if((c.state^state)&Hidden){
+ s := "unhide";
+ if(state&Hidden)
+ s = "hide";
+ for(wl := c.wins; wl != nil; wl = tl wl){
+ if(hd wl != w)
+ rioctl(hd wl, s);
+ if(c.state&Hidden)
+ sc.ctl <-= sys->sprint("!reshape %q -1 0 0 0 0 getwin", (hd wl).tag);
+ }
+ }
+ c.state = state;
+ w.state = state;
+ }
+ sc.stop <-= 1;
+ for(wl := c.wins; wl != nil; wl = tl wl)
+ delwin(hd wl);
+}
+
+handlerequest(c: ref Client, data: array of byte): string
+{
+ req := string data;
+#sys->print("%d: %s\n", c.sc.id, req);
+ if(req == nil)
+ return "no request";
+ args := str->unquoted(req);
+ n := len args;
+ case hd args {
+ "key" =>
+ return "permission denied";
+ "ptr" =>
+ # ptr x y
+ if(n != 3)
+ return "bad arg count";
+ if(c.ptrstarted == 0)
+ return "pointer not active";
+ for(w := c.wins; w != nil; w = tl w){
+ if((hd w).ptrfd != nil){
+ sys->fprint((hd w).ptrfd, "m%11d %11d", int hd tl args, int hd tl tl args);
+ return nil;
+ }
+ }
+ return "no windows";
+ "start" =>
+ if(n != 2)
+ return "bad arg count";
+ case hd tl args {
+ "ptr" or
+ "mouse" =>
+ if(c.ptrstarted == -1)
+ return "already started";
+ sync := chan of int;
+ for(w := c.wins; w != nil; w = tl w){
+ spawn ptrproc(hd w, c.ptr, c.resize, sync);
+ (hd w).ptrpid = <-sync;
+ }
+ c.ptrstarted = 1;
+ return nil;
+ "kbd" =>
+ if(c.kbdstarted == -1)
+ return "already started";
+ sync := chan of int;
+ for(w := c.wins; w != nil; w = tl w){
+ spawn kbdproc(hd w, c.sc.kbd, sync);
+ (hd w).kbdpid = <-sync;
+ }
+ return nil;
+ * =>
+ 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(r.dx() < Minwinsize.x)
+ r.max.x = r.min.x + Minwinsize.x;
+ if(r.dy() < Minwinsize.y)
+ r.max.y = r.min.y + Minwinsize.y;
+
+ spec := "";
+ if(args != nil){
+ case hd args{
+ "onscreen" =>
+ r = fitrect(r, screenr).inset(-Borderwidth);
+ spec = "-r " + r2s(r);
+ "place" =>
+ r = fitrect(r, screenr).inset(-Borderwidth);
+ spec = "-dx " + string r.dx() + " -dy " + string r.dy();
+ "exact" =>
+ spec = "-r " + r2s(r.inset(-Borderwidth));
+ "max" =>
+ r = screenr; # XXX don't obscure toolbar?
+ spec = "-r " + r2s(r.inset(Borderwidth));
+ "getwin" =>
+ ; # just get the new image
+ * =>
+ return "unkown placement method";
+ }
+ }else
+ spec = "-r " + r2s(r.inset(-Borderwidth));
+ return reshape(c, tag, spec);
+ "delete" =>
+ # delete tag
+ if(tl args == nil)
+ return "tag required";
+ tag := hd tl args;
+ nw: list of ref Riowin;
+ for(w := c.wins; w != nil; w = tl w){
+ if((hd w).tag == tag){
+ delwin(hd w);
+ wmsrv->c.sc.setimage(tag, nil);
+ }else
+ nw = hd w :: nw;
+ }
+ c.wins = nil;
+ for(; nw != nil; nw = tl nw)
+ c.wins = hd nw :: c.wins;
+ "label" =>
+ if(n != 2)
+ return "bad arg count";
+ for(w := c.wins; w != nil; w = tl w)
+ setlabel(hd w, hd tl args);
+ "raise" =>
+ for(w := c.wins; w != nil; w = tl w){
+ rioctl(hd w, "top");
+ if(tl w == nil)
+ rioctl(hd w, "current");
+ }
+ "lower" =>
+ for(w := c.wins; w != nil; w = tl w)
+ rioctl(hd w, "bottom");
+ "task" =>
+ if(n != 2)
+ return "bad arg count";
+ c.state |= Hidden;
+ for(w := c.wins; w != nil; w = tl w){
+ setlabel(hd w, hd tl args);
+ rioctl(hd w, "hide");
+ }
+ "untask" =>
+ wins: list of ref Riowin;
+ for(w := c.wins; w != nil; w = tl w)
+ wins = hd w :: wins;
+ for(; wins != nil; wins = tl wins)
+ rioctl(hd wins, "unhide");
+ "!move" =>
+ # !move tag reqid startx starty
+ if(n != 5)
+ return "bad arg count";
+ args = tl args;
+ tag := hd args; args = tl args;
+ args = tl args;
+ w := wmsrv->c.sc.window(tag);
+ if(w == nil)
+ return "no such tag";
+ return dragwin(c.ptr, c, w, Point(int hd args, int hd tl args));
+ "!size" =>
+ return "nope";
+ "kbdfocus" =>
+ if(n != 2)
+ return "bad arg count";
+ if(int hd tl args){
+ if(c.wins != nil)
+ return rioctl(hd c.wins, "current");
+ }
+ return nil;
+ * =>
+ return "unknown request";
+ }
+ return nil;
+}
+
+dragwin(ptr: chan of ref Draw->Pointer, c: ref Client, w: ref Wmsrv->Window, click: Point): string
+{
+# if(buttons == 0)
+# return "too late";
+ p: ref Draw->Pointer;
+ img := w.img.screen.image;
+ r := img.r;
+ off := click.sub(r.min);
+ do{
+ p = <-ptr;
+ img.origin(r.min, p.xy.sub(off));
+ } while (p.buttons != 0);
+ c.sc.ptr <-= p;
+# buttons = 0;
+ nr: Rect;
+ nr.min = p.xy.sub(off);
+ nr.max = nr.min.add(r.size());
+ if(nr.eq(r))
+ return "not moved";
+ reshape(c, w.tag, "-r " + r2s(nr));
+ return nil;
+}
+
+rioctl(w: ref Riowin, req: string): string
+{
+ if(sys->fprint(w.ctlfd, "%s", req) == -1){
+#sys->print("rioctl fail %s: %s: %r\n", w.dir, req);
+ return sys->sprint("%r");
+}
+#sys->print("rioctl %s: %s\n", w.dir, req);
+ return nil;
+}
+
+reshape(c: ref Client, tag: string, spec: string): string
+{
+ for(wl := c.wins; wl != nil; wl = tl wl)
+ if((hd wl).tag == tag)
+ break;
+ if(wl == nil){
+ (w, e) := newwin(c, tag, spec);
+ if(w == nil){
+sys->print("can't make new win (spec %q): %s\n", spec, e);
+ return e;
+ }
+ c.wins = w :: c.wins;
+ wmsrv->c.sc.setimage(tag, w.img);
+ sync := chan of int;
+ if(c.kbdstarted){
+ spawn kbdproc(w, c.sc.kbd, sync);
+ w.kbdpid = <-sync;
+ }
+ if(c.ptrstarted){
+ spawn ptrproc(w, c.ptr, c.resize, sync);
+ w.ptrpid = <-sync;
+ }
+ return nil;
+ }
+ w := hd wl;
+ if(spec != nil){
+ e := rioctl(w, "resize " + spec);
+ if(e != nil)
+ return e;
+ }
+ getwin(w);
+ if(w.img == nil)
+ return "getwin failed";
+ wmsrv->c.sc.setimage(tag, w.img);
+ return nil;
+}
+
+zriowin: Riowin;
+newwin(c: ref Client, tag, spec: string): (ref Riowin, string)
+{
+ wsys := readfile(P9PATH + "/env/wsys");
+ if(wsys == nil)
+ return (nil, "no $wsys");
+
+ d := "/mnt/"+string wsysseq++;
+ fd := sys->open(wsys, Sys->ORDWR);
+ if(fd == nil)
+ return (nil, sys->sprint("cannot open %q: %r\n", wsys));
+ # XXX this won't multiplex properly - srv9 should export attach files (actually that's what plan 9 should do)
+ if(sys->mount(fd, nil, d, Sys->MREPL, "new "+spec) == -1)
+ return (nil, sys->sprint("mount %q failed: %r", wsys));
+ (ok, nil) := sys->stat(d + "/winname");
+ if(ok == -1)
+ return (nil, "could not make window");
+ w := ref zriowin;
+ w.tag = tag;
+ w.dir = d;
+ getwin(w);
+ w.ctlfd = sys->open(d + "/wctl", Sys->ORDWR);
+ setlabel(w, "inferno "+string sys->pctl(0, nil)+"."+tag);
+ sync := chan of int;
+ spawn ctlproc(w, c.riowctl, sync);
+ w.ctlpid = <-sync;
+ return (w, nil);
+}
+
+setlabel(w: ref Riowin, s: string)
+{
+ fd := sys->open(w.dir + "/label", Sys->OWRITE);
+ if(fd != nil)
+ sys->fprint(fd, "%s", s);
+}
+
+ctlproc(w: ref Riowin, wctl: chan of (ref Riowin, int), sync: chan of int)
+{
+ sync <-= sys->pctl(0, nil);
+ buf := array[1024] of byte;
+ for(;;){
+ n := sys->read(w.ctlfd, buf, len buf);
+ if(n <= 0)
+ break;
+ if(n > 4*12){
+ state := 0;
+ (nil, toks) := sys->tokenize(string buf[4*12:], " ");
+ if(hd toks == "current")
+ state |= Current;
+ if(hd tl toks == "hidden")
+ state |= Hidden;
+ wctl <-= (w, state);
+ }
+ }
+#sys->print("riowctl eof\n");
+}
+
+delwin(w: ref Riowin)
+{
+ sys->unmount(nil, w.dir);
+ kill(w.ptrpid, "kill");
+ kill(w.kbdpid, "kill");
+ kill(w.ctlpid, "kill");
+}
+
+getwin(w: ref Riowin): int
+{
+ s := readfile(w.dir + "/winname");
+#sys->print("getwin %s\n", s);
+ i := display.namedimage(s);
+ if(i == nil)
+ return -1;
+ scr := Screen.allocate(i, display.white, 0);
+ if(scr == nil)
+ return -1;
+ wi := scr.newwindow(i.r.inset(Borderwidth), Draw->Refnone, Draw->Nofill);
+ if(wi == nil)
+ return -1;
+ w.img = wi;
+ return 0;
+}
+
+kbdproc(w: ref Riowin, keys: chan of int, sync: chan of int)
+{
+ sys->pctl(Sys->NEWFD, nil);
+ cctl := sys->open(w.dir + "/consctl", Sys->OWRITE);
+ sys->fprint(cctl, "rawon");
+ fd := sys->open(w.dir + "/cons", Sys->OREAD);
+ if(fd == nil){
+ sync <-= -1;
+ return;
+ }
+ sync <-= sys->pctl(0, nil);
+ buf := array[12] of byte;
+ while((n := sys->read(fd, buf, len buf)) > 0){
+ s := string buf[0:n];
+ for(j := 0; j < len s; j++)
+ keys <-= int s[j];
+ }
+#sys->print("eof on kbdproc\n");
+}
+
+# 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;
+}
+
+ptrproc(w: ref Riowin, ptr: chan of ref Draw->Pointer, resize: chan of ref Riowin, sync: chan of int)
+{
+ w.ptrfd = sys->open(w.dir + "/mouse", Sys->ORDWR);
+ if(w.ptrfd == nil){
+ sync <-= -1;
+ return;
+ }
+ sync <-= sys->pctl(0, nil);
+
+ b:= array[Ptrsize] of byte;
+ while((n := sys->read(w.ptrfd, b, len b)) > 0){
+ if(n > 0 && int b[0] == 'r'){
+#sys->print("ptrproc got resize: %s\n", string b[0:n]);
+ resize <-= w;
+ }else{
+ p := bytes2ptr(b);
+ if(p != nil)
+ ptr <-= p;
+ }
+ }
+#sys->print("eof on ptrproc\n");
+}
+
+bytes2ptr(b: array of byte): ref Draw->Pointer
+{
+ if(len b < Ptrsize || int b[0] != 'm')
+ return nil;
+ x := int string b[1:13];
+ y := int string b[13:25];
+ but := int string b[25:37];
+ msec := int string b[37:49];
+ return ref Draw->Pointer (but, (x, y), msec);
+}
+
+readfile(f: string): string
+{
+ fd := sys->open(f, sys->OREAD);
+ if(fd == nil)
+ return nil;
+
+ buf := array[8192] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n < 0)
+ return nil;
+
+ return string buf[0:n];
+}
+
+readscreenr()
+{
+ fd := sys->open(P9PATH + "/dev/screen", Sys->OREAD);
+ if(fd == nil)
+ return ;
+ buf := array[5*12] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n <= len buf)
+ return;
+ screenr.min.x = int string buf[12:23];
+ screenr.min.y = int string buf[24:35];
+ screenr.max.x = int string buf[36:47];
+ screenr.max.y = int string buf[48:];
+}
+
+r2s(r: Rect): string
+{
+ return string r.min.x + " " + string r.min.y + " " +
+ string r.max.x + " " + string r.max.y;
+}
+
+kill(pid: int, note: string): int
+{
+ fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE);
+ if(fd == nil || sys->fprint(fd, "%s", note) < 0)
+ return -1;
+ return 0;
+}