# Gui implementation for running under wm (tk window manager) implement Gui; include "common.m"; include "tk.m"; include "wmlib.m"; include "charon_gui.m"; sys: Sys; D: Draw; Font,Point, Rect, Image, Context, Screen, Display: import D; charon_gui:Charon_gui; CU: CharonUtils; E: Events; Event: import E; tk: Tk; wmlib: Wmlib; screen: ref Screen; tktop : ref Tk->Toplevel; mousegrabbed := 0; cfg := array[] of { "bind . {send gctl k %s}", "frame .f", "bind .f {send gctl b1p %X %Y}", "bind .f {send gctl b1p %X %Y}", "bind .f {send gctl b1r %X %Y}", "bind .f {send gctl b1d %X %Y}", "bind .f {send gctl b2p %X %Y}", "bind .f {send gctl b2p %X %Y}", "bind .f {send gctl b2r %X %Y}", "bind .f {send gctl b2d %X %Y}", "bind .f {send gctl b3p %X %Y}", "bind .f {send gctl b3p %X %Y}", "bind .f {send gctl b3r %X %Y}", "bind .f {send gctl b3d %X %Y}", "bind .f {send gctl m %X %Y}", "pack .f -side top -fill both -expand 1", }; gctl : chan of string; puch : chan of int; # popup synchronization reply chan WMargin : con 0; # want this much spare screen width HMargin : con 70; # want this much spare screen height (allow for titlebar, toolbar) totalr: Rect; # toplevel (".") screen coords (includes titlebar) mainr: Rect; # browser's main window coords ctlr: Rect; # browser's control window coords progr: Rect; # browser's progress window coords popupr: Rect; # popup window screen coords offset: Point; # ctlr.min-ctlwin.r.min (accounts for origin change, due to move) ctlh: int; # height that ctlwin is supposed to be progh: int; # height that progwin is supposed to be allwins : array of ref Image; tbinit(ctxt: ref Context, nil, nil: chan of string, rmain, rctl, rprog: Rect, cu: CharonUtils) : (Rect, Rect, Rect) { return init(ctxt, rmain, rctl, rprog, cu); } init(ctxt: ref Context, rmain, rctl, rprog: Rect, cu: CharonUtils) : (Rect, Rect, Rect) { sys = load Sys Sys->PATH; D = load Draw Draw->PATH; CU = cu; E = cu->E; tk = load Tk Tk->PATH; wmlib = load Wmlib Wmlib->PATH; if(wmlib == nil) CU->raise(sys->sprint("EXInternal: can't load module Wmlib: %r")); wmlib->init(); charon_gui=load Charon_gui Charon_gui->PATH; if(charon_gui==nil) CU->raise(sys->sprint("EXinternal:couldn't load Charon_gui:%r")); charon_gui->init(); mainw := rmain.dx(); mainh := rmain.dy(); ctlh = rctl.dy(); progh = rprog.dy(); if(mainw < 250 || mainh< 40) CU->raise(sys->sprint("EXInternal: window too small (%d x %d)", mainw, mainh)); if(ctxt == nil) CU->raise("EXInternal: need context to run under wm"); display = ctxt.display; screen = ctxt.screen; screenw := screen.image.r.dx(); screenh := screen.image.r.dy(); x := rctl.min.x; y := rctl.min.y; if(x + mainw > screenw-WMargin) { x = 0; if(mainw > screenw - WMargin) mainw = screenw - WMargin; } if(y + mainh + ctlh + progh > screenh-HMargin) { y = 0; if(mainh + ctlh + progh > screenh - HMargin) mainh = screenh - ctlh - progh - HMargin; } buts := (CU->config).buttons; flags := 0; for((nil, butl) := sys->tokenize(buts, ", \t"); butl != nil; butl = tl butl) { case hd butl { "help" => flags |= Wmlib->Help; "resize" => flags |= Wmlib->Resize; "hide" => flags |= Wmlib->Hide; } } wmctl: chan of string; (tktop, wmctl) = wmlib->titlebar(screen, "-x " + string x + " -y " + string y, "Charon", flags); gctl = chan of string; puch = chan of int; tk->namechan(tktop, gctl, "gctl"); tkcmds(tktop, cfg); tbarh := actr(tktop, ".Wm_t").dy(); totalr = Rect(Point(x,y),Point(x+mainw,y+tbarh+ctlh+mainh+progh)); offset = Point(0,0); tk->cmd(tktop, "pack propagate . 0"); makewins(tktop); spawn evhandle(tktop, gctl, wmctl, E->evchan); return (mainr, ctlr, progr); } tkcmds(top: ref Tk->Toplevel, cmds: array of string) { for (i := 0; i < len cmds; i++) if ((e := tk->cmd(top, cmds[i])) != nil && e[0] == '!') sys->print("tk error on '%s': %s\n", cmds[i], e); } # act(x,y) gives top-left, outside the border # act(width,height) give dimensions inside the border actr(t: ref Tk->Toplevel, wname: string) : Rect { x := int tk->cmd(t, wname + " cget -actx"); y := int tk->cmd(t, wname + " cget -acty"); w := int tk->cmd(t, wname + " cget -actwidth"); h := int tk->cmd(t, wname + " cget -actheight"); bd := int tk->cmd(t, wname + " cget -borderwidth"); return Rect((x,y),(x+w+2*bd,y+h+2*bd)); } evhandle(t: ref Tk->Toplevel, gctl, wmctl: chan of string, evchan: chan of ref Event) { for(;;) alt { s := <-gctl => (nil, l) := sys->tokenize(s, " "); case hd l { "b1p" or "b1r" or "b1d" or "b2p" or "b2r" or "b2d" or "b3p" or "b3r" or "b3d" or "m" => l = tl l; x := int hd l; y := int hd tl l; mtype := s2mtype(s); if(mtype == E->Mlbuttondown) tk->cmd(t, "focus ."); evchan <-= ref Event.Emouse(Point(x, y).sub(offset), mtype); "k" => k := int hd tl l; if(k != 0) evchan <-= ref Event.Ekey(k); "popup" => puch <- = 1; } s := <-wmctl => case s { "move" => wmlib->titlectl(t, "move"); r := actr(t, "."); if (totalr.min.x != r.min.x || totalr.min.y != r.min.y) { p := r.min; diff := p.sub(totalr.min); newctlr := ctlr.addpt(diff); newmainr := mainr.addpt(diff); newprogr := progr.addpt(diff); ctlwin.origin(ctlwin.r.min, newctlr.min); mainwin.origin(mainwin.r.min, newmainr.min); if(progwin != nil) progwin.origin(progwin.r.min, newprogr.min); if(popupwin != nil) { popupr = popupr.addpt(diff); popupwin.origin(popupwin.r.min, popupr.min); } totalr = totalr.addpt(diff); ctlr = newctlr; mainr = newmainr; progr = newprogr; offset = ctlr.min.sub(ctlwin.r.min); } screen.top(allwins); if(popupwin != nil) screen.top(array [] of {popupwin}); "exit" => evchan <-= ref Event.Equit(0); "help" => evchan <-= ref Event.Ehelp(1); "size" => # do not allow resize if popup active - only iconize, move and exit permitted if (popupwin == nil) { wmlib->titlectl(t, "size 355 335"); totalr = actr(t, "."); makewins(t); screen.top(allwins); evchan <-= ref Event.Ereshape(totalr); } "task" => # move the browser windows off the screen to hide them hidewins(); wmlib->titlectl(t, "task"); # restore position of the offscreen windows ctlwin.origin(ctlwin.r.min, ctlr.min); mainwin.origin(mainwin.r.min, mainr.min); if(progwin != nil) progwin.origin(progwin.r.min, progr.min); screen.top(allwins); if(popupwin != nil) { popupwin.origin(popupwin.r.min, popupr.min); screen.top(array [] of {popupwin}); } "raise" => screen.top(allwins); if (popupwin != nil) screen.top(array[] of {popupwin}); } } } s2mtype(s: string): int { mtype := E->Mmove; if(s[0] == 'm') mtype = E->Mmove; else { case s[1] { '1' => case s[2] { 'p' => mtype = E->Mlbuttondown; 'r' => mtype = E->Mlbuttonup; 'd' => mtype = E->Mldrag; } '2' => case s[2] { 'p' => mtype = E->Mmbuttondown; 'r' => mtype = E->Mmbuttonup; 'd' => mtype = E->Mmdrag; } '3' => case s[2] { 'p' => mtype = E->Mrbuttondown; 'r' => mtype = E->Mrbuttonup; 'd' => mtype = E->Mrdrag; } } } return mtype; } # BUG # mouse events for the popup window are obtained # through TK system for the main window. # re-sizing the main window can result in popup controls lying outside # of the main window rect, and so mouse events can never be received for # those co-ords. makepopup(width, height: int) : ref Draw->Image { # synchronize with evhandler to eliminate position change race gctl <- = "popup"; rmain := mainwin.r; x := rmain.min.x + (rmain.dx() - width) / 2; if(x < 0) x = 0; y := rmain.min.y + (rmain.dy() - height) / 2; if(y < 0) y = 0; r := Rect(Point(x,y),Point(x+width,y+height)); popupwin = screen.newwindow(r, display.rgb2cmap(16rDD, 16rDD, 16rDD)); # popupwin co-ords must match those of the main window as all mouse event # co-ords come from the main window. mainwin.r.min != screen position if # the window has been moved (e.g. by dragging) # # calculate the correct screen position of the popup popupr = r.addpt(mainr.subpt(mainwin.r.min).min); popupwin.origin(r.min, popupr.min); popupwin.flush(D->Flushoff); <- puch; return popupwin; } geticon(icon: int) : (ref Draw->Image, ref Draw->Image) { fname := "/icons/charon/"; case icon { IClogo => fname += charon_gui->iLogoBit; ICback => fname += charon_gui->iBackBit; ICfwd => fname += charon_gui->iFwdBit; ICreload => fname += charon_gui->iReloadBit; ICstop => fname += charon_gui->iStopBit; IChist => fname += charon_gui->iHistoryBit; ICbmark => fname += charon_gui->iBookmarkBit; ICedit => fname += charon_gui->iEditBit; ICconf => fname += charon_gui->iConfBit; IChelp => fname += charon_gui->iHelpBit; IChome => fname += charon_gui->iHomeBit; ICsslon => fname += charon_gui->iSslonBit; ICssloff => fname += charon_gui->iSsloffBit; ICup => fname += charon_gui->iUpBit; ICdown => fname += charon_gui->iDownBit; ICplus => fname += charon_gui->iPlusBit; ICminus => fname += charon_gui->iMinusBit; ICexit => fname += charon_gui->iExitBit; ICcopy => fname += charon_gui->iCopyBit; ICkeybd => fname += charon_gui->iKbdBit; * => CU->raise(sys->sprint("EXInternal: unknown icon index %d", icon)); } fd := sys->open(fname, Sys->OREAD); if(fd == nil) CU->raise(sys->sprint("EXInternal: can't open icon file %s: %r", fname)); im := display.readimage(fd); if(im == nil) CU->raise(sys->sprint("EXInternal: can't convert icon %s to image: %r", fname)); mask : ref Image = nil; if(icon != IClogo) mask = im; return (im, mask); } # Use tbarh, ctlh, totalr to calculate mainr and ctlr, # reconfigure "." to cover totalr, # and make (or remake) mainwin and ctlwin. makewins(t: ref Tk->Toplevel) { bd := int tk->cmd(t, ". cget -borderwidth"); tk->cmd(t, ". configure -x " + string totalr.min.x + " -y "+ string totalr.min.y + " -width " + string (totalr.dx() - bd*2) + " -height " + string (totalr.dy() - bd*2)); tk->cmd(t, "update"); clientr := actr(t, ".f"); offset = Point(0,0); ctlr = clientr; mainr = clientr; progr = clientr; mainr.min.y = ctlr.max.y = ctlr.min.y+ctlh; progr.min.y = mainr.max.y = mainr.max.y - progh; mainwin = screen.newwindow(mainr, D->White); ctlwin = screen.newwindow(ctlr, display.rgb2cmap(16rDD, 16rDD, 16rDD)); if(progh != 0) progwin = screen.newwindow(progr, display.rgb2cmap(16rDD, 16rDD, 16rDD)); if(mainwin == nil || ctlwin == nil || (progh != 0 && progwin == nil)) CU->raise(sys->sprint("EXFatal: can't initialize windows: %r")); mainwin.flush(D->Flushoff); ctlwin.flush(D->Flushoff); if(progwin != nil) progwin.flush(D->Flushoff); allwins = array[] of {mainwin, ctlwin, progwin}; screen.top(allwins); } snarfput(s: string) { wmlib->snarfput(s); } skeyboard(nil: ref Draw->Context) { } grabmouse(grab : int) { c : string; if (grab) c = "grab set ."; else c = "grab release ."; mousegrabbed = grab; # tk->cmd(tktop, c); } hidewins() { ctlwin.origin(ctlwin.r.min, (-3000, -3000)); mainwin.origin(mainwin.r.min, (-3000, -3000)); if (progwin != nil) progwin.origin(progwin.r.min, (-3000,-3000)); if (popupwin != nil) popupwin.origin(popupwin.r.min, (-3000,-3000)); tk->cmd(tktop, ". unmap"); }