ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/lib/tkclient.b/
implement Tkclient;
#
# 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 "tkclient.m";
Background: con int 16r777777FF; # should be drawn over immediately, but just in case...
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), "tkclient: 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), "tkclient: cannot load %s: %r\n", Titlebar->PATH);
raise "fail:bad module";
}
titlebar->init();
}
makedrawcontext(): ref Draw->Context
{
return wmlib->makedrawcontext();
}
toplevel(ctxt: ref Draw->Context, topconfig: string, title: string, buts: int): (ref Tk->Toplevel, chan of string)
{
wm := wmlib->connect(ctxt);
opts := "";
if((buts & Plain) == 0)
opts = "-borderwidth 1 -relief raised ";
top := tk->toplevel(wm.ctxt.display, opts+topconfig);
if (top == nil) {
sys->fprint(sys->fildes(2), "wmlib: window creation failed (top %ux, i %ux)\n", top, top.image);
raise "fail:window creation failed";
}
top.ctxt = wm;
readscreenrect(top);
c := titlebar->new(top, buts);
titlebar->settitle(top, title);
return (top, c);
}
readscreenrect(top: ref Tk->Toplevel)
{
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)
(top.screenr, nil) = s2r(string buf[0:n], 0);
}
}
onscreen(top: ref Tk->Toplevel, how: string)
{
if(how == nil)
how = "place";
wmctl(top, sys->sprint("!reshape . -1 %s %q",
r2s(tk->rect(top, ".", Tk->Border|Tk->Required)), how));
}
startinput(top: ref Tk->Toplevel, devs: list of string)
{
for(; devs != nil; devs = tl devs)
wmctl(top, sys->sprint("start %q", hd devs));
}
r2s(r: Rect): string
{
return sys->sprint("%d %d %d %d", r.min.x, r.min.y, r.max.x, r.max.y);
}
# commands originating both from tkclient and wm (via ctl)
wmctl(top: ref Tk->Toplevel, req: string): string
{
#sys->print("wmctl %s\n", req);
(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
titlebar->sendctl(top, "!move . -1 " + req[next:]);
"size" =>
minsz := titlebar->minsize(top);
titlebar->sendctl(top, "!size . -1 " + string minsz.x + " " + string minsz.y);
"ok" or
"help" =>
;
"rect" =>
r: Rect;
(c, next) = qword(req, next);
r.min.x = int c;
(c, next) = qword(req, next);
r.min.y = int c;
(c, next) = qword(req, next);
r.max.x = int c;
(c, next) = qword(req, next);
r.max.y = int c;
top.screenr = r;
"haskbdfocus" =>
in := int qword(req, next).t0 != 0;
cmd(top, "focus -global " + string in);
cmd(top, "update");
"task" =>
(r, nil) := splitqword(req, next);
if(r.t0 == r.t1)
req = sys->sprint("task %q", cmd(top, ".Wm_t.title cget -text"));
if(wmreq(top, c, req, next) == nil)
cmd(top, ". unmap; update");
"untask" =>
cmd(top, ". map; update");
return wmreq(top, c, req, next);
* =>
return wmreq(top, c, req, next);
}
return nil;
}
wmreq(top: ref Tk->Toplevel, c, req: string, e: int): string
{
err := wmreq1(top, c, req, e);
# if(err != nil)
# sys->fprint(sys->fildes(2), "tkclient: request %#q failed: %s\n", req, err);
return err;
}
wmreq1(top: ref Tk->Toplevel, c, req: string, e: int): string
{
name, reqid: string;
if(req != nil && req[0] == '!'){
(name, e) = qword(req, e);
(reqid, e) = qword(req, e);
if(name == nil || reqid == nil)
return "bad arg count";
}
if(top.ctxt.connfd != nil){
if(sys->fprint(top.ctxt.connfd, "%s", req) == -1)
return sys->sprint("%r");
if(req[0] == '!')
recvimage(top, name, reqid);
return nil;
}
if(req[0] != '!'){
(nil, nil, err) := wmlib->wmctl(top.ctxt, req);
return err;
}
# if there's no window manager, then we create a screen on the
# display image. there's nowhere to find the screen again except
# through the toplevel's image. that means that you can't create a
# menu without mapping a toplevel, and if you manage to unmap
# the toplevel without unmapping the menu, you'll have two
# screens on the same display image
# in the image, so
if(c != "!reshape")
return "unknown request";
i: ref Image;
if(top.image == nil){
if(name != ".")
return "screen not available";
di := top.display.image;
screen := Screen.allocate(di, top.display.color(Background), 0);
di.draw(di.r, screen.fill, nil, screen.fill.r.min);
i = screen.newwindow(di.r, Draw->Refbackup, Draw->Nofill);
}else{
if(name == ".")
i = top.image;
else
i = top.image.screen.newwindow(s2r(req, e).t0, Draw->Refbackup, Draw->Red);
}
tk->putimage(top, name+" "+reqid, i, nil);
return nil;
}
recvimage(top: ref Tk->Toplevel, name, reqid: string)
{
i := <-top.ctxt.images;
if(i == nil){
cmd(top, name + " suspend");
i = <-top.ctxt.images;
}
tk->putimage(top, name+" "+reqid, i, nil);
}
settitle(top: ref Tk->Toplevel, name: string): string
{
return titlebar->settitle(top, name);
}
handler(top: ref Tk->Toplevel, stop: chan of int)
{
ctxt := top.ctxt;
if(stop == nil)
stop = chan of int;
for(;;)alt{
c := <-ctxt.kbd =>
tk->keyboard(top, c);
p := <-ctxt.ptr =>
tk->pointer(top, *p);
c := <-ctxt.ctl or
c = <-top.wreq =>
wmctl(top, c);
<-stop =>
exit;
}
}
snarfget(): string
{
return wmlib->snarfget();
}
snarfput(buf: string)
{
return wmlib->snarfput(buf);
}
cmd(top: ref Tk->Toplevel, s: string): string
{
e := tk->cmd(top, s);
if (e != nil && e[0] == '!')
sys->fprint(sys->fildes(2), "tkclient: tk error %s on '%s'\n", e, s);
return e;
}