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)
{
d := P9PATH + "/dev";
(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;
}
|