1*37da2899SCharles.Forsythimplement Wmclient; 2*37da2899SCharles.Forsyth 3*37da2899SCharles.Forsyth# 4*37da2899SCharles.Forsyth# Copyright © 2003 Vita Nuova Holdings Limited 5*37da2899SCharles.Forsyth# 6*37da2899SCharles.Forsyth 7*37da2899SCharles.Forsythinclude "sys.m"; 8*37da2899SCharles.Forsyth sys: Sys; 9*37da2899SCharles.Forsythinclude "draw.m"; 10*37da2899SCharles.Forsyth draw: Draw; 11*37da2899SCharles.Forsyth Display, Image, Screen, Rect, Point, Pointer, Wmcontext, Context: import draw; 12*37da2899SCharles.Forsythinclude "tk.m"; 13*37da2899SCharles.Forsyth tk: Tk; 14*37da2899SCharles.Forsyth Toplevel: import tk; 15*37da2899SCharles.Forsythinclude "wmlib.m"; 16*37da2899SCharles.Forsyth wmlib: Wmlib; 17*37da2899SCharles.Forsyth qword, splitqword, s2r: import wmlib; 18*37da2899SCharles.Forsythinclude "titlebar.m"; 19*37da2899SCharles.Forsyth titlebar: Titlebar; 20*37da2899SCharles.Forsythinclude "wmclient.m"; 21*37da2899SCharles.Forsyth 22*37da2899SCharles.ForsythFocusnone, Focusimage, Focustitle: con iota; 23*37da2899SCharles.Forsyth 24*37da2899SCharles.ForsythBdup: con int 16rffffffff; 25*37da2899SCharles.ForsythBddown: con int 16radadadff; 26*37da2899SCharles.Forsyth 27*37da2899SCharles.Forsythinit() 28*37da2899SCharles.Forsyth{ 29*37da2899SCharles.Forsyth sys = load Sys Sys->PATH; 30*37da2899SCharles.Forsyth draw = load Draw Draw->PATH; 31*37da2899SCharles.Forsyth tk = load Tk Tk->PATH; 32*37da2899SCharles.Forsyth wmlib = load Wmlib Wmlib->PATH; 33*37da2899SCharles.Forsyth if(wmlib == nil){ 34*37da2899SCharles.Forsyth sys->fprint(sys->fildes(2), "wmclient: cannot load %s: %r\n", Wmlib->PATH); 35*37da2899SCharles.Forsyth raise "fail:bad module"; 36*37da2899SCharles.Forsyth } 37*37da2899SCharles.Forsyth wmlib->init(); 38*37da2899SCharles.Forsyth titlebar = load Titlebar Titlebar->PATH; 39*37da2899SCharles.Forsyth if(titlebar == nil){ 40*37da2899SCharles.Forsyth sys->fprint(sys->fildes(2), "wmclient: cannot load %s: %r\n", Titlebar->PATH); 41*37da2899SCharles.Forsyth raise "fail:bad module"; 42*37da2899SCharles.Forsyth } 43*37da2899SCharles.Forsyth titlebar->init(); 44*37da2899SCharles.Forsyth} 45*37da2899SCharles.Forsyth 46*37da2899SCharles.Forsythmakedrawcontext(): ref Draw->Context 47*37da2899SCharles.Forsyth{ 48*37da2899SCharles.Forsyth return wmlib->makedrawcontext(); 49*37da2899SCharles.Forsyth} 50*37da2899SCharles.Forsyth 51*37da2899SCharles.Forsythcursorspec(img: ref Draw->Image): string 52*37da2899SCharles.Forsyth{ 53*37da2899SCharles.Forsyth Hex: con "0123456789abcdef"; 54*37da2899SCharles.Forsyth if(img == nil || img.depth != 1) 55*37da2899SCharles.Forsyth return "cursor"; 56*37da2899SCharles.Forsyth display := img.display; 57*37da2899SCharles.Forsyth hot := img.r.min; 58*37da2899SCharles.Forsyth if(img.r.min.x != 0 || img.r.min.y != 0){ 59*37da2899SCharles.Forsyth n := display.newimage(((0, 0), img.r.size()), Draw->GREY1, 0, Draw->Nofill); 60*37da2899SCharles.Forsyth n.draw(n.r, img, nil, img.r.min); 61*37da2899SCharles.Forsyth img = n; 62*37da2899SCharles.Forsyth } 63*37da2899SCharles.Forsyth s := sys->sprint("cursor %d %d %d %d ", hot.x, hot.y, img.r.dx(), img.r.dy()); 64*37da2899SCharles.Forsyth nb := img.r.dy() * draw->bytesperline(img.r, img.depth); 65*37da2899SCharles.Forsyth buf := array[nb] of byte; 66*37da2899SCharles.Forsyth if(img.readpixels(img.r, buf) == -1) 67*37da2899SCharles.Forsyth return "cursor"; 68*37da2899SCharles.Forsyth 69*37da2899SCharles.Forsyth for(i := 0; i < nb; i++){ 70*37da2899SCharles.Forsyth c := int buf[i]; 71*37da2899SCharles.Forsyth s[len s] = Hex[c >> 4]; 72*37da2899SCharles.Forsyth s[len s] = Hex[c & 16rf]; 73*37da2899SCharles.Forsyth } 74*37da2899SCharles.Forsyth return s; 75*37da2899SCharles.Forsyth} 76*37da2899SCharles.Forsyth 77*37da2899SCharles.Forsythblankwin: Window; 78*37da2899SCharles.Forsythwindow(ctxt: ref Draw->Context, title: string, buts: int): ref Window 79*37da2899SCharles.Forsyth{ 80*37da2899SCharles.Forsyth w := ref blankwin; 81*37da2899SCharles.Forsyth w.ctxt = wmlib->connect(ctxt); 82*37da2899SCharles.Forsyth w.display = ctxt.display; 83*37da2899SCharles.Forsyth w.ctl = chan of string; 84*37da2899SCharles.Forsyth readscreenrect(w); 85*37da2899SCharles.Forsyth 86*37da2899SCharles.Forsyth if(buts & Plain) 87*37da2899SCharles.Forsyth return w; 88*37da2899SCharles.Forsyth 89*37da2899SCharles.Forsyth if(ctxt.wm == nil) 90*37da2899SCharles.Forsyth buts &= ~(Resize|Hide); 91*37da2899SCharles.Forsyth 92*37da2899SCharles.Forsyth w.bd = 1; 93*37da2899SCharles.Forsyth w.titlebar = tk->toplevel(ctxt.display, nil); 94*37da2899SCharles.Forsyth top := w.titlebar; 95*37da2899SCharles.Forsyth top.wreq = nil; 96*37da2899SCharles.Forsyth 97*37da2899SCharles.Forsyth w.ctl = titlebar->new(top, buts); 98*37da2899SCharles.Forsyth titlebar->settitle(top, title); 99*37da2899SCharles.Forsyth sizetb(w); 100*37da2899SCharles.Forsyth w.wmctl("fixedorigin"); 101*37da2899SCharles.Forsyth return w; 102*37da2899SCharles.Forsyth} 103*37da2899SCharles.Forsyth 104*37da2899SCharles.ForsythWindow.pointer(w: self ref Window, p: Draw->Pointer): int 105*37da2899SCharles.Forsyth{ 106*37da2899SCharles.Forsyth if(w.screen == nil || w.titlebar == nil) 107*37da2899SCharles.Forsyth return 0; 108*37da2899SCharles.Forsyth 109*37da2899SCharles.Forsyth if(p.buttons && (w.ptrfocus == Focusnone || w.buttons == 0)){ 110*37da2899SCharles.Forsyth if(p.xy.in(w.tbrect)) 111*37da2899SCharles.Forsyth w.ptrfocus = Focustitle; 112*37da2899SCharles.Forsyth else 113*37da2899SCharles.Forsyth w.ptrfocus = Focusimage; 114*37da2899SCharles.Forsyth } 115*37da2899SCharles.Forsyth w.buttons = p.buttons; 116*37da2899SCharles.Forsyth if(w.ptrfocus == Focustitle){ 117*37da2899SCharles.Forsyth tk->pointer(w.titlebar, p); 118*37da2899SCharles.Forsyth return 1; 119*37da2899SCharles.Forsyth } 120*37da2899SCharles.Forsyth return 0; 121*37da2899SCharles.Forsyth} 122*37da2899SCharles.Forsyth 123*37da2899SCharles.Forsyth# titlebar requested size might have changed: 124*37da2899SCharles.Forsyth# find out what size it's requesting. 125*37da2899SCharles.Forsythsizetb(w: ref Window) 126*37da2899SCharles.Forsyth{ 127*37da2899SCharles.Forsyth if(w.titlebar == nil) 128*37da2899SCharles.Forsyth return; 129*37da2899SCharles.Forsyth w.tbsize = tk->rect(w.titlebar, ".", Tk->Border|Tk->Required).size(); 130*37da2899SCharles.Forsyth} 131*37da2899SCharles.Forsyth 132*37da2899SCharles.Forsyth# reshape the image; the space needed for the 133*37da2899SCharles.Forsyth# titlebar is added to r. 134*37da2899SCharles.ForsythWindow.reshape(w: self ref Window, r: Rect) 135*37da2899SCharles.Forsyth{ 136*37da2899SCharles.Forsyth w.r = w.screenr(r); 137*37da2899SCharles.Forsyth if(w.screen == nil) 138*37da2899SCharles.Forsyth return; 139*37da2899SCharles.Forsyth w.wmctl(sys->sprint("!reshape . -1 %s", r2s(w.r))); 140*37da2899SCharles.Forsyth} 141*37da2899SCharles.Forsyth 142*37da2899SCharles.Forsythputimage(w: ref Window, i: ref Image) 143*37da2899SCharles.Forsyth{ 144*37da2899SCharles.Forsyth if(w.screen != nil && i == w.screen.image) 145*37da2899SCharles.Forsyth return; 146*37da2899SCharles.Forsyth w.screen = Screen.allocate(i, w.display.color(Draw->White), 0); 147*37da2899SCharles.Forsyth ir := i.r.inset(w.bd); 148*37da2899SCharles.Forsyth if(ir.dx() < 0) 149*37da2899SCharles.Forsyth ir.max.x = ir.min.x; 150*37da2899SCharles.Forsyth if(ir.dy() < 0) 151*37da2899SCharles.Forsyth ir.max.y = ir.min.y; 152*37da2899SCharles.Forsyth if(w.titlebar != nil){ 153*37da2899SCharles.Forsyth w.tbrect = Rect(ir.min, (ir.max.x, ir.min.y + w.tbsize.y)); 154*37da2899SCharles.Forsyth tbimage := w.screen.newwindow(w.tbrect, Draw->Refnone, Draw->Nofill); 155*37da2899SCharles.Forsyth tk->putimage(w.titlebar, ".", tbimage, nil); 156*37da2899SCharles.Forsyth ir.min.y = w.tbrect.max.y; 157*37da2899SCharles.Forsyth } 158*37da2899SCharles.Forsyth if(ir.dy() < 0) 159*37da2899SCharles.Forsyth ir.max.y = ir.min.y; 160*37da2899SCharles.Forsyth w.image = w.screen.newwindow(ir, Draw->Refnone, Draw->Nofill); 161*37da2899SCharles.Forsyth drawborder(w); 162*37da2899SCharles.Forsyth w.r = i.r; 163*37da2899SCharles.Forsyth} 164*37da2899SCharles.Forsyth 165*37da2899SCharles.Forsyth# return a rectangle suitable to hold image r when the 166*37da2899SCharles.Forsyth# titlebar and border are included. 167*37da2899SCharles.ForsythWindow.screenr(w: self ref Window, r: Rect): Rect 168*37da2899SCharles.Forsyth{ 169*37da2899SCharles.Forsyth if(w.titlebar != nil){ 170*37da2899SCharles.Forsyth if(r.dx() < w.tbsize.x) 171*37da2899SCharles.Forsyth r.max.x = r.min.x + w.tbsize.x; 172*37da2899SCharles.Forsyth r.min.y -= w.tbsize.y; 173*37da2899SCharles.Forsyth } 174*37da2899SCharles.Forsyth return r.inset(-w.bd); 175*37da2899SCharles.Forsyth} 176*37da2899SCharles.Forsyth 177*37da2899SCharles.Forsyth# return the available space inside r when space for 178*37da2899SCharles.Forsyth# border and titlebar is taken away. 179*37da2899SCharles.ForsythWindow.imager(w: self ref Window, r: Rect): Rect 180*37da2899SCharles.Forsyth{ 181*37da2899SCharles.Forsyth r = r.inset(w.bd); 182*37da2899SCharles.Forsyth if(r.dx() < 0) 183*37da2899SCharles.Forsyth r.max.x = r.min.x; 184*37da2899SCharles.Forsyth if(r.dy() < 0) 185*37da2899SCharles.Forsyth r.max.y = r.min.y; 186*37da2899SCharles.Forsyth if(w.titlebar != nil){ 187*37da2899SCharles.Forsyth r.min.y += w.tbsize.y; 188*37da2899SCharles.Forsyth if(r.dy() < 0) 189*37da2899SCharles.Forsyth r.max.y = r.min.y; 190*37da2899SCharles.Forsyth } 191*37da2899SCharles.Forsyth return r; 192*37da2899SCharles.Forsyth} 193*37da2899SCharles.Forsyth 194*37da2899SCharles.Forsyth# draw an imitation tk border. 195*37da2899SCharles.Forsythdrawborder(w: ref Window) 196*37da2899SCharles.Forsyth{ 197*37da2899SCharles.Forsyth if(w.screen == nil) 198*37da2899SCharles.Forsyth return; 199*37da2899SCharles.Forsyth col := w.display.color(Bdup); 200*37da2899SCharles.Forsyth i := w.screen.image; 201*37da2899SCharles.Forsyth r := w.screen.image.r; 202*37da2899SCharles.Forsyth i.draw((r.min, (r.min.x+w.bd, r.max.y)), col, nil, (0, 0)); 203*37da2899SCharles.Forsyth i.draw(((r.min.x+w.bd, r.min.y), (r.max.x, r.min.y+w.bd)), col, nil, (0, 0)); 204*37da2899SCharles.Forsyth col = w.display.color(Bddown); 205*37da2899SCharles.Forsyth i.draw(((r.max.x-w.bd, r.min.y+w.bd), r.max), col, nil, (0, 0)); 206*37da2899SCharles.Forsyth i.draw(((r.min.x+w.bd, r.max.y-w.bd), (r.max.x-w.bd, r.max.y)), col, nil, (0, 0)); 207*37da2899SCharles.Forsyth} 208*37da2899SCharles.Forsyth 209*37da2899SCharles.Forsythreadscreenrect(w: ref Window) 210*37da2899SCharles.Forsyth{ 211*37da2899SCharles.Forsyth if((fd := sys->open("/chan/wmrect", Sys->OREAD)) != nil){ 212*37da2899SCharles.Forsyth buf := array[12*4] of byte; 213*37da2899SCharles.Forsyth n := sys->read(fd, buf, len buf); 214*37da2899SCharles.Forsyth if(n > 0){ 215*37da2899SCharles.Forsyth (w.displayr, nil) = s2r(string buf[0:n], 0); 216*37da2899SCharles.Forsyth return; 217*37da2899SCharles.Forsyth } 218*37da2899SCharles.Forsyth } 219*37da2899SCharles.Forsyth w.displayr = w.display.image.r; 220*37da2899SCharles.Forsyth} 221*37da2899SCharles.Forsyth 222*37da2899SCharles.ForsythWindow.onscreen(w: self ref Window, how: string) 223*37da2899SCharles.Forsyth{ 224*37da2899SCharles.Forsyth if(how == nil) 225*37da2899SCharles.Forsyth how = "place"; 226*37da2899SCharles.Forsyth w.wmctl(sys->sprint("!reshape . -1 %s %q", r2s(w.r), how)); 227*37da2899SCharles.Forsyth} 228*37da2899SCharles.Forsyth 229*37da2899SCharles.ForsythWindow.startinput(w: self ref Window, devs: list of string) 230*37da2899SCharles.Forsyth{ 231*37da2899SCharles.Forsyth for(; devs != nil; devs = tl devs) 232*37da2899SCharles.Forsyth w.wmctl(sys->sprint("start %q", hd devs)); 233*37da2899SCharles.Forsyth} 234*37da2899SCharles.Forsyth 235*37da2899SCharles.Forsyth# commands originating both from tkclient and wm (via ctl) 236*37da2899SCharles.ForsythWindow.wmctl(w: self ref Window, req: string): string 237*37da2899SCharles.Forsyth{ 238*37da2899SCharles.Forsyth (c, next) := qword(req, 0); 239*37da2899SCharles.Forsyth case c { 240*37da2899SCharles.Forsyth "exit" => 241*37da2899SCharles.Forsyth sys->fprint(sys->open("/prog/" + string sys->pctl(0, nil) + "/ctl", Sys->OWRITE), "killgrp"); 242*37da2899SCharles.Forsyth exit; 243*37da2899SCharles.Forsyth # old-style requests: pass them back around in proper form. 244*37da2899SCharles.Forsyth "move" => 245*37da2899SCharles.Forsyth # move x y 246*37da2899SCharles.Forsyth if(w.titlebar != nil) 247*37da2899SCharles.Forsyth titlebar->sendctl(w.titlebar, "!move . -1 " + req[next:]); 248*37da2899SCharles.Forsyth "size" => 249*37da2899SCharles.Forsyth if(w.titlebar != nil){ 250*37da2899SCharles.Forsyth minsz := titlebar->minsize(w.titlebar); 251*37da2899SCharles.Forsyth titlebar->sendctl(w.titlebar, "!size . -1 " + string minsz.x + " " + string minsz.y); 252*37da2899SCharles.Forsyth } 253*37da2899SCharles.Forsyth "ok" or 254*37da2899SCharles.Forsyth "help" => 255*37da2899SCharles.Forsyth ; 256*37da2899SCharles.Forsyth "rect" => 257*37da2899SCharles.Forsyth (w.displayr, nil) = s2r(req, next); 258*37da2899SCharles.Forsyth "haskbdfocus" => 259*37da2899SCharles.Forsyth w.focused = int qword(req, next).t0; 260*37da2899SCharles.Forsyth if(w.titlebar != nil){ 261*37da2899SCharles.Forsyth tk->cmd(w.titlebar, "focus -global " + string w.focused); 262*37da2899SCharles.Forsyth tk->cmd(w.titlebar, "update"); 263*37da2899SCharles.Forsyth } 264*37da2899SCharles.Forsyth drawborder(w); 265*37da2899SCharles.Forsyth "task" => 266*37da2899SCharles.Forsyth title := ""; 267*37da2899SCharles.Forsyth if(w.titlebar != nil) 268*37da2899SCharles.Forsyth title = titlebar->title(w.titlebar); 269*37da2899SCharles.Forsyth wmreq(w, sys->sprint("task %q", title), next); 270*37da2899SCharles.Forsyth w.saved = w.r.min; 271*37da2899SCharles.Forsyth # send window out of the way 272*37da2899SCharles.Forsyth # XXX oops, can't do this for plain windows... 273*37da2899SCharles.Forsyth titlebar->sendctl(w.titlebar, "!reshape . -1 " + r2s((w.displayr.max, w.displayr.max.add(w.r.size())))); 274*37da2899SCharles.Forsyth "untask" => 275*37da2899SCharles.Forsyth wmreq(w, req, next); 276*37da2899SCharles.Forsyth # put window back where it was before. 277*37da2899SCharles.Forsyth # XXX what do we we do if the window manager window has been reshape in the meantime...? 278*37da2899SCharles.Forsyth titlebar->sendctl(w.titlebar, "!reshape . -1 " + r2s((w.saved, w.saved.add(w.r.size())))); 279*37da2899SCharles.Forsyth * => 280*37da2899SCharles.Forsyth return wmreq(w, req, next); 281*37da2899SCharles.Forsyth } 282*37da2899SCharles.Forsyth return nil; 283*37da2899SCharles.Forsyth} 284*37da2899SCharles.Forsyth 285*37da2899SCharles.Forsythwmreq(w: ref Window, req: string, e: int): string 286*37da2899SCharles.Forsyth{ 287*37da2899SCharles.Forsyth name: string; 288*37da2899SCharles.Forsyth if(req != nil && req[0] == '!'){ 289*37da2899SCharles.Forsyth (name, e) = qword(req, e); 290*37da2899SCharles.Forsyth if(name != ".") 291*37da2899SCharles.Forsyth return "invalid window name"; 292*37da2899SCharles.Forsyth } 293*37da2899SCharles.Forsyth if(w.ctxt.connfd != nil){ 294*37da2899SCharles.Forsyth if(sys->fprint(w.ctxt.connfd, "%s", req) == -1) 295*37da2899SCharles.Forsyth return sys->sprint("%r"); 296*37da2899SCharles.Forsyth if(req[0] == '!') 297*37da2899SCharles.Forsyth recvimage(w); 298*37da2899SCharles.Forsyth return nil; 299*37da2899SCharles.Forsyth } 300*37da2899SCharles.Forsyth # if we're getting an image and there's no window manager, 301*37da2899SCharles.Forsyth # then there's only one image to get... 302*37da2899SCharles.Forsyth if(req[0] == '!') 303*37da2899SCharles.Forsyth putimage(w, w.ctxt.ctxt.display.image); 304*37da2899SCharles.Forsyth else{ 305*37da2899SCharles.Forsyth (nil, nil, err) := wmlib->wmctl(w.ctxt, req); 306*37da2899SCharles.Forsyth return err; 307*37da2899SCharles.Forsyth } 308*37da2899SCharles.Forsyth return nil; 309*37da2899SCharles.Forsyth} 310*37da2899SCharles.Forsyth 311*37da2899SCharles.Forsythrecvimage(w: ref Window) 312*37da2899SCharles.Forsyth{ 313*37da2899SCharles.Forsyth i := <-w.ctxt.images; 314*37da2899SCharles.Forsyth if(i == nil) 315*37da2899SCharles.Forsyth i = <-w.ctxt.images; 316*37da2899SCharles.Forsyth putimage(w, i); 317*37da2899SCharles.Forsyth} 318*37da2899SCharles.Forsyth 319*37da2899SCharles.ForsythWindow.settitle(w: self ref Window, title: string): string 320*37da2899SCharles.Forsyth{ 321*37da2899SCharles.Forsyth if(w.titlebar == nil) 322*37da2899SCharles.Forsyth return nil; 323*37da2899SCharles.Forsyth oldr := w.imager(w.r); 324*37da2899SCharles.Forsyth old := titlebar->settitle(w.titlebar, title); 325*37da2899SCharles.Forsyth sizetb(w); 326*37da2899SCharles.Forsyth if(w.tbsize.x < w.r.dx()) 327*37da2899SCharles.Forsyth tk->putimage(w.titlebar, ".", w.titlebar.image, nil); # unsuspend the window 328*37da2899SCharles.Forsyth else 329*37da2899SCharles.Forsyth w.wmctl("!reshape . -1 " + r2s(w.screenr(oldr))); 330*37da2899SCharles.Forsyth return old; 331*37da2899SCharles.Forsyth} 332*37da2899SCharles.Forsyth 333*37da2899SCharles.Forsythsnarfget(): string 334*37da2899SCharles.Forsyth{ 335*37da2899SCharles.Forsyth return wmlib->snarfget(); 336*37da2899SCharles.Forsyth} 337*37da2899SCharles.Forsyth 338*37da2899SCharles.Forsythsnarfput(buf: string) 339*37da2899SCharles.Forsyth{ 340*37da2899SCharles.Forsyth return wmlib->snarfput(buf); 341*37da2899SCharles.Forsyth} 342*37da2899SCharles.Forsyth 343*37da2899SCharles.Forsythr2s(r: Rect): string 344*37da2899SCharles.Forsyth{ 345*37da2899SCharles.Forsyth return sys->sprint("%d %d %d %d", r.min.x, r.min.y, r.max.x, r.max.y); 346*37da2899SCharles.Forsyth} 347