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; }