ref: 866d74c0c4bb50e85e9e8bb95140c10d409e53be
dir: /appl/demo/whiteboard/wbsrv.b/
implement Wbserve;
include "sys.m";
sys: Sys;
include "draw.m";
draw: Draw;
Chans, Display, Image, Rect, Point : import draw;
Wbserve : module {
init : fn (ctxt : ref Draw->Context, args : list of string);
};
WBW : con 600;
WBH : con 400;
savefile := "";
init(nil: ref Draw->Context, args: list of string)
{
sys = load Sys Sys->PATH;
draw = load Draw Draw->PATH;
if (draw == nil)
badmod(Draw->PATH);
if (args == nil || tl args == nil)
error("usage: wbsrv mntpt [savefile]");
args = tl args;
mntpt := hd args;
args = tl args;
display := Display.allocate(nil);
if (display == nil)
error(sys->sprint("cannot allocate display: %r"));
bg: ref Draw->Image;
if (args != nil) {
savefile = hd args;
bg = display.open(savefile);
}
r := Rect(Point(0,0), Point(WBW, WBH));
wb := display.newimage(r, Draw->CMAP8, 0, Draw->White);
if (wb == nil)
error(sys->sprint("cannot allocate whiteboard image: %r"));
if (bg != nil) {
wb.draw(bg.r, bg, nil, Point(0,0));
bg = nil;
}
nextmsg = ref Msg (nil, nil);
sys->bind("#s", mntpt, Sys->MBEFORE);
bit := sys->file2chan(mntpt, "wb.bit");
strokes := sys->file2chan(mntpt, "strokes");
spawn srv(wb, bit, strokes);
if (savefile != nil)
spawn saveit(display, wb);
}
srv(wb: ref Image, bit, strokes: ref Sys->FileIO)
{
nwbbytes := draw->bytesperline(wb.r, wb.depth) * wb.r.dy();
bithdr := sys->aprint("%11s %11d %11d %11d %11d ", wb.chans.text(), 0, 0, WBW, WBH);
for (;;) alt {
(offset, count, fid, r) := <-bit.read =>
if (r == nil) {
closeclient(fid);
continue;
}
c := getclient(fid);
if (c == nil) {
# new client
c = newclient(fid);
data := array [len bithdr + nwbbytes] of byte;
data[0:] = bithdr;
wb.readpixels(wb.r, data[len bithdr:]);
c.bitdata = data;
}
if (offset >= len c.bitdata) {
rreply(r, (nil, nil));
continue;
}
rreply(r, (c.bitdata[offset:], nil));
(offset, data, fid, w) := <-bit.write =>
if (w != nil)
wreply(w, (0, "permission denied"));
(offset, count, fid, r) := <-strokes.read =>
if (r == nil) {
closeclient(fid);
continue;
}
c := getclient(fid);
if (c == nil) {
c = newclient(fid);
c.nextmsg = nextmsg;
}
d := c.nextmsg.data;
if (d == nil) {
c.pending = r;
c.pendlen = count;
continue;
}
c.nextmsg = c.nextmsg.next;
rreply(r, (d, nil));
(offset, data, fid, w) := <-strokes.write =>
if (w == nil) {
closeclient(fid);
continue;
}
err := drawstrokes(wb, data);
if (err != nil) {
wreply(w, (0, err));
continue;
}
wreply(w, (len data, nil));
writeclients(data);
}
}
rreply(rc: chan of (array of byte, string), reply: (array of byte, string))
{
alt {
rc <-= reply =>;
* =>;
}
}
wreply(wc: chan of (int, string), reply: (int, string))
{
alt {
wc <-= reply=>;
* =>;
}
}
export(fd : ref Sys->FD, done : chan of int)
{
sys->export(fd, "/", Sys->EXPWAIT);
done <-= 1;
}
Msg : adt {
data : array of byte;
next : cyclic ref Msg;
};
Client : adt {
fid : int;
bitdata : array of byte; # bit file client
nextmsg : ref Msg; # strokes file client
pending : Sys->Rread;
pendlen : int;
};
nextmsg : ref Msg;
clients : list of ref Client;
newclient(fid : int) : ref Client
{
c := ref Client(fid, nil, nil, nil, 0);
clients = c :: clients;
return c;
}
getclient(fid : int) : ref Client
{
for(cl := clients; cl != nil; cl = tl cl)
if((c := hd cl).fid == fid)
return c;
return nil;
}
closeclient(fid : int)
{
nl: list of ref Client;
for(cl := clients; cl != nil; cl = tl cl)
if((hd cl).fid != fid)
nl = hd cl :: nl;
clients = nl;
}
writeclients(data : array of byte)
{
nm := ref Msg(nil, nil);
nextmsg.data = data;
nextmsg.next = nm;
for(cl := clients; cl != nil; cl = tl cl){
if ((c := hd cl).pending != nil) {
n := c.pendlen;
if (n > len data)
n = len data;
alt{
c.pending <-= (data[0:n], nil) => ;
* => ;
}
c.pending = nil;
c.nextmsg = nm;
}
}
nextmsg = nm;
}
# data: colour width p0 p1 pn*
pencol: int;
pen: ref Image;
drawstrokes(wb: ref Image, data : array of byte) : string
{
(n, toks) := sys->tokenize(string data, " ");
if (n < 6 || n & 1)
return "bad data";
colour, width, x, y : int;
(colour, toks) = (int hd toks, tl toks);
(width, toks) = (int hd toks, tl toks);
(x, toks) = (int hd toks, tl toks);
(y, toks) = (int hd toks, tl toks);
if (pen == nil || colour != pencol) {
pencol = colour;
pen = wb.display.newimage(Rect(Point(0,0), Point(1,1)), Draw->CMAP8, 1, pencol);
}
p0 := Point(x, y);
while (toks != nil) {
(x, toks) = (int hd toks, tl toks);
(y, toks) = (int hd toks, tl toks);
p1 := Point(x, y);
# could use poly() instead of line()
wb.line(p0, p1, Draw->Enddisc, Draw->Enddisc, width, pen, pen.r.min);
p0 = p1;
}
return nil;
}
error(e: string)
{
sys->fprint(stderr(), "wbsrv: %s\n", e);
raise "fail:error";
}
stderr(): ref Sys->FD
{
return sys->fildes(2);
}
badmod(path: string)
{
sys->fprint(stderr(), "wbsrv: cannot load %s: %r\n", path);
exit;
}
saveit(display: ref Display, img: ref Image)
{
for (;;) {
sys->sleep(300000);
fd := sys->open(savefile, sys->OWRITE);
if (fd == nil)
exit;
display.writeimage(fd, img);
}
}