diff options
Diffstat (limited to 'appl/wm/drawmux/drawmux.b')
| -rw-r--r-- | appl/wm/drawmux/drawmux.b | 1827 |
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); +} |
