summaryrefslogtreecommitdiff
path: root/appl/wm/drawmux/drawmux.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/wm/drawmux/drawmux.b')
-rw-r--r--appl/wm/drawmux/drawmux.b1827
1 files changed, 1827 insertions, 0 deletions
diff --git a/appl/wm/drawmux/drawmux.b b/appl/wm/drawmux/drawmux.b
new file mode 100644
index 00000000..121efec0
--- /dev/null
+++ b/appl/wm/drawmux/drawmux.b
@@ -0,0 +1,1827 @@
+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);
+}