ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/wm/drawmux/drawmux.b/
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);
}