1implement Tkclient; 2 3# 4# Copyright © 2003 Vita Nuova Holdings Limited 5# 6 7include "sys.m"; 8 sys: Sys; 9include "draw.m"; 10 draw: Draw; 11 Display, Image, Screen, Rect, Point, Pointer, Wmcontext, Context: import draw; 12include "tk.m"; 13 tk: Tk; 14 Toplevel: import tk; 15include "wmlib.m"; 16 wmlib: Wmlib; 17 qword, splitqword, s2r: import wmlib; 18include "titlebar.m"; 19 titlebar: Titlebar; 20include "tkclient.m"; 21 22Background: con int 16r777777FF; # should be drawn over immediately, but just in case... 23 24init() 25{ 26 sys = load Sys Sys->PATH; 27 draw = load Draw Draw->PATH; 28 tk = load Tk Tk->PATH; 29 wmlib = load Wmlib Wmlib->PATH; 30 if(wmlib == nil){ 31 sys->fprint(sys->fildes(2), "tkclient: cannot load %s: %r\n", Wmlib->PATH); 32 raise "fail:bad module"; 33 } 34 wmlib->init(); 35 titlebar = load Titlebar Titlebar->PATH; 36 if(titlebar == nil){ 37 sys->fprint(sys->fildes(2), "tkclient: cannot load %s: %r\n", Titlebar->PATH); 38 raise "fail:bad module"; 39 } 40 titlebar->init(); 41} 42 43makedrawcontext(): ref Draw->Context 44{ 45 return wmlib->makedrawcontext(); 46} 47 48toplevel(ctxt: ref Draw->Context, topconfig: string, title: string, buts: int): (ref Tk->Toplevel, chan of string) 49{ 50 wm := wmlib->connect(ctxt); 51 opts := ""; 52 if((buts & Plain) == 0) 53 opts = "-borderwidth 1 -relief raised "; 54 top := tk->toplevel(wm.ctxt.display, opts+topconfig); 55 if (top == nil) { 56 sys->fprint(sys->fildes(2), "wmlib: window creation failed (top %ux, i %ux)\n", top, top.image); 57 raise "fail:window creation failed"; 58 } 59 top.ctxt = wm; 60 readscreenrect(top); 61 c := titlebar->new(top, buts); 62 titlebar->settitle(top, title); 63 return (top, c); 64} 65 66readscreenrect(top: ref Tk->Toplevel) 67{ 68 if((fd := sys->open("/chan/wmrect", Sys->OREAD)) != nil){ 69 buf := array[12*4] of byte; 70 n := sys->read(fd, buf, len buf); 71 if(n > 0) 72 (top.screenr, nil) = s2r(string buf[0:n], 0); 73 } 74} 75 76onscreen(top: ref Tk->Toplevel, how: string) 77{ 78 if(how == nil) 79 how = "place"; 80 wmctl(top, sys->sprint("!reshape . -1 %s %q", 81 r2s(tk->rect(top, ".", Tk->Border|Tk->Required)), how)); 82} 83 84startinput(top: ref Tk->Toplevel, devs: list of string) 85{ 86 for(; devs != nil; devs = tl devs) 87 wmctl(top, sys->sprint("start %q", hd devs)); 88} 89 90r2s(r: Rect): string 91{ 92 return sys->sprint("%d %d %d %d", r.min.x, r.min.y, r.max.x, r.max.y); 93} 94 95# commands originating both from tkclient and wm (via ctl) 96wmctl(top: ref Tk->Toplevel, req: string): string 97{ 98#sys->print("wmctl %s\n", req); 99 (c, next) := qword(req, 0); 100 case c { 101 "exit" => 102 sys->fprint(sys->open("/prog/" + string sys->pctl(0, nil) + "/ctl", Sys->OWRITE), "killgrp"); 103 exit; 104 # old-style requests: pass them back around in proper form. 105 "move" => 106 # move x y 107 titlebar->sendctl(top, "!move . -1 " + req[next:]); 108 "size" => 109 minsz := titlebar->minsize(top); 110 titlebar->sendctl(top, "!size . -1 " + string minsz.x + " " + string minsz.y); 111 "ok" or 112 "help" => 113 ; 114 "rect" => 115 r: Rect; 116 (c, next) = qword(req, next); 117 r.min.x = int c; 118 (c, next) = qword(req, next); 119 r.min.y = int c; 120 (c, next) = qword(req, next); 121 r.max.x = int c; 122 (c, next) = qword(req, next); 123 r.max.y = int c; 124 top.screenr = r; 125 "haskbdfocus" => 126 in := int qword(req, next).t0 != 0; 127 cmd(top, "focus -global " + string in); 128 cmd(top, "update"); 129 "task" => 130 (r, nil) := splitqword(req, next); 131 if(r.t0 == r.t1) 132 req = sys->sprint("task %q", cmd(top, ".Wm_t.title cget -text")); 133 if(wmreq(top, c, req, next) == nil) 134 cmd(top, ". unmap; update"); 135 "untask" => 136 cmd(top, ". map; update"); 137 return wmreq(top, c, req, next); 138 * => 139 return wmreq(top, c, req, next); 140 } 141 return nil; 142} 143 144wmreq(top: ref Tk->Toplevel, c, req: string, e: int): string 145{ 146 err := wmreq1(top, c, req, e); 147# if(err != nil) 148# sys->fprint(sys->fildes(2), "tkclient: request %#q failed: %s\n", req, err); 149 return err; 150} 151 152wmreq1(top: ref Tk->Toplevel, c, req: string, e: int): string 153{ 154 name, reqid: string; 155 if(req != nil && req[0] == '!'){ 156 (name, e) = qword(req, e); 157 (reqid, e) = qword(req, e); 158 if(name == nil || reqid == nil) 159 return "bad arg count"; 160 } 161 if(top.ctxt.connfd != nil){ 162 if(sys->fprint(top.ctxt.connfd, "%s", req) == -1) 163 return sys->sprint("%r"); 164 if(req[0] == '!') 165 recvimage(top, name, reqid); 166 return nil; 167 } 168 if(req[0] != '!'){ 169 (nil, nil, err) := wmlib->wmctl(top.ctxt, req); 170 return err; 171 } 172 # if there's no window manager, then we create a screen on the 173 # display image. there's nowhere to find the screen again except 174 # through the toplevel's image. that means that you can't create a 175 # menu without mapping a toplevel, and if you manage to unmap 176 # the toplevel without unmapping the menu, you'll have two 177 # screens on the same display image 178 # in the image, so 179 if(c != "!reshape") 180 return "unknown request"; 181 i: ref Image; 182 if(top.image == nil){ 183 if(name != ".") 184 return "screen not available"; 185 di := top.display.image; 186 screen := Screen.allocate(di, top.display.color(Background), 0); 187 di.draw(di.r, screen.fill, nil, screen.fill.r.min); 188 i = screen.newwindow(di.r, Draw->Refbackup, Draw->Nofill); 189 }else{ 190 if(name == ".") 191 i = top.image; 192 else 193 i = top.image.screen.newwindow(s2r(req, e).t0, Draw->Refbackup, Draw->Red); 194 } 195 tk->putimage(top, name+" "+reqid, i, nil); 196 return nil; 197} 198 199recvimage(top: ref Tk->Toplevel, name, reqid: string) 200{ 201 i := <-top.ctxt.images; 202 if(i == nil){ 203 cmd(top, name + " suspend"); 204 i = <-top.ctxt.images; 205 } 206 tk->putimage(top, name+" "+reqid, i, nil); 207} 208 209settitle(top: ref Tk->Toplevel, name: string): string 210{ 211 return titlebar->settitle(top, name); 212} 213 214handler(top: ref Tk->Toplevel, stop: chan of int) 215{ 216 ctxt := top.ctxt; 217 if(stop == nil) 218 stop = chan of int; 219 for(;;)alt{ 220 c := <-ctxt.kbd => 221 tk->keyboard(top, c); 222 p := <-ctxt.ptr => 223 tk->pointer(top, *p); 224 c := <-ctxt.ctl or 225 c = <-top.wreq => 226 wmctl(top, c); 227 <-stop => 228 exit; 229 } 230} 231 232snarfget(): string 233{ 234 return wmlib->snarfget(); 235} 236 237snarfput(buf: string) 238{ 239 return wmlib->snarfput(buf); 240} 241 242cmd(top: ref Tk->Toplevel, s: string): string 243{ 244 e := tk->cmd(top, s); 245 if (e != nil && e[0] == '!') 246 sys->fprint(sys->fildes(2), "tkclient: tk error %s on '%s'\n", e, s); 247 return e; 248} 249 250