implement Wmclient; ## Copyright © 2003 Vita Nuova Holdings Limited# include "sys.m"; sys: Sys;include "draw.m"; draw: Draw; Display, Image, Screen, Rect, Point, Pointer, Wmcontext, Context: import draw;include "tk.m"; tk: Tk; Toplevel: import tk;include "wmlib.m"; wmlib: Wmlib; qword, splitqword, s2r: import wmlib;include "titlebar.m"; titlebar: Titlebar;include "wmclient.m"; Focusnone, Focusimage, Focustitle: con iota; Bdup: con int 16rffffffff;Bddown: con int 16radadadff; init(){ sys = load Sys Sys->PATH; draw = load Draw Draw->PATH; tk = load Tk Tk->PATH; wmlib = load Wmlib Wmlib->PATH; if(wmlib == nil){ sys->fprint(sys->fildes(2), "wmclient: cannot load %s: %r\n", Wmlib->PATH); raise "fail:bad module"; } wmlib->init(); titlebar = load Titlebar Titlebar->PATH; if(titlebar == nil){ sys->fprint(sys->fildes(2), "wmclient: cannot load %s: %r\n", Titlebar->PATH); raise "fail:bad module"; } titlebar->init();} makedrawcontext(): ref Draw->Context{ return wmlib->makedrawcontext();} cursorspec(img: ref Draw->Image): string{ Hex: con "0123456789abcdef"; if(img == nil || img.depth != 1) return "cursor"; display := img.display; hot := img.r.min; if(img.r.min.x != 0 || img.r.min.y != 0){ n := display.newimage(((0, 0), img.r.size()), Draw->GREY1, 0, Draw->Nofill); n.draw(n.r, img, nil, img.r.min); img = n; } s := sys->sprint("cursor %d %d %d %d ", hot.x, hot.y, img.r.dx(), img.r.dy()); nb := img.r.dy() * draw->bytesperline(img.r, img.depth); buf := array[nb] of byte; if(img.readpixels(img.r, buf) == -1) return "cursor"; for(i := 0; i < nb; i++){ c := int buf[i]; s[len s] = Hex[c >> 4]; s[len s] = Hex[c & 16rf]; } return s;} blankwin: Window;window(ctxt: ref Draw->Context, title: string, buts: int): ref Window{ w := ref blankwin; w.ctxt = wmlib->connect(ctxt); w.display = ctxt.display; w.ctl = chan of string; readscreenrect(w); if(buts & Plain) return w; if(ctxt.wm == nil) buts &= ~(Resize|Hide); w.bd = 1; w.titlebar = tk->toplevel(ctxt.display, nil); top := w.titlebar; top.wreq = nil; w.ctl = titlebar->new(top, buts); titlebar->settitle(top, title); sizetb(w); w.wmctl("fixedorigin"); return w;} Window.pointer(w: self ref Window, p: Draw->Pointer): int{ if(w.screen == nil || w.titlebar == nil) return 0; if(p.buttons && (w.ptrfocus == Focusnone || w.buttons == 0)){ if(p.xy.in(w.tbrect)) w.ptrfocus = Focustitle; else w.ptrfocus = Focusimage; } w.buttons = p.buttons; if(w.ptrfocus == Focustitle){ tk->pointer(w.titlebar, p); return 1; } return 0;} # titlebar requested size might have changed:# find out what size it's requesting.sizetb(w: ref Window){ if(w.titlebar == nil) return; w.tbsize = tk->rect(w.titlebar, ".", Tk->Border|Tk->Required).size();} # reshape the image; the space needed for the# titlebar is added to r.Window.reshape(w: self ref Window, r: Rect){ w.r = w.screenr(r); if(w.screen == nil) return; w.wmctl(sys->sprint("!reshape . -1 %s", r2s(w.r)));} putimage(w: ref Window, i: ref Image){ if(w.screen != nil && i == w.screen.image) return; w.screen = Screen.allocate(i, w.display.color(Draw->White), 0); ir := i.r.inset(w.bd); if(ir.dx() < 0) ir.max.x = ir.min.x; if(ir.dy() < 0) ir.max.y = ir.min.y; if(w.titlebar != nil){ w.tbrect = Rect(ir.min, (ir.max.x, ir.min.y + w.tbsize.y)); tbimage := w.screen.newwindow(w.tbrect, Draw->Refnone, Draw->Nofill); tk->putimage(w.titlebar, ".", tbimage, nil); ir.min.y = w.tbrect.max.y; } if(ir.dy() < 0) ir.max.y = ir.min.y; w.image = w.screen.newwindow(ir, Draw->Refnone, Draw->Nofill); drawborder(w); w.r = i.r;} # return a rectangle suitable to hold image r when the# titlebar and border are included.Window.screenr(w: self ref Window, r: Rect): Rect{ if(w.titlebar != nil){ if(r.dx() < w.tbsize.x) r.max.x = r.min.x + w.tbsize.x; r.min.y -= w.tbsize.y; } return r.inset(-w.bd);} # return the available space inside r when space for# border and titlebar is taken away.Window.imager(w: self ref Window, r: Rect): Rect{ r = r.inset(w.bd); if(r.dx() < 0) r.max.x = r.min.x; if(r.dy() < 0) r.max.y = r.min.y; if(w.titlebar != nil){ r.min.y += w.tbsize.y; if(r.dy() < 0) r.max.y = r.min.y; } return r;} # draw an imitation tk border.drawborder(w: ref Window){ if(w.screen == nil) return; col := w.display.color(Bdup); i := w.screen.image; r := w.screen.image.r; i.draw((r.min, (r.min.x+w.bd, r.max.y)), col, nil, (0, 0)); i.draw(((r.min.x+w.bd, r.min.y), (r.max.x, r.min.y+w.bd)), col, nil, (0, 0)); col = w.display.color(Bddown); i.draw(((r.max.x-w.bd, r.min.y+w.bd), r.max), col, nil, (0, 0)); i.draw(((r.min.x+w.bd, r.max.y-w.bd), (r.max.x-w.bd, r.max.y)), col, nil, (0, 0));} readscreenrect(w: ref Window){ if((fd := sys->open("/chan/wmrect", Sys->OREAD)) != nil){ buf := array[12*4] of byte; n := sys->read(fd, buf, len buf); if(n > 0){ (w.displayr, nil) = s2r(string buf[0:n], 0); return; } } w.displayr = w.display.image.r;} Window.onscreen(w: self ref Window, how: string){ if(how == nil) how = "place"; w.wmctl(sys->sprint("!reshape . -1 %s %q", r2s(w.r), how));} Window.startinput(w: self ref Window, devs: list of string){ for(; devs != nil; devs = tl devs) w.wmctl(sys->sprint("start %q", hd devs));} # commands originating both from tkclient and wm (via ctl)Window.wmctl(w: self ref Window, req: string): string{ (c, next) := qword(req, 0); case c { "exit" => sys->fprint(sys->open("/prog/" + string sys->pctl(0, nil) + "/ctl", Sys->OWRITE), "killgrp"); exit; # old-style requests: pass them back around in proper form. "move" => # move x y if(w.titlebar != nil) titlebar->sendctl(w.titlebar, "!move . -1 " + req[next:]); "size" => if(w.titlebar != nil){ minsz := titlebar->minsize(w.titlebar); titlebar->sendctl(w.titlebar, "!size . -1 " + string minsz.x + " " + string minsz.y); } "ok" or "help" => ; "rect" => (w.displayr, nil) = s2r(req, next); "haskbdfocus" => w.focused = int qword(req, next).t0; if(w.titlebar != nil){ tk->cmd(w.titlebar, "focus -global " + string w.focused); tk->cmd(w.titlebar, "update"); } drawborder(w); "task" => title := ""; if(w.titlebar != nil) title = titlebar->title(w.titlebar); wmreq(w, sys->sprint("task %q", title), next); w.saved = w.r.min; # send window out of the way # XXX oops, can't do this for plain windows... titlebar->sendctl(w.titlebar, "!reshape . -1 " + r2s((w.displayr.max, w.displayr.max.add(w.r.size())))); "untask" => wmreq(w, req, next); # put window back where it was before. # XXX what do we we do if the window manager window has been reshape in the meantime...? titlebar->sendctl(w.titlebar, "!reshape . -1 " + r2s((w.saved, w.saved.add(w.r.size())))); * => return wmreq(w, req, next); } return nil;} wmreq(w: ref Window, req: string, e: int): string{ name: string; if(req != nil && req[0] == '!'){ (name, e) = qword(req, e); if(name != ".") return "invalid window name"; } if(w.ctxt.connfd != nil){ if(sys->fprint(w.ctxt.connfd, "%s", req) == -1) return sys->sprint("%r"); if(req[0] == '!') recvimage(w); return nil; } # if we're getting an image and there's no window manager, # then there's only one image to get... if(req[0] == '!') putimage(w, w.ctxt.ctxt.display.image); else{ (nil, nil, err) := wmlib->wmctl(w.ctxt, req); return err; } return nil;} recvimage(w: ref Window){ i := <-w.ctxt.images; if(i == nil) i = <-w.ctxt.images; putimage(w, i);} Window.settitle(w: self ref Window, title: string): string{ if(w.titlebar == nil) return nil; oldr := w.imager(w.r); old := titlebar->settitle(w.titlebar, title); sizetb(w); if(w.tbsize.x < w.r.dx()) tk->putimage(w.titlebar, ".", w.titlebar.image, nil); # unsuspend the window else w.wmctl("!reshape . -1 " + r2s(w.screenr(oldr))); return old;} snarfget(): string{ return wmlib->snarfget();} snarfput(buf: string){ return wmlib->snarfput(buf);} r2s(r: Rect): string{ return sys->sprint("%d %d %d %d", r.min.x, r.min.y, r.max.x, r.max.y);}
implement Wmclient;
#
# Copyright © 2003 Vita Nuova Holdings Limited
#
include "sys.m";
sys: Sys;
include "draw.m";
draw: Draw;
Display, Image, Screen, Rect, Point, Pointer, Wmcontext, Context: import draw;
include "tk.m";
tk: Tk;
Toplevel: import tk;
include "wmlib.m";
wmlib: Wmlib;
qword, splitqword, s2r: import wmlib;
include "titlebar.m";
titlebar: Titlebar;
include "wmclient.m";
Focusnone, Focusimage, Focustitle: con iota;
Bdup: con int 16rffffffff;
Bddown: con int 16radadadff;
init()
{
sys = load Sys Sys->PATH;
draw = load Draw Draw->PATH;
tk = load Tk Tk->PATH;
wmlib = load Wmlib Wmlib->PATH;
if(wmlib == nil){
sys->fprint(sys->fildes(2), "wmclient: cannot load %s: %r\n", Wmlib->PATH);
raise "fail:bad module";
}
wmlib->init();
titlebar = load Titlebar Titlebar->PATH;
if(titlebar == nil){
sys->fprint(sys->fildes(2), "wmclient: cannot load %s: %r\n", Titlebar->PATH);
raise "fail:bad module";
}
titlebar->init();
}
makedrawcontext(): ref Draw->Context
{
return wmlib->makedrawcontext();
}
cursorspec(img: ref Draw->Image): string
{
Hex: con "0123456789abcdef";
if(img == nil || img.depth != 1)
return "cursor";
display := img.display;
hot := img.r.min;
if(img.r.min.x != 0 || img.r.min.y != 0){
n := display.newimage(((0, 0), img.r.size()), Draw->GREY1, 0, Draw->Nofill);
n.draw(n.r, img, nil, img.r.min);
img = n;
}
s := sys->sprint("cursor %d %d %d %d ", hot.x, hot.y, img.r.dx(), img.r.dy());
nb := img.r.dy() * draw->bytesperline(img.r, img.depth);
buf := array[nb] of byte;
if(img.readpixels(img.r, buf) == -1)
return "cursor";
for(i := 0; i < nb; i++){
c := int buf[i];
s[len s] = Hex[c >> 4];
s[len s] = Hex[c & 16rf];
}
return s;
}
blankwin: Window;
window(ctxt: ref Draw->Context, title: string, buts: int): ref Window
{
w := ref blankwin;
w.ctxt = wmlib->connect(ctxt);
w.display = ctxt.display;
w.ctl = chan of string;
readscreenrect(w);
if(buts & Plain)
return w;
if(ctxt.wm == nil)
buts &= ~(Resize|Hide);
w.bd = 1;
w.titlebar = tk->toplevel(ctxt.display, nil);
top := w.titlebar;
top.wreq = nil;
w.ctl = titlebar->new(top, buts);
titlebar->settitle(top, title);
sizetb(w);
w.wmctl("fixedorigin");
return w;
}
Window.pointer(w: self ref Window, p: Draw->Pointer): int
{
if(w.screen == nil || w.titlebar == nil)
return 0;
if(p.buttons && (w.ptrfocus == Focusnone || w.buttons == 0)){
if(p.xy.in(w.tbrect))
w.ptrfocus = Focustitle;
else
w.ptrfocus = Focusimage;
}
w.buttons = p.buttons;
if(w.ptrfocus == Focustitle){
tk->pointer(w.titlebar, p);
return 1;
}
return 0;
}
# titlebar requested size might have changed:
# find out what size it's requesting.
sizetb(w: ref Window)
{
if(w.titlebar == nil)
return;
w.tbsize = tk->rect(w.titlebar, ".", Tk->Border|Tk->Required).size();
}
# reshape the image; the space needed for the
# titlebar is added to r.
Window.reshape(w: self ref Window, r: Rect)
{
w.r = w.screenr(r);
if(w.screen == nil)
return;
w.wmctl(sys->sprint("!reshape . -1 %s", r2s(w.r)));
}
putimage(w: ref Window, i: ref Image)
{
if(w.screen != nil && i == w.screen.image)
return;
w.screen = Screen.allocate(i, w.display.color(Draw->White), 0);
ir := i.r.inset(w.bd);
if(ir.dx() < 0)
ir.max.x = ir.min.x;
if(ir.dy() < 0)
ir.max.y = ir.min.y;
if(w.titlebar != nil){
w.tbrect = Rect(ir.min, (ir.max.x, ir.min.y + w.tbsize.y));
tbimage := w.screen.newwindow(w.tbrect, Draw->Refnone, Draw->Nofill);
tk->putimage(w.titlebar, ".", tbimage, nil);
ir.min.y = w.tbrect.max.y;
}
if(ir.dy() < 0)
ir.max.y = ir.min.y;
w.image = w.screen.newwindow(ir, Draw->Refnone, Draw->Nofill);
drawborder(w);
w.r = i.r;
}
# return a rectangle suitable to hold image r when the
# titlebar and border are included.
Window.screenr(w: self ref Window, r: Rect): Rect
{
if(w.titlebar != nil){
if(r.dx() < w.tbsize.x)
r.max.x = r.min.x + w.tbsize.x;
r.min.y -= w.tbsize.y;
}
return r.inset(-w.bd);
}
# return the available space inside r when space for
# border and titlebar is taken away.
Window.imager(w: self ref Window, r: Rect): Rect
{
r = r.inset(w.bd);
if(r.dx() < 0)
r.max.x = r.min.x;
if(r.dy() < 0)
r.max.y = r.min.y;
if(w.titlebar != nil){
r.min.y += w.tbsize.y;
if(r.dy() < 0)
r.max.y = r.min.y;
}
return r;
}
# draw an imitation tk border.
drawborder(w: ref Window)
{
if(w.screen == nil)
return;
col := w.display.color(Bdup);
i := w.screen.image;
r := w.screen.image.r;
i.draw((r.min, (r.min.x+w.bd, r.max.y)), col, nil, (0, 0));
i.draw(((r.min.x+w.bd, r.min.y), (r.max.x, r.min.y+w.bd)), col, nil, (0, 0));
col = w.display.color(Bddown);
i.draw(((r.max.x-w.bd, r.min.y+w.bd), r.max), col, nil, (0, 0));
i.draw(((r.min.x+w.bd, r.max.y-w.bd), (r.max.x-w.bd, r.max.y)), col, nil, (0, 0));
}
readscreenrect(w: ref Window)
{
if((fd := sys->open("/chan/wmrect", Sys->OREAD)) != nil){
buf := array[12*4] of byte;
n := sys->read(fd, buf, len buf);
if(n > 0){
(w.displayr, nil) = s2r(string buf[0:n], 0);
return;
}
}
w.displayr = w.display.image.r;
}
Window.onscreen(w: self ref Window, how: string)
{
if(how == nil)
how = "place";
w.wmctl(sys->sprint("!reshape . -1 %s %q", r2s(w.r), how));
}
Window.startinput(w: self ref Window, devs: list of string)
{
for(; devs != nil; devs = tl devs)
w.wmctl(sys->sprint("start %q", hd devs));
}
# commands originating both from tkclient and wm (via ctl)
Window.wmctl(w: self ref Window, req: string): string
{
(c, next) := qword(req, 0);
case c {
"exit" =>
sys->fprint(sys->open("/prog/" + string sys->pctl(0, nil) + "/ctl", Sys->OWRITE), "killgrp");
exit;
# old-style requests: pass them back around in proper form.
"move" =>
# move x y
if(w.titlebar != nil)
titlebar->sendctl(w.titlebar, "!move . -1 " + req[next:]);
"size" =>
if(w.titlebar != nil){
minsz := titlebar->minsize(w.titlebar);
titlebar->sendctl(w.titlebar, "!size . -1 " + string minsz.x + " " + string minsz.y);
}
"ok" or
"help" =>
;
"rect" =>
(w.displayr, nil) = s2r(req, next);
"haskbdfocus" =>
w.focused = int qword(req, next).t0;
if(w.titlebar != nil){
tk->cmd(w.titlebar, "focus -global " + string w.focused);
tk->cmd(w.titlebar, "update");
}
drawborder(w);
"task" =>
title := "";
if(w.titlebar != nil)
title = titlebar->title(w.titlebar);
wmreq(w, sys->sprint("task %q", title), next);
w.saved = w.r.min;
# send window out of the way
# XXX oops, can't do this for plain windows...
titlebar->sendctl(w.titlebar, "!reshape . -1 " + r2s((w.displayr.max, w.displayr.max.add(w.r.size()))));
"untask" =>
wmreq(w, req, next);
# put window back where it was before.
# XXX what do we we do if the window manager window has been reshape in the meantime...?
titlebar->sendctl(w.titlebar, "!reshape . -1 " + r2s((w.saved, w.saved.add(w.r.size()))));
* =>
return wmreq(w, req, next);
}
return nil;
}
wmreq(w: ref Window, req: string, e: int): string
{
name: string;
if(req != nil && req[0] == '!'){
(name, e) = qword(req, e);
if(name != ".")
return "invalid window name";
}
if(w.ctxt.connfd != nil){
if(sys->fprint(w.ctxt.connfd, "%s", req) == -1)
return sys->sprint("%r");
if(req[0] == '!')
recvimage(w);
return nil;
}
# if we're getting an image and there's no window manager,
# then there's only one image to get...
if(req[0] == '!')
putimage(w, w.ctxt.ctxt.display.image);
else{
(nil, nil, err) := wmlib->wmctl(w.ctxt, req);
return err;
}
return nil;
}
recvimage(w: ref Window)
{
i := <-w.ctxt.images;
if(i == nil)
i = <-w.ctxt.images;
putimage(w, i);
}
Window.settitle(w: self ref Window, title: string): string
{
if(w.titlebar == nil)
return nil;
oldr := w.imager(w.r);
old := titlebar->settitle(w.titlebar, title);
sizetb(w);
if(w.tbsize.x < w.r.dx())
tk->putimage(w.titlebar, ".", w.titlebar.image, nil); # unsuspend the window
else
w.wmctl("!reshape . -1 " + r2s(w.screenr(oldr)));
return old;
}
snarfget(): string
{
return wmlib->snarfget();
}
snarfput(buf: string)
{
return wmlib->snarfput(buf);
}
r2s(r: Rect): string
{
return sys->sprint("%d %d %d %d", r.min.x, r.min.y, r.max.x, r.max.y);
}