summaryrefslogtreecommitdiff
path: root/appl/lib/wmsrv.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/lib/wmsrv.b')
-rw-r--r--appl/lib/wmsrv.b610
1 files changed, 610 insertions, 0 deletions
diff --git a/appl/lib/wmsrv.b b/appl/lib/wmsrv.b
new file mode 100644
index 00000000..1f2c4e79
--- /dev/null
+++ b/appl/lib/wmsrv.b
@@ -0,0 +1,610 @@
+implement Wmsrv;
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+ draw: Draw;
+ Display, Image, Point, Rect, Screen, Pointer, Context, Wmcontext: import draw;
+include "wmsrv.m";
+
+zorder: ref Client; # top of z-order list, linked by znext.
+
+ZR: con Rect((0, 0), (0, 0));
+Iqueue: adt {
+ h, t: list of int;
+ n: int;
+ put: fn(q: self ref Iqueue, s: int);
+ get: fn(q: self ref Iqueue): int;
+ peek: fn(q: self ref Iqueue): int;
+ nonempty: fn(q: self ref Iqueue): int;
+};
+Squeue: adt {
+ h, t: list of string;
+ n: int;
+ put: fn(q: self ref Squeue, s: string);
+ get: fn(q: self ref Squeue): string;
+ peek: fn(q: self ref Squeue): string;
+ nonempty: fn(q: self ref Squeue): int;
+};
+# Ptrqueue is the same as the other queues except it merges events
+# that have the same button state.
+Ptrqueue: adt {
+ last: ref Pointer;
+ h, t: list of ref Pointer;
+ put: fn(q: self ref Ptrqueue, s: ref Pointer);
+ get: fn(q: self ref Ptrqueue): ref Pointer;
+ peek: fn(q: self ref Ptrqueue): ref Pointer;
+ nonempty: fn(q: self ref Ptrqueue): int;
+ flush: fn(q: self ref Ptrqueue);
+};
+
+init(): (chan of (string, chan of (string, ref Wmcontext)),
+ chan of (ref Client, chan of string),
+ chan of (ref Client, array of byte, Sys->Rwrite))
+{
+ sys = load Sys Sys->PATH;
+ draw = load Draw Draw->PATH;
+
+ sys->bind("#s", "/chan", Sys->MBEFORE);
+
+ ctlio := sys->file2chan("/chan", "wmctl");
+ if(ctlio == nil){
+ sys->werrstr(sys->sprint("can't create /chan/wmctl: %r"));
+ return (nil, nil, nil);
+ }
+
+ wmreq := chan of (string, chan of (string, ref Wmcontext));
+ join := chan of (ref Client, chan of string);
+ req := chan of (ref Client, array of byte, Sys->Rwrite);
+ spawn wm(ctlio, wmreq, join, req);
+ return (wmreq, join, req);
+}
+
+wm(ctlio: ref Sys->FileIO,
+ wmreq: chan of (string, chan of (string, ref Wmcontext)),
+ join: chan of (ref Client, chan of string),
+ req: chan of (ref Client, array of byte, Sys->Rwrite))
+{
+ clients: array of ref Client;
+
+ for(;;)alt{
+ (cmd, rc) := <-wmreq =>
+ token := int cmd;
+ for(i := 0; i < len clients; i++)
+ if(clients[i] != nil && clients[i].token == token)
+ break;
+
+ if(i == len clients){
+ spawn senderror(rc, "not found");
+ break;
+ }
+ c := clients[i];
+ if(c.stop != nil){
+ spawn senderror(rc, "already started");
+ break;
+ }
+ ok := chan of string;
+ join <-= (c, ok);
+ if((e := <-ok) != nil){
+ spawn senderror(rc, e);
+ break;
+ }
+ c.stop = chan of int;
+ spawn childminder(c, rc);
+
+ (nil, nbytes, fid, rc) := <-ctlio.read =>
+ if(rc == nil)
+ break;
+ c := findfid(clients, fid);
+ if(c == nil){
+ c = ref Client(
+ chan of int,
+ chan of ref Draw->Pointer,
+ chan of string,
+ nil,
+ 0,
+ nil,
+ nil,
+ nil,
+
+ chan of (ref Point, ref Image, chan of int),
+ -1,
+ fid,
+ fid, # token; XXX could be random integer + fid
+ newwmcontext()
+ );
+ clients = addclient(clients, c);
+ }
+ alt{
+ rc <-= (sys->aprint("%d", c.token), nil) => ;
+ * => ;
+ }
+ (nil, data, fid, wc) := <-ctlio.write =>
+ c := findfid(clients, fid);
+ if(wc != nil){
+ if(c == nil){
+ alt{
+ wc <-= (0, "must read first") => ;
+ * => ;
+ }
+ break;
+ }
+ req <-= (c, data, wc);
+ }else if(c != nil){
+ req <-= (c, nil, nil);
+ delclient(clients, c);
+ }
+ }
+}
+
+# buffer all events between a window manager and
+# a client, so that one recalcitrant child can't
+# clog the whole system.
+childminder(c: ref Client, rc: chan of (string, ref Wmcontext))
+{
+ wmctxt := c.wmctxt;
+
+ dummykbd := chan of int;
+ dummyptr := chan of ref Pointer;
+ dummyimg := chan of ref Image;
+ dummyctl := chan of string;
+
+ kbdq := ref Iqueue;
+ ptrq := ref Ptrqueue;
+ ctlq := ref Squeue;
+
+ Imgnone, Imgsend, Imgsendnil1, Imgsendnil2, Imgorigin: con iota;
+ img, sendimg: ref Image;
+ imgorigin: Point;
+ imgstate := Imgnone;
+
+ # send reply to client, but make sure we don't block.
+Reply:
+ for(;;) alt{
+ rc <-= (nil, ref *wmctxt) =>
+ break Reply;
+ <-c.stop =>
+ exit;
+ key := <-c.kbd =>
+ kbdq.put(key);
+ ptr := <-c.ptr =>
+ ptrq.put(ptr);
+ ctl := <-c.ctl =>
+ ctlq.put(ctl);
+ }
+
+ for(;;){
+ outkbd := dummykbd;
+ key := -1;
+ if(kbdq.nonempty()){
+ key = kbdq.peek();
+ outkbd = wmctxt.kbd;
+ }
+
+ outptr := dummyptr;
+ ptr: ref Pointer;
+ if(ptrq.nonempty()){
+ ptr = ptrq.peek();
+ outptr = wmctxt.ptr;
+ }
+
+ outctl := dummyctl;
+ ctl: string;
+ if(ctlq.nonempty()){
+ ctl = ctlq.peek();
+ outctl = wmctxt.ctl;
+ }
+
+ outimg := dummyimg;
+ case imgstate{
+ Imgsend =>
+ outimg = wmctxt.images;
+ sendimg = img;
+ Imgsendnil1 or
+ Imgsendnil2 or
+ Imgorigin =>
+ outimg = wmctxt.images;
+ sendimg = nil;
+ }
+
+ alt{
+ outkbd <-= key =>
+ kbdq.get();
+ outptr <-= ptr =>
+ ptrq.get();
+ outctl <-= ctl =>
+ ctlq.get();
+ outimg <-= sendimg =>
+ case imgstate{
+ Imgsend =>
+ imgstate = Imgnone;
+ img = sendimg = nil;
+ Imgsendnil1 =>
+ imgstate = Imgsendnil2;
+ Imgsendnil2 =>
+ imgstate = Imgnone;
+ Imgorigin =>
+ if(img.origin(imgorigin, imgorigin) == -1){
+ # XXX what can we do about this? there's no way at the moment
+ # of getting the information about the origin failure back to the wm,
+ # so we end up with an inconsistent window position.
+ # if the window manager blocks while we got the sync from
+ # the client, then a client could block the whole window manager
+ # which is what we're trying to avoid.
+ # but there's no other time we could set the origin of the window,
+ # and not risk mucking up the window contents.
+ # the short answer is that running out of image space is Bad News.
+ }
+ imgstate = Imgsend;
+ }
+
+ # XXX could mark the application as unresponding if any of these queues
+ # start growing too much.
+ ch := <-c.kbd =>
+ kbdq.put(ch);
+ p := <-c.ptr =>
+ if(p == nil)
+ ptrq.flush();
+ else
+ ptrq.put(p);
+ e := <-c.ctl =>
+ ctlq.put(e);
+ (o, i, reply) := <-c.images =>
+ # can't queue multiple image requests.
+ if(imgstate != Imgnone)
+ reply <-= -1;
+ else {
+ # if the origin is being set, then we first send a nil image
+ # to indicate that this is happening, and then the
+ # image itself (reorigined).
+ # if a nil image is being set, then we
+ # send nil twice.
+ if(o != nil){
+ imgorigin = *o;
+ imgstate = Imgorigin;
+ img = i;
+ }else if(i != nil){
+ img = i;
+ imgstate = Imgsend;
+ }else
+ imgstate = Imgsendnil1;
+ reply <-= 0;
+ }
+ <-c.stop =>
+ # XXX do we need to unblock channels, kill, etc.?
+ # we should perhaps drain the ctl output channel here
+ # if possible, exiting if it times out.
+ exit;
+ }
+ }
+}
+
+findfid(clients: array of ref Client, fid: int): ref Client
+{
+ for(i := 0; i < len clients; i++)
+ if(clients[i] != nil && clients[i].fid == fid)
+ return clients[i];
+ return nil;
+}
+
+addclient(clients: array of ref Client, c: ref Client): array of ref Client
+{
+ for(i := 0; i < len clients; i++)
+ if(clients[i] == nil){
+ clients[i] = c;
+ c.id = i;
+ return clients;
+ }
+ nc := array[len clients + 4] of ref Client;
+ nc[0:] = clients;
+ nc[len clients] = c;
+ c.id = len clients;
+ return nc;
+}
+
+delclient(clients: array of ref Client, c: ref Client)
+{
+ clients[c.id] = nil;
+}
+
+senderror(rc: chan of (string, ref Wmcontext), e: string)
+{
+ rc <-= (e, nil);
+}
+
+Client.window(c: self ref Client, tag: string): ref Window
+{
+ for (w := c.wins; w != nil; w = tl w)
+ if((hd w).tag == tag)
+ return hd w;
+ return nil;
+}
+
+Client.image(c: self ref Client, tag: string): ref Draw->Image
+{
+ w := c.window(tag);
+ if(w != nil)
+ return w.img;
+ return nil;
+}
+
+Client.setimage(c: self ref Client, tag: string, img: ref Draw->Image): int
+{
+ # if img is nil, remove window from list.
+ if(img == nil){
+ # usual case:
+ if(c.wins != nil && (hd c.wins).tag == tag){
+ c.wins = tl c.wins;
+ return -1;
+ }
+ nw: list of ref Window;
+ for (w := c.wins; w != nil; w = tl w)
+ if((hd w).tag != tag)
+ nw = hd w :: nw;
+ c.wins = nil;
+ for(; nw != nil; nw = tl nw)
+ c.wins = hd nw :: c.wins;
+ return -1;
+ }
+ for(w := c.wins; w != nil; w = tl w)
+ if((hd w).tag == tag)
+ break;
+ win: ref Window;
+ if(w != nil)
+ win = hd w;
+ else{
+ win = ref Window(tag, ZR, nil);
+ c.wins = win :: c.wins;
+ }
+ win.img = img;
+ win.r = img.r; # save so clients can set logical origin
+ rc := chan of int;
+ c.images <-= (nil, img, rc);
+ return <-rc;
+}
+
+# tell a client about a window that's moved to screen coord o.
+Client.setorigin(c: self ref Client, tag: string, o: Draw->Point): int
+{
+ w := c.window(tag);
+ if(w == nil)
+ return -1;
+ img := w.img;
+ if(img == nil)
+ return -1;
+ rc := chan of int;
+ c.images <-= (ref o, w.img, rc);
+ if(<-rc != -1){
+ w.r = (o, o.add(img.r.size()));
+ return 0;
+ }
+ return -1;
+}
+
+clientimages(c: ref Client): array of ref Image
+{
+ a := array[len c.wins] of ref Draw->Image;
+ i := 0;
+ for(w := c.wins; w != nil; w = tl w)
+ if((hd w).img != nil)
+ a[i++] = (hd w).img;
+ return a[0:i];
+}
+
+Client.top(c: self ref Client)
+{
+ imgs := clientimages(c);
+ if(len imgs > 0)
+ imgs[0].screen.top(imgs);
+
+ if(zorder == c)
+ return;
+
+ prev: ref Client;
+ for(z := zorder; z != nil; (prev, z) = (z, z.znext))
+ if(z == c)
+ break;
+ if(prev != nil)
+ prev.znext = c.znext;
+ c.znext = zorder;
+ zorder = c;
+}
+
+Client.bottom(c: self ref Client)
+{
+ if(c.znext == nil)
+ return;
+ imgs := clientimages(c);
+ if(len imgs > 0)
+ imgs[0].screen.bottom(imgs);
+ prev: ref Client;
+ for(z := zorder; z != nil; (prev, z) = (z, z.znext))
+ if(z == c)
+ break;
+ if(prev != nil)
+ prev.znext = c.znext;
+ else
+ zorder = c.znext;
+ z = c.znext;
+ c.znext = nil;
+ for(; z != nil; (prev, z) = (z, z.znext))
+ ;
+ if(prev != nil)
+ prev.znext = c;
+ else
+ zorder = c;
+}
+
+Client.hide(nil: self ref Client)
+{
+}
+
+Client.unhide(nil: self ref Client)
+{
+}
+
+Client.remove(c: self ref Client)
+{
+ prev: ref Client;
+ for(z := zorder; z != nil; (prev, z) = (z, z.znext))
+ if(z == c)
+ break;
+ if(z == nil)
+ return;
+ if(prev != nil)
+ prev.znext = z.znext;
+ else if(z != nil)
+ zorder = zorder.znext;
+}
+
+find(p: Draw->Point): ref Client
+{
+ for(z := zorder; z != nil; z = z.znext)
+ if(z.contains(p))
+ return z;
+ return nil;
+}
+
+top(): ref Client
+{
+ return zorder;
+}
+
+Client.contains(c: self ref Client, p: Point): int
+{
+ for(w := c.wins; w != nil; w = tl w)
+ if((hd w).r.contains(p))
+ return 1;
+ return 0;
+}
+
+r2s(r: Rect): string
+{
+ return string r.min.x + " " + string r.min.y + " " +
+ string r.max.x + " " + string r.max.y;
+}
+
+newwmcontext(): ref Wmcontext
+{
+ return ref Wmcontext(
+ chan of int,
+ chan of ref Pointer,
+ chan of string,
+ nil,
+ chan of ref Image,
+ nil,
+ nil
+ );
+}
+
+Iqueue.put(q: self ref Iqueue, s: int)
+{
+ q.t = s :: q.t;
+}
+Iqueue.get(q: self ref Iqueue): int
+{
+ s := -1;
+ if(q.h == nil){
+ for(t := q.t; t != nil; t = tl t)
+ q.h = hd t :: q.h;
+ q.t = nil;
+ }
+ if(q.h != nil){
+ s = hd q.h;
+ q.h = tl q.h;
+ }
+ return s;
+}
+Iqueue.peek(q: self ref Iqueue): int
+{
+ s := -1;
+ if (q.h == nil && q.t == nil)
+ return s;
+ s = q.get();
+ q.h = s :: q.h;
+ return s;
+}
+Iqueue.nonempty(q: self ref Iqueue): int
+{
+ return q.h != nil || q.t != nil;
+}
+
+
+Squeue.put(q: self ref Squeue, s: string)
+{
+ q.t = s :: q.t;
+}
+Squeue.get(q: self ref Squeue): string
+{
+ s: string;
+ if(q.h == nil){
+ for(t := q.t; t != nil; t = tl t)
+ q.h = hd t :: q.h;
+ q.t = nil;
+ }
+ if(q.h != nil){
+ s = hd q.h;
+ q.h = tl q.h;
+ }
+ return s;
+}
+Squeue.peek(q: self ref Squeue): string
+{
+ s: string;
+ if (q.h == nil && q.t == nil)
+ return s;
+ s = q.get();
+ q.h = s :: q.h;
+ return s;
+}
+Squeue.nonempty(q: self ref Squeue): int
+{
+ return q.h != nil || q.t != nil;
+}
+
+Ptrqueue.put(q: self ref Ptrqueue, s: ref Pointer)
+{
+ if(q.last != nil && s.buttons == q.last.buttons)
+ *q.last = *s;
+ else{
+ q.t = s :: q.t;
+ q.last = s;
+ }
+}
+Ptrqueue.get(q: self ref Ptrqueue): ref Pointer
+{
+ s: ref Pointer;
+ h := q.h;
+ if(h == nil){
+ for(t := q.t; t != nil; t = tl t)
+ h = hd t :: h;
+ q.t = nil;
+ }
+ if(h != nil){
+ s = hd h;
+ h = tl h;
+ if(h == nil)
+ q.last = nil;
+ }
+ q.h = h;
+ return s;
+}
+Ptrqueue.peek(q: self ref Ptrqueue): ref Pointer
+{
+ s: ref Pointer;
+ if (q.h == nil && q.t == nil)
+ return s;
+ t := q.last;
+ s = q.get();
+ q.h = s :: q.h;
+ q.last = t;
+ return s;
+}
+Ptrqueue.nonempty(q: self ref Ptrqueue): int
+{
+ return q.h != nil || q.t != nil;
+}
+Ptrqueue.flush(q: self ref Ptrqueue)
+{
+ q.h = q.t = nil;
+}