sjs@spectral.ctt.bellcore.com (Stan Switzer) (04/22/89)
This is an attempt at a recursive window manager. In the beginning it was a pretty fun hack, but little nit-picky things kept on complicating things to the point that I basically gave up on it for a few months. Most of the horrible bugs are out of it, but it must be regarded as a demonstration program only until I can ket rid of some of the voodo in here. In particular, I have had a VERY difficult time chasing out GC leaks that ultimately result in window carcasses om the screen. All KNOWN causes of this regretable circumstance have been aliminated, but considering the truly disgusting things that this program does, there may be no hope for a total solution. That said, it's a pretty neat program. You might be able to get some use out of it. I like to make several big WinWins and group related work in the same WinWin. I can then close up one of the winwins and open up another to simulate multiple desktops. Enjoy, Stan Switzer sjs@ctt.bellcore.com ------------------------------------------------ #!/usr/NeWS/bin/psh % % WinWin: Windows in windows in windows ... % % Copyright (C) 1989 by Stan Switzer. All rights reserved. % This program is provided for unrestricted use, provided that this % copyright message is preserved. There is no warranty, and no author % or distributer accepts responsibility for any damage caused by this % program. % % Recursive window managment % % Stan Switzer sjs@ctt.bellcore.com % % To be fixed: % Highly succeptable to GC leaks resulting in canvases not being % released. The event manager's bad habit of keeping a copy of % events around is a likely source of trouble. I have fixed this % once and for all by patching the event manager but it still doesn't % eliminate the leaks! % % Frailties: % Shared menus retain canvases whose parent could be anything. % The "fix" is to reparent the canvases. % % Interests with null canvases are problematic. % % Special behavior of "newcanvas" for framebuffer children is % annoying, and can't really be fixed completely by redefinition % owing to autobind. % % Shared menus make life interesting since their canvas's parent links % have to be changed on /popup. % % I really don't know why it doesn't work when you have a root piemenu. % Current "solution" is to change the rootmenu back to regular menu for % the "winwin." % % NeWS Bugs: % When a window is reshaped, where subwindows are and where they think % they are are two different things entirely. % % Only true root overlay canvases cover their siblings! % /WinWin DefaultWindow [ ] classbegin /IconImage /client def /PaintIcon { /PaintIcon super send IconBorderColor strokecanvas } def /FrameLabel (Window Window) def /ClientMenu null def /CreateClientCanvas { /ClientCanvas FrameCanvas newcanvas def /ptr /ptr_m ClientCanvas setstandardcursor ClientCanvas begin /Transparent false def /Retained false def /EventsConsumed /AllEvents def /Mapped true def end } def /PaintFrameBorder { % - => - (Paint frame border areas) clippath pathbbox rectpath BorderLeft .5 sub BorderBottom .5 sub FrameWidth BorderLeft BorderRight add sub 1 add FrameHeight BorderBottom BorderTop add sub 1 add FramePath gsave FrameFillColor setshade eofill grestore FrameBorderColor setshade stroke FrameBorderColor strokecanvas } def /ForkFrameEventMgr { /FrameEventMgr FrameInterests forkeventmgr def } def /destroy { createevent dup begin /Name /DoItEvent def /Action /Window def /ClientData [ % /EventMgrDict cvx /CurrentEvent null /put cvx % GC leak ClientCanvas /framebuffer cvx /eq cvx { destroy } /if cvx ] cvx def end sendevent % save some memory in case of GC leaks: ClientCanvas /Retained false put FrameCanvas /Retained false put % Kill some processes: /KillProcesses where { /KillProcesses get { killprocess } forall /KillProcesses null store } if % Kill some groups: /KillGroups where { /KillGroups get { killprocessgroup } forall /KillGroups null store } if % console (OK!\n) writestring console flushfile /destroy super send } def % we only drag the frame since draging the canvas doesn't work too % well when there are opaque children /slide { % - => - (Interactively move window) { Iconic? { /slide super send } { GetCanvas setcanvas InteractionLock { true dragcanvas } monitor currentcanvas ParentCanvas setcanvas getcanvaslocation /move self send } ifelse } fork pop } def classend systemdict 3 1 roll put currentdict framebuffer { begin } pop end % pop user dict 50 dict begin % The things we do for recursive WinWins! /framebuffer exch def /fboverlay framebuffer createoverlay def /newcanvas { dup framebuffer eq exch systemdict /newcanvas get exec exch { dup /Transparent false put dup /Color get not { dup /Retained true put } if } if } def /win framebuffer /new WinWin send def /reshapefromuser win send /map win send win /win null def end exch begin { end } pop /win exch def % systemdict /foo win put % pseudo-scientific visualizer systemdict /NextSocket known not { systemdict /NextSocket 2011 put } if /ServerSocket systemdict begin NextSocket /NextSocket NextSocket 1 add def end def /FakeServer (NEWSSERVER) getenv (;) search pop exch pop (.) search pop exch pop exch % sys addr port pop ServerSocket 20 string cvs (.) exch append append exch (;) exch append append def /rebindit { % proc key val => proc 2 index % proc key val proc &rebindit pop pop } def /&rebindit { % key val proc => key val dup length 0 1 3 2 roll 1 sub { % key val proc 0 1 lng-1 { ... } for 2 copy get % key val proc ix proc[ix] dup type /arraytype eq { % another proc? exch pop exch 4 1 roll % proc key val proc[ix] &rebindit 3 -1 roll % key val proc } { % key val dup xcheck { % executable 4 index eq { % ... and same as key? % key val proc ix 1 index exch 3 index put % key val proc } { pop } ifelse } { pop pop } ifelse } ifelse } for pop } def /Hacks dictbegin /framebuffer win /ClientCanvas get def /fboverlay framebuffer createoverlay def % /fboverlay { framebuffer createoverlay dup canvastotop } def /newcanvas { dup framebuffer eq exch systemdict /newcanvas get exec exch { dup /Transparent false put dup /Color get not { dup /Retained true put } if } if } def % note: we have to patch LiteWindow because newcanvas was autobound % and consequently the above definition has no effect. LiteWindow /CreateFrameCanvas get systemdict /newcanvas get /newcanvas cvx rebindit pop LiteWindow /CreateIconCanvas get systemdict /newcanvas get /newcanvas cvx rebindit pop % Likewise, LiteWindow ParentCanvas is defined as framebuffer in advance. % We'll fix this by defining it to be { framebuffer }. LiteMenu /ParentCanvas { framebuffer } put [ DefaultMenu /PieMenu where { pop PieMenu dup DefaultMenu eq { pop } if } if ] { % forall { % loop dup null eq { pop exit } if dup /showat known { dup /showat get systemdict /newcanvas get /newcanvas cvx rebindit % classdict showatproc 0 get /CheckParent ne { dup /showat 2 copy get { CheckParent } {} modifyproc put } if dup /CheckParent { MenuCanvas null ne { MenuCanvas /Parent ParentCanvas put } if } put } if /ParentDict get } loop } forall % This is gross, gross, gross, but we must improve the hygiene of % the event manager: /forkeventmgr load 2 get 19 get dup 22 get /repeat load eq { dup 21 { { pop } repeat EventMgrDict /CurrentEvent null put } put 22 /exec cvx put } { pop } ifelse /AllWin { % proc => - (Distributes proc to all windows!!) createevent dup begin /Name /DoItEvent def /Action /Window def /ClientData [ % /EventMgrDict cvx /CurrentEvent null /put cvx % GC leak framebuffer /framebuffer cvx /eq cvx [ 7 index cvx /exec cvx ] cvx /if cvx ] cvx def end sendevent pop } def /^C { % kill the Window of Windows createevent dup begin /Name /DoItEvent def /Action /Window def /ClientData [ % /EventMgrDict cvx /CurrentEvent null /put cvx % GC leak framebuffer { ClassName /WinWin eq { ClientCanvas eq { self /destroy exch send } if } { pop } ifelse } /exec cvx ] cvx def end sendevent pop } def /exitcleanly load systemdict /^C get /^C cvx rebindit pop /forkunix { (NEWSSERVER) getenv (NEWSSERVER) //FakeServer putenv exch systemdict /forkunix get exec (NEWSSERVER) exch putenv } def dictend def Hacks /def load forall framebuffer setcanvas PaintRoot /DefaultMenu LitePullRightMenu def /rootmenu /flipstyle rootmenu send def /serverproc { % fork clear (%socketl) //ServerSocket 20 string cvs append (r) file { % loop dup mark exch acceptconnection { % fork Hacks end 200 dict begin { def } forall initmatrix newprocessgroup exch pop exch pop dup getsocketpeername /OriginatingHost exch def RemoteHostRegistry OriginatingHost known OriginatingHost localhostname anchorsearch { pop (.) anchorsearch { pop pop RemoteHostRegistry localhostname known } { pop false } ifelse } { pop false } ifelse or % Let through if no security wanted NetSecurityWanted not or { cvx exec } { currentcursorlocation [ (Network security violation:) (Rejected connection from ) OriginatingHost append ] popmsg } ifelse currentprocess killprocessgroup } fork cleartomark } loop } fork def /rooteventmgr [ /rootmenu where { pop MenuButton { {/showat rootmenu send} fork pop } DownTransition framebuffer eventmgrinterest } if /Damaged { damagepath clipcanvas PaintRoot newpath clipcanvas} null framebuffer eventmgrinterest ] forkeventmgr def win /KillProcesses [ serverproc rooteventmgr ] put % win /KillGroups [ rooteventmgr ] put
sjs@spectral.ctt.bellcore.com (Stan Switzer) (04/25/89)
The hackman always posts twice.... Apologies for any inconvenience. The previous version of "winwin" didn't work. Without going into too much detail let's just say that it worked here when I posted it, but it had no chance to work anywhere else (unless you had autobind off when you define your root menu). I really have to watch those last-minute changes. Anyway, this version is fixed (really). It remains fragile in all ways detailed in the previous message, but it can be useful nonetheless. As I said before, it is quite useful to set up several large winwin windows, each containing related subwindows. In a way it's like having multiple desktops. As always, enjoy, Stan Switzer sjs@ctt.bellcore.com "Thou shalt not have no idea." -------------- #!/usr/NeWS/bin/psh % % WinWin: Windows in windows in windows ... % % Copyright (C) 1989 by Stan Switzer. All rights reserved. % This program is provided for unrestricted use, provided that this % copyright message is preserved. There is no warranty, and no author % or distributer accepts responsibility for any damage caused by this % program. % % Recursive window managment % % Stan Switzer sjs@ctt.bellcore.com % % To be fixed: % Highly succeptable to GC leaks resulting in canvases not being % released. The event manager's bad habit of keeping a copy of % events around is a likely source of trouble. I have fixed this % once and for all by patching the event manager but it still doesn't % eliminate the leaks! % % Frailties: % Shared menus retain canvases whose parent could be anything. % The "fix" is to reparent the canvases. % % Interests with null canvases are problematic. % % Special behavior of "newcanvas" for framebuffer children is % annoying, and can't really be fixed completely by redefinition % owing to autobind. % % Shared menus make life interesting since their canvas's parent links % have to be changed on /popup. % % I really don't know why it doesn't work when you have a root piemenu. % Current "solution" is to change the rootmenu back to regular menu for % the "winwin." % % NeWS Bugs: % When a window is reshaped, where subwindows are and where they think % they are are two different things entirely. % % Only true root overlay canvases cover their siblings! % /WinWin DefaultWindow [ ] classbegin /IconImage /client def /PaintIcon { /PaintIcon super send IconBorderColor strokecanvas } def /FrameLabel (Window Window) def /ClientMenu null def /CreateClientCanvas { /ClientCanvas FrameCanvas newcanvas def /ptr /ptr_m ClientCanvas setstandardcursor ClientCanvas begin /Transparent false def /Retained false def /EventsConsumed /AllEvents def /Mapped true def end } def /PaintFrameBorder { % - => - (Paint frame border areas) clippath pathbbox rectpath BorderLeft .5 sub BorderBottom .5 sub FrameWidth BorderLeft BorderRight add sub 1 add FrameHeight BorderBottom BorderTop add sub 1 add FramePath gsave FrameFillColor setshade eofill grestore FrameBorderColor setshade stroke FrameBorderColor strokecanvas } def /ForkFrameEventMgr { /FrameEventMgr FrameInterests forkeventmgr def } def /destroy { createevent dup begin /Name /DoItEvent def /Action /Window def /ClientData [ % /EventMgrDict cvx /CurrentEvent null /put cvx % GC leak ClientCanvas /framebuffer cvx /eq cvx { destroy } /if cvx ] cvx def end sendevent % save some memory in case of GC leaks: ClientCanvas /Retained false put FrameCanvas /Retained false put % Kill some processes: /KillProcesses where { /KillProcesses get { killprocess } forall /KillProcesses null store } if % Kill some groups: /KillGroups where { /KillGroups get { killprocessgroup } forall /KillGroups null store } if % console (OK!\n) writestring console flushfile /destroy super send } def % we only drag the frame since draging the canvas doesn't work too % well when there are opaque children /slide { % - => - (Interactively move window) { Iconic? { /slide super send } { GetCanvas setcanvas InteractionLock { true dragcanvas } monitor currentcanvas ParentCanvas setcanvas getcanvaslocation /move self send } ifelse } fork pop } def classend systemdict 3 1 roll put /copymenu { % - => newmenu -- a phony menu method self dup maxlength dict copy end begin /MenuCanvas null def /MenuWidth null def /MenuEventMgr null def /ChildMenu null def /MenuActions MenuActions dup length array copy def /MenuKeys MenuKeys dup length array copy def /MenuItems MenuItems dup length array copy def 0 1 MenuActions length 1 sub { % for dup getmenuaction dup type { % case(type) -- i menuaction /dicttype { /copymenu exch send % copy i menu' MenuActions 2 index 2 index put MenuItems 2 index MenuKeys 1 index get 3 index MakeMenuItem put } /arraytype { dup xcheck { systemdict /forkunix get /forkunix cvx rebindit } if } } case pop pop } for currentdict end } def /flipstyle { % - => newmenu 0 1 MenuActions length 1 sub { dup getmenuaction % fixed to use getmenuaction! dup type /dicttype eq { /flipstyle exch send % i menu' MenuActions 3 1 roll put % - } {pop pop} ifelse } for MenuKeys MenuActions /new DefaultMenu send } def currentdict framebuffer { begin } pop end % pop user dict 50 dict begin % The things we do for recursive WinWins! /framebuffer exch def /fboverlay framebuffer createoverlay def /newcanvas { dup framebuffer eq exch systemdict /newcanvas get exec exch { dup /Transparent false put dup /Color get not { dup /Retained true put } if } if } def /win framebuffer /new WinWin send def /reshapefromuser win send /map win send win /win null def end exch begin { end } pop /win exch def % systemdict /foo win put % pseudo-scientific visualizer systemdict /NextSocket known not { systemdict /NextSocket 2011 put } if /ServerSocket systemdict begin NextSocket /NextSocket NextSocket 1 add def end def /FakeServer (NEWSSERVER) getenv (;) search pop exch pop (.) search pop exch pop exch % sys addr port pop ServerSocket 20 string cvs (.) exch append append exch (;) exch append append def /rebindit { % proc key val => proc 2 index % proc key val proc &rebindit pop pop } def /&rebindit { % key val proc => key val dup length 0 1 3 2 roll 1 sub { % key val proc 0 1 lng-1 { ... } for 2 copy get % key val proc ix proc[ix] dup type /arraytype eq { % another proc? exch pop exch 4 1 roll % proc key val proc[ix] &rebindit 3 -1 roll % key val proc } { % key val dup xcheck { % executable 4 index eq { % ... and same as key? % key val proc ix 1 index exch 3 index put % key val proc } { pop } ifelse } { pop pop } ifelse } ifelse } for pop } def /Hacks dictbegin /framebuffer win /ClientCanvas get def /fboverlay framebuffer createoverlay def % /fboverlay { framebuffer createoverlay dup canvastotop } def /newcanvas { dup framebuffer eq exch systemdict /newcanvas get exec exch { dup /Transparent false put dup /Color get not { dup /Retained true put } if } if } def % note: we have to patch LiteWindow because newcanvas was autobound % and consequently the above definition has no effect. LiteWindow /CreateFrameCanvas get systemdict /newcanvas get /newcanvas cvx rebindit pop LiteWindow /CreateIconCanvas get systemdict /newcanvas get /newcanvas cvx rebindit pop % Likewise, LiteWindow ParentCanvas is defined as framebuffer in advance. % We'll fix this by defining it to be { framebuffer }. LiteMenu /ParentCanvas { framebuffer } put [ DefaultMenu /PieMenu where { pop PieMenu dup DefaultMenu eq { pop } if } if ] { % forall { % loop dup null eq { pop exit } if dup /showat known { dup /showat get systemdict /newcanvas get /newcanvas cvx rebindit % classdict showatproc 0 get /CheckParent ne { dup /showat 2 copy get { CheckParent } {} modifyproc put } if dup /CheckParent { MenuCanvas null ne { MenuCanvas /Parent ParentCanvas put } if } put } if /ParentDict get } loop } forall % This is gross, gross, gross, but we must improve the hygiene of % the event manager: /forkeventmgr load 2 get 19 get dup 22 get /repeat load eq { dup 21 { { pop } repeat EventMgrDict /CurrentEvent null put } put 22 /exec cvx put } { pop } ifelse /AllWin { % proc => - (Distributes proc to all windows!!) createevent dup begin /Name /DoItEvent def /Action /Window def /ClientData [ % /EventMgrDict cvx /CurrentEvent null /put cvx % GC leak framebuffer /framebuffer cvx /eq cvx [ 7 index cvx /exec cvx ] cvx /if cvx ] cvx def end sendevent pop } def /^C { % kill the Window of Windows createevent dup begin /Name /DoItEvent def /Action /Window def /ClientData [ % /EventMgrDict cvx /CurrentEvent null /put cvx % GC leak framebuffer { ClassName /WinWin eq { ClientCanvas eq { self /destroy exch send } if } { pop } ifelse } /exec cvx ] cvx def end sendevent pop } def /exitcleanly load systemdict /^C get /^C cvx rebindit pop /forkunix { (NEWSSERVER) getenv (NEWSSERVER) //FakeServer putenv exch systemdict /forkunix get exec (NEWSSERVER) exch putenv } def dictend def Hacks /def load forall framebuffer setcanvas PaintRoot /rootmenu /copymenu rootmenu send def /DefaultMenu LitePullRightMenu def /rootmenu /flipstyle rootmenu send def /serverproc { % fork clear (%socketl) //ServerSocket 20 string cvs append (r) file { % loop dup mark exch acceptconnection { % fork Hacks end 200 dict begin { def } forall initmatrix newprocessgroup exch pop exch pop dup getsocketpeername /OriginatingHost exch def RemoteHostRegistry OriginatingHost known OriginatingHost localhostname anchorsearch { pop (.) anchorsearch { pop pop RemoteHostRegistry localhostname known } { pop false } ifelse } { pop false } ifelse or % Let through if no security wanted NetSecurityWanted not or { cvx exec } { currentcursorlocation [ (Network security violation:) (Rejected connection from ) OriginatingHost append ] popmsg } ifelse currentprocess killprocessgroup } fork cleartomark } loop } fork def /rooteventmgr [ /rootmenu where { pop MenuButton { {/showat rootmenu send} fork pop } DownTransition framebuffer eventmgrinterest } if /Damaged { damagepath clipcanvas PaintRoot newpath clipcanvas} null framebuffer eventmgrinterest ] forkeventmgr def win /KillProcesses [ serverproc rooteventmgr ] put % win /KillGroups [ rooteventmgr ] put