New Upstream Release - ctwm

Ready changes

Summary

Merged new upstream version: 4.1.0 (was: 4.0.3).

Diff

diff --git a/.bzrignore b/.bzrignore
index 66bad32..c7c1237 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -10,3 +10,4 @@ doc/devman/build
 doc/manual/ctwm.1
 doc/manual/ctwm.1.xml
 tags
+compile_commands.json
diff --git a/CHANGES.html b/CHANGES.html
index ba2ea25..45d2ce8 100644
--- a/CHANGES.html
+++ b/CHANGES.html
@@ -7,6 +7,78 @@
 
 <h1 id="ctwmchangehistory">CTWM Change History</h1>
 
+<h2 id="4.1.02023-03-26">4.1.0 (2023&#8211;03&#8211;26)</h2>
+
+<h3 id="backward-incompatiblechangesandremovedfeatures">Backward-Incompatible Changes And Removed Features</h3>
+
+<ol>
+<li><p>Support for <code>VirtualScreens</code> has been removed. This was an early
+attempt to allow some manual configuration of multiple monitors, but
+carried with it a lot of caveats and strange behaviors. The current
+automatic RANDR and manual <code>MonitorLayout</code> features are a replacement
+for anywhere this ever really worked.</p></li>
+<li><p><code>ctwm</code>&#8217;s captive mode support has been removed. This includes the
+<code>--window</code> and <code>--name</code> command line arguments, and the
+<code>f.adoptwindow</code> and <code>f.hypermove</code> functions. Be sure to remove any
+references to those functions from your config file.</p></li>
+<li><p>Support for <code>WindowBox</code> has been removed, along with the
+<code>f.fittocontent</code> function related to it.</p></li>
+<li><p>The minimum cmake version has been bumped to 3.6. This is available
+in standard packages back to CentOS 6, and we appear to have actually
+been using syntax requiring 3.5 for a while unknowingly anyway.</p></li>
+</ol>
+
+<h3 id="newfeatures">New Features</h3>
+
+<ol>
+<li><p>Support for understanding multi-monitor layouts as something other
+than a single giant rectangle added. The RANDR X extension is used
+for determining how your monitors are laid out. The various
+<code>f.\*zoom</code> functions now zoom on the monitor the window is currently
+on, and new <code>f.x\*zoom</code> functions are added to zoom across your entire
+display. Various internal geometries can be specified
+RANDR-output-relative; see doc of <em>e.g.</em> <code>IconManagers</code>. Contributed
+by Maxime Soulé <a href="&#x6d;&#x61;&#105;&#x6c;&#x74;&#111;&#x3a;&#x62;&#116;&#105;&#107;&#x2d;&#x63;&#x74;&#x77;&#109;&#64;&#115;&#x63;&#x6f;&#x75;&#98;&#105;&#x64;&#111;&#x75;&#46;&#x63;&#x6f;&#x6d;">&#98;&#x74;&#x69;&#x6b;&#45;&#99;&#116;&#x77;&#x6d;&#x40;&#x73;&#x63;&#111;&#117;&#98;&#105;&#x64;&#x6f;&#117;&#x2e;&#x63;&#x6f;&#109;</a>.</p></li>
+<li><p>Added <code>MonitorLayout {}</code> config var for overriding the layout of
+multiple monitors. In normal cases with multiple monitors and a
+modern X server, this is unnecessary. It&#8217;s useful if the X server
+doesn&#8217;t support RANDR (<em>e.g.</em>, older servers), or if the info it
+provides is wrong (<em>e.g.</em>, multi-display simulation with Xephyr), or
+if you just prefer to specify things differently than they would
+otherwise be (<em>e.g.</em>, treat an ultra-wide display as 2 separate
+monitors).</p></li>
+<li><p>The EWMH <code>_NET_FRAME_EXTENTS</code> property is now set on windows when we
+take control of them. This should fix clients mispositioning other
+windows on top of themselves; visible with Firefox&#8217;s form autofilling
+and context menus. Contributed by Maxime Soulé
+<a href="&#x6d;&#97;&#105;&#x6c;&#x74;&#111;&#58;&#98;&#x74;&#105;&#107;&#x2d;&#99;&#116;&#119;&#x6d;&#x40;&#115;&#99;&#111;&#117;&#x62;&#105;&#100;&#x6f;&#x75;&#46;&#99;&#111;&#109;">&#x62;&#x74;&#x69;&#107;&#x2d;&#x63;&#x74;&#119;&#x6d;&#64;&#x73;&#99;&#x6f;&#x75;&#x62;&#x69;&#100;&#111;&#117;&#46;&#99;&#111;&#x6d;</a>.</p></li>
+</ol>
+
+<h3 id="bugfixes">Bugfixes</h3>
+
+<ol>
+<li><p>When restarting ctwm, the icon managers for the current workspace will
+now initially show up, rather than those for first WS.</p></li>
+<li><p>When restarting ctwm, the stacking order of windows is now preserved.</p></li>
+<li><p>Running <code>--cfgchk</code> without an available X server will now work. Some
+errors may only be discovered when it can talk to X (things relating
+to colors are a likely suspect). Checking configs for multi-Screen
+setups will now properly check all of them as well.</p></li>
+<li><p>Fix display of combined modifiers in TwmKeys menu. Contributed by
+Maxime Soulé <a href="&#109;&#97;&#105;&#108;&#x74;&#111;&#58;&#98;&#116;&#105;&#107;&#x2d;&#x63;&#x74;&#119;&#x6d;&#64;&#x73;&#x63;&#x6f;&#x75;&#x62;&#105;&#x64;&#x6f;&#x75;&#x2e;&#x63;&#111;&#109;">&#x62;&#116;&#105;&#x6b;&#45;&#99;&#x74;&#x77;&#x6d;&#64;&#115;&#x63;&#x6f;&#117;&#x62;&#x69;&#100;&#x6f;&#x75;&#x2e;&#x63;&#x6f;&#109;</a>.</p></li>
+<li><p>Fix window icon name spilling out into the border of icon manager
+entries. Contributed by Carl Svensson <a href="&#109;&#97;&#x69;&#x6c;&#116;&#x6f;&#58;&#99;&#x74;&#119;&#109;&#64;&#x64;&#97;&#x74;&#97;&#103;&#117;&#x62;&#98;&#101;&#46;&#x73;&#101;">&#99;&#x74;&#x77;&#x6d;&#64;&#x64;&#x61;&#x74;&#97;&#x67;&#117;&#98;&#x62;&#x65;&#x2e;&#115;&#101;</a>.</p></li>
+<li><p>Fix minor mis-sizing and postitioning of squeezed titlebar when window
+is squeezed away. Contributed by Maxime Soulé
+<a href="&#x6d;&#97;&#x69;&#108;&#116;&#x6f;&#x3a;&#x62;&#x74;&#105;&#x6b;&#45;&#x63;&#x74;&#x77;&#x6d;&#64;&#x73;&#x63;&#111;&#117;&#x62;&#105;&#100;&#111;&#x75;&#x2e;&#x63;&#111;&#x6d;">&#98;&#x74;&#x69;&#x6b;&#45;&#99;&#116;&#x77;&#x6d;&#x40;&#x73;&#99;&#111;&#117;&#98;&#x69;&#x64;&#111;&#x75;&#x2e;&#x63;&#x6f;&#x6d;</a>.</p></li>
+<li><p>Fix window placement when <code>DontMoveOff</code> is enabled without 3D borders.
+Contributed by Maxime Soulé <a href="&#x6d;&#x61;&#x69;&#108;&#116;&#x6f;&#x3a;&#x62;&#x74;&#105;&#x6b;&#45;&#99;&#x74;&#x77;&#x6d;&#64;&#x73;&#x63;&#111;&#117;&#x62;&#x69;&#x64;&#111;&#117;&#x2e;&#99;&#x6f;&#109;">&#x62;&#x74;&#x69;&#107;&#45;&#99;&#x74;&#x77;&#109;&#x40;&#115;&#99;&#111;&#x75;&#98;&#105;&#100;&#x6f;&#117;&#46;&#99;&#111;&#x6d;</a>.</p></li>
+<li><p>When window titles overflow the available space, always treat them as
+left-justified, to avoid bad behavior of other justifications and
+provide the best available behavior. Found by Carl Svensson
+<a href="&#109;&#97;&#x69;&#108;&#116;&#111;&#x3a;&#99;&#116;&#x77;&#x6d;&#x40;&#x64;&#x61;&#x74;&#x61;&#103;&#x75;&#98;&#x62;&#x65;&#x2e;&#115;&#101;">&#x63;&#x74;&#x77;&#x6d;&#64;&#100;&#x61;&#x74;&#97;&#103;&#x75;&#x62;&#x62;&#101;&#46;&#x73;&#101;</a>.</p></li>
+</ol>
+
 <h2 id="4.0.32019-07-21">4.0.3 (2019&#8211;07&#8211;21)</h2>
 
 <h3 id="bugfixes">Bugfixes</h3>
@@ -238,14 +310,14 @@ params (with existing param they invert in parens):</p>
 <li><p>Added <code>DontShowWelcomeWindow</code> config option to not show welcome
 splashscreen image.</p></li>
 <li><p>Selected a number of cleanups from Stefan Monnier
-<a href="&#x6d;&#x61;&#105;&#x6c;&#x74;&#111;&#x3a;&#x6d;&#111;&#110;&#110;&#x69;&#x65;&#x72;&#x40;&#73;&#82;&#79;&#x2e;&#x55;&#x4d;&#111;&#110;&#x74;&#114;&#x65;&#97;&#x6c;&#x2e;&#x43;&#65;">&#x6d;&#x6f;&#x6e;&#110;&#105;&#101;&#x72;&#x40;&#x49;&#x52;&#x4f;&#46;&#85;&#77;&#111;&#x6e;&#x74;&#114;&#x65;&#x61;&#x6c;&#46;&#x43;&#65;</a>, including rate-limiting of animations
+<a href="&#x6d;&#x61;&#105;&#x6c;&#116;&#111;&#x3a;&#x6d;&#x6f;&#x6e;&#110;&#x69;&#x65;&#x72;&#x40;&#73;&#x52;&#x4f;&#46;&#x55;&#x4d;&#x6f;&#x6e;&#x74;&#114;&#x65;&#x61;&#108;&#46;&#x43;&#65;">&#109;&#111;&#x6e;&#110;&#x69;&#101;&#x72;&#x40;&#73;&#82;&#x4f;&#46;&#x55;&#77;&#111;&#x6e;&#x74;&#x72;&#101;&#97;&#108;&#46;&#x43;&#65;</a>, including rate-limiting of animations
 using a new <code>_XA_WM_END_OF_ANIMATION</code> message. Font height is
 estimated based on used characters only. Added some similar changes,
 improved the prevention of placing windows off-screen, the
 <code>f.rescuewindows</code> function for emergencies, a hack-fix for
 <code>f.adoptwindow</code>. More virtual screen tweaks/fixes.</p></li>
 <li><p>Added the remaining OnTopPriority changes from Stefan Monnier
-<a href="&#109;&#x61;&#x69;&#108;&#116;&#111;&#x3a;&#109;&#111;&#x6e;&#110;&#105;&#101;&#x72;&#x40;&#73;&#82;&#79;&#46;&#x55;&#77;&#111;&#x6e;&#x74;&#114;&#101;&#97;&#108;&#x2e;&#x43;&#x41;">&#109;&#x6f;&#x6e;&#x6e;&#105;&#x65;&#114;&#x40;&#73;&#x52;&#x4f;&#x2e;&#x55;&#77;&#111;&#110;&#116;&#114;&#101;&#x61;&#108;&#46;&#67;&#65;</a>: <code>AutoPopup</code>, <code>AutoPriority</code>,
+<a href="&#109;&#x61;&#105;&#x6c;&#x74;&#111;&#58;&#x6d;&#111;&#110;&#110;&#x69;&#101;&#114;&#x40;&#x49;&#x52;&#79;&#46;&#85;&#x4d;&#111;&#110;&#116;&#x72;&#x65;&#x61;&#x6c;&#46;&#x43;&#65;">&#109;&#x6f;&#110;&#x6e;&#105;&#101;&#x72;&#x40;&#x49;&#82;&#x4f;&#x2e;&#x55;&#x4d;&#111;&#x6e;&#x74;&#x72;&#x65;&#x61;&#108;&#46;&#x43;&#65;</a>: <code>AutoPopup</code>, <code>AutoPriority</code>,
 <code>OnTopPriority</code>, <code>PrioritySwitching</code>, <code>f.changepriority</code>,
 <code>f.priorityswitching</code>, <code>f.setpriority</code>, <code>f.switchpriority</code>,
 <code>f.tinylower</code>, <code>f.tinyraise</code>. Currently consistency checking code is
@@ -360,7 +432,7 @@ each virtual workspace. As you switch workspaces, the cursor is
 automatically warped to the window previous in focus in the
 workspace. This significantly reduces the amount of mouse use.
 [Martin Blais]</p></li>
-<li><p>f.fill patch from Matthias Kretschmer <a href="&#x6d;&#97;&#105;&#108;&#116;&#111;&#58;&#x6b;&#x72;&#x65;&#116;&#x73;&#99;&#x68;&#x6d;&#x40;&#x63;&#x73;&#46;&#x75;&#x6e;&#x69;&#x2d;&#x62;&#111;&#110;&#x6e;&#46;&#100;&#x65;">&#107;&#114;&#x65;&#x74;&#x73;&#99;&#104;&#x6d;&#x40;&#99;&#x73;&#x2e;&#117;&#x6e;&#x69;&#x2d;&#x62;&#x6f;&#110;&#110;&#46;&#x64;&#x65;</a>.
+<li><p>f.fill patch from Matthias Kretschmer <a href="&#109;&#x61;&#105;&#x6c;&#116;&#x6f;&#58;&#107;&#114;&#101;&#x74;&#115;&#x63;&#104;&#x6d;&#64;&#x63;&#x73;&#x2e;&#x75;&#110;&#x69;&#x2d;&#98;&#x6f;&#110;&#110;&#x2e;&#100;&#101;">&#107;&#x72;&#101;&#116;&#x73;&#x63;&#x68;&#109;&#x40;&#x63;&#115;&#46;&#x75;&#x6e;&#105;&#x2d;&#98;&#111;&#x6e;&#x6e;&#46;&#100;&#x65;</a>.
 Without the patch, you might get windows which are increased by
 two times the border width more than it should be. Additionally
 if you place a window with no/not much size contrainst like
@@ -379,7 +451,7 @@ situation the size should not change at all&#8230;
 not being safely initialized, that sort of thing.
 [Richard Levitte]</p></li>
 <li><p>Fixed several memory leaks found by
-&#8220;Nadav Har&#8217;El&#8221; <a href="&#109;&#x61;&#105;&#108;&#x74;&#111;&#58;&#110;&#x79;&#104;&#x40;&#109;&#97;&#116;&#x68;&#46;&#116;&#101;&#x63;&#104;&#110;&#x69;&#x6f;&#x6e;&#46;&#x61;&#x63;&#x2e;&#105;&#x6c;">&#110;&#121;&#x68;&#x40;&#x6d;&#97;&#116;&#x68;&#46;&#x74;&#101;&#99;&#x68;&#x6e;&#x69;&#x6f;&#110;&#x2e;&#97;&#x63;&#x2e;&#x69;&#x6c;</a>.
+&#8220;Nadav Har&#8217;El&#8221; <a href="&#x6d;&#97;&#105;&#108;&#x74;&#111;&#x3a;&#x6e;&#121;&#104;&#x40;&#109;&#x61;&#x74;&#104;&#46;&#116;&#101;&#x63;&#104;&#x6e;&#x69;&#111;&#110;&#x2e;&#x61;&#99;&#x2e;&#105;&#x6c;">&#110;&#x79;&#x68;&#x40;&#x6d;&#97;&#116;&#104;&#46;&#116;&#x65;&#x63;&#x68;&#110;&#105;&#111;&#110;&#46;&#97;&#99;&#x2e;&#x69;&#x6c;</a>.
 [Olaf &#8220;Rhialto&#8221; Seibert]</p></li>
 <li><p>Merged in the <code>f.movetitlebar</code> command. By default this is bound to
 alt-left-click in the titlebar.
@@ -568,7 +640,7 @@ near the bottom edge instead of the top.
 <li><p>Workspace context (bkctwmws.patch)</p>
 
 <p>Makes it possible to bind keys specific to the workspace manager
-(by Bj&ouml;rn Knutsson). Use the event context &#8220;workspace&#8221; for this.</p></li>
+(by Björn Knutsson). Use the event context &#8220;workspace&#8221; for this.</p></li>
 <li><p>New keyword : AlwaysSqueezeToGravity</p>
 
 <p>If it is enabled, window squeezing always follows window gravity
@@ -610,7 +682,7 @@ same window) in 2 vscreens at the same time. The syntax is:</p>
 this because I have now 2 screens, but I was unable to get them working
 properly, so I use only one.</p></li>
 <li><p><strong>[ At this point, Claude has stopped working on CTWM, and the project
-is now in the hands of Richard Levitte <a href="&#109;&#x61;&#x69;&#108;&#116;&#x6f;&#58;&#114;&#105;&#x63;&#x68;&#x61;&#114;&#x64;&#64;&#x6c;&#x65;&#x76;&#105;&#116;&#116;&#x65;&#x2e;&#x6f;&#x72;&#103;">&#114;&#105;&#99;&#x68;&#x61;&#114;&#x64;&#x40;&#x6c;&#x65;&#x76;&#x69;&#x74;&#x74;&#101;&#46;&#x6f;&#x72;&#x67;</a>. ]</strong></p></li>
+is now in the hands of Richard Levitte <a href="&#109;&#x61;&#x69;&#108;&#116;&#111;&#x3a;&#x72;&#105;&#x63;&#x68;&#97;&#x72;&#x64;&#64;&#x6c;&#x65;&#x76;&#105;&#116;&#x74;&#x65;&#x2e;&#111;&#x72;&#103;">&#114;&#x69;&#99;&#x68;&#x61;&#x72;&#x64;&#64;&#x6c;&#x65;&#118;&#x69;&#x74;&#x74;&#x65;&#46;&#111;&#114;&#103;</a>. ]</strong></p></li>
 <li><p>Changed Imakefile to support a distribution target.</p></li>
 <li><p>Changed <code>:xpm:cross</code> to become a bit larger and have a slightly more
 3D appearance, and is visible even in very dark configurations.</p></li>
@@ -628,7 +700,7 @@ map.</p>
 if it found errors or not.</p>
 
 <p>This was contributed by Matthew D. Fuller.</p></li>
-<li><p><code>DontMoveOff</code> patch (by Bj&ouml;rn Knutsson)</p>
+<li><p><code>DontMoveOff</code> patch (by Björn Knutsson)</p>
 
 <p>Change the behavior of <code>DontMoveOff</code> / <code>MoveOffResistance</code> so that
 when you attempt to move a window off screen, it will not move at all
@@ -637,16 +709,16 @@ this time it will no longer &#8220;snap&#8221;, but instead it will start moving
 off screen. This means that you still have the old behavior of
 DontMoveOff, but now with the ability to move a window off screen
 less that <code>MoveOffResistance</code> pixels.</p></li>
-<li><p>Random placement and DontMoveOff patch (by Bj&ouml;rn Knutsson, changed)</p>
+<li><p>Random placement and DontMoveOff patch (by Björn Knutsson, changed)</p>
 
 <p>When random placement was used, DontMoveOff wasn&#8217;t honored.
 This behavior has now changed so a window will be kept within
 the screen when at all possible. When the window is too
 large, it&#8217;s top or left edge (or both) will be placed in
 coordinate 0.
-This change differs a little bit from Bj&ouml;rns contribution by
+This change differs a little bit from Björns contribution by
 not using rand() at all.</p></li>
-<li><p><code>f.warpring</code> patch (by Bj&ouml;rn Knutsson)</p>
+<li><p><code>f.warpring</code> patch (by Björn Knutsson)</p>
 
 <p>If <code>IconManagerFocus</code> is set, there&#8217;s no reason why the icon
 manager should get enter and leave events. This fixes some
@@ -654,24 +726,24 @@ disturbing in the warpring that would otherwise happen.</p></li>
 <li><p><code>f.movetoprevworkspace</code>,
 <code>f.movetonextworkspace</code>,
 <code>f.movetoprevworkspaceandfollow</code>,
-<code>f.movetonextworkspaceandfollow</code> patch (by Daniel Holmstr&ouml;m)</p>
+<code>f.movetonextworkspaceandfollow</code> patch (by Daniel Holmström)</p>
 
 <p>Makes it possible to move a window to the previous or next
 workspace and, if you like, go to that workspace and focus
 the moved window.</p></li>
-<li><p><code>f.fill</code> &#8220;vertical&#8221; patch (by Daniel Holmstr&ouml;m)</p>
+<li><p><code>f.fill</code> &#8220;vertical&#8221; patch (by Daniel Holmström)</p>
 
 <p>Expands the window vertically without overlapping any other window,
 much like <code>{ f.fill &quot;top&quot; f.fill &quot;bottom&quot; }</code> but with the exception
 that it doesn&#8217;t expand over window borders. It also sets the windows
 &#8220;zoomed&#8221; to <code>F_FULLZOOM</code>, so one can toggle between this size,
 original and maximized.</p></li>
-<li><p><code>RESIZEKEEPSFOCUS</code> bugfix patch (by Daniel Holmstr&ouml;m)</p>
+<li><p><code>RESIZEKEEPSFOCUS</code> bugfix patch (by Daniel Holmström)</p>
 
 <p>If a window is maximized with <code>togglemaximize</code> and then restored it
 might loose focus if the cursor is outside the restored window. This
 hack puts the cursor at the left-top corner of the window.</p></li>
-<li><p><code>f.zoom</code> bugfix patch (by Daniel Holmstr&ouml;m)</p>
+<li><p><code>f.zoom</code> bugfix patch (by Daniel Holmström)</p>
 
 <p><code>f.zoom</code> now doesn&#8217;t move the window up (as it sometimes did before)</p></li>
 <li><p><code>IgnoreTransient</code> patch (by Peter Berg Larsen)</p>
@@ -838,7 +910,7 @@ richard@levitte.org.
 
 <ol>
 <li><p>Fix line numbers for errors when using m4 preprocessor. Send thanks
-to Josh Wilmes <a href="&#x6d;&#97;&#x69;&#108;&#116;&#x6f;&#x3a;&#x6a;&#111;&#x73;&#x68;&#64;&#104;&#x69;&#x74;&#x63;&#104;&#104;&#x69;&#107;&#x65;&#114;&#x2e;&#x6f;&#x72;&#103;">&#106;&#111;&#x73;&#x68;&#64;&#x68;&#105;&#116;&#99;&#x68;&#104;&#105;&#107;&#x65;&#114;&#46;&#111;&#114;&#x67;</a>.</p></li>
+to Josh Wilmes <a href="&#109;&#x61;&#x69;&#x6c;&#x74;&#x6f;&#58;&#x6a;&#111;&#115;&#x68;&#x40;&#104;&#105;&#x74;&#x63;&#104;&#104;&#x69;&#x6b;&#x65;&#x72;&#x2e;&#x6f;&#114;&#103;">&#x6a;&#111;&#115;&#x68;&#64;&#104;&#105;&#x74;&#99;&#x68;&#x68;&#x69;&#x6b;&#101;&#114;&#46;&#x6f;&#114;&#x67;</a>.</p></li>
 <li><p>Fix the way menu entries are selected with the keyboard. Now
 when you type a letter, the pointer moves to the next entry
 whose first letter is this letter, but does not activate it.
@@ -846,8 +918,8 @@ The new keyword IgnoreCaseInMenuSelection, can be used to
 ignore case for this delection.</p></li>
 <li><p>New keyword: DontSave. Takes a window list as argument. All listed
 windows won&#8217;t have their characteristics saved for the session manager.
-Patch from Matthias Baake <a href="&#109;&#97;&#x69;&#108;&#116;&#111;&#x3a;&#77;&#97;&#x74;&#x74;&#x68;&#x69;&#x61;&#x73;&#x2e;&#66;&#x61;&#97;&#x6b;&#x65;&#x40;&#103;&#109;&#x78;&#x2e;&#x64;&#x65;">&#77;&#97;&#x74;&#x74;&#104;&#105;&#x61;&#x73;&#x2e;&#66;&#97;&#x61;&#107;&#x65;&#x40;&#103;&#x6d;&#120;&#46;&#x64;&#x65;</a></p></li>
-<li><p>Also from Matthias Baake <a href="&#x6d;&#x61;&#105;&#x6c;&#x74;&#x6f;&#x3a;&#77;&#x61;&#x74;&#116;&#x68;&#x69;&#x61;&#x73;&#x2e;&#66;&#x61;&#x61;&#107;&#101;&#x40;&#103;&#109;&#120;&#x2e;&#100;&#x65;">&#77;&#x61;&#x74;&#116;&#104;&#x69;&#97;&#x73;&#46;&#66;&#x61;&#x61;&#x6b;&#101;&#64;&#103;&#109;&#x78;&#46;&#100;&#x65;</a>:
+Patch from Matthias Baake <a href="&#109;&#97;&#105;&#108;&#x74;&#x6f;&#x3a;&#x4d;&#x61;&#116;&#x74;&#x68;&#x69;&#x61;&#x73;&#x2e;&#66;&#97;&#x61;&#x6b;&#101;&#64;&#x67;&#109;&#x78;&#x2e;&#x64;&#101;">&#x4d;&#97;&#x74;&#116;&#x68;&#105;&#97;&#115;&#x2e;&#66;&#97;&#97;&#107;&#x65;&#64;&#103;&#x6d;&#x78;&#x2e;&#100;&#x65;</a></p></li>
+<li><p>Also from Matthias Baake <a href="&#109;&#x61;&#x69;&#x6c;&#x74;&#x6f;&#58;&#x4d;&#97;&#x74;&#x74;&#104;&#105;&#x61;&#x73;&#x2e;&#66;&#97;&#x61;&#107;&#101;&#64;&#x67;&#109;&#120;&#46;&#100;&#101;">&#x4d;&#x61;&#116;&#x74;&#x68;&#105;&#x61;&#115;&#46;&#x42;&#x61;&#x61;&#107;&#101;&#x40;&#x67;&#109;&#x78;&#46;&#100;&#x65;</a>:
 With the new keywords BorderLeft, BorderRight, BorderBottom and
 BorderTop (each of them is optional with a default value of 0 and
 takes a nonnegative integer as argument) you can declare a border
@@ -855,15 +927,15 @@ takes a nonnegative integer as argument) you can declare a border
 real borders of the screen when moving windows; you can use
 f.forcemove to override them.</p></li>
 <li><p>Sloppy Focus added with keyword &#8220;SloppyFocus&#8221; in configuration file
-(DINH V. Hoa <a href="&#109;&#x61;&#x69;&#108;&#116;&#x6f;&#58;&#100;&#105;&#x6e;&#104;&#64;&#x65;&#x6e;&#x73;&#101;&#114;&#98;&#x2e;&#102;&#114;">&#100;&#x69;&#x6e;&#x68;&#x40;&#101;&#x6e;&#115;&#101;&#x72;&#98;&#x2e;&#102;&#114;</a>).</p></li>
+(DINH V. Hoa <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#x3a;&#100;&#x69;&#x6e;&#104;&#x40;&#x65;&#110;&#115;&#x65;&#x72;&#98;&#x2e;&#102;&#x72;">&#x64;&#x69;&#110;&#104;&#x40;&#101;&#x6e;&#115;&#101;&#114;&#x62;&#46;&#x66;&#114;</a>).</p></li>
 <li><p>The keyword &#8220;ClickToFocus&#8221; has been correctly implemented
-(DINH V. Hoa <a href="&#x6d;&#x61;&#x69;&#108;&#x74;&#x6f;&#x3a;&#x64;&#105;&#x6e;&#x68;&#x40;&#x65;&#x6e;&#115;&#101;&#x72;&#98;&#46;&#x66;&#114;">&#x64;&#105;&#x6e;&#104;&#64;&#101;&#110;&#x73;&#101;&#x72;&#98;&#x2e;&#102;&#x72;</a>).</p></li>
+(DINH V. Hoa <a href="&#x6d;&#x61;&#x69;&#x6c;&#x74;&#111;&#58;&#100;&#x69;&#110;&#x68;&#x40;&#x65;&#x6e;&#x73;&#x65;&#x72;&#98;&#46;&#x66;&#114;">&#x64;&#105;&#x6e;&#104;&#x40;&#101;&#110;&#x73;&#101;&#x72;&#x62;&#46;&#102;&#114;</a>).</p></li>
 <li><p>The keyword &#8220;IgnoreModifier&#8221; has been added, to use this feature, you
 have to add a line <code>IgnoreModifier { lock m2 }</code> in the configuration
 file. All bindings (buttons and keys) will ignore the modifiers you
 specified. It is useful when you use caps locks or num locks. You
 don&#8217;t need IgnoreLockModifier any more with this option. (DINH V.
-Hoa <a href="&#x6d;&#x61;&#x69;&#108;&#x74;&#x6f;&#58;&#x64;&#105;&#110;&#x68;&#64;&#101;&#110;&#x73;&#101;&#114;&#x62;&#x2e;&#x66;&#114;">&#x64;&#x69;&#110;&#104;&#x40;&#x65;&#110;&#x73;&#101;&#114;&#x62;&#x2e;&#102;&#114;</a>).</p></li>
+Hoa <a href="&#109;&#97;&#x69;&#x6c;&#116;&#x6f;&#58;&#x64;&#105;&#110;&#x68;&#64;&#x65;&#110;&#115;&#x65;&#114;&#x62;&#46;&#x66;&#114;">&#x64;&#x69;&#x6e;&#x68;&#x40;&#x65;&#110;&#115;&#x65;&#114;&#x62;&#46;&#x66;&#x72;</a>).</p></li>
 <li><p>New keyword: WindowBox. Creates a new window called a box, where
 all the client windows that match the windows list are opened in,
 instead of the roor window. This is useful to group small windows
@@ -896,7 +968,7 @@ in the TwmIcons menu.</p></li>
 standard X geometry syntax (e.g. <code>200x300+150-0</code>). Sets the current
 window to the specified geometry. The width and height are to be given
 in pixel, no base size or resize increment are used.</p></li>
-<li><p>AutoLower et <code>f.autolower</code>: from Kai Gro&szlig;johann
+<li><p>AutoLower et <code>f.autolower</code>: from Kai Großjohann
 (Kai.Grossjohann@CS.Uni-Dortmund.DE). Same as autoraise but with lower.</p></li>
 <li><p><code>WindowRingExclude</code>: Takes a window list as argument. All listed windows
 will be excluded from the WarpRing.</p></li>
@@ -915,7 +987,7 @@ I am getting old(er), I prefer a bare desktop.</p></li>
 <li><p><code>f.ring</code>: Selects a window and adds it to the WarpRing, or removes it if
 it was already in the ring. This command makes f.warpring much more
 useful, by making its configuration dynamic (thanks to Philip Kizer
-<a href="&#x6d;&#x61;&#105;&#108;&#116;&#x6f;&#58;&#x70;&#x63;&#107;&#105;&#x7a;&#101;&#x72;&#x40;&#116;&#97;&#109;&#117;&#x2e;&#101;&#x64;&#x75;">&#112;&#99;&#x6b;&#x69;&#122;&#x65;&#114;&#x40;&#116;&#x61;&#x6d;&#x75;&#x2e;&#101;&#100;&#117;</a>).</p></li>
+<a href="&#x6d;&#x61;&#105;&#x6c;&#116;&#x6f;&#x3a;&#x70;&#x63;&#x6b;&#105;&#122;&#x65;&#x72;&#64;&#116;&#x61;&#x6d;&#117;&#46;&#x65;&#x64;&#117;">&#x70;&#x63;&#x6b;&#x69;&#122;&#x65;&#x72;&#x40;&#116;&#97;&#109;&#117;&#46;&#x65;&#x64;&#x75;</a>).</p></li>
 <li><p>f.jumpleft, f.jumpright, f.jumpup, f.jumpdown : takes one integer
 argument (the step). These function are designed to be bound to keys,
 they move the current window (step * {X,Y}MoveGrid) pixels in the
@@ -991,11 +1063,11 @@ name. So it is possible to do something like :</p>
 <p>and the client will popus up in the workspace where the command was
 started even if you go elsewhere before it actually shows up.</p></li>
 <li><p>Fixes for the VMS version. From Richard Levitte - VMS Whacker
-<a href="&#109;&#97;&#x69;&#x6c;&#x74;&#111;&#58;&#108;&#101;&#118;&#105;&#116;&#x74;&#x65;&#x40;&#108;&#x70;&#x2e;&#115;&#101;">&#108;&#x65;&#x76;&#105;&#x74;&#x74;&#101;&#x40;&#x6c;&#112;&#x2e;&#x73;&#x65;</a>.</p></li>
-<li><p>Better I18N. From Toshiya Yasukawa <a href="&#109;&#97;&#x69;&#x6c;&#x74;&#111;&#x3a;&#116;&#45;&#x79;&#97;&#x73;&#x75;&#x6b;&#x61;&#64;&#x64;&#x64;&#46;&#x69;&#x69;&#x6a;&#x34;&#117;&#46;&#111;&#114;&#46;&#x6a;&#x70;">&#x74;&#x2d;&#x79;&#97;&#x73;&#117;&#107;&#x61;&#x40;&#100;&#100;&#x2e;&#x69;&#105;&#106;&#x34;&#x75;&#x2e;&#x6f;&#x72;&#x2e;&#106;&#112;</a>. (Define
+<a href="&#x6d;&#x61;&#105;&#108;&#116;&#111;&#x3a;&#x6c;&#x65;&#x76;&#105;&#116;&#x74;&#x65;&#x40;&#x6c;&#112;&#46;&#115;&#x65;">&#x6c;&#x65;&#118;&#x69;&#116;&#x74;&#x65;&#64;&#x6c;&#112;&#x2e;&#x73;&#101;</a>.</p></li>
+<li><p>Better I18N. From Toshiya Yasukawa <a href="&#109;&#97;&#105;&#108;&#116;&#x6f;&#58;&#x74;&#x2d;&#121;&#x61;&#115;&#x75;&#x6b;&#x61;&#64;&#x64;&#x64;&#x2e;&#x69;&#105;&#106;&#x34;&#x75;&#46;&#111;&#x72;&#x2e;&#106;&#112;">&#x74;&#45;&#x79;&#x61;&#115;&#x75;&#107;&#97;&#64;&#100;&#x64;&#46;&#x69;&#105;&#106;&#52;&#x75;&#46;&#x6f;&#x72;&#46;&#106;&#x70;</a>. (Define
 I18N in Imakefile to activate it).</p></li>
 <li><p>Better Session Management interface. Patches from Matthew McNeill
-<a href="&#x6d;&#97;&#105;&#x6c;&#116;&#111;&#58;&#x4d;&#46;&#x52;&#x2e;&#x4d;&#x63;&#78;&#101;&#105;&#x6c;&#108;&#x40;&#100;&#117;&#114;&#104;&#x61;&#x6d;&#x2e;&#x61;&#x63;&#46;&#x75;&#x6b;">&#x4d;&#x2e;&#x52;&#x2e;&#77;&#99;&#x4e;&#x65;&#105;&#108;&#x6c;&#64;&#x64;&#x75;&#x72;&#104;&#x61;&#109;&#x2e;&#97;&#x63;&#46;&#117;&#107;</a>.</p></li>
+<a href="&#x6d;&#97;&#x69;&#108;&#116;&#111;&#58;&#77;&#46;&#x52;&#46;&#x4d;&#99;&#78;&#x65;&#x69;&#108;&#108;&#x40;&#100;&#x75;&#x72;&#104;&#x61;&#109;&#x2e;&#97;&#99;&#46;&#x75;&#107;">&#x4d;&#x2e;&#x52;&#46;&#77;&#99;&#x4e;&#101;&#105;&#108;&#108;&#64;&#100;&#117;&#x72;&#x68;&#97;&#109;&#46;&#x61;&#x63;&#46;&#117;&#x6b;</a>.</p></li>
 <li><p>new flag : <code>-name</code>, useful only for captive Ctwm. Sets the name of the
 captive root window. Useful too for next point. If no name is
 specified ctwm-n is used, where n is a number automatically
@@ -1049,7 +1121,7 @@ want to define twice all your bindings.</p></li>
 <li><p>AutoFocusToTransients
 Transient windows get focus automatically when created. Useful with
 programs that have keyboard shortcuts that pop up windows. (patch
-from Kai Grossjohann <a href="&#x6d;&#97;&#105;&#108;&#116;&#x6f;&#58;&#103;&#x72;&#x6f;&#x73;&#115;&#x6a;&#111;&#x68;&#x61;&#x6e;&#x6e;&#x40;&#99;&#x68;&#97;&#x72;&#x6c;&#121;&#46;&#x63;&#x73;&#x2e;&#117;&#110;&#x69;&#45;&#100;&#111;&#x72;&#116;&#109;&#117;&#110;&#100;&#x2e;&#x64;&#101;">&#x67;&#x72;&#111;&#x73;&#115;&#106;&#x6f;&#x68;&#x61;&#110;&#110;&#x40;&#x63;&#104;&#x61;&#114;&#108;&#x79;&#46;&#99;&#115;&#46;&#117;&#110;&#x69;&#45;&#x64;&#x6f;&#114;&#x74;&#x6d;&#117;&#110;&#x64;&#x2e;&#100;&#x65;</a>).</p></li>
+from Kai Grossjohann <a href="&#109;&#97;&#x69;&#108;&#116;&#111;&#x3a;&#x67;&#x72;&#111;&#115;&#x73;&#106;&#111;&#104;&#97;&#x6e;&#110;&#x40;&#99;&#x68;&#97;&#x72;&#x6c;&#x79;&#x2e;&#99;&#x73;&#46;&#117;&#110;&#105;&#45;&#100;&#x6f;&#x72;&#116;&#x6d;&#x75;&#110;&#x64;&#46;&#x64;&#x65;">&#x67;&#114;&#x6f;&#x73;&#x73;&#x6a;&#x6f;&#104;&#97;&#110;&#x6e;&#x40;&#x63;&#104;&#x61;&#x72;&#x6c;&#x79;&#46;&#99;&#115;&#x2e;&#117;&#110;&#x69;&#x2d;&#x64;&#x6f;&#x72;&#x74;&#109;&#117;&#110;&#x64;&#46;&#x64;&#101;</a>).</p></li>
 <li><p>PackNewWindows
 Use f.movepack algorithm instead of f.move when opening a new window.</p></li>
 </ol>
@@ -1134,25 +1206,25 @@ colors. These symbolic colors allow the possiblity of using the same
 
 <p>Using these colors, I have built some 3d XPM files for various
 titlebars while still keeping the ability to change titlebar colors.
-[Matt Wormley <a href="&#109;&#x61;&#x69;&#x6c;&#116;&#111;&#x3a;&#109;&#x77;&#111;&#114;&#109;&#x6c;&#101;&#x79;&#64;&#x61;&#x69;&#x72;&#x73;&#x68;&#105;&#112;&#46;&#x61;&#114;&#x64;&#x66;&#x61;&#x2e;&#x63;&#x61;&#x6c;&#112;&#111;&#x6c;&#121;&#x2e;&#101;&#x64;&#117;">&#x6d;&#119;&#111;&#x72;&#109;&#x6c;&#x65;&#121;&#64;&#97;&#105;&#114;&#x73;&#x68;&#105;&#x70;&#46;&#x61;&#114;&#100;&#x66;&#97;&#x2e;&#99;&#97;&#x6c;&#112;&#x6f;&#108;&#x79;&#46;&#x65;&#x64;&#x75;</a>]</p></li>
+[Matt Wormley <a href="&#109;&#x61;&#105;&#108;&#x74;&#x6f;&#x3a;&#x6d;&#119;&#111;&#x72;&#x6d;&#108;&#x65;&#121;&#x40;&#97;&#105;&#x72;&#x73;&#x68;&#105;&#x70;&#46;&#97;&#x72;&#x64;&#102;&#x61;&#x2e;&#99;&#97;&#x6c;&#112;&#111;&#108;&#x79;&#x2e;&#x65;&#x64;&#117;">&#x6d;&#x77;&#x6f;&#x72;&#x6d;&#108;&#x65;&#121;&#64;&#x61;&#x69;&#114;&#115;&#104;&#x69;&#x70;&#46;&#x61;&#114;&#x64;&#x66;&#97;&#x2e;&#99;&#x61;&#x6c;&#112;&#x6f;&#108;&#x79;&#46;&#101;&#100;&#117;</a>]</p></li>
 <li><p>Added a keyword to the .ctwmrc file: &#8220;UseSunkTitlePixmap&#8221;. This
 makes it so the shadows are inversed for title pixmaps when focus is
 lost. This is similar to having the SunkFocusWindowTitle, but it
 makes your 3d XPM sink instead of just the whole bar.
-[Matt Wormley <a href="&#x6d;&#x61;&#x69;&#108;&#116;&#x6f;&#58;&#x6d;&#119;&#x6f;&#x72;&#x6d;&#x6c;&#101;&#x79;&#64;&#x61;&#x69;&#x72;&#x73;&#x68;&#105;&#112;&#x2e;&#x61;&#114;&#100;&#x66;&#x61;&#46;&#99;&#x61;&#x6c;&#112;&#x6f;&#x6c;&#x79;&#x2e;&#101;&#x64;&#x75;">&#x6d;&#119;&#111;&#114;&#109;&#108;&#x65;&#x79;&#x40;&#x61;&#x69;&#114;&#115;&#104;&#105;&#x70;&#x2e;&#x61;&#x72;&#100;&#102;&#x61;&#x2e;&#x63;&#x61;&#108;&#112;&#111;&#x6c;&#x79;&#x2e;&#101;&#x64;&#117;</a>]</p></li>
+[Matt Wormley <a href="&#109;&#x61;&#x69;&#108;&#116;&#x6f;&#x3a;&#109;&#x77;&#111;&#114;&#109;&#x6c;&#101;&#x79;&#x40;&#97;&#105;&#x72;&#x73;&#x68;&#x69;&#x70;&#46;&#97;&#x72;&#x64;&#102;&#x61;&#46;&#x63;&#x61;&#x6c;&#112;&#111;&#x6c;&#x79;&#46;&#x65;&#x64;&#117;">&#109;&#119;&#x6f;&#x72;&#109;&#x6c;&#101;&#x79;&#64;&#x61;&#105;&#114;&#115;&#x68;&#105;&#x70;&#46;&#97;&#x72;&#x64;&#x66;&#x61;&#x2e;&#x63;&#x61;&#108;&#112;&#x6f;&#x6c;&#x79;&#46;&#x65;&#100;&#117;</a>]</p></li>
 <li><p>Added 3 new builtin 3d buttons for &#8220;Iconify&#8221;, &#8220;Resize&#8221; and &#8220;Box&#8221;. They
 are available with the :xpm: identifier in the .ctwmrc file.
-[Matt Wormley <a href="&#x6d;&#x61;&#105;&#x6c;&#116;&#x6f;&#x3a;&#109;&#119;&#111;&#114;&#109;&#108;&#x65;&#121;&#x40;&#x61;&#105;&#x72;&#115;&#x68;&#x69;&#x70;&#46;&#x61;&#x72;&#x64;&#x66;&#97;&#46;&#x63;&#x61;&#108;&#112;&#x6f;&#x6c;&#121;&#46;&#x65;&#100;&#x75;">&#x6d;&#119;&#x6f;&#114;&#109;&#108;&#101;&#x79;&#64;&#x61;&#105;&#114;&#115;&#x68;&#105;&#x70;&#x2e;&#97;&#114;&#x64;&#x66;&#97;&#x2e;&#99;&#97;&#108;&#112;&#111;&#108;&#x79;&#46;&#x65;&#100;&#117;</a>]</p></li>
+[Matt Wormley <a href="&#109;&#x61;&#105;&#108;&#x74;&#111;&#58;&#x6d;&#x77;&#111;&#114;&#x6d;&#x6c;&#x65;&#x79;&#x40;&#x61;&#105;&#x72;&#115;&#104;&#105;&#x70;&#x2e;&#97;&#x72;&#100;&#102;&#97;&#46;&#x63;&#97;&#x6c;&#x70;&#111;&#x6c;&#121;&#x2e;&#101;&#100;&#x75;">&#x6d;&#119;&#x6f;&#114;&#109;&#x6c;&#x65;&#x79;&#x40;&#x61;&#x69;&#x72;&#x73;&#x68;&#105;&#x70;&#46;&#97;&#114;&#x64;&#102;&#97;&#x2e;&#x63;&#x61;&#108;&#x70;&#x6f;&#x6c;&#x79;&#46;&#101;&#100;&#x75;</a>]</p></li>
 <li><p>Added another keyword to the .ctwmrc file: &#8220;WorkSpaceFont&#8221;. This
 allows you to specify the font to use in the workspace manager.
-[Matt Wormley <a href="&#x6d;&#x61;&#105;&#108;&#x74;&#111;&#x3a;&#x6d;&#119;&#x6f;&#114;&#x6d;&#108;&#101;&#121;&#x40;&#97;&#x69;&#x72;&#x73;&#104;&#105;&#112;&#x2e;&#97;&#114;&#100;&#102;&#97;&#46;&#99;&#x61;&#x6c;&#112;&#111;&#108;&#x79;&#x2e;&#101;&#100;&#x75;">&#109;&#119;&#x6f;&#114;&#109;&#108;&#x65;&#x79;&#x40;&#97;&#105;&#x72;&#115;&#104;&#105;&#112;&#x2e;&#97;&#x72;&#100;&#x66;&#97;&#x2e;&#x63;&#x61;&#x6c;&#112;&#x6f;&#108;&#121;&#46;&#101;&#100;&#117;</a>]</p></li>
+[Matt Wormley <a href="&#x6d;&#x61;&#x69;&#x6c;&#116;&#111;&#x3a;&#109;&#x77;&#111;&#x72;&#109;&#x6c;&#x65;&#x79;&#x40;&#97;&#x69;&#x72;&#115;&#x68;&#105;&#112;&#x2e;&#x61;&#x72;&#x64;&#102;&#x61;&#x2e;&#99;&#x61;&#x6c;&#112;&#111;&#108;&#x79;&#46;&#x65;&#100;&#117;">&#x6d;&#119;&#111;&#114;&#109;&#108;&#101;&#x79;&#64;&#x61;&#x69;&#x72;&#115;&#x68;&#x69;&#x70;&#x2e;&#x61;&#114;&#100;&#102;&#97;&#46;&#x63;&#97;&#108;&#x70;&#111;&#108;&#x79;&#x2e;&#x65;&#x64;&#x75;</a>]</p></li>
 <li><p>8 new xpm pixmaps for buttons, title highlite, etc&#8230; :
 3dcircle.xpm 3ddimple.xpm 3ddot.xpm 3dfeet.xpm 3dleopard.xpm 3dpie.xpm
 3dpyramid.xpm 3dslant.xpm
-[Matt Wormley <a href="&#x6d;&#x61;&#105;&#x6c;&#x74;&#111;&#x3a;&#109;&#x77;&#x6f;&#x72;&#109;&#x6c;&#x65;&#x79;&#x40;&#x61;&#105;&#114;&#115;&#x68;&#x69;&#x70;&#46;&#x61;&#x72;&#x64;&#x66;&#97;&#46;&#99;&#x61;&#108;&#112;&#x6f;&#x6c;&#x79;&#x2e;&#x65;&#x64;&#117;">&#109;&#119;&#x6f;&#114;&#x6d;&#108;&#101;&#x79;&#64;&#97;&#x69;&#x72;&#x73;&#x68;&#105;&#112;&#x2e;&#x61;&#114;&#x64;&#102;&#x61;&#46;&#99;&#x61;&#x6c;&#x70;&#111;&#x6c;&#121;&#46;&#x65;&#x64;&#117;</a>]</p></li>
+[Matt Wormley <a href="&#x6d;&#x61;&#x69;&#x6c;&#116;&#111;&#58;&#x6d;&#119;&#111;&#114;&#109;&#108;&#101;&#x79;&#64;&#97;&#105;&#114;&#x73;&#x68;&#105;&#112;&#x2e;&#x61;&#114;&#100;&#102;&#97;&#x2e;&#x63;&#x61;&#108;&#x70;&#x6f;&#x6c;&#121;&#x2e;&#x65;&#x64;&#x75;">&#109;&#x77;&#111;&#114;&#109;&#x6c;&#x65;&#x79;&#64;&#x61;&#x69;&#x72;&#x73;&#x68;&#105;&#x70;&#46;&#97;&#x72;&#x64;&#x66;&#x61;&#x2e;&#99;&#x61;&#x6c;&#112;&#111;&#108;&#x79;&#x2e;&#101;&#x64;&#x75;</a>]</p></li>
 <li><p>2 new functions : f.forwmapiconmgr and f.backmapiconmgr, similar to
 f.forwiconmgr and f.backiconmgr but only stops on mapped windows.
-[Scott Bolte <a href="&#x6d;&#x61;&#105;&#108;&#x74;&#111;&#58;&#115;&#x63;&#x6f;&#x74;&#x74;&#98;&#x40;&#x63;&#x69;&#x72;&#x71;&#117;&#x65;&#46;&#109;&#x6f;&#x6e;&#101;&#110;&#103;&#x2e;&#x6d;&#101;&#x69;&#46;&#x63;&#x6f;&#109;">&#x73;&#99;&#x6f;&#x74;&#116;&#x62;&#64;&#x63;&#105;&#114;&#113;&#117;&#101;&#x2e;&#x6d;&#111;&#110;&#x65;&#x6e;&#103;&#x2e;&#109;&#101;&#105;&#x2e;&#99;&#x6f;&#x6d;</a>]</p></li>
+[Scott Bolte <a href="&#x6d;&#97;&#x69;&#108;&#x74;&#x6f;&#x3a;&#x73;&#99;&#x6f;&#x74;&#116;&#x62;&#64;&#99;&#105;&#114;&#113;&#x75;&#101;&#x2e;&#109;&#111;&#110;&#101;&#110;&#103;&#x2e;&#109;&#x65;&#x69;&#46;&#99;&#111;&#109;">&#x73;&#x63;&#111;&#116;&#116;&#x62;&#64;&#x63;&#105;&#114;&#x71;&#x75;&#101;&#46;&#109;&#111;&#x6e;&#101;&#110;&#103;&#x2e;&#x6d;&#x65;&#105;&#x2e;&#99;&#x6f;&#x6d;</a>]</p></li>
 <li><p>Last minute: PixmapDirectory now accept a colon separated list of
 directories.</p></li>
 <li><p>If you use m4, ctwm now defines <code>TWM_VERSION</code> which is the version in
@@ -1213,17 +1285,17 @@ Shift key toggles this behaviour.</dd>
 </ul>
 
 <p>Do what you expect.</p></li>
-<li><p>The function f.raiseicons (from Rickard Westman <a href="&#109;&#97;&#x69;&#x6c;&#x74;&#x6f;&#x3a;&#114;&#105;&#x63;&#x77;&#101;&#x40;&#105;&#x64;&#x61;&#x2e;&#108;&#105;&#x75;&#x2e;&#115;&#x65;">&#x72;&#105;&#99;&#119;&#x65;&#x40;&#105;&#x64;&#97;&#x2e;&#108;&#x69;&#117;&#46;&#115;&#x65;</a>).
+<li><p>The function f.raiseicons (from Rickard Westman <a href="&#x6d;&#x61;&#105;&#108;&#x74;&#x6f;&#58;&#x72;&#x69;&#99;&#119;&#101;&#x40;&#x69;&#x64;&#97;&#46;&#108;&#x69;&#x75;&#x2e;&#x73;&#x65;">&#114;&#105;&#x63;&#x77;&#101;&#x40;&#x69;&#x64;&#97;&#x2e;&#x6c;&#x69;&#x75;&#46;&#x73;&#101;</a>).
 Raises all icons.</p></li>
 <li><p>A new keyword: IconRegionAlignement. Like IconRegionJustification
 but align vertically. The parameter is &#8220;top&#8221;, &#8220;center&#8221;, &#8220;bottom&#8221; or
 &#8220;border&#8221;.</p></li>
 <li><p>f.addtoworkspace, f.removefromworkspace and f.toggleoccupation. (idea
-from Kai Grossjohann <a href="&#109;&#x61;&#105;&#108;&#x74;&#x6f;&#x3a;&#x67;&#x72;&#x6f;&#x73;&#115;&#106;&#x6f;&#x68;&#x40;&#108;&#x69;&#110;&#117;&#115;&#x2e;&#105;&#110;&#x66;&#111;&#114;&#x6d;&#x61;&#116;&#105;&#x6b;&#x2e;&#x75;&#x6e;&#x69;&#x2d;&#100;&#x6f;&#114;&#116;&#109;&#x75;&#x6e;&#100;&#x2e;&#100;&#101;">&#103;&#114;&#x6f;&#115;&#x73;&#x6a;&#111;&#x68;&#64;&#x6c;&#105;&#110;&#x75;&#x73;&#46;&#x69;&#110;&#102;&#x6f;&#x72;&#x6d;&#x61;&#x74;&#x69;&#x6b;&#x2e;&#x75;&#110;&#x69;&#45;&#100;&#111;&#x72;&#116;&#109;&#x75;&#x6e;&#x64;&#46;&#x64;&#x65;</a>). They
+from Kai Grossjohann <a href="&#109;&#97;&#105;&#108;&#x74;&#x6f;&#x3a;&#103;&#114;&#x6f;&#115;&#115;&#x6a;&#x6f;&#x68;&#64;&#x6c;&#105;&#x6e;&#x75;&#115;&#x2e;&#x69;&#x6e;&#102;&#x6f;&#x72;&#x6d;&#97;&#116;&#x69;&#107;&#x2e;&#x75;&#x6e;&#x69;&#45;&#x64;&#x6f;&#x72;&#116;&#x6d;&#117;&#110;&#100;&#x2e;&#100;&#x65;">&#103;&#114;&#111;&#115;&#115;&#x6a;&#x6f;&#104;&#64;&#108;&#x69;&#110;&#x75;&#115;&#46;&#x69;&#x6e;&#102;&#111;&#114;&#109;&#x61;&#116;&#x69;&#107;&#x2e;&#117;&#x6e;&#x69;&#45;&#x64;&#111;&#114;&#x74;&#x6d;&#x75;&#110;&#x64;&#x2e;&#x64;&#x65;</a>). They
 take one argument that is a workspace name. When applied to a window,
 they add to, remove from, or toggle the occupation of this window in
 this workspace.</p></li>
-<li><p>AlwaysOnTop (from Stefan Monnier <a href="&#x6d;&#x61;&#105;&#108;&#116;&#x6f;&#x3a;&#x6d;&#x6f;&#x6e;&#110;&#105;&#x65;&#114;&#x40;&#100;&#x69;&#46;&#x65;&#x70;&#x66;&#x6c;&#46;&#x63;&#x68;">&#109;&#x6f;&#110;&#110;&#x69;&#x65;&#x72;&#x40;&#100;&#x69;&#x2e;&#101;&#x70;&#x66;&#108;&#46;&#99;&#x68;</a>). Accept a list
+<li><p>AlwaysOnTop (from Stefan Monnier <a href="&#109;&#97;&#105;&#x6c;&#116;&#111;&#x3a;&#x6d;&#111;&#110;&#x6e;&#105;&#101;&#114;&#64;&#x64;&#105;&#x2e;&#x65;&#112;&#x66;&#108;&#x2e;&#99;&#x68;">&#109;&#x6f;&#110;&#x6e;&#x69;&#101;&#x72;&#x40;&#x64;&#105;&#x2e;&#x65;&#x70;&#102;&#x6c;&#x2e;&#99;&#x68;</a>). Accept a list
 of windows as argument. Ctwm will do it&#8217;s best to keep these windows
 on top of the screen. Not perfect.</p></li>
 <li><p>Some moving stuff.</p>
@@ -1294,12 +1366,12 @@ following syntax :</p>
 </code></pre>
 
 <p>The old syntax is of course accepted.
-Patch from Stefan Monnier <a href="&#109;&#x61;&#105;&#108;&#x74;&#111;&#58;&#83;&#116;&#101;&#102;&#x61;&#110;&#x5f;&#x4d;&#x6f;&#110;&#x6e;&#x69;&#x65;&#x72;&#x40;&#78;&#73;&#65;&#71;&#65;&#x52;&#65;&#46;&#x4e;&#69;&#67;&#x54;&#x41;&#x52;&#x2e;&#x43;&#x53;&#x2e;&#x43;&#x4d;&#85;&#46;&#69;&#x44;&#85;">&#83;&#116;&#101;&#102;&#97;&#x6e;&#95;&#77;&#111;&#110;&#x6e;&#x69;&#101;&#114;&#x40;&#x4e;&#73;&#65;&#71;&#65;&#x52;&#x41;&#x2e;&#78;&#x45;&#x43;&#x54;&#65;&#x52;&#x2e;&#x43;&#x53;&#46;&#x43;&#77;&#85;&#46;&#x45;&#x44;&#x55;</a>.</p></li>
+Patch from Stefan Monnier <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#x3a;&#83;&#x74;&#x65;&#102;&#97;&#110;&#x5f;&#77;&#x6f;&#x6e;&#110;&#105;&#101;&#114;&#x40;&#78;&#73;&#65;&#71;&#x41;&#82;&#x41;&#x2e;&#78;&#69;&#67;&#84;&#65;&#82;&#46;&#67;&#83;&#46;&#67;&#77;&#85;&#46;&#69;&#68;&#x55;">&#83;&#x74;&#101;&#x66;&#x61;&#110;&#x5f;&#77;&#111;&#110;&#x6e;&#x69;&#101;&#114;&#x40;&#78;&#x49;&#65;&#x47;&#65;&#82;&#x41;&#46;&#78;&#69;&#x43;&#84;&#65;&#82;&#46;&#x43;&#83;&#x2e;&#x43;&#x4d;&#x55;&#x2e;&#x45;&#x44;&#x55;</a>.</p></li>
 <li><p>A lot of new animated title buttons : <code>%xpm:menu-up</code>, <code>%xpm:menu-down</code>,
 <code>%xpm:resize-out-top</code>, <code>%xpm:resize-in-top</code>, <code>%xpm:resize-out-bot</code>,
 <code>%xpm:resize-in-bot</code>, <code>%xpm:maze-out</code>, <code>%xpm:maze-in</code>, <code>%xpm:zoom-out</code>,
 <code>%xpm:zoom-in</code> and <code>%xpm:zoom-inout</code>. From Stefan Monnier
-<a href="&#109;&#x61;&#x69;&#x6c;&#x74;&#x6f;&#58;&#x53;&#116;&#101;&#x66;&#x61;&#x6e;&#x5f;&#x4d;&#111;&#x6e;&#x6e;&#105;&#101;&#114;&#x40;&#x4e;&#73;&#x41;&#x47;&#x41;&#82;&#x41;&#46;&#x4e;&#x45;&#x43;&#x54;&#65;&#x52;&#x2e;&#67;&#x53;&#46;&#67;&#77;&#85;&#46;&#x45;&#68;&#x55;">&#83;&#116;&#101;&#102;&#97;&#110;&#x5f;&#77;&#x6f;&#x6e;&#110;&#105;&#101;&#114;&#x40;&#x4e;&#73;&#65;&#71;&#x41;&#82;&#x41;&#46;&#78;&#x45;&#x43;&#84;&#65;&#82;&#46;&#x43;&#83;&#46;&#67;&#x4d;&#x55;&#x2e;&#69;&#x44;&#85;</a>.</p></li>
+<a href="&#109;&#97;&#x69;&#108;&#x74;&#111;&#58;&#x53;&#x74;&#101;&#102;&#97;&#110;&#95;&#x4d;&#111;&#110;&#110;&#105;&#101;&#114;&#x40;&#x4e;&#x49;&#65;&#x47;&#65;&#82;&#65;&#x2e;&#78;&#x45;&#x43;&#x54;&#x41;&#82;&#46;&#67;&#83;&#46;&#x43;&#x4d;&#85;&#46;&#69;&#x44;&#85;">&#83;&#116;&#101;&#102;&#97;&#110;&#95;&#x4d;&#111;&#x6e;&#110;&#105;&#101;&#114;&#x40;&#x4e;&#x49;&#65;&#71;&#65;&#x52;&#65;&#x2e;&#x4e;&#x45;&#x43;&#x54;&#65;&#82;&#46;&#67;&#83;&#x2e;&#67;&#77;&#x55;&#x2e;&#69;&#x44;&#85;</a>.</p></li>
 <li><p>2 new builtin menus: TwmAllWindows and TwmWorkspaces. Guess what they
 do.</p></li>
 <li><p>You can now bind menus to keys. When a menu is visible, you can
@@ -1310,7 +1382,7 @@ one if several entries match). If the first letter is ~ then
 Meta-the-second-letter activates it, if this first letter is ^ then
 Control-the-second-letter activates it, and if this first letter is space,
 then the second letter activates it.</p></li>
-<li><p>Support for VMS. Patch from Peter Chang <a href="&#x6d;&#x61;&#x69;&#x6c;&#116;&#111;&#x3a;&#x70;&#101;&#x74;&#x65;&#114;&#99;&#64;&#x76;&#x32;&#x2e;&#112;&#104;&#46;&#x6d;&#x61;&#x6e;&#x2e;&#x61;&#99;&#46;&#x75;&#x6b;">&#112;&#x65;&#x74;&#x65;&#114;&#x63;&#x40;&#x76;&#x32;&#46;&#x70;&#104;&#46;&#109;&#97;&#110;&#x2e;&#x61;&#x63;&#46;&#117;&#x6b;</a>.
+<li><p>Support for VMS. Patch from Peter Chang <a href="&#109;&#97;&#x69;&#x6c;&#x74;&#x6f;&#58;&#112;&#101;&#x74;&#x65;&#114;&#x63;&#x40;&#118;&#x32;&#x2e;&#x70;&#x68;&#x2e;&#x6d;&#x61;&#x6e;&#x2e;&#x61;&#x63;&#x2e;&#117;&#x6b;">&#x70;&#101;&#116;&#x65;&#114;&#x63;&#x40;&#x76;&#x32;&#46;&#x70;&#x68;&#46;&#x6d;&#x61;&#x6e;&#x2e;&#97;&#99;&#x2e;&#117;&#x6b;</a>.
 Completely untested. If you have problems to build on VMS ask
 Peter Chang.</p></li>
 <li><p>New keyword: <code>MoveOffResistance</code>. Idea borrowed to fvwm. If you set
@@ -1323,7 +1395,7 @@ dontmoveoff. A negative value puts you back into &#8220;never moveoff&#8221; mod
 <li><p>The files <code>background[1-7].xpm</code> and <code>background9.xpm</code> have been
 removed from the distribution. Someone tells me that they are
 copyrighted. I tried to contact him in order to join his copyright,
-but his mail address is invalid. <a href="&#109;&#97;&#x69;&#x6c;&#x74;&#111;&#x3a;&#100;&#x65;&#x73;&#107;&#x74;&#x6f;&#x70;&#45;&#x74;&#x65;&#x78;&#116;&#117;&#x72;&#101;&#x73;&#x40;&#x61;&#x76;&#101;&#x72;&#x6e;&#x75;&#115;&#x2e;&#99;&#111;&#109;">&#x64;&#101;&#x73;&#107;&#116;&#111;&#112;&#45;&#x74;&#x65;&#120;&#116;&#117;&#x72;&#101;&#x73;&#64;&#97;&#x76;&#x65;&#114;&#110;&#117;&#115;&#x2e;&#99;&#x6f;&#109;</a>.
+but his mail address is invalid. <a href="&#x6d;&#97;&#105;&#x6c;&#116;&#x6f;&#x3a;&#100;&#101;&#x73;&#107;&#116;&#111;&#112;&#45;&#x74;&#101;&#x78;&#x74;&#117;&#x72;&#101;&#115;&#x40;&#97;&#x76;&#x65;&#114;&#110;&#117;&#x73;&#x2e;&#x63;&#111;&#109;">&#x64;&#101;&#x73;&#107;&#x74;&#111;&#112;&#45;&#116;&#101;&#120;&#116;&#x75;&#x72;&#x65;&#x73;&#x40;&#97;&#x76;&#x65;&#x72;&#110;&#117;&#115;&#46;&#99;&#x6f;&#109;</a>.
 Most of these backgrounds and much more can be obtained in the AIcons
 package on ftp.x.org. Particularly in cl-bgnd/Textures: bg_blu.gif,
 concrete.gif, marble1.gif, sharks.gif bg_grn.gif, granite_dark.gif,
@@ -1373,7 +1445,7 @@ executed whenever the corresponding event occurs. Useful for sounds :</p>
 icon title (if any).</p></li>
 <li><p>flex is now supported.</p></li>
 <li><p>The IconRegion keyword now support an optionnal winlist argument.
-Thanks to Mike Hoswell <a href="&#x6d;&#97;&#x69;&#x6c;&#116;&#x6f;&#58;&#104;&#x6f;&#x73;&#x77;&#101;&#x6c;&#x6c;&#x40;&#x6e;&#99;&#97;&#114;&#x2e;&#117;&#99;&#x61;&#x72;&#46;&#101;&#x64;&#117;">&#104;&#111;&#115;&#x77;&#101;&#x6c;&#x6c;&#64;&#x6e;&#99;&#x61;&#114;&#x2e;&#117;&#x63;&#97;&#x72;&#x2e;&#101;&#x64;&#x75;</a> for adding this.</p></li>
+Thanks to Mike Hoswell <a href="&#x6d;&#97;&#x69;&#108;&#116;&#x6f;&#58;&#104;&#x6f;&#x73;&#x77;&#x65;&#x6c;&#x6c;&#64;&#110;&#x63;&#x61;&#114;&#46;&#117;&#99;&#x61;&#114;&#46;&#101;&#x64;&#117;">&#x68;&#111;&#x73;&#119;&#101;&#x6c;&#108;&#64;&#110;&#99;&#97;&#x72;&#46;&#117;&#x63;&#97;&#x72;&#46;&#x65;&#x64;&#x75;</a> for adding this.</p></li>
 <li><p>f.separator now works (does something) with 3D menus.</p></li>
 <li><p>The format xwd is now accepted for images (icons, background, &#8230;). You
 have to prefix the image file name with xwd: to use this format.
@@ -1393,7 +1465,7 @@ mine, and I don&#8217;t use it, so don&#8217;t expect a good support if you have
 problems with it.</p></li>
 <li><p>A new keyword : NoBorder with a window list argument. These windows
 won&#8217;t have borders. Thanks to J.P. Albers van der Linden
-<a href="&#x6d;&#97;&#x69;&#x6c;&#x74;&#111;&#x3a;&#x61;&#108;&#x62;&#101;&#114;&#115;&#64;&#112;&#97;&#x73;&#105;&#x63;&#x68;&#118;&#97;&#46;&#x73;&#101;&#x72;&#x69;&#103;&#97;&#116;&#101;&#x2e;&#112;&#104;&#105;&#108;&#x69;&#112;&#x73;&#x2e;&#110;&#108;">&#97;&#108;&#98;&#101;&#114;&#115;&#64;&#112;&#97;&#115;&#105;&#99;&#104;&#118;&#x61;&#46;&#x73;&#101;&#x72;&#x69;&#103;&#x61;&#116;&#101;&#46;&#x70;&#x68;&#105;&#108;&#x69;&#112;&#x73;&#46;&#x6e;&#108;</a> for this patch.</p></li>
+<a href="&#109;&#97;&#x69;&#108;&#x74;&#x6f;&#58;&#97;&#108;&#98;&#x65;&#x72;&#x73;&#x40;&#112;&#97;&#115;&#x69;&#x63;&#x68;&#118;&#97;&#x2e;&#115;&#x65;&#x72;&#x69;&#x67;&#x61;&#x74;&#x65;&#x2e;&#x70;&#104;&#x69;&#x6c;&#105;&#112;&#x73;&#x2e;&#110;&#x6c;">&#x61;&#x6c;&#98;&#101;&#x72;&#115;&#x40;&#x70;&#x61;&#115;&#105;&#x63;&#104;&#118;&#x61;&#46;&#x73;&#x65;&#114;&#x69;&#x67;&#x61;&#116;&#x65;&#x2e;&#112;&#x68;&#x69;&#x6c;&#x69;&#x70;&#115;&#x2e;&#110;&#x6c;</a> for this patch.</p></li>
 <li><p>Ctwm has a new option selectable with the flag <code>-w</code>, if used, ctwm
 will not take over the whole screen(s), instead it will create a new
 window and manage it. The <code>-w</code> has an optional argument which is a
@@ -1418,7 +1490,7 @@ edge of the window where the title is missing does not get a 3d
 border.</p></li>
 <li><p>Now, WindowRing can be specified without argument, in this case all
 the windows are in the ring. (Alec Wolman
-<a href="&#109;&#x61;&#105;&#108;&#116;&#x6f;&#58;&#119;&#111;&#108;&#x6d;&#97;&#x6e;&#x40;&#x62;&#x6c;&#x75;&#x65;&#x2e;&#x63;&#115;&#46;&#x77;&#97;&#x73;&#104;&#105;&#x6e;&#x67;&#116;&#111;&#110;&#46;&#101;&#x64;&#117;">&#119;&#111;&#108;&#109;&#97;&#x6e;&#x40;&#x62;&#108;&#x75;&#101;&#46;&#99;&#x73;&#46;&#x77;&#x61;&#x73;&#x68;&#105;&#110;&#103;&#116;&#111;&#x6e;&#x2e;&#101;&#100;&#117;</a>)</p></li>
+<a href="&#x6d;&#x61;&#105;&#x6c;&#116;&#x6f;&#58;&#x77;&#x6f;&#x6c;&#x6d;&#97;&#x6e;&#x40;&#98;&#108;&#x75;&#x65;&#46;&#99;&#x73;&#x2e;&#119;&#97;&#115;&#x68;&#x69;&#110;&#103;&#116;&#x6f;&#110;&#46;&#101;&#100;&#117;">&#x77;&#x6f;&#x6c;&#x6d;&#97;&#x6e;&#64;&#98;&#108;&#x75;&#101;&#46;&#99;&#x73;&#46;&#x77;&#x61;&#x73;&#x68;&#x69;&#110;&#103;&#x74;&#111;&#110;&#x2e;&#101;&#x64;&#x75;</a>)</p></li>
 <li><p>New keyword: WarpRingOnScreen, if present, tells ctwm that f.warpring
 should warp pointer only to windows visible in the current workspace.</p></li>
 </ol>
@@ -1485,7 +1557,7 @@ it will be displayed very quickly.</p></li>
 a line separator between the previous and the following entry. The
 name selector part in the menu is not used. f.separator works only
 with conventionnal menus, not with 3D menus.</p></li>
-<li><p>Thanks to <a href="&#x6d;&#97;&#105;&#108;&#116;&#111;&#58;&#98;&#114;&#x65;&#116;&#x40;&#101;&#115;&#115;&#101;&#x78;&#x2e;&#x61;&#99;&#46;&#117;&#x6b;">&#98;&#x72;&#x65;&#x74;&#x40;&#x65;&#115;&#115;&#101;&#120;&#46;&#x61;&#99;&#46;&#x75;&#x6b;</a>, the man page is integrated with the
+<li><p>Thanks to <a href="&#109;&#x61;&#x69;&#108;&#116;&#x6f;&#x3a;&#98;&#x72;&#x65;&#x74;&#64;&#101;&#x73;&#x73;&#x65;&#120;&#x2e;&#x61;&#99;&#x2e;&#x75;&#x6b;">&#98;&#114;&#x65;&#116;&#64;&#x65;&#x73;&#115;&#101;&#x78;&#x2e;&#97;&#x63;&#46;&#x75;&#107;</a>, the man page is integrated with the
 original twm one, and is of a much better quality.</p></li>
 <li><p>While moving a window, the position is displayed in a similar way as
 the size when resizing.</p></li>
@@ -1813,8 +1885,8 @@ the map window on a per-client basis.</p></li>
 <li><p>f.togglestate: You can guess.</p></li>
 </ul></li>
 </ul></li>
-<li><p>AutoRaise with RaiseDelay. Thanks to Johan Vromans <a href="&#109;&#x61;&#105;&#108;&#116;&#x6f;&#x3a;&#x6a;&#x76;&#64;&#109;&#104;&#x2e;&#x6e;&#108;">&#x6a;&#x76;&#64;&#x6d;&#x68;&#x2e;&#x6e;&#x6c;</a> who
-gave me this patch. I think Warren Jessop <a href="&#x6d;&#x61;&#x69;&#x6c;&#x74;&#x6f;&#x3a;&#119;&#x68;&#x6a;&#64;&#99;&#x73;&#46;&#x77;&#x61;&#x73;&#x68;&#105;&#x6e;&#x67;&#116;&#x6f;&#x6e;&#x2e;&#x65;&#100;&#117;">&#x77;&#104;&#x6a;&#x40;&#99;&#115;&#x2e;&#119;&#x61;&#x73;&#104;&#105;&#x6e;&#103;&#116;&#111;&#110;&#46;&#x65;&#100;&#x75;</a>
+<li><p>AutoRaise with RaiseDelay. Thanks to Johan Vromans <a href="&#x6d;&#97;&#105;&#x6c;&#116;&#111;&#x3a;&#x6a;&#118;&#64;&#x6d;&#104;&#x2e;&#x6e;&#x6c;">&#106;&#x76;&#64;&#109;&#104;&#46;&#110;&#108;</a> who
+gave me this patch. I think Warren Jessop <a href="&#109;&#x61;&#105;&#x6c;&#x74;&#111;&#x3a;&#x77;&#x68;&#106;&#64;&#99;&#x73;&#46;&#119;&#97;&#x73;&#104;&#x69;&#x6e;&#x67;&#116;&#111;&#110;&#x2e;&#101;&#x64;&#x75;">&#119;&#104;&#x6a;&#64;&#x63;&#115;&#46;&#x77;&#97;&#115;&#104;&#x69;&#x6e;&#103;&#116;&#x6f;&#110;&#x2e;&#x65;&#100;&#117;</a>
 wrote it for twm.</p></li>
 </ol>
 
diff --git a/CHANGES.md b/CHANGES.md
index b6ad893..1e34101 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,86 @@
 # CTWM Change History
 
 
+## 4.1.0  (2023-03-26)
+
+### Backward-Incompatible Changes And Removed Features
+
+1. Support for `VirtualScreens` has been removed.  This was an early
+   attempt to allow some manual configuration of multiple monitors, but
+   carried with it a lot of caveats and strange behaviors.  The current
+   automatic RANDR and manual `MonitorLayout` features are a replacement
+   for anywhere this ever really worked.
+
+1. `ctwm`'s captive mode support has been removed.  This includes the
+   `--window` and `--name` command line arguments, and the
+   `f.adoptwindow` and `f.hypermove` functions.  Be sure to remove any
+   references to those functions from your config file.
+
+1. Support for `WindowBox` has been removed, along with the
+   `f.fittocontent` function related to it.
+
+1. The minimum cmake version has been bumped to 3.6.  This is available
+   in standard packages back to CentOS 6, and we appear to have actually
+   been using syntax requiring 3.5 for a while unknowingly anyway.
+
+### New Features
+
+1. Support for understanding multi-monitor layouts as something other
+   than a single giant rectangle added.  The RANDR X extension is used
+   for determining how your monitors are laid out.  The various
+   `f.\*zoom` functions now zoom on the monitor the window is currently
+   on, and new `f.x\*zoom` functions are added to zoom across your entire
+   display.  Various internal geometries can be specified
+   RANDR-output-relative; see doc of _e.g._ `IconManagers`.  Contributed
+   by Maxime Soulé <<btik-ctwm@scoubidou.com>>.
+
+1. Added `MonitorLayout {}` config var for overriding the layout of
+   multiple monitors.  In normal cases with multiple monitors and a
+   modern X server, this is unnecessary.  It's useful if the X server
+   doesn't support RANDR (_e.g._, older servers), or if the info it
+   provides is wrong (_e.g._, multi-display simulation with Xephyr), or
+   if you just prefer to specify things differently than they would
+   otherwise be (_e.g._, treat an ultra-wide display as 2 separate
+   monitors).
+
+1. The EWMH `_NET_FRAME_EXTENTS` property is now set on windows when we
+   take control of them.  This should fix clients mispositioning other
+   windows on top of themselves; visible with Firefox's form autofilling
+   and context menus.  Contributed by Maxime Soulé
+   <<btik-ctwm@scoubidou.com>>.
+
+### Bugfixes
+
+1. When restarting ctwm, the icon managers for the current workspace will
+   now initially show up, rather than those for first WS.
+
+1. When restarting ctwm, the stacking order of windows is now preserved.
+
+1. Running `--cfgchk` without an available X server will now work.  Some
+   errors may only be discovered when it can talk to X (things relating
+   to colors are a likely suspect).  Checking configs for multi-Screen
+   setups will now properly check all of them as well.
+
+1. Fix display of combined modifiers in TwmKeys menu.  Contributed by
+   Maxime Soulé <<btik-ctwm@scoubidou.com>>.
+
+1. Fix window icon name spilling out into the border of icon manager
+   entries.  Contributed by Carl Svensson <<ctwm@datagubbe.se>>.
+
+1. Fix minor mis-sizing and postitioning of squeezed titlebar when window
+   is squeezed away.  Contributed by Maxime Soulé
+   <<btik-ctwm@scoubidou.com>>.
+
+1. Fix window placement when `DontMoveOff` is enabled without 3D borders.
+   Contributed by Maxime Soulé <<btik-ctwm@scoubidou.com>>.
+
+1. When window titles overflow the available space, always treat them as
+   left-justified, to avoid bad behavior of other justifications and
+   provide the best available behavior. Found by Carl Svensson
+   <<ctwm@datagubbe.se>>.
+
+
+
 ## 4.0.3  (2019-07-21)
 
 ### Bugfixes
@@ -614,7 +694,7 @@ to work.
 1. Workspace context (bkctwmws.patch)
 
     Makes it possible to bind keys specific to the workspace manager
-    (by Bj&ouml;rn Knutsson). Use the event context "workspace" for this.
+    (by Björn Knutsson). Use the event context "workspace" for this.
 
 1. New keyword : AlwaysSqueezeToGravity
 
@@ -687,7 +767,7 @@ to work.
 
     This was contributed by Matthew D. Fuller.
 
-1. `DontMoveOff` patch (by Bj&ouml;rn Knutsson)
+1. `DontMoveOff` patch (by Björn Knutsson)
 
     Change the behavior of `DontMoveOff` / `MoveOffResistance` so that
     when you attempt to move a window off screen, it will not move at all
@@ -697,17 +777,17 @@ to work.
     DontMoveOff, but now with the ability to move a window off screen
     less that `MoveOffResistance` pixels.
 
-1. Random placement and DontMoveOff patch (by Bj&ouml;rn Knutsson, changed)
+1. Random placement and DontMoveOff patch (by Björn Knutsson, changed)
 
     When random placement was used, DontMoveOff wasn't honored.
     This behavior has now changed so a window will be kept within
     the screen when at all possible.  When the window is too
     large, it's top or left edge (or both) will be placed in
     coordinate 0.
-    This change differs a little bit from Bj&ouml;rns contribution by
+    This change differs a little bit from Björns contribution by
     not using rand() at all.
 
-1. `f.warpring` patch (by Bj&ouml;rn Knutsson)
+1. `f.warpring` patch (by Björn Knutsson)
 
     If `IconManagerFocus` is set, there's no reason why the icon
     manager should get enter and leave events.  This fixes some
@@ -716,13 +796,13 @@ to work.
 1. `f.movetoprevworkspace`,
     `f.movetonextworkspace`,
     `f.movetoprevworkspaceandfollow`,
-    `f.movetonextworkspaceandfollow` patch (by Daniel Holmstr&ouml;m)
+    `f.movetonextworkspaceandfollow` patch (by Daniel Holmström)
 
     Makes it possible to move a window to the previous or next
     workspace and, if you like, go to that workspace and focus
     the moved window.
 
-1. `f.fill` "vertical" patch (by Daniel Holmstr&ouml;m)
+1. `f.fill` "vertical" patch (by Daniel Holmström)
 
     Expands the window vertically without overlapping any other window,
     much like `{ f.fill "top" f.fill "bottom" }` but with the exception
@@ -730,13 +810,13 @@ to work.
     "zoomed" to `F_FULLZOOM`, so one can toggle between this size,
     original and maximized.
 
-1. `RESIZEKEEPSFOCUS` bugfix patch (by Daniel Holmstr&ouml;m)
+1. `RESIZEKEEPSFOCUS` bugfix patch (by Daniel Holmström)
 
     If a window is maximized with `togglemaximize` and then restored it
     might loose focus if the cursor is outside the restored window.  This
     hack puts the cursor at the left-top corner of the window.
 
-1. `f.zoom` bugfix patch (by Daniel Holmstr&ouml;m)
+1. `f.zoom` bugfix patch (by Daniel Holmström)
 
     `f.zoom` now doesn't move the window up (as it sometimes did before)
 
@@ -998,7 +1078,7 @@ to work.
     window to the specified geometry. The width and height are to be given
     in pixel, no base size or resize increment are used.
 
-1. AutoLower et `f.autolower`: from Kai Gro&szlig;johann
+1. AutoLower et `f.autolower`: from Kai Großjohann
     (Kai.Grossjohann@CS.Uni-Dortmund.DE). Same as autoraise but with lower.
 
 1. `WindowRingExclude`: Takes a window list as argument. All listed windows
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8e75072..1c5464b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,11 +1,10 @@
-cmake_minimum_required(VERSION 2.6)
-project(ctwm C)
+cmake_minimum_required(VERSION 3.6)
 
 # Figure our version
 file(READ "VERSION" vf_str)
 string(STRIP ${vf_str} vf_str)
-# Not currently using the split out variants
-if(0)
+# Use split variants for project() call.
+if(1)
 	set(version_file_re "([0-9]+)\\.([0-9]+)\\.([0-9]+)(.*)")
 	string(REGEX REPLACE ${version_file_re} "\\1" ctwm_version_major ${vf_str})
 	string(REGEX REPLACE ${version_file_re} "\\2" ctwm_version_minor ${vf_str})
@@ -16,6 +15,12 @@ else()
 	set(ctwm_version_str ${vf_str})
 endif()
 
+# Now we can delcade that
+project(ctwm
+	VERSION ${ctwm_version_major}.${ctwm_version_minor}.${ctwm_version_patch}
+	LANGUAGES C
+	)
+
 # Modules we'll need
 include(CheckIncludeFiles)
 include(CheckFunctionExists)
@@ -28,6 +33,10 @@ if(insrc)
 			"directly in the source tree.")
 endif(insrc)
 
+# Let cmake write out compile commands for external tools.  Requires
+# cmake 3.5+, but setting an unknown var won't hurt earlier versions.
+set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
+
 
 #
 # Most of our bits are broken out into smaller files in a local dir
@@ -124,14 +133,9 @@ include(ctags)
 # with all our contents except the trivial main() wrapper, then build
 # ctwm with that; this makes it easy to build other binaries (like tests)
 # with access to all our internal funcs.
-add_library(ctwmlib STATIC ${CTWMSRC})
+add_library(ctwmlib OBJECT ${CTWMSRC})
 add_executable(ctwm "ctwm_wrap.c")
-
-# Keep ctwmlib before the shared libs.  With some ld(1)'s and build
-# environments that set --as-needed, having it after means that ld will
-# have already discarded all the shared libs as "unused" before it gets
-# to all the actual ctwm objects that need the symbols.
-target_link_libraries(ctwm ctwmlib)
+target_sources(ctwm PUBLIC $<TARGET_OBJECTS:ctwmlib>)
 target_link_libraries(ctwm ${CTWMLIBS})
 
 # This doesn't really serve much purpose at the moment, so it's not even
@@ -160,10 +164,6 @@ include(mktar_support)
 # Pull in dtrace bits
 include(dtrace_support)
 
-# Let cmake write out compile commands for external tools.  Requires
-# cmake 3.5+, but setting an unknown var won't hurt earlier versions.
-set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
-
 # Include tests
 include(CTest)
 if(BUILD_TESTING)
diff --git a/COPYRIGHT b/COPYRIGHT
index 3971ea0..6d3888c 100644
--- a/COPYRIGHT
+++ b/COPYRIGHT
@@ -7,7 +7,7 @@ separate license (e.g., files in ext/).
 /*
  * [ ctwm ]
  *
- * Copyright 1992-2018 Claude Lecommandeur and ctwm contributors
+ * Copyright 1992-2023 Claude Lecommandeur and ctwm contributors
  *
  * Permission to use, copy, modify and distribute this software and its
  * documentation for any purpose is hereby granted without fee, provided
diff --git a/Makefile b/Makefile
index 832323d..fabb973 100644
--- a/Makefile
+++ b/Makefile
@@ -2,9 +2,10 @@
 all build ctwm man man-html man-all install clean: build/Makefile
 	( cd build && ${MAKE} ${@} )
 
+CMAKE?=cmake
 build/Makefile cmake: CMakeLists.txt
 	( mkdir -p build && cd build && \
-		cmake -DCMAKE_C_FLAGS:STRING="${CFLAGS}" ${CMAKE_EXTRAS} .. )
+		${CMAKE} -DCMAKE_C_FLAGS:STRING="${CFLAGS}" ${CMAKE_EXTRAS} .. )
 
 allclean distclean:
 	rm -rf build/*
@@ -22,13 +23,13 @@ man-pdf doxygen doxyclean tags: build/Makefile
 
 # Make sure everything's build before running tests
 .PHONY: test
-test:
+test: build/Makefile
 	( cd build && ${MAKE} test_bins )
 	( cd build && ${MAKE} CTEST_OUTPUT_ON_FAILURE=1 ${@} )
 
 # Reindent files
 indent:
-	astyle -n --options=tools/ctwm.astyle *.h *.c
+	astyle -n --options=tools/ctwm.astyle *.[ch] tests/*/*.[ch]
 
 
 # Build documentation files
@@ -83,7 +84,8 @@ ${GEN}/lex.c: ${GEN} lex.l build/MKTAR_GENFILES
 
 # Setup version file
 ${GEN}/version.c.in: ${GEN} version.c.in .bzr/checkout/dirstate
-	tools/rewrite_version_bzr.sh < version.c.in > ${GEN}/version.c.in
+	env BZR_CMD=brz tools/rewrite_version_bzr.sh < version.c.in \
+			> ${GEN}/version.c.in
 
 # Generate pregen'd manuals
 ${GEN}/ctwm.1: ${UMAN}/ctwm.1
diff --git a/README.html b/README.html
index 69c088d..3960da4 100644
--- a/README.html
+++ b/README.html
@@ -126,6 +126,11 @@ isn&#8217;t present.
 <dd>Build with sound support via librplay. <code>USE_SOUND</code> is a still
 valid but deprecated alias for this, and will give a warning.
 (<strong>OFF</strong> by default)</dd>
+
+<dt>USE_XRANDR</dt>
+<dd>Enables the use of multi-monitors of different sizes via
+libXrandr. Disable if libXrandr isn&#8217;t present or is older than 1.5.
+(<strong>ON</strong> by default)</dd>
 </dl>
 
 <p>Additional vars you might need to set:</p>
@@ -162,10 +167,10 @@ funny prompt&gt; make
 <p>ctwm requires various X11 libraries to be present. That list will
 generally include libX11, libXext, libXmu, libXt, libSM, and libICE.
 Depending on your configuration, you may require extra libs as discussed
-above (libXpm and libjpeg are included in the default config). If you&#8217;re
-on a system that separates header files etc. from the shared lib itself
-(many Linux dists do), you&#8217;ll probably need -devel or similarly named
-packages installed for each of them as well.</p>
+above (libXpm, libjpeg, and libXrandr are included in the default
+config). If you&#8217;re on a system that separates header files etc. from the
+shared lib itself (many Linux dists do), you&#8217;ll probably need -devel or
+similarly named packages installed for each of them as well.</p>
 
 <h2 id="installation">Installation</h2>
 
@@ -192,7 +197,7 @@ sending a mail with the subject &#8220;subscribe ctwm&#8221; to
 
 <h3 id="repository">Repository</h3>
 
-<p>ctwm development uses bazaar (see <a href="http://bazaar.canonical.com/">http://bazaar.canonical.com/</a>) for
+<p>ctwm development uses breezy (see <a href="https://www.breezy-vcs.org/">https://www.breezy-vcs.org/</a>) for
 version control. The code is available on launchpad as <code>lp:ctwm</code>. See
 <a href="https://launchpad.net/ctwm">https://launchpad.net/ctwm</a> for more details.</p>
 
diff --git a/README.md b/README.md
index 015c319..652d0d4 100644
--- a/README.md
+++ b/README.md
@@ -117,6 +117,11 @@ USE_RPLAY
         valid but deprecated alias for this, and will give a warning.
         (**OFF** by default)
 
+USE_XRANDR
+:       Enables the use of multi-monitors of different sizes via
+        libXrandr.  Disable if libXrandr isn't present or is older than 1.5.
+        (**ON** by default)
+
 
 Additional vars you might need to set:
 
@@ -148,10 +153,10 @@ directly:
 ctwm requires various X11 libraries to be present.  That list will
 generally include libX11, libXext, libXmu, libXt, libSM, and libICE.
 Depending on your configuration, you may require extra libs as discussed
-above (libXpm and libjpeg are included in the default config).  If you're
-on a system that separates header files etc. from the shared lib itself
-(many Linux dists do), you'll probably need -devel or similarly named
-packages installed for each of them as well.
+above (libXpm, libjpeg, and libXrandr are included in the default
+config).  If you're on a system that separates header files etc. from the
+shared lib itself (many Linux dists do), you'll probably need -devel or
+similarly named packages installed for each of them as well.
 
 
 
@@ -179,7 +184,7 @@ sending a mail with the subject "subscribe ctwm" to
 
 ### Repository
 
-ctwm development uses bazaar (see <http://bazaar.canonical.com/>) for
+ctwm development uses breezy (see <https://www.breezy-vcs.org/>) for
 version control.  The code is available on launchpad as `lp:ctwm`.  See
 <https://launchpad.net/ctwm> for more details.
 
diff --git a/VERSION b/VERSION
index c4e41f9..ee74734 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-4.0.3
+4.1.0
diff --git a/add_window.c b/add_window.c
index 7d3d219..369aa1e 100644
--- a/add_window.c
+++ b/add_window.c
@@ -34,7 +34,9 @@
 #include <X11/extensions/shape.h>
 
 #include "add_window.h"
+#ifdef CAPTIVE
 #include "captive.h"
+#endif
 #include "colormaps.h"
 #include "ctwm_atoms.h"
 #include "functions.h"
@@ -51,17 +53,25 @@
 #include "occupation.h"
 #include "otp.h"
 #include "parse.h"
+#include "r_area.h"
+#include "r_layout.h"
 #include "screen.h"
+#ifdef SESSION
 #include "session.h"
+#endif
 #include "util.h"
 #include "vscreen.h"
+#ifdef WINBOX
 #include "windowbox.h"
+#endif
 #include "win_decorations.h"
 #include "win_ops.h"
 #include "win_regions.h"
 #include "win_resize.h"
+#include "win_ring.h"
 #include "win_utils.h"
 #include "workspace_manager.h"
+#include "xparsegeometry.h"
 
 
 int AddingX;
@@ -71,8 +81,10 @@ unsigned int AddingH;
 
 static int PlaceX = -1;
 static int PlaceY = -1;
+#ifdef VSCREEN
 static void DealWithNonSensicalGeometries(Display *dpy, Window vroot,
                 TwmWindow *tmp_win);
+#endif
 
 char NoName[] = "Untitled"; /* name if no name is specified */
 bool resizeWhenAdd;
@@ -105,19 +117,24 @@ AddWindow(Window w, AWType wtype, IconMgr *iconp, VirtualScreen *vs)
 	int gravx, gravy;                   /* gravity signs for positioning */
 	int namelen;
 	int bw2;
+#ifdef SESSION
 	short restore_icon_x, restore_icon_y;
 	bool restore_iconified = false;
 	bool restore_icon_info_present = false;
+#endif
 	bool restoredFromPrevSession = false;
 	int saved_occupation = 0; /* <== [ Matthew McNeill Feb 1997 ] == */
 	bool random_placed = false;
+#ifdef WINBOX
 	WindowBox *winbox;
+#endif
 	Window vroot;
 
 #ifdef DEBUG
 	fprintf(stderr, "AddWindow: w = 0x%x\n", w);
 #endif
 
+#ifdef CAPTIVE
 	/*
 	 * Possibly this window should be in a captive sub-ctwm?  If so, we
 	 * shouldn't mess with it at all.
@@ -126,6 +143,7 @@ AddWindow(Window w, AWType wtype, IconMgr *iconp, VirtualScreen *vs)
 		/* XXX x-ref comment by SetNoRedirect() */
 		return (NULL);
 	}
+#endif
 
 
 	/*
@@ -151,7 +169,9 @@ AddWindow(Window w, AWType wtype, IconMgr *iconp, VirtualScreen *vs)
 	tmp_win->iconmgrp = iconp;
 	tmp_win->iswspmgr = (wtype == AWT_WORKSPACE_MANAGER);
 	tmp_win->isoccupy = (wtype == AWT_OCCUPY);
+#ifdef WINBOX
 	tmp_win->iswinbox = (wtype == AWT_WINDOWBOX);
+#endif
 	tmp_win->vs = vs;
 	tmp_win->parent_vs = vs;
 	tmp_win->savevs = NULL;
@@ -257,6 +277,7 @@ AddWindow(Window w, AWType wtype, IconMgr *iconp, VirtualScreen *vs)
 	}
 
 
+#ifdef SESSION
 	/*
 	 * Look up saved X Session info for the window if we have it.
 	 */
@@ -291,6 +312,7 @@ AddWindow(Window w, AWType wtype, IconMgr *iconp, VirtualScreen *vs)
 			}
 		}
 	}
+#endif
 
 
 	/*
@@ -321,6 +343,7 @@ AddWindow(Window w, AWType wtype, IconMgr *iconp, VirtualScreen *vs)
 		}
 	}
 
+#ifdef SESSION
 	/*
 	 * Override a few bits with saved stuff from previous session, if we
 	 * have it.
@@ -335,6 +358,7 @@ AddWindow(Window w, AWType wtype, IconMgr *iconp, VirtualScreen *vs)
 		tmp_win->wmhints->icon_y = restore_icon_y;
 		tmp_win->wmhints->flags |= IconPositionHint;
 	}
+#endif
 
 	/* Munge as necessary for other stuff */
 	munge_wmhints(tmp_win, tmp_win->wmhints);
@@ -437,20 +461,10 @@ AddWindow(Window w, AWType wtype, IconMgr *iconp, VirtualScreen *vs)
 	                 && EwmhOnWindowRing(tmp_win)
 #endif /* EWMH */
 	                 && !CHKL(WindowRingExcludeL))) {
-		if(Scr->Ring) {
-			tmp_win->ring.next = Scr->Ring->ring.next;
-			if(Scr->Ring->ring.next->ring.prev) {
-				Scr->Ring->ring.next->ring.prev = tmp_win;
-			}
-			Scr->Ring->ring.next = tmp_win;
-			tmp_win->ring.prev = Scr->Ring;
-		}
-		else {
-			tmp_win->ring.next = tmp_win->ring.prev = Scr->Ring = tmp_win;
-		}
+		AddWindowToRing(tmp_win);
 	}
 	else {
-		tmp_win->ring.next = tmp_win->ring.prev = NULL;
+		InitWindowNotOnRing(tmp_win);
 	}
 
 
@@ -537,6 +551,7 @@ AddWindow(Window w, AWType wtype, IconMgr *iconp, VirtualScreen *vs)
 		 * - And specific NoTitle overrides MakeTitle.
 		 */
 		have_title = true;
+		ALLOW_DEAD_STORE(have_title);
 #ifdef EWMH
 		have_title = EwmhHasTitle(tmp_win);
 #endif /* EWMH */
@@ -575,6 +590,16 @@ AddWindow(Window w, AWType wtype, IconMgr *iconp, VirtualScreen *vs)
 	}
 
 
+#ifdef EWMH
+	/*
+	 * Now that we know the title_height and the frame border width, we
+	 * can set an EWMH property to tell the client how much we're adding
+	 * around them.
+	 */
+	EwmhSet_NET_FRAME_EXTENTS(tmp_win);
+#endif
+
+
 	/*
 	 * Need the GetWindowAttributes() call and setting ->old_bw and
 	 * ->frame_bw3D for some of the math in looking up the
@@ -660,8 +685,10 @@ AddWindow(Window w, AWType wtype, IconMgr *iconp, VirtualScreen *vs)
 	SetupOccupation(tmp_win, saved_occupation);
 
 
+#ifdef WINBOX
 	/* Does it go in a window box? */
 	winbox = findWindowBox(tmp_win);
+#endif
 
 
 	/*
@@ -703,9 +730,10 @@ AddWindow(Window w, AWType wtype, IconMgr *iconp, VirtualScreen *vs)
 	{
 		char *geom = LookInListWin(Scr->WindowGeometries, tmp_win);
 		if(geom) {
-			int mask = XParseGeometry(geom, &tmp_win->attr.x, &tmp_win->attr.y,
-			                          (unsigned int *) &tmp_win->attr.width,
-			                          (unsigned int *) &tmp_win->attr.height);
+			int mask = RLayoutXParseGeometry(Scr->Layout, geom,
+			                                 &tmp_win->attr.x, &tmp_win->attr.y,
+			                                 (unsigned int *) &tmp_win->attr.width,
+			                                 (unsigned int *) &tmp_win->attr.height);
 
 			if(mask & XNegative) {
 				tmp_win->attr.x += Scr->rootw - tmp_win->attr.width;
@@ -726,9 +754,11 @@ AddWindow(Window w, AWType wtype, IconMgr *iconp, VirtualScreen *vs)
 		vroot = Scr->Root;      /* never */
 		tmp_win->parent_vs = Scr->currentvs;
 	}
+#ifdef WINBOX
 	if(winbox) {
 		vroot = winbox->window;
 	}
+#endif
 
 
 	/*
@@ -842,7 +872,7 @@ AddWindow(Window w, AWType wtype, IconMgr *iconp, VirtualScreen *vs)
 				   pixels less wide than the screen. */
 				if((tmp_win->attr.x + tmp_win->attr.width)  > Scr->rootw) {
 					available = Scr->rootw - tmp_win->attr.width
-					            - 2 * (bw2 + tmp_win->frame_bw3D);
+					            - 2 * tmp_win->frame_bw3D - bw2;
 
 #ifdef DEBUG
 					fprintf(stderr, "DEBUG[DontMoveOff]: availableX: %d\n",
@@ -872,7 +902,8 @@ AddWindow(Window w, AWType wtype, IconMgr *iconp, VirtualScreen *vs)
 				   with the title height and the frame widths.  */
 				if((tmp_win->attr.y + tmp_win->attr.height)  > Scr->rooth) {
 					available = Scr->rooth - tmp_win->attr.height
-					            - tmp_win->title_height - 2 * (bw2 + tmp_win->frame_bw3D);
+					            - tmp_win->title_height
+					            - 2 * tmp_win->frame_bw3D - bw2;
 
 #ifdef DEBUG
 					fprintf(stderr, "DEBUG[DontMoveOff]: availableY: %d\n",
@@ -968,9 +999,11 @@ AddWindow(Window w, AWType wtype, IconMgr *iconp, VirtualScreen *vs)
 					}
 					firsttime = false;
 				}
+#ifdef WINBOX
 				if(winbox) {
 					vroot = winbox->window;
 				}
+#endif
 
 				/*
 				 * wait for buttons to come up; yuck
@@ -1000,15 +1033,16 @@ AddWindow(Window w, AWType wtype, IconMgr *iconp, VirtualScreen *vs)
 				               tmp_win->name, namelen,
 				               &ink_rect, &logical_rect);
 				width = SIZE_HINDENT + ink_rect.width;
-				height = logical_rect.height + SIZE_VINDENT * 2;
+				height = Scr->SizeFont.height + SIZE_VINDENT * 2;
 
 				XmbTextExtents(Scr->SizeFont.font_set,
 				               ": ", 2,  NULL, &logical_rect);
 				Scr->SizeStringOffset = width + logical_rect.width;
 			}
 
-			XResizeWindow(dpy, Scr->SizeWindow, Scr->SizeStringOffset +
-			              Scr->SizeStringWidth + SIZE_HINDENT, height);
+			MoveResizeSizeWindow(AddingX, AddingY,
+			                     Scr->SizeStringOffset + Scr->SizeStringWidth + SIZE_HINDENT,
+			                     height);
 			XMapRaised(dpy, Scr->SizeWindow);
 			InstallRootColormap();
 			FB(Scr->DefaultC.fore, Scr->DefaultC.back);
@@ -1017,9 +1051,11 @@ AddWindow(Window w, AWType wtype, IconMgr *iconp, VirtualScreen *vs)
 			                   SIZE_VINDENT + Scr->SizeFont.ascent,
 			                   tmp_win->name, namelen);
 
+#ifdef WINBOX
 			if(winbox) {
 				ConstrainedToWinBox(tmp_win, AddingX, AddingY, &AddingX, &AddingY);
 			}
+#endif
 
 			AddingW = tmp_win->attr.width + bw2 + 2 * tmp_win->frame_bw3D;
 			AddingH = tmp_win->attr.height + tmp_win->title_height +
@@ -1036,8 +1072,8 @@ AddWindow(Window w, AWType wtype, IconMgr *iconp, VirtualScreen *vs)
 			 * The TryTo*() and DoResize() calls below rely on having
 			 * frame_{width,height} set, so set them.
 			 */
-			tmp_win->frame_width  = AddingW;
-			tmp_win->frame_height = AddingH;
+			tmp_win->frame_width  = AddingW - bw2;
+			tmp_win->frame_height = AddingH - bw2;
 			/*SetFocus (NULL, CurrentTime);*/
 			while(1) {
 				if(Scr->OpenWindowTimeout) {
@@ -1119,8 +1155,9 @@ AddWindow(Window w, AWType wtype, IconMgr *iconp, VirtualScreen *vs)
 					               ": ", 2,  NULL, &logical_rect);
 					Scr->SizeStringOffset = width + logical_rect.width;
 
-					XResizeWindow(dpy, Scr->SizeWindow, Scr->SizeStringOffset +
-					              Scr->SizeStringWidth + SIZE_HINDENT, height);
+					MoveResizeSizeWindow(event.xbutton.x_root, event.xbutton.y_root,
+					                     Scr->SizeStringOffset + Scr->SizeStringWidth + SIZE_HINDENT,
+					                     height);
 
 					XmbDrawImageString(dpy, Scr->SizeWindow, Scr->SizeFont.font_set,
 					                   Scr->NormalGC, width,
@@ -1206,18 +1243,23 @@ AddWindow(Window w, AWType wtype, IconMgr *iconp, VirtualScreen *vs)
 					}
 				}
 				else if(event.xbutton.button == Button3) {
-					int maxw = Scr->rootw - Scr->BorderRight  - AddingX - bw2;
-					int maxh = Scr->rooth - Scr->BorderBottom - AddingY - bw2;
+					RArea area;
+					int max_bottom, max_right;
+
+					area = RAreaNew(AddingX, AddingY, AddingW, AddingH);
+
+					max_bottom = RLayoutFindMonitorBottomEdge(Scr->BorderedLayout, &area) - bw2;
+					max_right = RLayoutFindMonitorRightEdge(Scr->BorderedLayout, &area) - bw2;
 
 					/*
 					 * Make window go to bottom of screen, and clip to right edge.
 					 * This is useful when popping up large windows and fixed
 					 * column text windows.
 					 */
-					if(AddingW > maxw) {
-						AddingW = maxw;
+					if(AddingX + AddingW - 1 > max_right) {
+						AddingW = max_right - AddingX + 1;
 					}
-					AddingH = maxh;
+					AddingH = max_bottom - AddingY + 1;
 
 					ConstrainSize(tmp_win, &AddingW, &AddingH);   /* w/o borders */
 					AddingW += bw2;
@@ -1359,20 +1401,7 @@ AddWindow(Window w, AWType wtype, IconMgr *iconp, VirtualScreen *vs)
 	 */
 	if(XGetGeometry(dpy, tmp_win->w, &JunkRoot, &JunkX, &JunkY,
 	                &JunkWidth, &JunkHeight, &JunkBW, &JunkDepth) == 0) {
-		TwmWindow *prev = tmp_win->ring.prev, *next = tmp_win->ring.next;
-
-		if(prev) {
-			prev->ring.next = next;
-		}
-		if(next) {
-			next->ring.prev = prev;
-		}
-		if(Scr->Ring == tmp_win) {
-			Scr->Ring = (next != tmp_win ? next : NULL);
-		}
-		if(!Scr->Ring || Scr->RingLeader == tmp_win) {
-			Scr->RingLeader = Scr->Ring;
-		}
+		UnlinkWindowFromRing(tmp_win);
 
 		/* XXX Leaky as all hell */
 		free(tmp_win);
@@ -1433,16 +1462,59 @@ AddWindow(Window w, AWType wtype, IconMgr *iconp, VirtualScreen *vs)
 		}
 
 		/* No matter what, make sure SOME part of the window is on-screen */
-		if((tmp_win->frame_x > Scr->rootw) ||
-		                (tmp_win->frame_y > Scr->rooth) ||
-		                ((int)(tmp_win->frame_x + tmp_win->frame_width)  < 0) ||
-		                ((int)(tmp_win->frame_y + tmp_win->frame_height) < 0)) {
-			tmp_win->frame_x = 0;
-			tmp_win->frame_y = 0;
+		{
+			RArea area;
+			int min_x, min_y, max_bottom, max_right;
+			const RLayout *layout = Scr->BorderedLayout;
+
+			area = RAreaNew(tmp_win->frame_x, tmp_win->frame_y,
+			                (int)tmp_win->frame_width,
+			                (int)tmp_win->frame_height);
+
+#ifdef EWMH
+			// Hack: windows with EWMH struts defined are trying to
+			// reserve a bit of the screen for themselves.  We currently
+			// do that by hacking strut'ed space into the BorderedLayout,
+			// which is a bogus way of doing things.  But it also means
+			// that here we're forcing the windows to be outside their
+			// own struts, which is nonsensical.
+			//
+			// Hack around that by making strut'd windows just use
+			// Layout, rather than BorderedLayout.  This is Wrong(tm)
+			// because the whole point of BorderedLayout is space
+			// reservation by the user, which we'd now be ignoring.  Also
+			// just because a window has its own struts doesn't mean it
+			// should get to ignore everyone else's struts too. However,
+			// this is at least consistent with pre-4.1.0 behavior, so
+			// it's not a _new_ bug.  And forcing windows outside their
+			// own reservation is way stupider...
+			if(tmp_win->ewmhFlags & EWMH_HAS_STRUT) {
+				layout = Scr->Layout;
+			}
+#endif
+
+			RLayoutFindTopBottomEdges(layout, &area,
+			                          &min_y, &max_bottom);
+			RLayoutFindLeftRightEdges(layout, &area,
+			                          &min_x, &max_right);
+
+			// These conditions would only be true if the window was
+			// completely off-screen; in that case, the RLayout* calls
+			// above would have found the closest edges to move it to.
+			// We wind up sticking it in the top-left of the
+			// bottom-right-most monitor it would touch.
+			if(area.x > max_right || area.y > max_bottom ||
+			                area.x + area.width <= min_x ||
+			                area.y + area.height <= min_y) {
+				tmp_win->frame_x = min_x;
+				tmp_win->frame_y = min_y;
+			}
 		}
 
+#ifdef VSCREEN
 		/* May need adjusting for vscreens too */
 		DealWithNonSensicalGeometries(dpy, vroot, tmp_win);
+#endif
 
 
 		/*
@@ -1769,12 +1841,14 @@ AddWindow(Window w, AWType wtype, IconMgr *iconp, VirtualScreen *vs)
 	}
 
 
+#ifdef CAPTIVE
 	/*
 	 * If ths window being created is a new captive [sub-]ctwm, we setup
 	 * a property on it for unclear reasons.  x-ref comments on the
 	 * function.
 	 */
 	SetPropsIfCaptiveCtwm(tmp_win);
+#endif
 
 
 	/*
@@ -2014,6 +2088,7 @@ void GrabKeys(TwmWindow *tmp_win)
 #undef ungrabkey
 
 
+#ifdef VSCREEN
 /*
  * This is largely for Xinerama support with VirtualScreens.
  * In this case, windows may be on something other then the main screen
@@ -2072,3 +2147,4 @@ DealWithNonSensicalGeometries(Display *mydpy, Window vroot,
 	}
 
 }
+#endif // VSCREEN
diff --git a/animate.c b/animate.c
index b57988f..c54e57a 100644
--- a/animate.c
+++ b/animate.c
@@ -6,6 +6,7 @@
 #include "ctwm.h"
 
 #include <sys/time.h>
+#include <assert.h>
 #include <stdio.h>
 #include <string.h>
 
@@ -222,6 +223,10 @@ Animate(void)
 	}
 	MaybeAnimate |= AnimateRoot();
 	if(MaybeAnimate) {
+		// Impossible: scr==NULL means we had no valid screens, which
+		// means we'd'a bomed out WAY earlier than trying to animate
+		// something...
+		assert(scr != NULL);
 		Animating++;
 		send_clientmessage(scr->currentvs->wsw->w, XA_WM_END_OF_ANIMATION,
 		                   EventTime);
diff --git a/captive.h b/captive.h
index d0af7de..aaef7a8 100644
--- a/captive.h
+++ b/captive.h
@@ -5,6 +5,11 @@
 #ifndef _CTWM_CAPTIVE_H
 #define _CTWM_CAPTIVE_H
 
+// Guard to help catch non-ifdef'd references
+#ifndef CAPTIVE
+#error "You're unconditionally including captive.h!"
+#endif
+
 
 typedef struct CaptiveCTWM {
 	Window        root;
diff --git a/clargs.c b/clargs.c
index 4478576..040a269 100644
--- a/clargs.c
+++ b/clargs.c
@@ -36,9 +36,11 @@ ctwm_cl_args CLarg = {
 #else
 	.ShowWelcomeWindow  = true,
 #endif
+#ifdef CAPTIVE
 	.is_captive      = false,
 	.capwin          = (Window) 0,
 	.captivename     = NULL,
+#endif
 #ifdef USEM4
 	.KeepTmpFile     = false,
 	.keepM4_filename = NULL,
@@ -84,9 +86,11 @@ clargs_parse(int argc, char *argv[])
 
 		/* Misc control bits */
 		{ "display",   required_argument, NULL, 'd' },
+		{ "xrm",       required_argument, NULL, 0 },
+#ifdef CAPTIVE
 		{ "window",    optional_argument, NULL, 'w' },
 		{ "name",      required_argument, NULL, 0 },
-		{ "xrm",       required_argument, NULL, 0 },
+#endif
 
 #ifdef EWMH
 		{ "replace",   no_argument,       NULL, 0 },
@@ -113,7 +117,10 @@ clargs_parse(int argc, char *argv[])
 	 * I assume '::' for optional args is portable; getopt_long(3)
 	 * doesn't describe it, but it's a GNU extension for getopt(3).
 	 */
-	const char *short_options = "vqWf:hd:w::"
+	const char *short_options = "vqWf:hd:"
+#ifdef CAPTIVE
+	                            "w::"
+#endif
 #ifdef USEM4
 	                            "kK:n"
 #endif
@@ -180,6 +187,7 @@ clargs_parse(int argc, char *argv[])
 			case 'd':
 				CLarg.display_name = optarg;
 				break;
+#ifdef CAPTIVE
 			case 'w':
 				CLarg.is_captive = true;
 				CLarg.MultiScreen = false;
@@ -188,6 +196,7 @@ clargs_parse(int argc, char *argv[])
 					/* Failure will just leave capwin as initialized */
 				}
 				break;
+#endif
 
 #ifdef USEM4
 			/* Args that only mean anything if we're built with m4 */
@@ -230,10 +239,12 @@ clargs_parse(int argc, char *argv[])
 #endif
 
 				/* Simple value-setting */
+#ifdef CAPTIVE
 				IFIS("name") {
 					CLarg.captivename = optarg;
 					break;
 				}
+#endif
 				IFIS("clientId") {
 					CLarg.client_id = optarg;
 					break;
@@ -320,12 +331,26 @@ clargs_check(void)
 	}
 #endif
 
+#ifdef CAPTIVE
 	/* If we're not captive, captivename is meaningless too */
 	if(CLarg.captivename && !CLarg.is_captive) {
 		fprintf(stderr, "--name is meaningless without --window.\n");
 		usage();
 	}
 
+	/*
+	 * Being captive and --cfgchk'ing is kinda meaningless.  There's no
+	 * reason to create a window just to destroy things, and it never
+	 * adds anything.  And it's one more way we're forcing changes on the
+	 * X side before we parse the actual config, so let's just disallow
+	 * it.
+	 */
+	if(CLarg.is_captive && CLarg.cfgchk) {
+		fprintf(stderr, "--window is incompatible with --cfgchk.\n");
+		usage();
+	}
+#endif
+
 	/* Guess that's it */
 	return;
 }
@@ -361,7 +386,9 @@ usage(void)
 	fprintf(stderr, "%*s[--version]  [--info]  [--nowelcome | -W]\n",
 	        llen, "");
 
+#ifdef CAPTIVE
 	fprintf(stderr, "%*s[(--window | -w) [win-id]]  [--name name]\n", llen, "");
+#endif
 
 	/* Semi-intentionally not documenting --clientId/--restore */
 
diff --git a/cmake_files/basic_vars.cmake b/cmake_files/basic_vars.cmake
index 5257708..98ca045 100644
--- a/cmake_files/basic_vars.cmake
+++ b/cmake_files/basic_vars.cmake
@@ -17,12 +17,13 @@ set(CTWMSRC
 	# Basic files  ##STDSRC-START
 	add_window.c
 	animate.c
-	captive.c
 	clargs.c
 	clicktofocus.c
 	colormaps.c
 	ctopts.c
 	ctwm_main.c
+	ctwm_shutdown.c
+	ctwm_takeover.c
 	cursor.c
 	drawing.c
 	event_core.c
@@ -30,7 +31,6 @@ set(CTWMSRC
 	event_names.c
 	event_utils.c
 	functions.c
-	functions_captive.c
 	functions_icmgr_wsmgr.c
 	functions_identify.c
 	functions_misc.c
@@ -55,7 +55,10 @@ set(CTWMSRC
 	parse.c
 	parse_be.c
 	parse_yacc.c
-	session.c
+	r_area.c
+	r_area_list.c
+	r_layout.c
+	signals.c
 	util.c
 	vscreen.c
 	win_decorations.c
@@ -64,11 +67,12 @@ set(CTWMSRC
 	win_ops.c
 	win_regions.c
 	win_resize.c
+	win_ring.c
 	win_utils.c
-	windowbox.c
 	workspace_config.c
 	workspace_manager.c
 	workspace_utils.c
+	xparsegeometry.c
 
 	# External libs
 	ext/repl_str.c
diff --git a/cmake_files/build_options.cmake b/cmake_files/build_options.cmake
index 259ebca..6fc229e 100644
--- a/cmake_files/build_options.cmake
+++ b/cmake_files/build_options.cmake
@@ -13,6 +13,14 @@ option(USE_M4     "Enable m4 support"                  ON )
 option(USE_RPLAY  "Enable librplay sound support"      OFF)
 option(USE_SREGEX "Use regex from libc"                ON )
 option(USE_EWMH   "Support some Extended Window Manager Hints"  ON )
+option(USE_XRANDR "Enable Xrandr support"              ON )
+
+# Temp and hidden-ish, to make it easier to deorbit all at once
+option(USE_CAPTIVE "Enable captive CTWM support" OFF )
+option(USE_VSCREEN "Enable VirtualScreens support" OFF )
+option(USE_WINBOX  "Enable WindowBox support" OFF )
+option(USE_SESSION "Enable XSMP support" ON )
+
 
 
 
@@ -120,3 +128,70 @@ if(USE_SREGEX)
 else()
 	message(FATAL_ERROR "USE_SREGEX=OFF no longer supported.")
 endif(USE_SREGEX)
+
+
+# Is Xrandr of a suitable version available?
+if(USE_XRANDR)
+	if(NOT X11_Xrandr_FOUND)
+		message(FATAL_ERROR "Couldn't find Xrandr libs")
+	endif(NOT X11_Xrandr_FOUND)
+
+	# We need XRRGetMonitors()
+	set(OLD_CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES})
+	set(OLD_CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
+	set(CMAKE_REQUIRED_INCLUDES  ${X11_Xrandr_INCLUDE_PATH})
+	set(CMAKE_REQUIRED_LIBRARIES ${X11_Xrandr_LIB})
+	check_symbol_exists(XRRGetMonitors "X11/extensions/Xrandr.h" HAVE_XRRGETMONITORS)
+	set(CMAKE_REQUIRED_INCLUDES ${OLD_CMAKE_REQUIRED_INCLUDES})
+	set(CMAKE_REQUIRED_LIBRARIES ${OLD_CMAKE_REQUIRED_LIBRARIES})
+
+	if(NOT HAVE_XRRGETMONITORS)
+	       message(FATAL_ERROR "Xrandr lib does not implement XRRGetMonitors, Xrandr 1.5 needed")
+	endif(NOT HAVE_XRRGETMONITORS)
+
+	# Got it
+	include_directories(${X11_Xrandr_INCLUDE_PATH})
+	list(APPEND CTWMLIBS ${X11_Xrandr_LIB})
+	list(APPEND CTWMSRC xrandr.c)
+	message(STATUS "Enabling Xrandr support: ${X11_Xrandr_LIB}")
+else()
+	message(STATUS "Disabling Xrandr support.")
+endif(USE_XRANDR)
+
+
+# Captive mode
+if(USE_CAPTIVE)
+	# This isn't going to be here long, you shouldn't be enabling it
+	# unless you're ready to argue on the mailing list to preserve it.
+	message(WARNING "Captive mode will not be supported in future versions")
+
+	# Some whole files are involved
+	list(APPEND CTWMSRC captive.c functions_captive.c)
+else()
+	# Nothing much...
+endif(USE_CAPTIVE)
+
+
+# VirtualScreens (going the heck away)
+if(USE_VSCREEN)
+	message(WARNING "VirtualScreens will not be supported in future versions")
+else()
+endif(USE_VSCREEN)
+
+
+# WindowBox's
+if(USE_WINBOX)
+	message(WARNING "WindowBox will not be supported in future versions")
+
+	list(APPEND CTWMSRC windowbox.c)
+else()
+endif(USE_WINBOX)
+
+
+# XSMP session manager support
+if(USE_SESSION)
+	#message(WARNING "XSMP will not be supported in future versions")
+
+	list(APPEND CTWMSRC session.c)
+else()
+endif(USE_SESSION)
diff --git a/cmake_files/check_funcs_etc.cmake b/cmake_files/check_funcs_etc.cmake
index 4f6463d..82f81a8 100644
--- a/cmake_files/check_funcs_etc.cmake
+++ b/cmake_files/check_funcs_etc.cmake
@@ -35,6 +35,10 @@ endif(NOT HAS_GETOPT_LONG)
 # least some versions of AIX, etc.  There's a version in openssh-portable
 # that I believe is pretty portable if we find a system we care about
 # lacking it and need to pull in a local version, but I don't expect to.
+#
+# n.b.; this slightly overlaps with earlier run code that sometimes needs
+# to crank on extra -D's to show this up in headers, but it differs a
+# little, so I won't bother collapsing them...
 check_function_exists(asprintf HAS_ASPRINTF)
 if(NOT HAS_ASPRINTF)
 	message(FATAL_ERROR "You don't seem to have asprintf(3).")
diff --git a/cmake_files/compiler_feature_checks.cmake b/cmake_files/compiler_feature_checks.cmake
index 3c7a27d..915b253 100644
--- a/cmake_files/compiler_feature_checks.cmake
+++ b/cmake_files/compiler_feature_checks.cmake
@@ -11,58 +11,119 @@
 # but is a sign that it's a compiler or platform we're moving further
 # away from.
 #
+# cmake 3.1+ has C_STANDARD and related vars that seem like they'd help
+# with this, but it's unclear that they actually solve the whole
+# problem...
+#
 # Known alternate spellings:
 #   -xc99  (Sun C 5.10 SunOS_i386, sunstudio12.1, OpenIndiana)
 include(CheckCCompilerFlag)
-set(c99_flag_options -std=c99 -xc99)
-foreach(_C99_FLAG ${c99_flag_options})
-	# CheckCCompilerFlag calls into CheckCSourceCompiles, which won't do
-	# anything if the result var is already set in the cache, so we have
-	# to unset it.  Otherwise, the second and later invocations don't
-	# actually do anything, and it'll never check any flag after the
-	# first.
-	unset(COMPILER_C99_FLAG CACHE)
-	check_c_compiler_flag(${_C99_FLAG} COMPILER_C99_FLAG)
-	if(COMPILER_C99_FLAG)
-		set(C99_FLAG ${_C99_FLAG})
-		break()
-	endif(COMPILER_C99_FLAG)
-endforeach(_C99_FLAG)
-if(C99_FLAG)
-	message(STATUS "Enabling C99 flag: ${C99_FLAG}")
-	add_definitions(${C99_FLAG})
-else()
-	message(WARNING "Compiler doesn't support known C99 flag, "
-			"building without it.")
-endif(C99_FLAG)
-
-
-# With -std=c99, GNU libc's includes get strict about what they export.
-# Particularly, a lot of POSIX stuff doesn't get defined unless we
-# explicitly ask for it.  Do our best at checking for what's there...
-check_include_files(features.h HAS_FEATURES_H)
-if(HAS_FEATURES_H)
-	# Check if including it with our args sets __USE_ISOC99; that's a
-	# sign it's what we're looking for here.
-	check_symbol_exists(__USE_ISOC99 features.h SETS_USE_ISOC99)
-	if(SETS_USE_ISOC99)
-		# OK, it does.  Assume that's a good enough test that things are
-		# acting as we expect.
-		set(GLIBC_FEATURE_FLAGS
-			"-D_POSIX_C_SOURCE=200809L"
-			"-D_XOPEN_SOURCE=700"
-			)
-		# asprintf() seems to need _GNU_SOURCE; no other way to expose it
-		# I can find.
-		check_symbol_exists(__GNU_LIBRARY__ features.h SETS_GNU_LIBRARY)
-		if(SETS_GNU_LIBRARY)
-			list(APPEND GLIBC_FEATURE_FLAGS "-D_GNU_SOURCE")
-		endif(SETS_GNU_LIBRARY)
-
-		message(STATUS "Enabling glibc feature macros: ${GLIBC_FEATURE_FLAGS}")
-		add_definitions(${GLIBC_FEATURE_FLAGS})
+set(MANUAL_C_STD_FLAG true)
+if(NOT MANUAL_C_STD_FLAG)
+	# This is the Better Way(tm), but is disabled by default because, as
+	# with the manual one below, the added arg doesn't apply in
+	# check_symbol_exists(), so it screws up the tests below.  I'm unable
+	# to find a way to get info from cmake about what arg it would add
+	# for the specified standard, so we can't pull it out manually to add
+	# like we do our found C99_FLAG below, so...
+	if(NOT "c_std_99" IN_LIST CMAKE_C_COMPILE_FEATURES)
+		message(WARNING "cmake doesn't know about c99 support for this "
+			"compiler, trying manual search...")
+		set(MANUAL_C_STD_FLAG true)
+	else()
+		message(STATUS "Enabling C99 mode")
+		set(CMAKE_C_EXTENSIONS false)
+		set(CMAKE_C_STANDARD 99)
+	endif()
+endif()
+if(MANUAL_C_STD_FLAG)
+	set(c99_flag_options -std=c99 -xc99)
+	foreach(_C99_FLAG ${c99_flag_options})
+		# CheckCCompilerFlag calls into CheckCSourceCompiles, which won't do
+		# anything if the result var is already set in the cache, so we have
+		# to unset it.  Otherwise, the second and later invocations don't
+		# actually do anything, and it'll never check any flag after the
+		# first.
+		unset(COMPILER_C99_FLAG CACHE)
+		check_c_compiler_flag(${_C99_FLAG} COMPILER_C99_FLAG)
+		if(COMPILER_C99_FLAG)
+			set(C99_FLAG ${_C99_FLAG})
+			break()
+		endif(COMPILER_C99_FLAG)
+	endforeach(_C99_FLAG)
+	if(C99_FLAG)
+		message(STATUS "Enabling C99 flag: ${C99_FLAG}")
+		add_definitions(${C99_FLAG})
+	else()
+		message(WARNING "Compiler doesn't support known C99 flag, "
+				"building without it.")
+	endif(C99_FLAG)
+endif()
+
+
+
+# With -std=c99, some systems/compilers/etc enable ANSI strictness, and
+# so don't export symbols from headers for e.g. a lot of POSIX etc stuff.
+# So we may need some extra -D's.
+# Some refs:
+# https://www.gnu.org/software/libc/manual/html_node/Feature-Test-Macros.html
+# https://illumos.org/man/5/standards
+
+# Somewhat irritatingly, check_symbol_exists() doesn't use the extra
+# flags we set above for the C standard.  So we have to add it manually,
+# and stash up the old C_R_D.  x-ref above.
+set(OLD_CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS})
+list(APPEND CMAKE_REQUIRED_DEFINITIONS ${C99_FLAG})
+
+# What might and will we add?
+set(AVAIL_SYM_FLAGS -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700
+	-D_GNU_SOURCE -D__EXTENSIONS__)
+set(EXTRA_SYM_FLAGS "")
+
+# Abstract
+macro(_check_func_flag FUNC HEADER)
+	unset(_HAS_FUNC CACHE)
+	check_symbol_exists(${FUNC} ${HEADER} _HAS_FUNC)
+	if(NOT _HAS_FUNC)
+		foreach(_SFLAG ${AVAIL_SYM_FLAGS})
+			unset(_HAS_FUNC CACHE)
+			list(APPEND CMAKE_REQUIRED_DEFINITIONS ${_SFLAG})
+			check_symbol_exists(${FUNC} ${HEADER} _HAS_FUNC)
+			list(REMOVE_ITEM CMAKE_REQUIRED_DEFINITIONS ${_SFLAG})
+			if(_HAS_FUNC)
+				message(STATUS "${FUNC}() needs ${_SFLAG}")
+				list(APPEND EXTRA_SYM_FLAGS ${_SFLAG})
+				break()
+			endif()
+		endforeach()
+		if(NOT _HAS_FUNC)
+			message(WARNING "Couldn't find def for ${FUNC}, not good...")
+		endif()
 	endif()
-endif(HAS_FEATURES_H)
+	unset(_HAS_FUNC CACHE)
+endmacro(_check_func_flag)
+
+# strdup is POSIX, so see if we have to ask for that.  Probably
+# _POSIX_C_SOURCE.
+_check_func_flag(strdup string.h)
+
+# isascii falls into XOPEN on glibc, POSIX on Illumos.
+_check_func_flag(isascii ctype.h)
+
+# asprintf() is even weirder.  glibc apparently usually uses _GNU_SOURCE,
+# Illumos has a pure __EXTENSIONS__
+_check_func_flag(asprintf stdio.h)
+
+if(EXTRA_SYM_FLAGS)
+	list(REMOVE_DUPLICATES EXTRA_SYM_FLAGS)
+	message(STATUS "Adding extra visibility flags: ${EXTRA_SYM_FLAGS}")
+	add_definitions(${EXTRA_SYM_FLAGS})
+endif()
+
+# And restore
+set(CMAKE_REQUIRED_DEFINITIONS ${OLD_CMAKE_REQUIRED_DEFINITIONS})
+
+
 
 
 # Some compilers (like Sun's) don't take -W flags for warnings.  Do a
diff --git a/cmake_files/do_install.cmake b/cmake_files/do_install.cmake
index 95b6f11..5045d35 100644
--- a/cmake_files/do_install.cmake
+++ b/cmake_files/do_install.cmake
@@ -31,9 +31,11 @@ endif(USE_XPM)
 # If we don't have the manpage, that's pretty exceptional, so give a
 # warning about it.
 if(NOT HAS_MAN)
-	# STRING(CONCAT x y z) could build this message, but requires cmake
-	# 3.0 which we aren't yet willing to require.
-	install(CODE "message(WARNING \"No manpage to install: recheck config if this is unexpected.\")")
+	string(CONCAT NOMAN
+		"message(WARNING \"No manpage to install: recheck config "
+		"if this is unexpected.\")"
+		)
+	install(CODE ${NOMAN})
 else()
 	install(FILES ${INSTMAN}
 		DESTINATION ${MAN1PATH}
diff --git a/cmake_files/gen_source_files.cmake b/cmake_files/gen_source_files.cmake
index 717ef44..91a7664 100644
--- a/cmake_files/gen_source_files.cmake
+++ b/cmake_files/gen_source_files.cmake
@@ -100,7 +100,7 @@ if(IS_BZR_CO AND HAS_BZR)
 	set(rw_ver_bzr "${TOOLS}/rewrite_version_bzr.sh")
 	add_custom_command(OUTPUT ${version_c}
 		DEPENDS ${version_c_in} ${BZR_DIRSTATE_FILE} ${rw_ver_bzr}
-		COMMAND ${rw_ver_bzr} < ${version_c_in} > ${version_c}
+		COMMAND env BZR_CMD=${BZR_CMD} ${rw_ver_bzr} < ${version_c_in} > ${version_c}
 		COMMENT "Generating version.c from current WT state."
 		WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
 	)
diff --git a/cmake_files/handle_manual.cmake b/cmake_files/handle_manual.cmake
index 8f76046..8a7cbb3 100644
--- a/cmake_files/handle_manual.cmake
+++ b/cmake_files/handle_manual.cmake
@@ -34,10 +34,8 @@ set(MANPDF   ${CMAKE_BINARY_DIR}/ctwm.1.pdf)
 # configure_file() for this, as it opens up too many chances for
 # something to accidentally get sub'd, since we assume people will write
 # pretty freeform in the manual.
-# \-escaped @ needed for pre-3.1 CMake compat and warning avoidance;
-# x-ref `cmake --help-policy CMP0053`
-set(MANSED_CMD sed -e \"s,\@ETCDIR@,${ETCDIR},\"
-	-e \"s,\@ctwm_version_str@,`head -1 ${CMAKE_SOURCE_DIR}/VERSION`,\")
+set(MANSED_CMD sed -e \"s,@ETCDIR@,${ETCDIR},\"
+	-e \"s,@ctwm_version_str@,`head -1 ${CMAKE_SOURCE_DIR}/VERSION`,\")
 
 # Pregen'd doc file paths we might have, in case we can't build them
 # ourselves.
diff --git a/cmake_files/setup_yacc.cmake b/cmake_files/setup_yacc.cmake
index 2c78654..75215b7 100644
--- a/cmake_files/setup_yacc.cmake
+++ b/cmake_files/setup_yacc.cmake
@@ -8,11 +8,8 @@
 # Setup flags, and have an escape to debug the parser, if that's ever
 # useful.
 #
-# Making this a list messes with BISON_TARGET() which requires a string
-# according to the docs (though only cmake 3.4 start complaining about
-# getting a list).  A string might be nicer, but we'd really need
-# string(CONCAT) for that, and x-ref in do_install.cmake for notes on
-# that.
+# YFLAGS being a list is the Right(tm) choice here, though it messes with
+# BISON_TARGET() below.
 set(YFLAGS -d -b gram)
 if(DO_DEBUGPARSER)
 	list(APPEND YFLAGS -t -v)
@@ -30,8 +27,15 @@ if(NOT FORCE_PREGEN_FILES)
 endif()
 
 if(BISON_FOUND)
-	# What a stupid way to spell 'stringify'...
-	string(REPLACE ";" " " _YFSTR "${YFLAGS}")
+	# BISON_TARGET requires a string, not a list, for COMPILE_FLAGS.
+	# list(JOIN) would be the proper solution here, but requires cmake
+	# 3.12.
+	if(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 3.12)
+		list(JOIN YFLAGS " " _YFSTR)
+	else()
+		# So until then, this is our stupid stringify hack...
+		string(REPLACE ";" " " _YFSTR "${YFLAGS}")
+	endif()
 	BISON_TARGET(ctwm_parser gram.y ${CMAKE_CURRENT_BINARY_DIR}/gram.tab.c
 		COMPILE_FLAGS ${_YFSTR})
 elseif(YACC)
diff --git a/cmake_files/vcs_checks.cmake b/cmake_files/vcs_checks.cmake
index b4b6c29..f85d3d4 100644
--- a/cmake_files/vcs_checks.cmake
+++ b/cmake_files/vcs_checks.cmake
@@ -17,10 +17,10 @@ endif()
 # If we are, see if we can find bzr(1) installed
 set(HAS_BZR 0)
 if(IS_BZR_CO)
-	find_program(BZR_CMD bzr)
+	find_program(BZR_CMD NAMES bzr brz)
 	if(BZR_CMD)
 		set(HAS_BZR 1)
-		message(STATUS "Building from a checkout and found bzr.")
+		message(STATUS "Building from a checkout and found bzr (${BZR_CMD}).")
 	else()
 		message(STATUS "Building from a checkout, but no bzr found.")
 	endif(BZR_CMD)
@@ -42,7 +42,7 @@ if(NOT IS_BZR_CO)
 		find_program(GIT_CMD git)
 		if(GIT_CMD)
 			set(HAS_GIT 1)
-			message(STATUS "Building from git repo and found git.")
+			message(STATUS "Building from git repo and found git (${GIT_CMD}).")
 		else()
 			message(STATUS "Building from git repo, but no git found.")
 		endif(GIT_CMD)
diff --git a/colormaps.c b/colormaps.c
index fe26c63..5da686a 100644
--- a/colormaps.c
+++ b/colormaps.c
@@ -97,7 +97,7 @@ InstallColormaps(int type, Colormaps *cmaps)
 
 	state = CM_INSTALLED;
 
-	for(i = n = 0; i < number_cwins; i++) {
+	for(i = 0; i < number_cwins; i++) {
 		cwins[i]->colormap->state &= ~CM_INSTALL;
 	}
 	for(i = n = 0; i < number_cwins && n < Scr->cmapInfo.maxCmaps; i++) {
@@ -123,6 +123,12 @@ InstallColormaps(int type, Colormaps *cmaps)
 			cmap->state |= CM_INSTALL;
 		}
 	}
+
+	// Hack: special-case startup
+	if(!dpy) {
+		return true;
+	}
+
 	Scr->cmapInfo.first_req = NextRequest(dpy);
 
 	for(; n > 0 && maxcwin >= &cwins[0]; maxcwin--) {
@@ -241,17 +247,19 @@ CreateTwmColormap(Colormap c)
 {
 	TwmColormap *cmap;
 	cmap = malloc(sizeof(TwmColormap));
-	if(!cmap || XSaveContext(dpy, c, ColormapContext, (XPointer) cmap)) {
-		if(cmap) {
-			free(cmap);
-		}
-		return (NULL);
+	if(!cmap) {
+		return NULL;
 	}
 	cmap->c = c;
 	cmap->state = 0;
 	cmap->install_req = 0;
 	cmap->w = None;
 	cmap->refcnt = 1;
+
+	if(XSaveContext(dpy, c, ColormapContext, (XPointer) cmap)) {
+		free(cmap);
+		return NULL;
+	}
 	return (cmap);
 }
 
@@ -270,49 +278,64 @@ CreateColormapWindow(Window w, bool creating_parent, bool property_window)
 	XWindowAttributes attributes;
 
 	cwin = malloc(sizeof(ColormapWindow));
-	if(cwin) {
-		if(!XGetWindowAttributes(dpy, w, &attributes) ||
-		                XSaveContext(dpy, w, ColormapContext, (XPointer) cwin)) {
-			free(cwin);
-			return (NULL);
-		}
+	if(cwin == NULL) {
+		return NULL;
+	}
 
-		if(XFindContext(dpy, attributes.colormap,  ColormapContext,
-		                (XPointer *)&cwin->colormap) == XCNOENT) {
-			cwin->colormap = cmap = CreateTwmColormap(attributes.colormap);
-			if(!cmap) {
-				XDeleteContext(dpy, w, ColormapContext);
-				free(cwin);
-				return (NULL);
-			}
-		}
-		else {
-			cwin->colormap->refcnt++;
-		}
+	// Common
+	cwin->w = w;
 
-		cwin->w = w;
-		/*
-		 * Assume that windows in colormap list are
-		 * obscured if we are creating the parent window.
-		 * Otherwise, we assume they are unobscured.
-		 */
-		cwin->visibility = creating_parent ?
-		                   VisibilityPartiallyObscured : VisibilityUnobscured;
-		cwin->refcnt = 1;
+	/*
+	 * Assume that windows in colormap list are
+	 * obscured if we are creating the parent window.
+	 * Otherwise, we assume they are unobscured.
+	 */
+	cwin->visibility = creating_parent ?
+	                   VisibilityPartiallyObscured : VisibilityUnobscured;
+	cwin->refcnt = 1;
 
-		/*
-		 * If this is a ColormapWindow property window and we
-		 * are not monitoring ColormapNotify or VisibilityNotify
-		 * events, we need to.
-		 */
-		if(property_window &&
-		                (attributes.your_event_mask &
-		                 (ColormapChangeMask | VisibilityChangeMask)) !=
-		                (ColormapChangeMask | VisibilityChangeMask)) {
-			XSelectInput(dpy, w, attributes.your_event_mask |
-			             (ColormapChangeMask | VisibilityChangeMask));
+
+	// Stub for special cases
+	if(dpy == NULL) {
+		cwin->colormap = NULL;
+		cwin->colormap = calloc(1, sizeof(TwmColormap));
+		cwin->colormap->refcnt = 1;
+
+		return cwin;
+	}
+
+
+	if(!XGetWindowAttributes(dpy, w, &attributes) ||
+	                XSaveContext(dpy, w, ColormapContext, (XPointer) cwin)) {
+		free(cwin);
+		return (NULL);
+	}
+
+	if(XFindContext(dpy, attributes.colormap,  ColormapContext,
+	                (XPointer *)&cwin->colormap) == XCNOENT) {
+		cwin->colormap = cmap = CreateTwmColormap(attributes.colormap);
+		if(!cmap) {
+			XDeleteContext(dpy, w, ColormapContext);
+			free(cwin);
+			return (NULL);
 		}
 	}
+	else {
+		cwin->colormap->refcnt++;
+	}
+
+	/*
+	 * If this is a ColormapWindow property window and we
+	 * are not monitoring ColormapNotify or VisibilityNotify
+	 * events, we need to.
+	 */
+	if(property_window &&
+	                (attributes.your_event_mask &
+	                 (ColormapChangeMask | VisibilityChangeMask)) !=
+	                (ColormapChangeMask | VisibilityChangeMask)) {
+		XSelectInput(dpy, w, attributes.your_event_mask |
+		             (ColormapChangeMask | VisibilityChangeMask));
+	}
 
 	return (cwin);
 }
@@ -549,11 +572,12 @@ InsertRGBColormap(Atom a, XStandardColormap *maps, int nmaps,
 			        ProgramName, (unsigned long) sizeof(StdCmap));
 			return;
 		}
+		replace = false;  // Didn't find one, can't replace
 	}
 
 	if(replace) {                       /* just update contents */
 		if(sc->maps) {
-			XFree(maps);
+			XFree(sc->maps);
 		}
 		if(sc == Scr->StdCmapInfo.mru) {
 			Scr->StdCmapInfo.mru = NULL;
diff --git a/ctopts.c b/ctopts.c
index ce37902..6c7f0d0 100644
--- a/ctopts.c
+++ b/ctopts.c
@@ -31,6 +31,9 @@ static char *ctopts[] = {
 #ifdef EWMH
 	"EWMH",
 #endif
+#ifdef XRANDR
+	"XRANDR",
+#endif
 #ifdef DEBUG
 	"DEBUG",
 #endif
diff --git a/ctwm.h b/ctwm.h
index 9383731..6a3af23 100644
--- a/ctwm.h
+++ b/ctwm.h
@@ -50,21 +50,27 @@
 #define  __attribute__(x)  /*NOTHING*/
 #endif
 
-/* signal(3) handlers have been void since c89 */
-#define SIGNAL_T void
-
 #define BW 2                    /* border width */
 #define BW2 4                   /* border width  * 2 */
 
 #define MAX_BUTTONS     11      /* max mouse buttons supported */
 
-/* contexts for button presses */
+
+/*
+ * Contexts for button presses.
+ * n.b.: These go alongside the ModXMask X11 defs, so better stay above
+ * them!
+ */
 #define Alt1Mask        (1<<8)
 #define Alt2Mask        (1<<9)
 #define Alt3Mask        (1<<10)
 #define Alt4Mask        (1<<11)
 #define Alt5Mask        (1<<12)
 
+// X-ref the Over_Mask's used for testing in mk_twmkeys_entry() if we
+// grow more here, to avoid collision.
+
+
 #define C_NO_CONTEXT    -1
 #define C_WINDOW        0
 #define C_TITLE         1
@@ -293,6 +299,7 @@ struct WindowEntry {
 	bool                used;
 };
 
+#ifdef WINBOX
 struct WindowBox {
 	struct WindowBox    *next;
 	char                *name;
@@ -301,6 +308,7 @@ struct WindowBox {
 	Window              window;
 	struct TwmWindow    *twmwin;
 };
+#endif
 
 
 /*
@@ -316,12 +324,8 @@ struct WindowBox {
 #define DoesWmDeleteWindow      (1L << 2)
 
 
-void Reborder(Time tim);
-SIGNAL_T Done(int signum) __attribute__((noreturn));
-void CreateFonts(ScreenInfo *scr);
-
-void RestoreWithdrawnLocation(TwmWindow *tmp);
 extern char *ProgramName;
+extern size_t ProgramNameLen;
 extern Display *dpy;
 extern XtAppContext appContext;
 extern Window ResizeWindow;     /* the window we are resizing */
@@ -360,12 +364,22 @@ extern char **Argv;
 
 extern bool RestartPreviousState;
 
-extern bool RestartFlag;        /* Flag that is set when SIGHUP is caught */
-void DoRestart(Time t);         /* Function to perform a restart */
+extern bool SignalFlag;    ///< Some signal flag has been set
 
 #define OCCUPY(w, b) ((b == NULL) ? 1 : (w->occupation & (1 << b->number)))
 
 
+/*
+ * Dev utils
+ */
+// Quiet static analyzer warnings
+#if defined(__clang_analyzer__)
+#define ALLOW_DEAD_STORE(x) (void)(x)
+#else
+#define ALLOW_DEAD_STORE(x) (void)0
+#endif
+
+
 /*
  * Command-line arg handling bits
  */
@@ -379,9 +393,11 @@ typedef struct _ctwm_cl_args {
 	bool   PrintErrorMessages; // --verbose, show more debug output
 	bool   ShowWelcomeWindow;  // ! --nowelcome, show splash screen
 
+#ifdef CAPTIVE
 	bool   is_captive;         // --window (flag), running captive
 	Window capwin;             // --window (arg), existing window to capture
 	char  *captivename;        // --name, captive name
+#endif
 
 #ifdef USEM4
 	bool   KeepTmpFile;        // --keep-defs, keep generated m4 defs
diff --git a/ctwm_config.h.in b/ctwm_config.h.in
index fbb7d81..3678d04 100644
--- a/ctwm_config.h.in
+++ b/ctwm_config.h.in
@@ -61,3 +61,33 @@
 #ifdef USE_SREGEX
 # define USE_SYS_REGEX
 #endif
+
+/* Is usable xrandr available? */
+#cmakedefine USE_XRANDR
+#ifdef USE_XRANDR
+# define XRANDR
+#endif
+
+/* Enable captive mode (ctwm -w) related bits */
+#cmakedefine USE_CAPTIVE
+#ifdef USE_CAPTIVE
+# define CAPTIVE
+#endif
+
+/* Fragments of remaining VirtualScreens support */
+#cmakedefine USE_VSCREEN
+#ifdef USE_VSCREEN
+# define VSCREEN
+#endif
+
+/* WindowBox support */
+#cmakedefine USE_WINBOX
+#ifdef USE_WINBOX
+# define WINBOX
+#endif
+
+/* Session support */
+#cmakedefine USE_SESSION
+#ifdef USE_SESSION
+# define SESSION
+#endif
diff --git a/ctwm_main.c b/ctwm_main.c
index 625d27d..0a0aefb 100644
--- a/ctwm_main.c
+++ b/ctwm_main.c
@@ -23,23 +23,17 @@
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <signal.h>
 #include <unistd.h>
 #include <locale.h>
 
-#ifdef __WAIT_FOR_CHILDS
-#  include <sys/wait.h>
-#endif
-
 #include <fcntl.h>
-#include <X11/Xproto.h>
 #include <X11/Xatom.h>
-#include <X11/Xmu/Error.h>
 #include <X11/extensions/shape.h>
 
 
 #include "ctwm_atoms.h"
 #include "ctwm_main.h"
+#include "ctwm_takeover.h"
 #include "clargs.h"
 #include "add_window.h"
 #include "gc.h"
@@ -54,12 +48,25 @@
 #include "icons.h"
 #include "iconmgr.h"
 #include "list.h"
+#ifdef SESSION
 #include "session.h"
+#endif
 #include "occupation.h"
 #include "otp.h"
 #include "cursor.h"
+#ifdef WINBOX
 #include "windowbox.h"
+#endif
+#ifdef CAPTIVE
 #include "captive.h"
+#endif
+#ifdef XRANDR
+#include "xrandr.h"
+#endif
+#include "r_area.h"
+#include "r_area_list.h"
+#include "r_layout.h"
+#include "signals.h"
 #include "vscreen.h"
 #include "win_decorations_init.h"
 #include "win_ops.h"
@@ -77,20 +84,20 @@ XtAppContext appContext;        /* Xt application context */
 Display *dpy;                   /* which display are we talking to */
 Window ResizeWindow;            /* the window we are resizing */
 
+Atom XCTWMAtom[NUM_CTWM_XATOMS]; ///< Our various common atoms
+
 int NumScreens;                 /* number of screens in ScreenList */
 bool HasShape;                  /* server supports shape extension? */
 int ShapeEventBase, ShapeErrorBase;
 ScreenInfo **ScreenList;        /* structures for each screen */
 ScreenInfo *Scr = NULL;         /* the cur and prev screens */
 int PreviousScreen;             /* last screen that we were on */
-static bool RedirectError;      /* true ==> another window manager running */
-/* for settting RedirectError */
-static int CatchRedirectError(Display *display, XErrorEvent *event);
-/* for everything else */
-static int TwmErrorHandler(Display *display, XErrorEvent *event);
+static bool cfgerrs = false;    ///< Whether there were config parsing errors
+
+#ifdef CAPTIVE
 static Window CreateCaptiveRootWindow(int x, int y,
                                       unsigned int width, unsigned int height);
-static void InternUsefulAtoms(void);
+#endif
 ScreenInfo *InitScreenInfo(int scrnum, Window croot, int crootx, int crooty,
                            unsigned int crootw, unsigned int crooth);
 static bool MappedNotOverride(Window w);
@@ -137,35 +144,40 @@ int JunkX, JunkY;
 unsigned int JunkWidth, JunkHeight, JunkBW, JunkDepth, JunkMask;
 
 char *ProgramName;
+size_t ProgramNameLen;
 int Argc;
 char **Argv;
 
 bool RestartPreviousState = true;      /* try to restart in previous state */
 
-bool RestartFlag = false;
-SIGNAL_T Restart(int signum);
-SIGNAL_T Crash(int signum);
-#ifdef __WAIT_FOR_CHILDS
-SIGNAL_T ChildExit(int signum);
-#endif
 
-/***********************************************************************
- *
- *  Procedure:
- *      main - start of twm
- *
- ***********************************************************************
- */
+/// Magic flag for tests.  Nothing else should touch this!
+bool ctwm_test = false;
+
+/// Magic callback for tests.  This will trigger right after config file
+/// parsing if it's set, and then exit.  Nothing else should ever touch
+/// this!
+int (*ctwm_test_postparse)(void) = NULL;
+
+
+
 
+/**
+ * Start up ctwm.  This is effectively main(), just wrapped for various
+ * unimportant reasons.
+ */
 int
 ctwm_main(int argc, char *argv[])
 {
 	int numManaged, firstscrn, lastscrn;
 	bool FirstScreen;
+	bool takeover = true;
+	bool nodpyok = false;
 
 	setlocale(LC_ALL, "");
 
 	ProgramName = argv[0];
+	ProgramNameLen = strlen(ProgramName);
 	Argc = argc;
 	Argv = argv;
 
@@ -184,24 +196,33 @@ ctwm_main(int argc, char *argv[])
 	clargs_check();
 	/* If we get this far, it was all good */
 
+	/* Some clargs mean we're not actually trying to take over the screen */
+	if(CLarg.cfgchk) {
+		takeover = false;
+	}
+#ifdef CAPTIVE
+	if(CLarg.is_captive) {
+		takeover = false;
+	}
+#endif
 
-#define newhandler(sig, action) \
-    if (signal (sig, SIG_IGN) != SIG_IGN) signal (sig, action)
+	/* And some mean we actually don't care if we lack an X server */
+	if(CLarg.cfgchk) {
+		nodpyok = true;
+	}
 
-	newhandler(SIGINT, Done);
-	signal(SIGHUP, Restart);
-	newhandler(SIGQUIT, Done);
-	newhandler(SIGTERM, Done);
-#ifdef __WAIT_FOR_CHILDS
-	newhandler(SIGCHLD, ChildExit);
-#endif
-	signal(SIGALRM, SIG_IGN);
-#ifdef NOTRAP
-	signal(SIGSEGV, Crash);
-	signal(SIGBUS,  Crash);
-#endif
+	/* Support for tests: be ready to fake everything */
+	if(ctwm_test) {
+		takeover = false;
+		nodpyok  = true;
+	}
+
+
+	/*
+	 * Hook up signal handlers
+	 */
+	setup_signal_handlers();
 
-#undef newhandler
 
 	// Various bits of code care about $HOME
 	Home = getenv("HOME");
@@ -226,38 +247,66 @@ ctwm_main(int argc, char *argv[])
 		XtToolkitInitialize();
 		appContext = XtCreateApplicationContext();
 
-		if(!(dpy = XtOpenDisplay(appContext, CLarg.display_name, "twm", "twm",
-		                         NULL, 0, &zero, NULL))) {
+		// Tests don't talk to a real X server.
+		// XXX This needs revisiting if we ever get one that _does_.
+		// We'll have to add another flag...
+		if(!ctwm_test) {
+			// Connect
+			dpy = XtOpenDisplay(appContext, CLarg.display_name, "twm", "twm",
+			                    NULL, 0, &zero, NULL);
+		}
+
+		// Failed?  Usually a problem, but somethings we allow faking...
+		if(!dpy && !nodpyok) {
 			fprintf(stderr, "%s:  unable to open display \"%s\"\n",
 			        ProgramName, XDisplayName(CLarg.display_name));
 			exit(1);
 		}
 
-		if(fcntl(ConnectionNumber(dpy), F_SETFD, FD_CLOEXEC) == -1) {
+		if(dpy && fcntl(ConnectionNumber(dpy), F_SETFD, FD_CLOEXEC) == -1) {
 			fprintf(stderr,
 			        "%s:  unable to mark display connection as close-on-exec\n",
 			        ProgramName);
 			exit(1);
 		}
+
+		if(!dpy && !ctwm_test) {
+			// At least warn, except for tests
+			fprintf(stderr, "%s: Can't connect to X server, proceeding anyway...\n",
+			        ProgramName);
+		}
 	}
 
 
+#ifdef SESSION
 	// Load session stuff
 	if(CLarg.restore_filename) {
 		ReadWinConfigFile(CLarg.restore_filename);
 	}
+#endif
 
-	// Load up info about X extensions
-	HasShape = XShapeQueryExtension(dpy, &ShapeEventBase, &ShapeErrorBase);
 
-	// Allocate contexts/atoms/etc we use
-	TwmContext = XUniqueContext();
-	MenuContext = XUniqueContext();
-	ScreenContext = XUniqueContext();
-	ColormapContext = XUniqueContext();
-	InitWorkSpaceManagerContext();
+	if(dpy) {
+		// Load up info about X extensions
+		HasShape = XShapeQueryExtension(dpy, &ShapeEventBase, &ShapeErrorBase);
 
-	InternUsefulAtoms();
+		// Allocate contexts/atoms/etc we use
+		TwmContext = XUniqueContext();
+		MenuContext = XUniqueContext();
+		ScreenContext = XUniqueContext();
+		ColormapContext = XUniqueContext();
+		InitWorkSpaceManagerContext();
+
+		// Load up our standard set of atoms
+		XInternAtoms(dpy, XCTWMAtomNames, NUM_CTWM_XATOMS, False, XCTWMAtom);
+
+		NumScreens = ScreenCount(dpy);
+		PreviousScreen = DefaultScreen(dpy);
+	}
+	else {
+		NumScreens = 1;
+		PreviousScreen = 0;
+	}
 
 	// Allocate/define common cursors
 	NewFontCursor(&TopLeftCursor, "top_left_corner");
@@ -276,11 +325,13 @@ ctwm_main(int argc, char *argv[])
 
 
 	// Prep up the per-screen global info
-	NumScreens = ScreenCount(dpy);
 	if(CLarg.MultiScreen) {
 		firstscrn = 0;
 		lastscrn = NumScreens - 1;
 	}
+	else if(!dpy) {
+		firstscrn = lastscrn = 0;
+	}
 	else {
 		firstscrn = lastscrn = DefaultScreen(dpy);
 	}
@@ -293,13 +344,12 @@ ctwm_main(int argc, char *argv[])
 		exit(1);
 	}
 
-	// Initialize
-	PreviousScreen = DefaultScreen(dpy);
-
 
 	// Do a little early initialization
 #ifdef EWMH
-	EwmhInit();
+	if(dpy) {
+		EwmhInit();
+	}
 #endif /* EWMH */
 #ifdef SOUNDS
 	// Needs init'ing before we get to config parsing
@@ -307,21 +357,27 @@ ctwm_main(int argc, char *argv[])
 #endif
 	InitEvents();
 
+
+
 	// Start looping over the screens
 	numManaged = 0;
 	FirstScreen = true;
 	for(int scrnum = firstscrn ; scrnum <= lastscrn; scrnum++) {
 		Window croot;
-		unsigned long attrmask;
 		int crootx, crooty;
 		unsigned int crootw, crooth;
 		bool screenmasked;
 		char *welcomefile;
 
+
 		/*
 		 * First, setup the root window for the screen.
 		 */
-		if(CLarg.is_captive) {
+		if(0) {
+			// Dummy
+		}
+#ifdef CAPTIVE
+		else if(CLarg.is_captive) {
 			// Captive ctwm.  We make a fake root.
 			XWindowAttributes wa;
 			if(CLarg.capwin && XGetWindowAttributes(dpy, CLarg.capwin, &wa)) {
@@ -342,23 +398,24 @@ ctwm_main(int argc, char *argv[])
 				croot = CreateCaptiveRootWindow(crootx, crooty, crootw, crooth);
 			}
 		}
+#endif
 		else {
 			// Normal; get the real display's root.
-			croot  = RootWindow(dpy, scrnum);
 			crootx = 0;
 			crooty = 0;
-			crootw = DisplayWidth(dpy, scrnum);
-			crooth = DisplayHeight(dpy, scrnum);
+
+			if(dpy) {
+				croot  = RootWindow(dpy, scrnum);
+				crootw = DisplayWidth(dpy, scrnum);
+				crooth = DisplayHeight(dpy, scrnum);
+			}
+			else {
+				croot = None;
+				crootw = 1280;
+				crooth = 768;
+			}
 		}
 
-		// Initialize to empty.  This gets populated with SaveColor{}
-		// results.  String values get done via assign_var_savecolor()
-		// call below, but keyword choicse wind up getting put in on the
-		// fly during config file parsing, so we have to clear it before
-		// we get to the config.
-		// XXX Maybe we should change that...
-		XChangeProperty(dpy, croot, XA__MIT_PRIORITY_COLORS,
-		                XA_CARDINAL, 32, PropModeReplace, NULL, 0);
 
 
 		/*
@@ -375,92 +432,67 @@ ctwm_main(int argc, char *argv[])
 			continue;
 		}
 
-		// Not trying to take over if we're just checking config or
-		// making a new captive ctwm.
-		if(CLarg.cfgchk || CLarg.is_captive) {
-			Scr->takeover = false;
-		}
-
 		// Other misc adjustments to default config.
 		Scr->ShowWelcomeWindow = CLarg.ShowWelcomeWindow;
 
 
-#ifdef EWMH
-		// Early EWMH setup
-		EwmhInitScreenEarly(Scr);
-#endif /* EWMH */
-
-		// Early OTP setup
-		OtpScrInitData(Scr);
-
 
 		/*
-		 * Subscribe to various events on the root window.  Because X
-		 * only allows a single client to subscribe to
-		 * SubstructureRedirect and ButtonPress bits, this also serves to
-		 * mutex who is The WM for the root window, and thus (aside from
-		 * captive) the Screen.
-		 *
-		 * To catch whether that failed, we set a special one-shot error
-		 * handler to flip a var that we test to find out whether the
-		 * redirect failed.
+		 * Figure out the layout of our various monitors if RANDR is
+		 * around and can tell us.
 		 */
-		XSync(dpy, 0); // Flush possible previous errors
-		RedirectError = false;
-		XSetErrorHandler(CatchRedirectError);
-		attrmask = ColormapChangeMask | EnterWindowMask | PropertyChangeMask |
-		           SubstructureRedirectMask | KeyPressMask | ButtonPressMask |
-		           ButtonReleaseMask;
-#ifdef EWMH
-		attrmask |= StructureNotifyMask;
-#endif /* EWMH */
-		if(CLarg.is_captive) {
-			attrmask |= StructureNotifyMask;
+#ifdef XRANDR
+		if(dpy) {
+			Scr->Layout = XrandrNewLayout(dpy, Scr->XineramaRoot);
 		}
-		XSelectInput(dpy, croot, attrmask);
-		XSync(dpy, 0); // Flush the RedirectError, if we had one
-
-		// Back to our normal handler
-		XSetErrorHandler(TwmErrorHandler);
-
-		if(RedirectError && Scr->takeover) {
-			fprintf(stderr, "%s:  another window manager is already running",
-			        ProgramName);
-			if(CLarg.MultiScreen && NumScreens > 0) {
-				fprintf(stderr, " on screen %d?\n", scrnum);
-			}
-			else {
-				fprintf(stderr, "?\n");
-			}
-
-			// XSetErrorHandler() isn't local to the Screen; it's for the
-			// whole connection.  We wind up in a slightly weird state
-			// once we've set it up, but decided we aren't taking over
-			// this screen, but resetting it would be a little weird too,
-			// because maybe we have taken over some other screen.  So,
-			// just throw up our hands.
+#endif
+		if(Scr->Layout == NULL) {
+			// No RANDR, so as far as we know, the layout is just one
+			// monitor with our full size.
+			RArea *fs;
+			RAreaList *fsl;
+
+			fs = RAreaNewStatic(Scr->rootx, Scr->rooty, Scr->rootw, Scr->rooth);
+			fsl = RAreaListNew(1, fs, NULL);
+			Scr->Layout = RLayoutNew(fsl);
+		}
+#ifdef DEBUG
+		fprintf(stderr, "Layout: ");
+		RLayoutPrint(Scr->Layout);
+#endif
+		if(RLayoutNumMonitors(Scr->Layout) < 1) {
+			fprintf(stderr, "Error: No monitors found on screen %d!\n", scrnum);
 			continue;
 		}
 
 
-		// We now manage it (or are in the various special circumstances
-		// where it's near enough).
-		numManaged ++;
-
 
 		// Now we can stash some info about the screen
-		Scr->d_depth = DefaultDepth(dpy, scrnum);
-		Scr->d_visual = DefaultVisual(dpy, scrnum);
-		Scr->RealRoot = RootWindow(dpy, scrnum);
+		if(dpy) {
+			Scr->d_depth = DefaultDepth(dpy, scrnum);
+			Scr->d_visual = DefaultVisual(dpy, scrnum);
+			Scr->RealRoot = RootWindow(dpy, scrnum);
+			{
+				// Stash these for m4
+				Screen *tscr = ScreenOfDisplay(dpy, scrnum);
+				Scr->mm_w = tscr->mwidth;
+				Scr->mm_h = tscr->mheight;
+			}
+		}
+		else {
+			// Standin; fake the values we need in m4 parsing
+			Scr->d_visual = calloc(1, sizeof(Visual));
+			Scr->d_visual->bits_per_rgb = 8;
+			Scr->d_visual->class = TrueColor;
+		}
+
 
 		// Now that we have d_depth...
 		Scr->XORvalue = (((unsigned long) 1) << Scr->d_depth) - 1;
 
-		// Stash up a ref to our Scr on the root, so we can find the
-		// right Scr for events etc.
-		XSaveContext(dpy, Scr->Root, ScreenContext, (XPointer) Scr);
-
-		// Init captive bits
+#ifdef CAPTIVE
+		// Init captive bits.  We stick this name into m4 props, so do it
+		// before config processing.
 		if(CLarg.is_captive) {
 			Scr->CaptiveRoot = croot;
 			Scr->captivename = AddToCaptiveList(CLarg.captivename);
@@ -470,9 +502,12 @@ ctwm_main(int argc, char *argv[])
 				                   NULL, 0, NULL, NULL, NULL);
 			}
 		}
+#endif
 
 
-		// Init some colormap bits
+		// Init some colormap bits.  We need this before we get into the
+		// config parsing, since various things in there poke into
+		// colormaps.
 		{
 			// 1 on the root
 			Scr->RootColormaps.number_cwins = 1;
@@ -483,7 +518,10 @@ ctwm_main(int argc, char *argv[])
 
 			// Initialize storage for all maps the Screen can hold
 			Scr->cmapInfo.cmaps = NULL;
-			Scr->cmapInfo.maxCmaps = MaxCmapsOfScreen(ScreenOfDisplay(dpy, Scr->screen));
+			if(dpy) {
+				Scr->cmapInfo.maxCmaps = MaxCmapsOfScreen(ScreenOfDisplay(dpy,
+				                         Scr->screen));
+			}
 			Scr->cmapInfo.root_pushes = 0;
 			InstallColormaps(0, &Scr->RootColormaps);
 
@@ -491,12 +529,14 @@ ctwm_main(int argc, char *argv[])
 			Scr->StdCmapInfo.head = Scr->StdCmapInfo.tail
 			                        = Scr->StdCmapInfo.mru = NULL;
 			Scr->StdCmapInfo.mruindex = 0;
-			LocateStandardColormaps();
+			if(dpy) {
+				LocateStandardColormaps();
+			}
 		}
 
 
 		// Are we monochrome?  Or do we care this millennium?
-		if(CLarg.Monochrome || DisplayCells(dpy, scrnum) < 3) {
+		if(CLarg.Monochrome || (dpy && DisplayCells(dpy, scrnum) < 3)) {
 			Scr->Monochrome = MONOCHROME;
 		}
 		else {
@@ -530,7 +570,7 @@ ctwm_main(int argc, char *argv[])
 
 		// The first time around, we focus onto the root [of the first
 		// Screen].  Maybe we should revisit this...
-		if(FirstScreen) {
+		if(dpy && FirstScreen) {
 			// XXX This func also involves a lot of stuff that isn't
 			// setup yet, and probably only works by accident.  Maybe we
 			// should just manually extract out the couple bits we
@@ -550,44 +590,66 @@ ctwm_main(int argc, char *argv[])
 		 * info from config file about it.
 		 */
 		screenmasked = false;
-		if(Scr->ShowWelcomeWindow && (welcomefile = getenv("CTWM_WELCOME_FILE"))) {
+		if(dpy && takeover && Scr->ShowWelcomeWindow
+		                && (welcomefile = getenv("CTWM_WELCOME_FILE"))) {
 			screenmasked = true;
 			MaskScreen(welcomefile);
 		}
 
 
+
 		/*
 		 * Load up config file
 		 */
-		if(CLarg.cfgchk) {
-			if(LoadTwmrc(CLarg.InitFile) == false) {
-				/* Error return */
-				fprintf(stderr, "Errors found\n");
-				exit(1);
-			}
-			else {
-				fprintf(stderr, "No errors found\n");
-				exit(0);
+		{
+			bool ok = LoadTwmrc(CLarg.InitFile);
+
+			// cfgchk just displays whether there are errors, then moves
+			// on.
+			if(CLarg.cfgchk) {
+				if(ok) {
+					fprintf(stderr, "%d: No errors found\n", scrnum);
+				}
+				else {
+					fprintf(stderr, "%d: Errors found\n", scrnum);
+					cfgerrs = true;
+				}
+				continue;
 			}
+
+			// In non-config-check mode, we historically proceed even if
+			// there were errors, so keep doing that...
 		}
-		else {
-			LoadTwmrc(CLarg.InitFile);
+
+
+		// For testing, it's useful to do all that initial setup up
+		// through parsing, and then inspect Scr and the like.
+		// Long-term, IWBNI we had a better way to do all the necessary
+		// initialization and then call the parse ourselves at that
+		// level.  But for now, provide a callback func that can pass
+		// control back to the test code, then just exits.
+		if(ctwm_test_postparse != NULL) {
+			exit(ctwm_test_postparse());
 		}
 
 
+
 		/*
-		 * Setup stuff relating to VirtualScreens.  If something to do
-		 * with it is set in the config, this all implements stuff needed
-		 * for that.  If not, InitVirtualScreens() creates a single one
-		 * mirroring our real root.
+		 * Since we've loaded the config, go ahead and take over the
+		 * screen.
 		 */
-		InitVirtualScreens(Scr);
-#ifdef EWMH
-		EwmhInitVirtualRoots(Scr);
-#endif /* EWMH */
+		if(takeover) {
+			if(takeover_screen(Scr) != true) {
+				// Well, move on to the next one, maybe we'll get it...
+				if(screenmasked) {
+					UnmaskScreen();
+				}
+				continue;
+			}
 
-		// Setup WSM[s] (per-vscreen)
-		ConfigureWorkSpaceManager();
+			// Well, we got this one
+			numManaged++;
+		}
 
 		// If the config wants us to show the splash screen and we
 		// haven't already, do it now.
@@ -600,6 +662,8 @@ ctwm_main(int argc, char *argv[])
 		/*
 		 * Do various setup based on the results from the config file.
 		 */
+
+		// Few simple var defaults
 		if(Scr->ClickToFocus) {
 			Scr->FocusRoot  = false;
 			Scr->TitleFocus = false;
@@ -610,6 +674,43 @@ ctwm_main(int argc, char *argv[])
 		}
 
 
+		// Now that we know what Border's there may be, create our
+		// BorderedLayout.
+		Scr->BorderedLayout = RLayoutCopyCropped(Scr->Layout,
+		                      Scr->BorderLeft, Scr->BorderRight,
+		                      Scr->BorderTop, Scr->BorderBottom);
+		if(Scr->BorderedLayout == NULL) {
+			Scr->BorderedLayout = Scr->Layout;        // nothing to crop
+		}
+		else if(Scr->BorderedLayout->monitors->len == 0) {
+			fprintf(stderr,
+			        "Borders too large! correct BorderLeft, BorderRight, BorderTop and/or BorderBottom parameters\n");
+			exit(1);
+		}
+#ifdef DEBUG
+		fprintf(stderr, "Bordered: ");
+		RLayoutPrint(Scr->BorderedLayout);
+#endif
+
+
+		/*
+		 * Setup stuff relating to VirtualScreens.  If something to do
+		 * with it is set in the config, this all implements stuff needed
+		 * for that.  If not, InitVirtualScreens() creates a single one
+		 * mirroring our real root.
+		 */
+		InitVirtualScreens(Scr);
+#ifdef VSCREEN
+#ifdef EWMH
+		EwmhInitVirtualRoots(Scr);
+#endif /* EWMH */
+#endif // vscreen
+
+		// Setup WSM[s] (per-vscreen).  This also sets up the about the
+		// workspaces for each vscreen and which is currently displayed.
+		ConfigureWorkSpaceManager(Scr);
+
+
 		/*
 		 * Various decoration default overrides for 3d/2d.  Values that
 		 * [presumtively] look "nice" on 75/100dpi displays.  -100 is a
@@ -729,6 +830,16 @@ ctwm_main(int argc, char *argv[])
 			Scr->TitleHeight++;
 		}
 
+
+
+		/*
+		 * Now we can start making various things.
+		 */
+
+		// Stash up a ref to our Scr on the root, so we can find the
+		// right Scr for events etc.
+		XSaveContext(dpy, Scr->Root, ScreenContext, (XPointer) Scr);
+
 		// Setup GC's for drawing, so we can start making stuff we have
 		// to actually draw.  Could move earlier, has to preceed a lot of
 		// following.
@@ -764,8 +875,10 @@ ctwm_main(int argc, char *argv[])
 		// menus made.
 		MakeWorkspacesMenu();
 
+#ifdef WINBOX
 		// setup WindowBox's
 		createWindowBoxes();
+#endif
 
 		// Initialize Xrm stuff; things with setting occupation etc use
 		// Xrm bits.
@@ -878,7 +991,16 @@ ctwm_main(int argc, char *argv[])
 		 * operations.
 		 */
 		{
-			int sx, sy;
+			// Stick the SizeWindow at the top left of the first monitor
+			// we found on this Screen.  That _may_ not be (0,0) (imagine
+			// a shorter left and taller right monitor, with their bottom
+			// edges lined up instead of top), so we have to look up what
+			// that coordinate is.  If we're CenterFeedbackWindow'ing,
+			// the window will have to move between monitors depending on
+			// where the window we're moving is (starts), but
+			// MoveResizeSizeWindow() will handle that.  If not, it
+			// always stays in the top-left of the first display.
+			RArea area = RLayoutGetAreaIndex(Scr->Layout, 0);
 			XRectangle ink_rect;
 			XRectangle logical_rect;
 			unsigned long valuemask;
@@ -891,19 +1013,13 @@ ctwm_main(int argc, char *argv[])
 			valuemask = (CWBorderPixel | CWBackPixel | CWBitGravity);
 			attributes.bit_gravity = NorthWestGravity;
 
-			if(Scr->CenterFeedbackWindow) {
-				sx = (Scr->rootw / 2) - (Scr->SizeStringWidth / 2);
-				sy = (Scr->rooth / 2) - ((Scr->SizeFont.height + SIZE_VINDENT * 2) / 2);
-				if(Scr->SaveUnder) {
-					attributes.save_under = True;
-					valuemask |= CWSaveUnder;
-				}
-			}
-			else {
-				sx = 0;
-				sy = 0;
+			if(Scr->SaveUnder) {
+				attributes.save_under = True;
+				valuemask |= CWSaveUnder;
 			}
-			Scr->SizeWindow = XCreateWindow(dpy, Scr->Root, sx, sy,
+
+			Scr->SizeWindow = XCreateWindow(dpy, Scr->Root,
+			                                area.x, area.y,
 			                                Scr->SizeStringWidth,
 			                                (Scr->SizeFont.height +
 			                                 SIZE_VINDENT * 2),
@@ -929,6 +1045,12 @@ ctwm_main(int argc, char *argv[])
 	} // for each screen on display
 
 
+	// If we're just checking the config, there's nothing more to do.
+	if(CLarg.cfgchk) {
+		exit(cfgerrs);
+	}
+
+
 	// We're not much of a window manager if we didn't get stuff to
 	// manage...
 	if(numManaged == 0) {
@@ -938,8 +1060,10 @@ ctwm_main(int argc, char *argv[])
 		exit(1);
 	}
 
+#ifdef SESSION
 	// Hook up session
 	ConnectToSessionManager(CLarg.client_id);
+#endif
 
 #ifdef SOUNDS
 	// Announce ourselves
@@ -951,11 +1075,11 @@ ctwm_main(int argc, char *argv[])
 	// XXX This doesn't seem right?
 	RestartPreviousState = true;
 
-	// Do some late initialization
-	HandlingEvents = true;
+	// Set vars to enable animation bits
 	StartAnimation();
 
 	// Main loop.
+	HandlingEvents = true;
 	HandleEvents();
 
 	// Should never get here...
@@ -964,6 +1088,7 @@ ctwm_main(int argc, char *argv[])
 }
 
 
+
 /**
  * Initialize ScreenInfo for a Screen.  This allocates the struct,
  * assigns in the info we pass it about the screen and dimensions, and
@@ -1004,10 +1129,17 @@ InitScreenInfo(int scrnum, Window croot, int crootx, int crooty,
 	// derived dimension-related bits.
 	scr->screen = scrnum;
 	scr->XineramaRoot = scr->Root = croot;
-	scr->rootx = scr->crootx = crootx;
-	scr->rooty = scr->crooty = crooty;
-	scr->rootw = scr->crootw = crootw;
-	scr->rooth = scr->crooth = crooth;
+	scr->rootx = crootx;
+	scr->rooty = crooty;
+	scr->rootw = crootw;
+	scr->rooth = crooth;
+
+#ifdef CAPTIVE
+	scr->crootx = crootx;
+	scr->crooty = crooty;
+	scr->crootw = crootw;
+	scr->crooth = crooth;
+#endif
 
 	// Don't allow icon titles wider than the screen
 	scr->MaxIconTitleWidth = scr->rootw;
@@ -1038,10 +1170,6 @@ InitScreenInfo(int scrnum, Window croot, int crootx, int crooty,
 	// uses are fairly bogus.
 	scr->FirstTime = true;
 
-	// We're a WM, we're usually trying to take over (x-ref later code in
-	// caller)
-	scr->takeover = true;
-
 	// Sentinel values for defaulting config values
 	scr->FramePadding = -100;
 	scr->TitlePadding = -100;
@@ -1169,8 +1297,20 @@ InitScreenInfo(int scrnum, Window croot, int crootx, int crooty,
 #ifdef EWMH
 	scr->PreferredIconWidth = 48;
 	scr->PreferredIconHeight = 48;
+
+	scr->ewmh_CLIENT_LIST_used = 0;
+	scr->ewmh_CLIENT_LIST_size = 16;
+	scr->ewmh_CLIENT_LIST = calloc(scr->ewmh_CLIENT_LIST_size,
+	                               sizeof(scr->ewmh_CLIENT_LIST[0]));
+	if(scr->ewmh_CLIENT_LIST == NULL) {
+		free(scr);
+		return NULL;
+	}
 #endif
 
+	// OTP structure bits
+	OtpScrInitData(scr);
+
 
 	// WorkSpaceManager stuff
 	scr->workSpaceMgr.initialstate  = WMS_map;
@@ -1206,268 +1346,27 @@ InitScreenInfo(int scrnum, Window croot, int crootx, int crooty,
 #undef DEFAULT_FAST_FONT
 #undef DEFAULT_NICE_FONT
 
+
+	// Set some fallback values that we set from the X server, for
+	// special cases where we may not actually be talking to one.
+	scr->d_depth = 24;
+	scr->RealRoot = croot;
+	scr->mm_w = 406; // 16 in
+	scr->mm_h = 229; // 9 in
+	scr->Monochrome = COLOR;
+
 	// Cleanup poisoning
 #undef Scr
 	return scr;
 }
 
 
-void CreateFonts(ScreenInfo *scr)
-{
-#define LOADFONT(fld) (GetFont(&scr->fld##Font))
-	LOADFONT(TitleBar);
-	LOADFONT(Menu);
-	LOADFONT(Icon);
-	LOADFONT(Size);
-	LOADFONT(IconManager);
-	LOADFONT(Default);
-	LOADFONT(workSpaceMgr.window);
-#undef LOADFONT
-
-	scr->HaveFonts = true;
-}
-
-
-void RestoreWithdrawnLocation(TwmWindow *tmp)
-{
-	int gravx, gravy;
-	unsigned int bw, mask;
-	XWindowChanges xwc;
-
-	if(tmp->UnmapByMovingFarAway && !visible(tmp)) {
-		XMoveWindow(dpy, tmp->frame, tmp->frame_x, tmp->frame_y);
-	}
-	if(tmp->squeezed) {
-		Squeeze(tmp);
-	}
-	if(XGetGeometry(dpy, tmp->w, &JunkRoot, &xwc.x, &xwc.y,
-	                &JunkWidth, &JunkHeight, &bw, &JunkDepth)) {
-
-		GetGravityOffsets(tmp, &gravx, &gravy);
-		if(gravy < 0) {
-			xwc.y -= tmp->title_height;
-		}
-		xwc.x += gravx * tmp->frame_bw3D;
-		xwc.y += gravy * tmp->frame_bw3D;
-
-		if(bw != tmp->old_bw) {
-			int xoff, yoff;
-
-			if(!Scr->ClientBorderWidth) {
-				xoff = gravx;
-				yoff = gravy;
-			}
-			else {
-				xoff = 0;
-				yoff = 0;
-			}
-
-			xwc.x -= (xoff + 1) * tmp->old_bw;
-			xwc.y -= (yoff + 1) * tmp->old_bw;
-		}
-		if(!Scr->ClientBorderWidth) {
-			xwc.x += gravx * tmp->frame_bw;
-			xwc.y += gravy * tmp->frame_bw;
-		}
-
-		mask = (CWX | CWY);
-		if(bw != tmp->old_bw) {
-			xwc.border_width = tmp->old_bw;
-			mask |= CWBorderWidth;
-		}
-
-#if 0
-		if(tmp->vs) {
-			xwc.x += tmp->vs->x;
-			xwc.y += tmp->vs->y;
-		}
-#endif
-
-		if(tmp->winbox && tmp->winbox->twmwin && tmp->frame) {
-			int xbox, ybox;
-			if(XGetGeometry(dpy, tmp->frame, &JunkRoot, &xbox, &ybox,
-			                &JunkWidth, &JunkHeight, &bw, &JunkDepth)) {
-				ReparentWindow(dpy, tmp, WinWin, Scr->Root, xbox, ybox);
-			}
-		}
-		XConfigureWindow(dpy, tmp->w, mask, &xwc);
-
-		if(tmp->wmhints->flags & IconWindowHint) {
-			XUnmapWindow(dpy, tmp->wmhints->icon_window);
-		}
-
-	}
-}
-
-
-/***********************************************************************
- *
- *  Procedure:
- *      Done - cleanup and exit twm
- *
- *  Returned Value:
- *      none
- *
- *  Inputs:
- *      none
- *
- *  Outputs:
- *      none
- *
- *  Special Considerations:
- *      none
- *
- ***********************************************************************
- */
-
-void Reborder(Time mytime)
-{
-	TwmWindow *tmp;                     /* temp twm window structure */
-	int scrnum;
-	ScreenInfo *savedScreen;            /* Its better to avoid coredumps */
-
-	/* put a border back around all windows */
-
-	XGrabServer(dpy);
-	savedScreen = Scr;
-	for(scrnum = 0; scrnum < NumScreens; scrnum++) {
-		if((Scr = ScreenList[scrnum]) == NULL) {
-			continue;
-		}
-
-		InstallColormaps(0, &Scr->RootColormaps);       /* force reinstall */
-		for(tmp = Scr->FirstWindow; tmp != NULL; tmp = tmp->next) {
-			RestoreWithdrawnLocation(tmp);
-			XMapWindow(dpy, tmp->w);
-		}
-	}
-	Scr = savedScreen;
-	XUngrabServer(dpy);
-	SetFocus(NULL, mytime);
-}
-
-SIGNAL_T Done(int signum)
-{
-#ifdef SOUNDS
-	play_exit_sound();
-#endif
-	Reborder(CurrentTime);
-#ifdef EWMH
-	EwmhTerminate();
-#endif /* EWMH */
-	XDeleteProperty(dpy, Scr->Root, XA_WM_WORKSPACESLIST);
-	if(CLarg.is_captive) {
-		RemoveFromCaptiveList(Scr->captivename);
-	}
-	XCloseDisplay(dpy);
-	exit(0);
-}
-
-SIGNAL_T Crash(int signum)
-{
-	Reborder(CurrentTime);
-	XDeleteProperty(dpy, Scr->Root, XA_WM_WORKSPACESLIST);
-	if(CLarg.is_captive) {
-		RemoveFromCaptiveList(Scr->captivename);
-	}
-	XCloseDisplay(dpy);
-
-	fprintf(stderr, "\nCongratulations, you have found a bug in ctwm\n");
-	fprintf(stderr, "If a core file was generated in your directory,\n");
-	fprintf(stderr, "can you please try extract the stack trace,\n");
-	fprintf(stderr,
-	        "and mail the results, and a description of what you were doing,\n");
-	fprintf(stderr, "to ctwm@ctwm.org.  Thank you for your support.\n");
-	fprintf(stderr, "...exiting ctwm now.\n\n");
 
-	abort();
-}
 
-
-SIGNAL_T Restart(int signum)
-{
-	fprintf(stderr, "%s:  setting restart flag\n", ProgramName);
-	RestartFlag = true;
-}
-
-void DoRestart(Time t)
-{
-	RestartFlag = false;
-
-	StopAnimation();
-	XSync(dpy, 0);
-	Reborder(t);
-	XSync(dpy, 0);
-
-	if(smcConn) {
-		SmcCloseConnection(smcConn, 0, NULL);
-	}
-
-	fprintf(stderr, "%s:  restarting:  %s\n",
-	        ProgramName, *Argv);
-	execvp(*Argv, Argv);
-	fprintf(stderr, "%s:  unable to restart:  %s\n", ProgramName, *Argv);
-}
-
-#ifdef __WAIT_FOR_CHILDS
-/*
- * Handler for SIGCHLD. Needed to avoid zombies when an .xinitrc
- * execs ctwm as the last client. (All processes forked off from
- * within .xinitrc have been inherited by ctwm during the exec.)
- * Jens Schweikhardt <jens@kssun3.rus.uni-stuttgart.de>
- */
-SIGNAL_T
-ChildExit(int signum)
-{
-	int Errno = errno;
-	signal(SIGCHLD, ChildExit);  /* reestablish because we're a one-shot */
-	waitpid(-1, NULL, WNOHANG);   /* reap dead child, ignore status */
-	errno = Errno;               /* restore errno for interrupted sys calls */
-}
-#endif
-
-/*
- * Error Handlers.  If a client dies, we'll get a BadWindow error (except for
- * GetGeometry which returns BadDrawable) for most operations that we do before
- * manipulating the client's window.
- */
-
-static XErrorEvent LastErrorEvent;
-
-static int TwmErrorHandler(Display *display, XErrorEvent *event)
-{
-	LastErrorEvent = *event;
-
-	if(CLarg.PrintErrorMessages &&                 /* don't be too obnoxious */
-	                event->error_code != BadWindow &&       /* watch for dead puppies */
-	                (event->request_code != X_GetGeometry &&         /* of all styles */
-	                 event->error_code != BadDrawable)) {
-		XmuPrintDefaultErrorMessage(display, event, stderr);
-	}
-	return 0;
-}
-
-
-/* ARGSUSED*/
-static int CatchRedirectError(Display *display, XErrorEvent *event)
-{
-	RedirectError = true;
-	LastErrorEvent = *event;
-	return 0;
-}
-
-/*
- * XA_MIT_PRIORITY_COLORS     Create priority colors if necessary.
- * XA_WM_END_OF_ANIMATION     Used to throttle animation.
+#ifdef CAPTIVE
+/**
+ * Create a new window to use for a captive ctwm.
  */
-
-Atom XCTWMAtom[NUM_CTWM_XATOMS];
-
-void InternUsefulAtoms(void)
-{
-	XInternAtoms(dpy, XCTWMAtomNames, NUM_CTWM_XATOMS, False, XCTWMAtom);
-}
-
 static Window
 CreateCaptiveRootWindow(int x, int y,
                         unsigned int width, unsigned int height)
@@ -1492,9 +1391,11 @@ CreateCaptiveRootWindow(int x, int y,
 	XMapWindow(dpy, ret);
 	return (ret);
 }
+#endif
 
 
-/*
+
+/**
  * Return true if a window is not set to override_redirect ("Hey!  WM!
  * Leave those wins alone!"), and isn't unmapped.  Used during startup to
  * fake mapping for wins that should be up.
diff --git a/ctwm_shutdown.c b/ctwm_shutdown.c
new file mode 100644
index 0000000..91b8293
--- /dev/null
+++ b/ctwm_shutdown.c
@@ -0,0 +1,277 @@
+/*
+ * Shutdown (/restart) bits
+ */
+
+#include "ctwm.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "animate.h"
+#ifdef CAPTIVE
+#include "captive.h"
+#endif
+#include "colormaps.h"
+#include "ctwm_atoms.h"
+#include "ctwm_shutdown.h"
+#include "screen.h"
+#ifdef SESSION
+#include "session.h"
+#endif
+#ifdef SOUNDS
+# include "sound.h"
+#endif
+#include "otp.h"
+#include "win_ops.h"
+#include "win_utils.h"
+
+
+static void RestoreForShutdown(Time mytime);
+
+
+/**
+ * Put a window back where it should be if we don't (any longer) control
+ * it and reparent it back up to the root.  This leaves it where it was
+ * before we started (well, adjusted by any moves we've made to it
+ * since), and placed so that if we restart and take it back over, it'll
+ * wind up right where it is now, so restarting doesn't shift windows all
+ * over the place.
+ */
+void
+RestoreWinConfig(TwmWindow *tmp)
+{
+	int gravx, gravy;
+	int newx, newy;
+
+	// Things adjusting by the border have to move our border size, but
+	// subtract away from that the old border we're restoring.
+	const int borders = tmp->frame_bw + tmp->frame_bw3D - tmp->old_bw;
+
+	// If this window is "unmapped" by moving it way offscreen, and is in
+	// that state, move it back onto the window.
+	if(tmp->UnmapByMovingFarAway && !visible(tmp)) {
+		XMoveWindow(dpy, tmp->frame, tmp->frame_x, tmp->frame_y);
+	}
+
+	// If it's squeezed, unsqueeze it.
+	if(tmp->squeezed) {
+		Squeeze(tmp);
+	}
+
+	// This is apparently our standard "is this window still around?"
+	// idiom.
+	if(XGetGeometry(dpy, tmp->w, &JunkRoot, &JunkX, &JunkY,
+	                &JunkWidth, &JunkHeight, &JunkBW, &JunkDepth) == 0) {
+		// Well, give up...
+		return;
+	}
+
+	// Get gravity bits to know how to move stuff around when we take
+	// away the decorations.
+	GetGravityOffsets(tmp, &gravx, &gravy);
+
+	// We want to move the window to where it should be "outside" of our
+	// frame.  This varies depending on the window gravity detail, and we
+	// have to account for that, since on re-startup we'll be doing it to
+	// resposition it after we re-wrap it.
+	//
+	// e.g., in simple "NorthWest" gravity, we just made the frame start
+	// where the window did, and shifted the app window right (by the
+	// border width) and down (by the border width + titlebar).  However,
+	// "SouthEast" gravity means the bottom right of the frame is where
+	// the windows' was, and the window itself shifted left/up by the
+	// border.  Compare e.g. an xterm with -geometry "+0+0" with one
+	// using "-0-0" as an easy trick to make windows with different
+	// geoms.
+	newx = tmp->frame_x;
+	newy = tmp->frame_y;
+
+
+	// So, first consider the north/south gravity.  If gravity is North,
+	// we put the top of the frame where the window was and shifted
+	// everything inside down, so the top of the frame now is where the
+	// window should be put.  With South-y gravity, the window should
+	// wind up at the bottom of the frame, which means we need to shift
+	// it down by the titlebar height, plus twice the border width.  But
+	// if the vertical gravity is neutral, then the window needs to wind
+	// up staying right where it is, because we built the frame around it
+	// without moving it.
+	//
+	// Previous code here (and code elsewhere) expressed this calculation
+	// by the rather confusing idiom ((gravy + 1) * border_width), which
+	// gives the right answer, but is confusing as hell...
+	if(gravy < 0) {
+		// North; where the frame starts (already set)
+	}
+	else if(gravy > 0) {
+		// South; shift down title + 2*border
+		newy += tmp->title_height + 2 * borders;
+	}
+	else {
+		// Neutral; down by the titlebar + border.
+		newy += tmp->title_height + borders;
+	}
+
+
+	// Now east/west.  West means align with the frame start, east means
+	// align with the frame right edge, neutral means where it already
+	// is.
+	if(gravx < 0) {
+		// West; it's already correct
+	}
+	else if(gravx > 0) {
+		// East; shift over by 2*border
+		newx += 2 * borders;
+	}
+	else {
+		// Neutral; over by the left border
+		newx += borders;
+	}
+
+
+#ifdef WINBOX
+	// If it's in a WindowBox, reparent the frame back up to our real root
+	if(tmp->winbox && tmp->winbox->twmwin && tmp->frame) {
+		int xbox, ybox;
+		unsigned int j_bw;
+
+		// XXX This isn't right, right?  This will give coords relative
+		// to the window box, but we're using them relative to the real
+		// screen root?
+		if(XGetGeometry(dpy, tmp->frame, &JunkRoot, &xbox, &ybox,
+		                &JunkWidth, &JunkHeight, &j_bw, &JunkDepth)) {
+			ReparentWindow(dpy, tmp, WinWin, Scr->Root, xbox, ybox);
+		}
+	}
+#endif
+
+
+	// Restore the original window border if there were one
+	if(tmp->old_bw) {
+		XSetWindowBorderWidth(dpy, tmp->w, tmp->old_bw);
+	}
+
+	// Reparent and move back to where it otter be
+	XReparentWindow(dpy, tmp->w, Scr->Root, newx, newy);
+
+	// If it came with a pre-made icon window, hide it
+	if(tmp->wmhints->flags & IconWindowHint) {
+		XUnmapWindow(dpy, tmp->wmhints->icon_window);
+	}
+
+	// Done
+	return;
+}
+
+
+/**
+ * Restore some window positions/etc in preparation for going away.
+ */
+static void
+RestoreForShutdown(Time mytime)
+{
+	ScreenInfo *savedScr = Scr;  // We need Scr flipped around...
+
+	XGrabServer(dpy);
+
+	for(int scrnum = 0; scrnum < NumScreens; scrnum++) {
+		if((Scr = ScreenList[scrnum]) == NULL) {
+			continue;
+		}
+
+		// Force reinstalling any colormaps
+		InstallColormaps(0, &Scr->RootColormaps);
+
+		// Pull all the windows out of their frames and reposition them
+		// where the frame was, with approprate adjustments for gravity.
+		// This will preserve their positions when we restart, or put
+		// them back where they were before we started.  Do it from the
+		// bottom up of our stacking order to preserve the stacking.
+		for(TwmWindow *tw = OtpBottomWin() ; tw != NULL
+		                ; tw = OtpNextWinUp(tw)) {
+			if(tw->isiconmgr || tw->iswspmgr || tw->isoccupy) {
+				// Don't bother with internals...
+				continue;
+			}
+			RestoreWinConfig(tw);
+		}
+	}
+
+	XUngrabServer(dpy);
+
+	// Restore
+	Scr = savedScr;
+
+	// Focus away from any windows
+	SetFocus(NULL, mytime);
+}
+
+
+/**
+ * Cleanup and exit ctwm
+ */
+void
+DoShutdown(void)
+{
+
+#ifdef SOUNDS
+	// Denounce ourselves
+	play_exit_sound();
+#endif
+
+	// Restore windows/colormaps for our absence.
+	RestoreForShutdown(CurrentTime);
+
+#ifdef EWMH
+	// Clean up EWMH properties
+	EwmhTerminate();
+#endif
+
+	// Clean up our list of workspaces
+	XDeleteProperty(dpy, Scr->Root, XA_WM_WORKSPACESLIST);
+
+#ifdef CAPTIVE
+	// Shut down captive stuff
+	if(CLarg.is_captive) {
+		RemoveFromCaptiveList(Scr->captivename);
+	}
+#endif
+
+	// Close up shop
+	XCloseDisplay(dpy);
+	exit(0);
+}
+
+
+/**
+ * exec() ourself to restart.
+ */
+void
+DoRestart(Time t)
+{
+	// Don't try to do any further animating
+	StopAnimation();
+	XSync(dpy, 0);
+
+	// Replace all the windows/colormaps as if we were going away.  'cuz
+	// we are.
+	RestoreForShutdown(t);
+	XSync(dpy, 0);
+
+#ifdef SESSION
+	// Shut down session management connection cleanly.
+	shutdown_session();
+#endif
+
+	// Re-run ourself
+	fprintf(stderr, "%s:  restarting:  %s\n", ProgramName, *Argv);
+	execvp(*Argv, Argv);
+
+	// Uh oh, we shouldn't get here...
+	fprintf(stderr, "%s:  unable to restart:  %s\n", ProgramName, *Argv);
+
+	// We should probably un-RestoreForShutdown() etc.  If that exec
+	// fails, we're in a really weird state...
+	XBell(dpy, 0);
+}
diff --git a/ctwm_shutdown.h b/ctwm_shutdown.h
new file mode 100644
index 0000000..c91d4fe
--- /dev/null
+++ b/ctwm_shutdown.h
@@ -0,0 +1,12 @@
+/*
+ * Shutdown (and restart) bits
+ */
+#ifndef _CTWM_SHUTDOWN_H
+#define _CTWM_SHUTDOWN_H
+
+void RestoreWinConfig(TwmWindow *tmp);
+void DoShutdown(void) __attribute__((noreturn));
+void DoRestart(Time t);
+
+
+#endif /* _CTWM_SHUTDOWN_H */
diff --git a/ctwm_takeover.c b/ctwm_takeover.c
new file mode 100644
index 0000000..7157d5d
--- /dev/null
+++ b/ctwm_takeover.c
@@ -0,0 +1,146 @@
+/*
+ * Taking over the screen
+ */
+
+#include "ctwm.h"
+
+#include <stdio.h>
+#include <X11/Xproto.h>
+#include <X11/Xmu/Error.h>
+
+#include "ctwm_takeover.h"
+#include "screen.h"
+
+
+/// Flag for "we got an error trying to take over".  Set in temporary
+/// error handler.
+static bool RedirectError;
+
+// Our special during-takeover and normal operating error handlers.
+static int CatchRedirectError(Display *display, XErrorEvent *event);
+static int TwmErrorHandler(Display *display, XErrorEvent *event);
+
+
+
+/**
+ * Take over as WM for a screen
+ */
+bool
+takeover_screen(ScreenInfo *scr)
+{
+	unsigned long attrmask;
+
+#ifdef EWMH
+	// Early EWMH setup.  This tries to do the EWMH display takeover.
+	EwmhInitScreenEarly(scr);
+#endif
+
+
+	/*
+	 * Subscribe to various events on the root window.  Because X
+	 * only allows a single client to subscribe to
+	 * SubstructureRedirect and ButtonPress bits, this also serves to
+	 * mutex who is The WM for the root window, and thus (aside from
+	 * captive) the Screen.
+	 *
+	 * To catch whether that failed, we set a special one-shot error
+	 * handler to flip a var that we test to find out whether the
+	 * redirect failed.
+	 */
+	// Flush out any previous errors
+	XSync(dpy, 0);
+
+	// Set our event listening mask
+	RedirectError = false;
+	XSetErrorHandler(CatchRedirectError);
+	attrmask = ColormapChangeMask | EnterWindowMask |
+	           PropertyChangeMask | SubstructureRedirectMask |
+	           KeyPressMask | ButtonPressMask | ButtonReleaseMask;
+#ifdef EWMH
+	attrmask |= StructureNotifyMask;
+#endif
+#ifdef CAPTIVE
+	if(CLarg.is_captive) {
+		attrmask |= StructureNotifyMask;
+	}
+#endif
+	XSelectInput(dpy, scr->Root, attrmask);
+
+	// Make sure we flush out any errors that may have caused.  This
+	// ensures our RedirectError flag will be set if the server sent us
+	// one.
+	XSync(dpy, 0);
+
+	// Go ahead and set our normal-operation error handler.
+	XSetErrorHandler(TwmErrorHandler);
+
+	// So, did we fail?
+	if(RedirectError) {
+		fprintf(stderr, "%s: another window manager is already running",
+		        ProgramName);
+		if(CLarg.MultiScreen && NumScreens > 0) {
+			fprintf(stderr, " on screen %d?\n", scr->screen);
+		}
+		else {
+			fprintf(stderr, "?\n");
+		}
+
+		// XSetErrorHandler() isn't local to the Screen; it's for the
+		// whole connection.  We wind up in a slightly weird state
+		// once we've set it up, but decided we aren't taking over
+		// this screen, but resetting it would be a little weird too,
+		// because maybe we have taken over some other screen.  So,
+		// just throw up our hands.
+		return false;
+	}
+
+	// Nope, it's ours!
+	return true;
+}
+
+
+
+/**
+ * Temporary error handler used during startup.  We expect an
+ * error if we fail to take over some of the XSelectInput() events
+ * we're trying to (which only 1 thing at a time is allowed to).
+ * Probably that would be a BadAccess error type?  But really, any error
+ * means we're in trouble and should skip over the display, so we don't
+ * check any more deeply...
+ */
+static int
+CatchRedirectError(Display *display, XErrorEvent *event)
+{
+	// Set the global flag
+	RedirectError = true;
+	return 0;
+}
+
+
+/**
+ * Error handler used in normal operation.  Or, perhaps, error ignorer
+ * used in normal operation.  If run with `-v`, we'll print out a lot of
+ * the errors we might get, though we always skip several.
+ */
+static int
+TwmErrorHandler(Display *display, XErrorEvent *event)
+{
+	if(!CLarg.PrintErrorMessages) {
+		// Not `-v`?  Always be silent.
+		return 0;
+	}
+
+	// If a client dies and we try to touch it before we notice, we get a
+	// BadWindow error for most operations, except a few (GetGeometry
+	// being the big one?) where we'll get a BadDrawable.  This isn't
+	// really an "error", just a harmless race.
+	if(event->error_code == BadWindow
+	                || (event->request_code == X_GetGeometry && event->error_code != BadDrawable)) {
+		return 0;
+	}
+
+	// Else, we're `-v`'d, and it wasn't one of our 'expected' bits, so
+	// talk about it.
+	XmuPrintDefaultErrorMessage(display, event, stderr);
+	return 0;
+}
diff --git a/ctwm_takeover.h b/ctwm_takeover.h
new file mode 100644
index 0000000..f4ce80b
--- /dev/null
+++ b/ctwm_takeover.h
@@ -0,0 +1,9 @@
+/*
+ * Take over the screen
+ */
+#ifndef _CTWM_TAKEOVER_H
+#define _CTWM_TAKEOVER_H
+
+bool takeover_screen(ScreenInfo *scr);
+
+#endif /* _CTWM_TAKEOVER_H */
diff --git a/ctwm_test.h b/ctwm_test.h
new file mode 100644
index 0000000..c3e3918
--- /dev/null
+++ b/ctwm_test.h
@@ -0,0 +1,22 @@
+/**
+ * Special magic for use in tests.  If you're not one of our tests, you
+ * probably shouldn't be including this file.  Or even looking at it.
+ * Avert your eyes!
+ */
+
+#ifndef _CTWM_CTWM_TEST_H
+#define _CTWM_CTWM_TEST_H
+
+// extern's for our magic flag and callback connections
+extern int (*ctwm_test_postparse)(void);
+extern bool ctwm_test;
+
+
+// Provide a macro for hooking up callback
+#define TEST_POSTPARSE(cb) do { \
+               ctwm_test = true; \
+               ctwm_test_postparse = (cb); \
+        } while(0)
+
+
+#endif // _CTWM_CTWM_TEST_H
diff --git a/cursor.c b/cursor.c
index a443084..3a257e3 100644
--- a/cursor.c
+++ b/cursor.c
@@ -117,12 +117,19 @@ static struct _CursorName {
 void NewFontCursor(Cursor *cp, const char *str)
 {
 	int i;
+	const Display *ldpy = dpy;  // Give compiler help to hoist
 
 	for(i = 0; i < sizeof(cursor_names) / sizeof(struct _CursorName); i++) {
 		if(strcmp(str, cursor_names[i].name) == 0) {
-			if(cursor_names[i].cursor == None)
+			if(ldpy == NULL) {
+				// No display connection, but we found it
+				*cp = None;
+				return;
+			}
+			if(cursor_names[i].cursor == None) {
 				cursor_names[i].cursor = XCreateFontCursor(dpy,
 				                         cursor_names[i].shape);
+			}
 			*cp = cursor_names[i].cursor;
 			return;
 		}
@@ -140,6 +147,12 @@ int NewBitmapCursor(Cursor *cp, char *source, char *mask)
 	Pixmap spm, mpm;
 	Colormap cmap = Scr->RootColormaps.cwins[0]->colormap->c;
 
+	if(dpy == NULL) {
+		// Handle special cases like --cfgchk
+		*cp = None;
+		return 0;
+	}
+
 	fore.pixel = Scr->Black;
 	XQueryColor(dpy, cmap, &fore);
 	back.pixel = Scr->White;
diff --git a/debian/changelog b/debian/changelog
index 2b41474..acfef6f 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+ctwm (4.1.0-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Mon, 17 Apr 2023 16:56:14 -0000
+
 ctwm (4.0.3-2) unstable; urgency=medium
 
   * QA upload.
diff --git a/doc/devman/index.adoc b/doc/devman/index.adoc
index fc1fab7..99a722b 100644
--- a/doc/devman/index.adoc
+++ b/doc/devman/index.adoc
@@ -14,6 +14,9 @@ reminded of.
 * <<functions.adoc#,f.functions>> talks about how the various
 `f.whatever` functions are dispatched and how you'd go about writing one.
 
+* <<rlayout.adoc#,RLayout>> describes the functions and data structures
+involving `RArea` and `RLayout` bits.
+
 * <<principles.adoc#,Principles Of Operation>> is a pre-existing dev
 document that covers a number of areas.  Probably over time it'll get
 piecewise integrated into bigger individual sections.
diff --git a/doc/devman/mkmk.sh b/doc/devman/mkmk.sh
index 30f6cb7..cd880a8 100755
--- a/doc/devman/mkmk.sh
+++ b/doc/devman/mkmk.sh
@@ -21,6 +21,7 @@ all: \${BDIR}
 
 \${BDIR}:
 	mkdir -p \${BDIR}
+	(cd \${BDIR} && ln -s ../static .)
 
 clean:
 	rm -rf \${BDIR}
diff --git a/doc/devman/rlayout.adoc b/doc/devman/rlayout.adoc
new file mode 100644
index 0000000..586dc4b
--- /dev/null
+++ b/doc/devman/rlayout.adoc
@@ -0,0 +1,182 @@
+# RLayout
+
+
+## TODO
+
+This should be expanded to walk a little more through the codepaths the
+various examples take, with some detail of the structure contents in the
+various cases.
+
+Also, the meat of the examples should probably migrate into user-level
+documentation at some point, when we have things in shape to do that
+(_i.e._, not having a manpage as the main user doc).
+
+
+## Summary
+
+The RLayout code came in as part of the support for the RandR X
+extension, but isn't tied to RandR itself.  Rather, it's a generalized
+scheme for describing and determining intersecting layouts of abstract
+things.
+
+In practice, most uses boil down to a description of the layout of your
+monitor[s], and its functions are used to map windows onto them, figure
+out which monitor[s] the window is on, and figure out what it would mean
+to `f.*zoom` that window in various ways, either keeping it on a single
+monitor or crossing multiple.
+
+This document attempts to give an overview of the pieces defined in the
+various `r_area*.[ch]` and `r_layout.[ch]` files.  There is fairly
+extensive documentation in Doxygen comments on the structures and
+functions (x-ref <<doxygen.adoc,Doxygen bits>> in this manual for how to
+build and use them), as well as fairly extensive narrative comments
+through the code.  This document won't attempt to cover all those sort of
+details, but rather to give a high level view of the goals of the code.
+
+
+
+## Data Structures
+
+### RArea
+
+`RArea` defines an area of space, given by X/Y coordinates, and
+width/height extents.  In the global layout sense, each monitor is
+represented by an RArea giving its position on the whole desktop space.
+When positioning or sizing windows, an (emphemeral) `RArea` is created
+defining the window's size/position, to be used by the various
+`RArea*()` or `RLayout*()` functions.
+
+### RAreaList
+
+`RAreaList` is just a container for a set of ``RArea``'s.  It's used
+anywhere we need to build or pass around lists of ``RArea``'s.  Commonly
+these will involve monitors.  _e.g._, the list of all your monitors is an
+`RAreaList`, as would be the result of asking "`Which monitor[s] is this
+window being displayed on?`", etc.
+
+
+### RLayout
+
+`RLayout` is used to hold derived attributes of an `RAreaList`.  This
+generally means "`the layout of your monitors`".  It holds the list of
+monitors, and also separately (if your setup provides such) the list of
+output names provided by RandR, which can be used to set the geometry
+of things like icon managers in an output-relative way.
+
+It also contains pre-built ``RAreaList``'s of all the rectangular
+horizontal and vertical stripes that make up the space covered by the
+union of your monitors.  This is used in calculating horizontal and
+vertical ``f.zoom``'s of windows, so they cover the maximum available
+space without stretching outside the area covered by monitors.  This is
+important when your monitor setup doesn't make a single rectangle
+(_e.g._, you have 2 monitors with different resolutions).
+
+
+## Examples
+
+Let's consider an example layout.
+
+image::static/monitors.svg#v_monitors=1[opts=interactive]
+
+When starting up, ctwm will find the maximal horizontal:
+
+image::static/monitors.svg#v_hstripe=1[opts=interactive]
+
+and vertical:
+
+image::static/monitors.svg#v_vstripe=1[opts=interactive]
+
+stripes.  This get stashed up in the `RLayout` for the Screen, and will
+be used when figuring various zooms.
+
+
+### Zooming
+
+Let's put a window on the screen, and see what various ``f.zoom``'s will
+do to it.
+
+#### Horizontal zooming
+
+image::static/monitors.svg#v_monitors=1&v_xwin=1&g_xwin_x=300&g_xwin_y=40[opts=interactive]
+
+This window is now _mostly_ on `Mon1`, and mostly on the "top half" of
+things.  If we `f.horizoom` it, it thus zooms up to the full width of
+`Mon1`:
+
+image::static/monitors.svg#v_monitors=1&v_xwin=1&g_xwin_w=400&g_xwin_x=3&g_xwin_y=40[opts=interactive]
+
+But if we `f.xhorizoom` it, it takes up the full width of the horizontal
+stripe it's on (_note showing stripes in the background here, not
+monitors like the surrounding images_):
+
+image::static/monitors.svg#v_hstripe=1&v_xwin=1&g_xwin_w=600&g_xwin_x=3&g_xwin_y=40[opts=interactive]
+
+Or, if we moved it over so most of it was on `Mon2` instead,
+
+image::static/monitors.svg#v_monitors=1&v_xwin=1&g_xwin_x=350&g_xwin_y=40[opts=interactive]
+
+then the `f.horizoom` would zoom it up to the full width of `Mon2`:
+
+image::static/monitors.svg#v_monitors=1&v_xwin=1&g_xwin_w=200&g_xwin_x=403&g_xwin_y=40[opts=interactive]
+
+and `f.xhorizoom` would do the same thing as above.
+
+
+#### Vertical zooming
+
+Imagine the same thing, except adjusting the height on the vertical stripes.
+
+
+#### Full zooming
+
+Now let's consider `f.fullscreenzoom` and its `x` variant.
+
+image::static/monitors.svg#v_monitors=1&v_xwin=1&g_xwin_x=300&g_xwin_y=100[opts=interactive]
+
+The window is still mostly on `Mon1`.  Note that part of the window is
+currently in dead space, and so not visible.  If `f.fullscreenzoom` it,
+because it's mostly on `Mon1`, it will cover that monitor completely:
+
+image::static/monitors.svg#v_monitors=1&v_xwin=1&g_xwin_w=400&g_xwin_h=200&g_xwin_x=3&g_xwin_y=3[opts=interactive]
+
+Or, if we pushed it over to be mostly on `Mon2`,
+
+image::static/monitors.svg#v_monitors=1&v_xwin=1&g_xwin_x=350&g_xwin_y=100[opts=interactive]
+
+then `f.fullscreenzoom` will fill that out:
+
+image::static/monitors.svg#v_monitors=1&v_xwin=1&g_xwin_w=200&g_xwin_h=400&g_xwin_x=403&g_xwin_y=3[opts=interactive]
+
+`f.xfullscreenzoom`, like the other `x` variants, will span monitors.  In
+the case of `fullscreenzoom`, it will consider both the horizontal and
+vertical stripes the window lives in, and fill it over whichever makes
+the window bigger.
+
+In both cases above, the top horizontal stripe is the biggest; it's a
+full monitor size, plus half of another.  The bottom horizontal stripe is
+only half a monitor's size, and both vertical stripes are the same size
+as a monitor.  So ``f.xfullscreenzoom``'ing the window will make it cover
+up the whole top horizontal stripe:
+
+image::static/monitors.svg#v_monitors=1&v_xwin=1&g_xwin_w=600&g_xwin_h=200&g_xwin_x=3&g_xwin_y=3[opts=interactive]
+
+If, however, we first moved it down so it didn't occupy the top
+horizontal stripe at all,
+
+image::static/monitors.svg#v_monitors=1&v_xwin=1&g_xwin_x=350&g_xwin_y=225[opts=interactive]
+
+then the largest stripe available for it to fill would be the right
+vertical stripe, leading to the same result as the right-size
+`f.fullscreenzoom` above:
+
+image::static/monitors.svg#v_monitors=1&v_xwin=1&g_xwin_w=200&g_xwin_h=400&g_xwin_x=403&g_xwin_y=3[opts=interactive]
+
+In this case, it's technically filling `Vstripe2`, rather than `Mon2`,
+but in the case of the particular layout we're working with, it's the
+same thing.
+
+In the case where your layout doesn't have any dead space (_e.g._, 2
+same-resolution monitors stacked horizontally or vertically, 4
+same-resolution monitors in a square, etc), there will only be one
+horizontal and one vertical stripe, which cover the whole desktop area.
+So `f.xfullscreenzoom` will always wind up covering the whole thing.
diff --git a/doc/devman/static/monitors.svg b/doc/devman/static/monitors.svg
new file mode 100644
index 0000000..c467896
--- /dev/null
+++ b/doc/devman/static/monitors.svg
@@ -0,0 +1,181 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg version="1.1" baseProfile="full" width="606" height="406"
+  xmlns="http://www.w3.org/2000/svg">
+ <style type="text/css">
+  rect {
+   fill: #DDDDDD;
+   stroke: #000000;
+   stroke-width: 4;
+  }
+  rect.xwin {
+   fill: #000000;
+  }
+  polyline {
+   fill: none;
+   stroke: #000000;
+  }
+ </style>
+
+ <!-- Monitors -->
+ <rect width="400" height="200" x="3" y="3" class="monitors" />
+ <text x="15" y="25" class="monlabel">Mon1</text>
+ <rect width="200" height="400" x="403" y="3" class="monitors" />
+ <text x="585" y="25" text-anchor="end" class="monlabel">Mon2</text>
+
+ <!-- Fill out the rest of the root -->
+ <polyline points="2,202 2,404 402,404" class="monitors"
+   style="stroke-width: 2; stroke-dasharray: 2,3;" />
+ <text x="15" y="385" class="monlabel">(dead space)</text>
+
+ <!-- Horizontal stripes -->
+ <rect width="600" height="200" x="3" y="3" class="hstripe" />
+ <text x="15" y="25" class="hslabel">Hstripe1</text>
+ <rect width="200" height="200" x="403" y="203" class="hstripe" />
+ <text x="585" y="385" text-anchor="end" class="hslabel">Hstripe2</text>
+
+ <!-- Vertical stripes -->
+ <rect width="400" height="200" x="3" y="3" class="vstripe" />
+ <text x="15" y="25" class="vslabel">Vstripe1</text>
+ <rect width="200" height="400" x="403" y="3" class="vstripe" />
+ <text x="585" y="385" text-anchor="end" class="vslabel">Vstripe2</text>
+
+ <!-- A window to position -->
+ <rect width="150" height="150" x="25" y="25" class="xwin" id="xwin" />
+
+
+
+ <!-- JSery to grub out query params -->
+ <script type="text/javascript"><![CDATA[
+	// Parameters we expect and their defaults
+	var params = {
+		// Visibility
+		v_monitors: 0,
+		v_monlabel: 0,
+		v_hstripe: 0,
+		v_hslabel: 0,
+		v_vstripe: 0,
+		v_vslabel: 0,
+		v_xwin: 0,
+
+		// Opacity
+		o_monitors: 1.0,
+		o_hstripe:  1.0,
+		o_vstripe:  1.0,
+		o_xwin:     0.5,
+
+		// Flying around xtra window
+		g_xwin_w: 150,
+		g_xwin_h: 150,
+		g_xwin_x: 25,
+		g_xwin_y: 25,
+	};
+
+	// Stash keys
+	var pkeys = Object.keys(params);
+
+	// Params we were actually given (rather than defaulting)
+	var qpset = {};
+
+	// Parse params
+	(function() {
+		var qstr = window.location.hash.substring(1);
+		var xtr_re = /([^&=]+)=?([^&]*)/g;
+		var unencode = function (s) {
+			return decodeURIComponent(s.replace(/\+/g, " "));
+		};
+		var qv;
+
+		while(qv = xtr_re.exec(qstr)) {
+			if(typeof params[qv[1]] === 'undefined') {
+				console.log("Unexpected param: " + qv[1]);
+				continue;
+			}
+
+			params[unencode(qv[1])] = unencode(qv[2]);
+			qpset[unencode(qv[1])] = 1;
+		}
+	})();
+
+	// Do some defaulting
+	if(!qpset.v_monlabel)
+		params.v_monlabel = params.v_monitors;
+	if(!qpset.v_vslabel)
+		params.v_vslabel = params.v_vstripe;
+	if(!qpset.v_hslabel)
+		params.v_hslabel = params.v_hstripe;
+
+	function do_vis(fld) {
+		var vis;
+
+		// All vis params are v_
+		if(fld.indexOf('v_') !== 0) {
+			return;
+		}
+
+		if(typeof params[fld] === 'undefined')
+			return;
+		if(params[fld] != 0)
+			vis = 'visible';
+		else
+			vis = 'hidden';
+
+		var elclass = fld.substring(2);
+		var els = document.getElementsByClassName(elclass);
+		for(var i = 0 ; i < els.length ; i++) {
+			els[i].style.visibility = vis;
+		}
+
+		return;
+	}
+
+	function do_opacity(fld) {
+		// All opaq params are o_
+		if(fld.indexOf('o_') !== 0) {
+			return;
+		}
+
+		if(typeof params[fld] === 'undefined')
+			return;
+
+		var elclass = fld.substring(2);
+		var els = document.getElementsByClassName(elclass);
+		for(var i = 0 ; i < els.length ; i++) {
+			els[i].style.opacity = params[fld];
+		}
+
+		return;
+	}
+
+	function handle_geom(wname) {
+		var tw = document.getElementById(wname);
+		if(typeof tw === 'undefined' || tw == null) {
+			console.log("Can't find handle_geom(" + wname + ")");
+			return;
+		}
+
+		tw.setAttribute('width',  params["g_" + wname + "_w"]);
+		tw.setAttribute('height', params["g_" + wname + "_h"]);
+		tw.setAttribute('x',      params["g_" + wname + "_x"]);
+		tw.setAttribute('y',      params["g_" + wname + "_y"]);
+
+		return;
+	}
+
+	// Init
+	for(var i = 0 ; i < pkeys.length ; i++) {
+		var pk = pkeys[i];
+
+		if(pk.indexOf('v_') == 0) {
+			do_vis(pkeys[i]);
+		}
+		else if(pk.indexOf('o_') == 0) {
+			do_opacity(pkeys[i]);
+		}
+		else if(pk.indexOf('g_') == 0) {
+			// Geometry handling: do this separately
+		}
+	}
+
+	handle_geom('xwin');
+ ]]></script>
+</svg>
diff --git a/doc/manual/ctwm.1.adoc b/doc/manual/ctwm.1.adoc
index ed8dee6..43abceb 100644
--- a/doc/manual/ctwm.1.adoc
+++ b/doc/manual/ctwm.1.adoc
@@ -12,7 +12,6 @@ ctwm [(--display | -d) dpy]  [--replace]  [--single]
      [--nom4 | -n]  [(--keep-defs | -k)]  [(--keep | -K) m4file]
      [--verbose | -v]  [--quiet | -q]  [--mono]  [--xrm resource]
      [--version]  [--info]  [--nowelcome | -W]
-     [(--window | -w) [win-id]]  [--name name]
      [--clientId clid]  [--restore resfname]
      [--help | -h]
 
@@ -36,7 +35,8 @@ This program is usually started by the user's session manager or
 startup script.  When used from `xdm(1)` or `xinit(1)` without
 a session manager, ctwm is frequently executed in the foreground
 as the last client.  When run this way, exiting ctwm causes the
-session to be terminated (i.e. logged out).
+X session to be terminated, shutting down the X server and killing off
+any other running clients.
 
 By default, application windows are surrounded by a ``frame'' with a
 titlebar at the top and a special border around the window.  The titlebar
@@ -146,21 +146,6 @@ to touch the bottom the screen.
 --info::
   ctwm prints its detailed version and compile time options.
 
---window[=`win-id`], -w[`win-id`]::
-  If `-w` is specified without a win-id value, ctwm does not take over
-  the whole screen(s); instead it creates a new window that becomes its
-  root window.  If the win-id value is given, it is considered to be the
-  id of an existing window, in which case, ctwm will try to use this
-  window as root window.  You can run any number of instantiations of
-  ctwm at the same time.  You can even have embedded ctwm instantiations.
-  This is totally useless, but I like it.  The f.adoptwindow function can
-  be used to capture an existing window belonging to another ctwm.  A
-  possible use of such mode can be to test new configuration file without
-  restarting ctwm.
-
---name=`name`, -n `name`::
-  Set the captivename when using `--window`.
-
 --nowelcome, -W::
   This option tells ctwm not to display any welcome when starting.
 
@@ -176,14 +161,6 @@ means that args can be passed via `--long=arg` and `--long arg`, as well
 as `-l arg` and `-larg`, and short args can be bundled like `-vnk` as
 well as `-v -n -k`.
 
-====
-Note, however, that the handling of optional args is slightly different;
-they *must* be specified with = or no space.  e.g., `--window=123` will
-set the win-id to 123, but `--window 123` will not; the 123 will be
-treated as a separate argument.  Similarly, it must be set as `-w123`,
-not `-w 123`.
-====
-
 
 == CUSTOMIZATION
 Much of ctwm's appearance and behavior can be controlled by providing
@@ -390,22 +367,20 @@ I18N::
   optional since 3.8 and is always compiled in.  The definition will be
   removed in a future version.
 
-=== ctwm run-time options
-
-TWM_CAPTIVE::
-  This will be either ``Yes'' or ``No''. ``Yes'' if the current ctwm is captive
-  (flag -w), ``No'' in the other case.
-
-TWM_CAPTIVE_NAME::
-  Defined only if TWM_CAPTIVE is also defined. Contains the name of the captive
-  ctwm (flag --name).
-
 === Obsolete options
 
 GNOME::
   Defined when ctwm was compiled with GNOME1 support.  Removed after
   3.8.2.
 
+TWM_CAPTIVE::
+  This was either ``Yes'' or ``No''. ``Yes'' if the current ctwm is captive
+  (flag -w), ``No'' in the other case.  Removed in 4.1.0.
+
+TWM_CAPTIVE_NAME::
+  Defined only if TWM_CAPTIVE is also defined. Contains the name of the captive
+  ctwm (flag --name).  Removed in 4.1.0.
+
 
 == VARIABLES
 Many of the aspects of ctwm's user interface are controlled by variables
@@ -968,7 +943,10 @@ IconManagerGeometry `string` [ `columns` ]::
   the initial full size of the icon manager.  The icon manager window is
   then broken into `columns` pieces and scaled according to the number
   of entries in the icon manager.  Extra entries are wrapped to form
-  additional rows.  The default number of columns is 1.
+  additional rows.  The default number of columns is 1. When XrandR is
+  compiled, the geometry can be relative to a monitor, by prefixing
+  its name (visible with `xrandr(1)` command line) followed by `:`. This name
+  is ignored when XrandR is not available.
 
 IconManagerHighlight `string` [{ `win-list` }]::
   This variable specifies the border color to be used when highlighting
@@ -989,17 +967,20 @@ IconManagers { `iconmgr-list` }::
 ------
 +
 [normal]
-  where `winname` is the name of the windows that should be put into this
-  icon manager, `iconname` is the name of that icon manager window's icon,
-  `geometry` is a standard geometry specification, and `columns` is
-  the number of columns in this icon manager as described in
-  `IconManagerGeometry`.  For example:
+  where `winname` is the name of the windows that should be put into
+  this icon manager, `iconname` is the name of that icon manager
+  window's icon, `geometry` is a standard geometry specification, and
+  `columns` is the number of columns in this icon manager as described
+  in `IconManagerGeometry`. When XrandR is compiled, the geometry can
+  be relative to a monitor, by prefixing its name (visible with
+  `xrandr(1)` command line) followed by `:`. This name is ignored when
+  XrandR is not available.  For example:
 +
 ------
 IconManagers
 {
-    "XTerm"  "=300x5+800+5"  5
-    "myhost" "=400x5+100+5"  2
+    "XTerm"  "=300x5+800+5"       5
+    "myhost" "HDMI2:400x5+100+5"  2
 }
 ------
 +
@@ -1023,7 +1004,10 @@ IconMenuDontShow { `win-list` }::
 IconRegion `geomstring` `vgrav hgrav gridwidth gridheight` [`iconjust`] [`iconregjust`] [`iconregalign`] [{ `win-list` }]::
   This variable specifies an area on the root window in which icons are placed
   if no specific icon location is provided by the client.  The `geomstring`
-  is a quoted string containing a standard geometry specification. If more than
+  is a quoted string containing a standard geometry specification. When XrandR
+  is compiled, the geometry can be relative to a monitor, by prefixing
+  its name (visible with `xrandr(1)` command line) followed by `:`.
+  This name is ignored when XrandR is not available. If more than
   one `IconRegion` lines are given, icons will be put into the succeeding
   icon regions when the first is full. The `vgrav` argument should be either
   `North` or `South`  and is used to control whether icons are
@@ -1203,6 +1187,45 @@ MenuTitleForeground `string`::
   can only be specified inside of a
   `Color` or `Monochrome` list.  The default is ``black''.
 
+MonitorLayout { `monitor-list` }::
+  This allows manually configuring what ctwm will consider as the list of
+  monitors.  If `XRANDR` support is compiled in (the default as of
+  4.1.0), and the `RANDR` extension is available on your server, then
+  `ctwm` will use that to determine the size and layout of your monitors.
++
+[normal]
+  However, if either is not the case, or you want to override the results
+  it returns, you can specify the names and layouts of your desired
+  ``monitors'' with this.  For instance, if you have a very wide monitor,
+  and would prefer to treat it as several narrower side-by-side monitors,
+  you could use this to tell ctwm to treat it that way.
++
+------
+MonitorLayout
+{
+    # Imagine a 5000x1000 monitor, that we want to split into two 2k wide
+    # sections at the far left and right, with a 1k wide section in the
+    # middle.
+    "Left:2000x1000+0+0"
+    "1000x1000+2000+0"       # Middle section unnamed
+    "Right:2000x1000+3000+0"
+}
+------
++
+[normal]
+  With `m4` support, you could even make it automatically split your full
+  display into 2 ``monitors'':
++
+-----
+define(LeftWidth, eval(WIDTH / 2))dnl
+define(RightWidth, eval(WIDTH - LeftWidth))dnl
+MonitorLayout
+{
+    "`AutoL:'LeftWidth`x'HEIGHT`+0+0'"
+    "`AutoR:'RightWidth`x'HEIGHT`+'LeftWidth`+0'"
+}
+-----
+
 Monochrome { `colors` }::
   This variable specifies a list of color assignments that should be made if
   the screen has a depth of 1.  See the description of `Color`.
@@ -1920,29 +1943,6 @@ UseThreeDTitles::
 UseThreeDWMap::
   Tells ctwm to use 3D for the small windows in the workspace map.
 
-VirtualScreens { `geometries-list` }::
-+
-[normal]
-  This variable specifies a list of geometries for virtual screens. Virtual screens
-  are designed to be used when you have several physical screens bound together
-  with the Xinerama X extension.
-+
-[normal]
-  `geometries-list` is a list of valid geometry strings, that correspond to
-  your actual physical screens.
-+
-[normal]
-  Example :
-+
-------
-VirtualScreens
-{
-    "1280x1024+0+0"
-    "1600x1200+1280+0"
-}
-------
-+
-
 WarpCursor [{ `win-list` }]::
   This variable indicates that the pointer should be warped into windows when
   they are deiconified.  If the optional `win-list` is given, the pointer
@@ -1978,26 +1978,16 @@ WarpUnmapped::
   binding that will pop a particular window (such as `xmh`), no matter
   where it is.  The default is for `f.warpto` to ignore iconified windows.
 
-WindowBox [{ `win-list` }]::
-  creates a new window called a box, where
-  all the client windows that match the windows list are opened in,
-  instead of the root window. This is useful to group small windows
-  in the same box (xload for instance)
-+
-------
-WindowBox "xloadbox" "320x100+0-0" {
-    "xload"
-}
-------
-+
-
 WindowGeometries { `win-list` }::
-  Used to give a default geometry to some clients :
+  Used to give a default geometry to some clients. When XrandR is
+  compiled, the geometry can be relative to a monitor, by prefixing
+  its name (visible with `xrandr(1)` command line) followed by `:`. This name
+  is ignored when XrandR is not available:
 +
 ------
 WindowGeometries {
     "Mozilla*"       "1000x800+10+10"
-    "jpilot*"        "800x600-0-0"
+    "jpilot*"        "HDMI1:800x600-0-0"
 }
 ------
 +
@@ -2037,11 +2027,15 @@ WorkSpaceFont `string`::
 WorkSpaceManagerGeometry `string` [ `columns` ]::
   This variable specifies the geometry of the workspace manager window. The
   `string` argument is standard geometry specification that indicates
-  the initial full size of the workspace manager. The `columns` argument
+  the initial full size of the workspace manager. When XrandR is
+  compiled, the geometry can be relative to a monitor, by prefixing
+  its name (visible with `xrandr(1)` command line) followed by `:`. This name
+  is ignored when XrandR is not available. The `columns` argument
   indicates the number of columns to use for the workspace manager window.
 +
 ------
 WorkSpaceManagerGeometry        "360x60+60-0" 8
+WorkSpaceManagerGeometry        "HDMI1:600x30+1235+0" 12
 ------
 +
 
@@ -2389,11 +2383,6 @@ f.addtoworkspace `string`::
   This function adds the selected window to the workspace whose name is
   `string`.
 
-f.adoptwindow::
-  This function asks for the user to select a window with the mouse, and then
-  adopt this window if it doesn't belong to the current ctwm. Useful only
-  with the -w flag.
-
 f.altcontext::
   Set the alternate context. The next key or button event ctwm receives will
   be interpreted using the alternate context. To define bindings in the alternate
@@ -2449,8 +2438,8 @@ f.beep::
 
 f.bottomzoom::
   This function stretches the bottom side of the window out to the bottom
-  edge of the screen, or restores the original size if the window was
-  already bottomzoom'd.
+  edge of the current monitor, or restores the original size if the
+  window was already bottomzoom'd.
 
 f.changepriority `rel-value`::
   Change the priority of a window by `rel-value` (enclosed within double
@@ -2549,10 +2538,6 @@ f.fill `string`::
   f.fill ``vertical'' sets the window status to ``zoomed'' and toggles, ie
   calling it again will reset the previous window size.
 
-f.fittocontent::
-  Can be used only with window boxes. The result is to have the box have the
-  minimal size that contains all its children windows.
-
 f.focus::
   This function toggles the keyboard focus of the server to the
   selected window, changing the focus rule from pointer-driven if necessary.
@@ -2574,15 +2559,15 @@ f.forwmapiconmgr::
 f.fullscreenzoom::
   This function is similar to the `f.fullzoom` function, except that it
   makes the client window (the part inside the frame) the size of the
-  screen, so the window decorations are off-screen.  This gives the same
-  visual effect as the window covering the whole screen with no
+  current monitor, so the window decorations are off-screen.  This gives
+  the same visual effect as the window covering the whole screen with no
   decorations.  If the window is already fullscreenzoom'd, it restores
   the original size.
 
 f.fullzoom::
   This function resizes the selected window to the full size of the
-  screen, or restores the original size if the window was already
-  fullzoom'd.
+  current monitor, or restores the original size if the window was
+  already fullzoom'd.
 
 f.function `string`::
   This function executes the user-defined function whose name is specified
@@ -2602,17 +2587,12 @@ f.hideworkspacemgr::
 
 f.horizoom::
   This function stretches the window so that it covers the whole width of
-  the screen, or restores the original size if the window was already
-  horizoom'd.
+  the current monitor, or restores the original size if the window was
+  already horizoom'd.
 
 f.htzoom::
   This function is a synonym for `f.topzoom`.
 
-f.hypermove::
-  Use this function to ``move'' a window between 2 captives ctwm (or between a
-  captive and the root ctwm). Of course 2 ctwms are completely different
-  universes. You have to go in hyperspace to achieve this, hence the name.
-
 f.hzoom::
   This function is a synonym for `f.horizoom`.
 
@@ -2655,8 +2635,8 @@ f.leftworkspace::
 
 f.leftzoom::
   This function stretches the left side of the window out to the left
-  edge of the screen, or restores the original size if the window was
-  already leftzoom'd.
+  edge of the current monitor, or restores the original size if the
+  window was already leftzoom'd.
 
 f.lower::
   This function lowers the selected window.
@@ -2703,6 +2683,10 @@ f.moveresize `geometry`::
   standard X geometry syntax (e.g. 200x300+150-0). Sets the current window
   to the specified geometry. The width and height are to be given in pixel,
   no base size or resize increment are used.
+  When XrandR is compiled, the geometry can be relative to a monitor,
+  by prefixing its name (visible with `xrandr(1)` command line) followed by
+  `:` (e.g. HDMI1:200x300+150-0). This name is ignored when XrandR is not
+  available.
 
 f.movetitlebar::
   If applied to a squeezed titlebar (see `SqueezeTitle`) you can drag
@@ -2841,8 +2825,8 @@ f.rightworkspace::
 
 f.rightzoom::
   This function stretches the right side of the window out to the right
-  edge of the screen, or restores the original size if the window was
-  already rightzoom'd.
+  edge of the current monitor, or restores the original size if the
+  window was already rightzoom'd.
 
 f.ring::
   Selects a window and adds it to the WarpRing, or removes it if it
@@ -2949,8 +2933,8 @@ f.toggleworkspacemgr::
   it is mapped, it will be unmapped and vice versa.
 
 f.topzoom::
-  This function stretches the top side of the window out to the top
-  edge of the screen, or restores the original size if the window was
+  This function stretches the top side of the window out to the top edge
+  of the current monitor, or restores the original size if the window was
   already topzoom'd.
 
 f.trace `string`::
@@ -3032,10 +3016,42 @@ f.winrefresh::
   This function is similar to the `f.refresh` function except that only the
   selected window is refreshed.
 
+f.xbottomzoom::
+  This function is similar to the `f.bottomzoom` function, but will cross
+  monitors.
+
+f.xfullscreenzoom::
+  This function is similar to the `f.fullscreenzoom` function, but will
+  cross monitors.
+
+f.xfullzoom::
+  This function is similar to the `f.fullzoom` function, but will cross
+  monitors.
+
+f.xhorizoom::
+  This function is similar to the `f.horizoom` function, but will cross
+  monitors.
+
+f.xleftzoom::
+  This function is similar to the `f.leftzoom` function, but will cross
+  monitors.
+
+f.xrightzoom::
+  This function is similar to the `f.rightzoom` function, but will cross
+  monitors.
+
+f.xtopzoom::
+  This function is similar to the `f.topzoom` function, but will cross
+  monitors.
+
+f.xzoom::
+  This function is similar to the `f.zoom` function, but will cross
+  monitors.
+
 f.zoom::
-  This function stretches the window so that it covers the whole height of
-  the screen, or restores the original size if the window was already
-  zoom'd.  It's the vertical counterpart fo `f.horizoom`; perhaps
+  This function stretches the window so that it covers the whole height
+  of the current monitor, or restores the original size if the window was
+  already zoom'd.  It's the vertical counterpart fo `f.horizoom`; perhaps
   `f.vertzoom` would be a better name...
 
 
diff --git a/event_core.c b/event_core.c
index 059a00b..a86b811 100644
--- a/event_core.c
+++ b/event_core.c
@@ -35,7 +35,9 @@
 #include <X11/extensions/shape.h>
 
 #include "animate.h"
+#ifdef CAPTIVE
 #include "captive.h"
+#endif
 #include "colormaps.h"
 #include "events.h"
 #include "event_handlers.h"
@@ -45,6 +47,7 @@
 #include "iconmgr.h"
 #include "image.h"
 #include "screen.h"
+#include "signals.h"
 #include "util.h"
 #include "version.h"
 #include "win_utils.h"
@@ -221,8 +224,8 @@ CtwmNextEvent(Display *display, XEvent *event)
 
 #define NEXTEVENT XtAppNextEvent(appContext, event)
 
-	if(RestartFlag) {
-		DoRestart(CurrentTime);
+	if(SignalFlag) {
+		handle_signal_flag(CurrentTime);
 	}
 	if(XEventsQueued(display, QueuedAfterFlush) != 0) {
 		NEXTEVENT;
@@ -233,8 +236,8 @@ CtwmNextEvent(Display *display, XEvent *event)
 	if(animate) {
 		TryToAnimate();
 	}
-	if(RestartFlag) {
-		DoRestart(CurrentTime);
+	if(SignalFlag) {
+		handle_signal_flag(CurrentTime);
 	}
 	if(! MaybeAnimate) {
 		NEXTEVENT;
@@ -253,8 +256,8 @@ CtwmNextEvent(Display *display, XEvent *event)
 			timeout = AnimateTimeout;
 		}
 		found = select(fd + 1, &mask, NULL, NULL, tout);
-		if(RestartFlag) {
-			DoRestart(CurrentTime);
+		if(SignalFlag) {
+			handle_signal_flag(CurrentTime);
 		}
 		if(found < 0) {
 			if(errno != EINTR) {
@@ -270,8 +273,8 @@ CtwmNextEvent(Display *display, XEvent *event)
 			if(animate) {
 				TryToAnimate();
 			}
-			if(RestartFlag) {
-				DoRestart(CurrentTime);
+			if(SignalFlag) {
+				handle_signal_flag(CurrentTime);
 			}
 			if(! MaybeAnimate) {
 				NEXTEVENT;
@@ -315,6 +318,7 @@ DispatchEvent(void)
 	}
 	Scr = thisScr;
 
+#ifdef CAPTIVE
 	if(CLarg.is_captive) {
 		if((Event.type == ConfigureNotify)
 		                && (Event.xconfigure.window == Scr->CaptiveRoot)) {
@@ -322,6 +326,7 @@ DispatchEvent(void)
 			return false;
 		}
 	}
+#endif
 	FixRootEvent(&Event);
 	if(Event.type >= 0 && Event.type < MAX_X_EVENT) {
 #ifdef SOUNDS
diff --git a/event_handlers.c b/event_handlers.c
index f433270..6417597 100644
--- a/event_handlers.c
+++ b/event_handlers.c
@@ -37,6 +37,7 @@
 #include "clicktofocus.h"
 #include "colormaps.h"
 #include "ctwm_atoms.h"
+#include "ctwm_shutdown.h"
 #include "events.h"
 #include "event_handlers.h"
 #include "event_internal.h"
@@ -59,6 +60,7 @@
 #include "win_ops.h"
 #include "win_regions.h"
 #include "win_resize.h"
+#include "win_ring.h"
 #include "win_utils.h"
 #include "workspace_manager.h"
 #include "workspace_utils.h"
@@ -434,7 +436,7 @@ HandleFocusOut(void)
 /*
  * Only sent if SubstructureNotifyMask is selected on the (root) window.
  */
-void HandleCirculateNotify()
+void HandleCirculateNotify(void)
 {
 	VirtualScreen *vs;
 #ifdef DEBUG_EVENTS
@@ -1688,8 +1690,6 @@ void HandleExpose(void)
 
 static void remove_window_from_ring(TwmWindow *tmp)
 {
-	TwmWindow *prev = tmp->ring.prev, *next = tmp->ring.next;
-
 	if(enter_win == tmp) {
 		enter_flag = false;
 		enter_win = NULL;
@@ -1705,24 +1705,7 @@ static void remove_window_from_ring(TwmWindow *tmp)
 		lower_win = NULL;
 	}
 
-	/*
-	 * 1. Unlink window
-	 * 2. If window was only thing in ring, null out ring
-	 * 3. If window was ring leader, set to next (or null)
-	 */
-	if(prev) {
-		prev->ring.next = next;
-	}
-	if(next) {
-		next->ring.prev = prev;
-	}
-	if(Scr->Ring == tmp) {
-		Scr->Ring = (next != tmp ? next : NULL);
-	}
-
-	if(!Scr->Ring || Scr->RingLeader == tmp) {
-		Scr->RingLeader = Scr->Ring;
-	}
+	UnlinkWindowFromRing(tmp);
 }
 
 
@@ -2094,31 +2077,43 @@ void HandleMapNotify(void)
 	 * the client would think that the window has a chance of being viewable
 	 * when it really isn't.
 	 */
-
 	XGrabServer(dpy);
+
+	// Mapping the window, hide its icon
 	if(Tmp_win->icon && Tmp_win->icon->w) {
 		XUnmapWindow(dpy, Tmp_win->icon->w);
 	}
-	if(Tmp_win->title_w) {
-		XMapSubwindows(dpy, Tmp_win->title_w);
-	}
+
+	// Map up everything inside the frame (not the frame itself, so if it
+	// wasn't already up, nothing will show yet)
 	XMapSubwindows(dpy, Tmp_win->frame);
-	if(Scr->Focus != Tmp_win && Tmp_win->hilite_wl) {
-		XUnmapWindow(dpy, Tmp_win->hilite_wl);
-	}
-	if(Scr->Focus != Tmp_win && Tmp_win->hilite_wr) {
-		XUnmapWindow(dpy, Tmp_win->hilite_wr);
-	}
-	if(Scr->Focus == Tmp_win && Tmp_win->lolite_wl) {
-		XUnmapWindow(dpy, Tmp_win->lolite_wl);
+
+	// Choose which of the hi/lolite's should be left up, based on the
+	// focus
+	if(Scr->Focus != Tmp_win) {
+		if(Tmp_win->hilite_wl) {
+			XUnmapWindow(dpy, Tmp_win->hilite_wl);
+		}
+		if(Tmp_win->hilite_wr) {
+			XUnmapWindow(dpy, Tmp_win->hilite_wr);
+		}
 	}
-	if(Scr->Focus == Tmp_win && Tmp_win->lolite_wr) {
-		XUnmapWindow(dpy, Tmp_win->lolite_wr);
+	else {
+		if(Tmp_win->lolite_wl) {
+			XUnmapWindow(dpy, Tmp_win->lolite_wl);
+		}
+		if(Tmp_win->lolite_wr) {
+			XUnmapWindow(dpy, Tmp_win->lolite_wr);
+		}
 	}
 
+	// Now map the frame itself (which brings all the rest into view)
 	XMapWindow(dpy, Tmp_win->frame);
+
 	XUngrabServer(dpy);
 	XFlush(dpy);
+
+	// Set the flags
 	Tmp_win->mapped = true;
 	Tmp_win->isicon = false;
 	Tmp_win->icon_on = false;
@@ -2182,17 +2177,23 @@ void HandleUnmapNotify(void)
 		                  ReparentNotify, &ev);
 		SetMapStateProp(Tmp_win, WithdrawnState);
 		if(reparented) {
-			if(Tmp_win->old_bw) XSetWindowBorderWidth(dpy,
-				                Event.xunmap.window,
-				                Tmp_win->old_bw);
+			// It got reparented, get rid of our alterations.
+			if(Tmp_win->old_bw) {
+				XSetWindowBorderWidth(dpy,
+				                      Event.xunmap.window,
+				                      Tmp_win->old_bw);
+			}
 			if(Tmp_win->wmhints->flags & IconWindowHint) {
 				XUnmapWindow(dpy, Tmp_win->wmhints->icon_window);
 			}
 		}
 		else {
+			// Didn't get reparented, so we should do it ourselves
 			XReparentWindow(dpy, Event.xunmap.window, Tmp_win->attr.root,
 			                dstx, dsty);
-			RestoreWithdrawnLocation(Tmp_win);
+			// XXX Need to think more about just what the roots and
+			// coords are here...
+			RestoreWinConfig(Tmp_win);
 		}
 		XRemoveFromSaveSet(dpy, Event.xunmap.window);
 		XSelectInput(dpy, Event.xunmap.window, NoEventMask);
@@ -2229,11 +2230,13 @@ void HandleMotionNotify(void)
 		}
 
 		Tmp_win = GetTwmWindow(ResizeWindow);
+#ifdef WINBOX
 		if(Tmp_win && Tmp_win->winbox) {
 			XTranslateCoordinates(dpy, Scr->Root, Tmp_win->winbox->window,
 			                      Event.xmotion.x_root, Event.xmotion.y_root,
 			                      &(Event.xmotion.x_root), &(Event.xmotion.y_root), &JunkChild);
 		}
+#endif
 		DoResize(Event.xmotion.x_root, Event.xmotion.y_root, Tmp_win);
 	}
 	else if(Scr->BorderCursors && Tmp_win && Event.xany.window == Tmp_win->frame) {
@@ -2266,11 +2269,13 @@ void HandleButtonRelease(void)
 		MoveOutline(Scr->XineramaRoot, 0, 0, 0, 0, 0, 0);
 
 		Tmp_win = GetTwmWindow(DragWindow);
+#ifdef WINBOX
 		if(Tmp_win->winbox) {
 			XTranslateCoordinates(dpy, Scr->Root, Tmp_win->winbox->window,
 			                      Event.xbutton.x_root, Event.xbutton.y_root,
 			                      &(Event.xbutton.x_root), &(Event.xbutton.y_root), &JunkChild);
 		}
+#endif
 		if(DragWindow == Tmp_win->frame) {
 			xl = Event.xbutton.x_root - DragX - Tmp_win->frame_bw;
 			yt = Event.xbutton.y_root - DragY - Tmp_win->frame_bw;
@@ -2318,6 +2323,7 @@ void HandleButtonRelease(void)
 
 		CurrentDragX = xl;
 		CurrentDragY = yt;
+#ifdef VSCREEN
 		/*
 		 * sometimes getScreenOf() replies with the wrong window when moving
 		 * y to a negative number.  Need to figure out why... [XXX]
@@ -2351,6 +2357,7 @@ void HandleButtonRelease(void)
 				yt = desty;
 			}
 		}
+#endif
 		if(DragWindow == Tmp_win->frame) {
 			SetupWindow(Tmp_win, xl, yt,
 			            Tmp_win->frame_width, Tmp_win->frame_height, -1);
@@ -2823,17 +2830,21 @@ void HandleButtonPress(void)
 				                      &chwin);
 				Event.xbutton.window = Tmp_win->w;
 
+#ifdef WINBOX
 				if(Tmp_win->iswinbox && chwin) {
 					int x, y;
+					TwmWindow *wtmp;
 					XTranslateCoordinates(dpy, Tmp_win->w, chwin,
 					                      Event.xbutton.x, Event.xbutton.y,
 					                      &x, &y, &chwin);
-					if(chwin && (Tmp_win = GetTwmWindow(chwin))) {
+					if(chwin && (wtmp = GetTwmWindow(chwin))) {
 						Event.xany.window = chwin;
 						Event.xbutton.x   = x;
 						Event.xbutton.y   = y;
+						Tmp_win = wtmp;
 					}
 				}
+#endif
 				Context = C_WINDOW;
 			}
 			else if(Event.xbutton.subwindow
@@ -2843,6 +2854,7 @@ void HandleButtonPress(void)
 			else {
 				Context = C_FRAME;
 			}
+
 			if(Scr->ClickToFocus && Tmp_win->wmhints->input) {
 				SetFocus(Tmp_win, CurrentTime);
 			}
@@ -2870,7 +2882,6 @@ void HandleButtonPress(void)
 	 */
 	if(RootFunction != 0) {
 		if(Event.xany.window == Scr->Root) {
-			Window win;
 			int x, y;
 
 			/* if the window was the Root, we don't know for sure it
@@ -2884,7 +2895,9 @@ void HandleButtonPress(void)
 
 			if(Event.xany.window != 0 &&
 			                (Tmp_win = GetTwmWindow(Event.xany.window))) {
+#ifdef WINBOX
 				if(Tmp_win->iswinbox) {
+					Window win;
 					XTranslateCoordinates(dpy, Scr->Root, Event.xany.window,
 					                      x, y, &x, &y, &win);
 					XTranslateCoordinates(dpy, Event.xany.window, win,
@@ -2893,6 +2906,7 @@ void HandleButtonPress(void)
 						Event.xany.window = win;
 					}
 				}
+#endif
 			}
 			if(Event.xany.window == 0 ||
 			                !(Tmp_win = GetTwmWindow(Event.xany.window))) {
@@ -3107,13 +3121,18 @@ void HandleEnterNotify(void)
 		for(vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
 			if(ewp->window == vs->window) {
 				Scr->Root  = vs->window;
+#ifdef CAPTIVE
 				Scr->rootx = Scr->crootx + vs->x;
 				Scr->rooty = Scr->crooty + vs->y;
+#else
+				Scr->rootx = vs->x;
+				Scr->rooty = vs->y;
+#endif
 				Scr->rootw = vs->w;
 				Scr->rooth = vs->h;
 				Scr->currentvs = vs;
 #if 0
-				fprintf(stderr, "entering new vs : 0x%x, 0x%x, %d, %d, %d, %d\n",
+				fprintf(stderr, "entering new vs : %p, 0x%lx, %d, %d, %d, %d\n",
 				        vs, Scr->Root, vs->x, vs->y, vs->w, vs->h);
 #endif
 				return;
@@ -3357,7 +3376,7 @@ void HandleEnterNotify(void)
 			/*
 			 * set ring leader
 			 */
-			if(Tmp_win->ring.next && (!enter_flag || raise_win == enter_win)) {
+			if(WindowIsOnRing(Tmp_win) && (!enter_flag || raise_win == enter_win)) {
 				Scr->RingLeader = Tmp_win;
 			}
 			XSync(dpy, 0);
diff --git a/ewmh.c b/ewmh.c
index 3127940..6a5220d 100644
--- a/ewmh.c
+++ b/ewmh.c
@@ -35,6 +35,7 @@
 #include <X11/extensions/shape.h>
 
 #include "ctwm_atoms.h"
+#include "ctwm_shutdown.h"
 #include "ewmh_atoms.h"
 #include "screen.h"
 #include "events.h"
@@ -46,6 +47,8 @@
 #include "list.h"
 #include "functions.h"
 #include "occupation.h"
+#include "r_layout.h"
+#include "util.h"
 #include "vscreen.h"
 #include "win_iconify.h"
 #include "win_ops.h"
@@ -80,6 +83,7 @@ static void EwmhClientMessage_NET_WM_DESKTOP(XClientMessageEvent *msg);
 static void EwmhClientMessage_NET_WM_STATE(XClientMessageEvent *msg);
 static void EwmhClientMessage_NET_ACTIVE_WINDOW(XClientMessageEvent *msg);
 static void EwmhClientMessage_NET_WM_MOVERESIZE(XClientMessageEvent *msg);
+static void EwmhClientMessage_NET_CLOSE_WINDOW(XClientMessageEvent *msg);
 static XEvent synth_btnevent_for_moveresize(TwmWindow *twm_win);
 static unsigned long EwmhGetWindowProperty(Window w, Atom name, Atom type);
 static void EwmhGetStrut(TwmWindow *twm_win, bool update);
@@ -193,11 +197,6 @@ static bool EwmhReplaceWM(ScreenInfo *scr)
 	Atom wmAtom;
 	Window selectionOwner;
 
-	/* If we're not trying to take over the screen, don't do this at all */
-	if(!scr->takeover) {
-		return false;
-	}
-
 	snprintf(atomname, sizeof(atomname), "WM_S%d", scr->screen);
 	wmAtom = XInternAtom(dpy, atomname, False);
 
@@ -309,14 +308,6 @@ bool EwmhInitScreenEarly(ScreenInfo *scr)
 {
 	XSetWindowAttributes attrib;
 
-	scr->ewmh_CLIENT_LIST_used = 0;
-	scr->ewmh_CLIENT_LIST_size = 16;
-	scr->ewmh_CLIENT_LIST = calloc(scr->ewmh_CLIENT_LIST_size,
-	                               sizeof(scr->ewmh_CLIENT_LIST[0]));
-	if(scr->ewmh_CLIENT_LIST == NULL) {
-		return false;
-	}
-
 #ifdef DEBUG_EWMH
 	fprintf(stderr, "EwmhInitScreenEarly: XCreateWindow\n");
 #endif
@@ -345,8 +336,6 @@ bool EwmhInitScreenEarly(ScreenInfo *scr)
 		return false;
 	}
 
-	scr->ewmhStruts = NULL;
-
 #ifdef DEBUG_EWMH
 	fprintf(stderr, "EwmhInitScreenEarly: return true\n");
 #endif
@@ -458,6 +447,7 @@ void EwmhInitScreenLate(ScreenInfo *scr)
 	supported[i++] = XA__NET_WM_STATE_SHADED;
 	supported[i++] = XA__NET_WM_STATE_ABOVE;
 	supported[i++] = XA__NET_WM_STATE_BELOW;
+	supported[i++] = XA__NET_CLOSE_WINDOW;
 
 	XChangeProperty(dpy, scr->XineramaRoot,
 	                XA__NET_SUPPORTED, XA_ATOM,
@@ -465,6 +455,8 @@ void EwmhInitScreenLate(ScreenInfo *scr)
 	                (unsigned char *)supported, i);
 }
 
+
+#ifdef VSCREEN
 /*
  * Set up the _NET_VIRTUAL_ROOTS property, which indicates that we're
  * using virtual root windows.
@@ -514,6 +506,8 @@ void EwmhInitVirtualRoots(ScreenInfo *scr)
 		                (unsigned char *)&d0, 1);
 	}
 }
+#endif
+
 
 static void EwmhTerminateScreen(ScreenInfo *scr)
 {
@@ -554,7 +548,7 @@ void EwmhSelectionClear(XSelectionClearEvent *sev)
 #ifdef DEBUG_EWMH
 	fprintf(stderr, "sev->window = %x\n", (unsigned)sev->window);
 #endif
-	Done(0);
+	DoShutdown();
 }
 
 /*
@@ -587,6 +581,10 @@ bool EwmhClientMessage(XClientMessageEvent *msg)
 		EwmhClientMessage_NET_WM_MOVERESIZE(msg);
 		return true;
 	}
+	else if(msg->message_type == XA__NET_CLOSE_WINDOW) {
+		EwmhClientMessage_NET_CLOSE_WINDOW(msg);
+		return true;
+	}
 
 	/* Messages regarding the root window */
 	if(msg->window != Scr->XineramaRoot &&
@@ -604,6 +602,7 @@ bool EwmhClientMessage(XClientMessageEvent *msg)
 	}
 	else if(msg->message_type == XA__NET_SHOWING_DESKTOP) {
 		ShowBackground(Scr->currentvs, msg->data.l[0] ? 1 : 0);
+		return true;
 	}
 	else {
 #ifdef DEBUG_EWMH
@@ -649,7 +648,7 @@ Image *EwmhGetIcon(ScreenInfo *scr, TwmWindow *twm_win)
 	int smaller_offset, larger_offset;
 	int i;
 
-	int area, width, height;
+	int width, height;
 
 	fetch_offset = 0;
 	if(XGetWindowProperty(dpy, twm_win->w, XA__NET_WM_ICON,
@@ -677,7 +676,6 @@ Image *EwmhGetIcon(ScreenInfo *scr, TwmWindow *twm_win)
 	wanted_area = Scr->PreferredIconWidth * Scr->PreferredIconHeight;
 	smaller = 0;
 	larger = 999999;
-	offset = 0;
 	smaller_offset = -1;
 	larger_offset = -1;
 	i = 0;
@@ -689,7 +687,7 @@ Image *EwmhGetIcon(ScreenInfo *scr, TwmWindow *twm_win)
 		int h = prop[i++];
 		int size = w * h;
 
-		area = w * h;
+		const int area = w * h;
 
 #ifdef DEBUG_EWMH
 		fprintf(stderr, "[%d+%d] w=%d h=%d\n", fetch_offset, offset, w, h);
@@ -751,7 +749,8 @@ Image *EwmhGetIcon(ScreenInfo *scr, TwmWindow *twm_win)
 	/*
 	 * Choose which icon approximates our desired size best.
 	 */
-	area = 0;
+	int area = 0;
+	ALLOW_DEAD_STORE(area); // all branches below init it
 
 	if(smaller_offset >= 0) {
 		if(larger_offset >= 0) {
@@ -1370,6 +1369,34 @@ synth_btnevent_for_moveresize(TwmWindow *twm_win)
 }
 
 
+/*
+ * Implementation of _NET_CLOSE_WINDOW
+ *
+ * window = window to be closed
+ * message_type = _NET_CLOSE_WINDOW
+ * format = 32
+ * data.l[0] = timestamp
+ * data.l[1] = source indication:
+ *             0 Clients that support only older version of this spec
+ *             1 for normal applications, and
+ *             2 for pagers and other Clients that represent direct user actions
+ * data.l[2] = 0
+ * data.l[3] = 0
+ * data.l[4] = 0
+ */
+static void EwmhClientMessage_NET_CLOSE_WINDOW(XClientMessageEvent *msg)
+{
+	TwmWindow *tmp_win;
+
+	tmp_win = GetTwmWindow(msg->window);
+
+	if(tmp_win != NULL) {
+		ButtonPressed = -1;
+		ExecuteFunction(F_DELETE, NULL, msg->window, tmp_win,
+		                (XEvent *)msg, C_NO_CONTEXT, 0);
+	}
+}
+
 /*
  * Handle any PropertyNotify.
  */
@@ -1879,11 +1906,6 @@ bool EwmhOnWindowRing(TwmWindow *twm_win)
 	}
 }
 
-static inline int max(int a, int b)
-{
-	return a > b ? a : b;
-}
-
 /*
  * Recalculate the effective border values from the remembered struts.
  * Interestingly it is not documented how to do that.
@@ -1912,6 +1934,13 @@ static void EwmhRecalculateStrut(void)
 	Scr->BorderTop    = top;
 	Scr->BorderBottom = bottom;
 
+	// Bordered layout may have changed
+	Scr->BorderedLayout = RLayoutCopyCropped(Scr->Layout,
+	                      left, right, top, bottom);
+	if(Scr->BorderedLayout == NULL) {
+		Scr->BorderedLayout = Scr->Layout;        // nothing to crop
+	}
+
 	EwmhSet_NET_WORKAREA(Scr);
 }
 /*
@@ -2058,6 +2087,32 @@ static void EwmhRemoveStrut(TwmWindow *twm_win)
 	}
 }
 
+
+/**
+ * Set _NET_FRAME_EXTENTS property.
+ * This tells the client how much space is being taken up by the window
+ * decorations.  Some clients may need this information to position other
+ * windows on top of themselves.  e.g., Firefox's form autofill and
+ * context menu will be positioned a bit wrong (high, by the height of
+ * the titlebar) without this.
+ */
+void EwmhSet_NET_FRAME_EXTENTS(TwmWindow *twm_win)
+{
+	long data[4];
+	const long w = twm_win->frame_bw3D + twm_win->frame_bw;
+
+	data[0] = w; // left
+	data[1] = w; // right
+	data[2] = twm_win->title_height + w; // top
+	data[3] = w; // bottom
+
+	XChangeProperty(dpy, twm_win->w,
+	                XA__NET_FRAME_EXTENTS, XA_CARDINAL,
+	                32, PropModeReplace,
+	                (unsigned char *)data, 4);
+}
+
+
 void EwmhSet_NET_SHOWING_DESKTOP(int state)
 {
 	unsigned long prop[1];
diff --git a/ewmh.h b/ewmh.h
index 5d9d4fe..9f1dff8 100644
--- a/ewmh.h
+++ b/ewmh.h
@@ -58,7 +58,9 @@ typedef struct EwmhStrut {
 void EwmhInit(void);
 bool EwmhInitScreenEarly(ScreenInfo *scr);
 void EwmhInitScreenLate(ScreenInfo *scr);
+#ifdef VSCREEN
 void EwmhInitVirtualRoots(ScreenInfo *scr);
+#endif
 void EwmhTerminate(void);
 void EwmhSelectionClear(XSelectionClearEvent *sev);
 bool EwmhClientMessage(XClientMessageEvent *msg);
@@ -77,6 +79,7 @@ int EwmhGetInitPriority(TwmWindow *twm_win);
 bool EwmhHasBorder(TwmWindow *twm_win);
 bool EwmhHasTitle(TwmWindow *twm_win);
 bool EwmhOnWindowRing(TwmWindow *twm_win);
+void EwmhSet_NET_FRAME_EXTENTS(TwmWindow *twm_win);
 void EwmhSet_NET_SHOWING_DESKTOP(int state);
 void EwmhSet_NET_WM_STATE(TwmWindow *twm_win, int changes);
 
diff --git a/ewmh_atoms.in b/ewmh_atoms.in
index 6cd5397..e189382 100644
--- a/ewmh_atoms.in
+++ b/ewmh_atoms.in
@@ -2,6 +2,7 @@ MANAGER
 _NET_ACTIVE_WINDOW
 _NET_CLIENT_LIST
 _NET_CLIENT_LIST_STACKING
+_NET_CLOSE_WINDOW
 _NET_CURRENT_DESKTOP
 _NET_DESKTOP_GEOMETRY
 _NET_DESKTOP_VIEWPORT
@@ -31,3 +32,4 @@ _NET_WM_WINDOW_TYPE_DESKTOP
 _NET_WM_WINDOW_TYPE_DOCK
 _NET_WM_WINDOW_TYPE_NORMAL
 _NET_WORKAREA
+_NET_FRAME_EXTENTS
diff --git a/examples/example.ctwmrc b/examples/example.ctwmrc
index d37cd30..e38fa18 100644
--- a/examples/example.ctwmrc
+++ b/examples/example.ctwmrc
@@ -153,7 +153,6 @@ menu "windowops" {
     "Toggle workspace"		f.togglestate
     "ShowWorkspaceManager"	f.showWorkspaceMgr
     "HideWorkspaceManager"	f.hideWorkspaceMgr
-    "Adopt Window"		f.adoptwindow
 }
 
 menu "windowmenu" {
diff --git a/examples/levitte.ctwmrc b/examples/levitte.ctwmrc
index 8358524..b131810 100644
--- a/examples/levitte.ctwmrc
+++ b/examples/levitte.ctwmrc
@@ -260,7 +260,6 @@ menu "windowops" {
     "Toggle autoraise"	f.autoraise
     ""			f.separator
     "Beep"		f.beep
-    #"Adopt Window"	f.adoptwindow
     "Animation"		f.menu "Anim"
     "Zoom"		f.menu "Zoom"
     ""			f.separator
diff --git a/functions.c b/functions.c
index 6480932..e8eed19 100644
--- a/functions.c
+++ b/functions.c
@@ -366,6 +366,13 @@ ReGrab(void)
 static bool
 DeferExecution(int context, int func, Cursor cursor)
 {
+	Window confine_to = Scr->Root;
+#ifdef CAPTIVE
+	if(func == F_ADOPTWINDOW) {
+		confine_to = None;
+	}
+#endif
+
 	if((context == C_ROOT) || (context == C_ALTERNATE)) {
 		LastCursor = cursor;
 		XGrabPointer(dpy,
@@ -374,7 +381,7 @@ DeferExecution(int context, int func, Cursor cursor)
 		             ButtonPressMask | ButtonReleaseMask,
 		             GrabModeAsync,
 		             GrabModeAsync,
-		             (func == F_ADOPTWINDOW) ? None : Scr->Root,
+		             confine_to,
 		             cursor,
 		             CurrentTime);
 		RootFunction = func;
diff --git a/functions_defs.list b/functions_defs.list
index b9af06a..3eb24a8 100644
--- a/functions_defs.list
+++ b/functions_defs.list
@@ -20,7 +20,7 @@
 #
 # START(main)
 addtoworkspace        S CS -
-adoptwindow           - -  -
+adoptwindow           - -  CAPTIVE
 altcontext            - -  -
 altkeymap             S -  -
 autolower             - CS -
@@ -43,7 +43,7 @@ downiconmgr           - -  -
 downworkspace         - -  -
 exec                  S -  -
 fill                  S CS -
-fittocontent          - CS -
+fittocontent          - CS WINBOX
 focus                 - CS -
 forcemove             - CM -
 forwiconmgr           - -  -
@@ -55,7 +55,7 @@ gotoworkspace         S -  -
 hideiconmgr           - -  -
 hideworkspacemgr      - -  -
 horizoom              - CS -
-hypermove             - CM -
+hypermove             - CM CAPTIVE
 iconify               - CS -
 identify              - CS -
 initsize              - CS -
@@ -140,6 +140,14 @@ warpto                S -  -
 warptoiconmgr         S -  -
 warptoscreen          S -  -
 winrefresh            - CS -
+xbottomzoom           - CS -
+xfullscreenzoom       - CS -
+xfullzoom             - CS -
+xhorizoom             - CS -
+xleftzoom             - CS -
+xrightzoom            - CS -
+xtopzoom              - CS -
+xzoom                 - CS -
 zoom                  - CS -
 # END(main)
 
diff --git a/functions_identify.c b/functions_identify.c
index 8816a5b..8daef04 100644
--- a/functions_identify.c
+++ b/functions_identify.c
@@ -15,6 +15,8 @@
 #include "functions_internal.h"
 #include "icons.h"
 #include "otp.h"
+#include "r_area.h"
+#include "r_layout.h"
 #include "screen.h"
 #include "version.h"
 #include "vscreen.h"
@@ -107,8 +109,11 @@ Identify(TwmWindow *t)
 	CHKN;
 
 	if(t) {
+		// The border would be on the frame, not t->w, so assume our
+		// internal tracking is right for it
 		XGetGeometry(dpy, t->w, &JunkRoot, &JunkX, &JunkY,
-		             &wwidth, &wheight, &bw, &depth);
+		             &wwidth, &wheight, &JunkBW, &depth);
+		bw = t->frame_bw;
 		XTranslateCoordinates(dpy, t->w, Scr->Root, 0, 0,
 		                      &x, &y, &junk);
 		snprintf(Info[n++], INFO_SIZE, "Name               = \"%s\"",
@@ -226,24 +231,33 @@ info_dismiss:
 	height += 10;               /* some padding */
 	if(XQueryPointer(dpy, Scr->Root, &JunkRoot, &JunkChild,
 	                 &dummy, &dummy, &px, &py, &udummy)) {
-		px -= (width / 2);
-		py -= (height / 3);
-		if(px + width + BW2 >= Scr->rootw) {
-			px = Scr->rootw - width - BW2;
+		px -= width / 2;
+		py -= height / 3;
+	}
+	else {
+		px = py = 0;
+	}
+
+	{
+		RArea area = RAreaNew(px, py, width, height);
+		int min_x, min_y, max_bottom, max_right;
+
+		RLayoutFindLeftRightEdges(Scr->Layout, &area, &min_x, &max_right);
+		if(px < min_x) {
+			px = min_x;
 		}
-		if(py + height + BW2 >= Scr->rooth) {
-			py = Scr->rooth - height - BW2;
+		else if(px + width - 1 > max_right) {
+			px = max_right - width + 1;
 		}
-		if(px < 0) {
-			px = 0;
+
+		RLayoutFindTopBottomEdges(Scr->Layout, &area, &min_y, &max_bottom);
+		if(py < min_y) {
+			py = min_y;
 		}
-		if(py < 0) {
-			py = 0;
+		else if(py + height - 1 > max_bottom) {
+			py = max_bottom - height + 1;
 		}
 	}
-	else {
-		px = py = 0;
-	}
 	XMoveResizeWindow(dpy, Scr->InfoWindow.win, px, py, width, height);
 	XMapRaised(dpy, Scr->InfoWindow.win);
 	Scr->InfoWindow.mapped = true;
diff --git a/functions_internal.h b/functions_internal.h
index 2fa8056..94fee75 100644
--- a/functions_internal.h
+++ b/functions_internal.h
@@ -65,6 +65,14 @@ DFHANDLER(leftzoom);
 DFHANDLER(rightzoom);
 DFHANDLER(topzoom);
 DFHANDLER(bottomzoom);
+DFHANDLER(xhorizoom);
+DFHANDLER(xfullzoom);
+DFHANDLER(xfullscreenzoom);
+DFHANDLER(xleftzoom);
+DFHANDLER(xrightzoom);
+DFHANDLER(xtopzoom);
+DFHANDLER(xbottomzoom);
+DFHANDLER(xzoom);
 DFHANDLER(fill);
 DFHANDLER(initsize);
 DFHANDLER(moveresize);
@@ -94,9 +102,11 @@ DFHANDLER(upworkspace);
 DFHANDLER(downworkspace);
 
 
+#ifdef CAPTIVE
 /* functions_captive.c */
 DFHANDLER(adoptwindow);
 DFHANDLER(hypermove);
+#endif
 
 
 /* functions_identify.c */
@@ -157,7 +167,9 @@ DFHANDLER(quit);
 DFHANDLER(restart);
 DFHANDLER(beep);
 DFHANDLER(trace);
+#ifdef WINBOX
 DFHANDLER(fittocontent);
+#endif
 DFHANDLER(showbackground);
 DFHANDLER(raiseicons);
 DFHANDLER(rescuewindows);
@@ -183,5 +195,4 @@ extern Time last_time;
 /* Several places need to frob this to leave the cursor alone */
 extern bool func_reset_cursor;
 
-
 #endif /* _CTWM_FUNCTIONS_INTERNAL_H */
diff --git a/functions_misc.c b/functions_misc.c
index b5e5086..f9edf82 100644
--- a/functions_misc.c
+++ b/functions_misc.c
@@ -11,6 +11,7 @@
 #include <stdlib.h>
 
 #include "animate.h"
+#include "ctwm_shutdown.h"
 #include "functions.h"
 #include "functions_defs.h"
 #include "functions_internal.h"
@@ -22,7 +23,9 @@
 #endif
 #include "util.h"
 #include "win_iconify.h"
+#ifdef WINBOX
 #include "windowbox.h"
+#endif
 #include "workspace_utils.h"
 
 #include "ext/repl_str.h"
@@ -189,7 +192,7 @@ DFHANDLER(altcontext)
  */
 DFHANDLER(quit)
 {
-	Done(0);
+	DoShutdown();
 }
 
 DFHANDLER(restart)
@@ -209,6 +212,7 @@ DFHANDLER(trace)
 
 
 
+#ifdef WINBOX
 /*
  * Special windowbox-related
  */
@@ -220,6 +224,7 @@ DFHANDLER(fittocontent)
 	}
 	fittocontent(tmp_win);
 }
+#endif
 
 
 
@@ -444,6 +449,7 @@ Execute(const char *_s)
 		s = tmp;
 	}
 
+#ifdef CAPTIVE
 	subs = strstr(s, "$redirect");
 	if(subs) {
 		char *tmp;
@@ -466,6 +472,7 @@ Execute(const char *_s)
 
 		free(redir);
 	}
+#endif
 
 
 	/*
diff --git a/functions_warp.c b/functions_warp.c
index 615c5bf..691c319 100644
--- a/functions_warp.c
+++ b/functions_warp.c
@@ -20,6 +20,7 @@
 #include "otp.h"
 #include "screen.h"
 #include "win_iconify.h"
+#include "win_ring.h"
 #include "win_utils.h"
 
 
@@ -120,47 +121,16 @@ DFHANDLER(warptoiconmgr)
 	}
 }
 
-
 /* Taken from vtwm version 5.3 */
 DFHANDLER(ring)
 {
-	if(tmp_win->ring.next || tmp_win->ring.prev) {
+	if(WindowIsOnRing(tmp_win)) {
 		/* It's in the ring, let's take it out. */
-		TwmWindow *prev = tmp_win->ring.prev, *next = tmp_win->ring.next;
-
-		/*
-		* 1. Unlink window
-		* 2. If window was only thing in ring, null out ring
-		* 3. If window was ring leader, set to next (or null)
-		*/
-		if(prev) {
-			prev->ring.next = next;
-		}
-		if(next) {
-			next->ring.prev = prev;
-		}
-		if(Scr->Ring == tmp_win) {
-			Scr->Ring = (next != tmp_win ? next : NULL);
-		}
-
-		if(!Scr->Ring || Scr->RingLeader == tmp_win) {
-			Scr->RingLeader = Scr->Ring;
-		}
-		tmp_win->ring.next = tmp_win->ring.prev = NULL;
+		UnlinkWindowFromRing(tmp_win);
 	}
 	else {
 		/* Not in the ring, so put it in. */
-		if(Scr->Ring) {
-			tmp_win->ring.next = Scr->Ring->ring.next;
-			if(Scr->Ring->ring.next->ring.prev) {
-				Scr->Ring->ring.next->ring.prev = tmp_win;
-			}
-			Scr->Ring->ring.next = tmp_win;
-			tmp_win->ring.prev = Scr->Ring;
-		}
-		else {
-			tmp_win->ring.next = tmp_win->ring.prev = Scr->Ring = tmp_win;
-		}
+		AddWindowToRing(tmp_win);
 	}
 	/*tmp_win->ring.cursor_valid = false;*/
 }
@@ -219,9 +189,6 @@ WarpAlongRing(XButtonEvent *ev, bool forward)
 
 	if(forward) {
 		for(r = head->ring.next; r != head; r = r->ring.next) {
-			if(!r) {
-				break;
-			}
 			if(r->mapped && (Scr->WarpRingAnyWhere || visible(r))) {
 				break;
 			}
@@ -229,18 +196,25 @@ WarpAlongRing(XButtonEvent *ev, bool forward)
 	}
 	else {
 		for(r = head->ring.prev; r != head; r = r->ring.prev) {
-			if(!r) {
-				break;
-			}
 			if(r->mapped && (Scr->WarpRingAnyWhere || visible(r))) {
 				break;
 			}
 		}
 	}
 
-	/* Note: (Scr->Focus != r) is necessary when we move to a workspace that
-	   has a single window and we want warping to warp to it. */
-	if(r && (r != head || Scr->Focus != r)) {
+	/*
+	 * Note: (Scr->Focus == NULL) is necessary when we move to (or
+	 * are in) a workspace that has a single window, and we're not
+	 * on that window (but the window is head), and we want f.warpring
+	 * to warp to it.
+	 * Generalised that is also true if we are on a window but it is
+	 * not on the ring.
+	 * TODO: on an empty screen, it still moves the mouse cursor...
+	 */
+
+	if(r != head
+	                || Scr->Focus == NULL
+	                || !WindowIsOnRing(Scr->Focus)) {
 		TwmWindow *p = Scr->RingLeader, *t;
 
 		Scr->RingLeader = r;
diff --git a/functions_win.c b/functions_win.c
index 2444b99..785d48f 100644
--- a/functions_win.c
+++ b/functions_win.c
@@ -279,7 +279,10 @@ DFHANDLER(delete)
 		HideIconManager();
 		return;
 	}
-	if(tmp_win->iswinbox || tmp_win->iswspmgr
+	if(tmp_win->iswspmgr
+#ifdef WINBOX
+	                || tmp_win->iswinbox
+#endif
 	                || (Scr->workSpaceMgr.occupyWindow
 	                    && tmp_win == Scr->workSpaceMgr.occupyWindow->twm_win)) {
 		XBell(dpy, 0);
@@ -303,7 +306,10 @@ DFHANDLER(delete)
 
 DFHANDLER(destroy)
 {
-	if(tmp_win->isiconmgr || tmp_win->iswinbox || tmp_win->iswspmgr
+	if(tmp_win->isiconmgr || tmp_win->iswspmgr
+#ifdef WINBOX
+	                || tmp_win->iswinbox
+#endif
 	                || (Scr->workSpaceMgr.occupyWindow
 	                    && tmp_win == Scr->workSpaceMgr.occupyWindow->twm_win)) {
 		XBell(dpy, 0);
@@ -327,7 +333,10 @@ DFHANDLER(deleteordestroy)
 		HideIconManager();
 		return;
 	}
-	if(tmp_win->iswinbox || tmp_win->iswspmgr
+	if(tmp_win->iswspmgr
+#ifdef WINBOX
+	                || tmp_win->iswinbox
+#endif
 	                || (Scr->workSpaceMgr.occupyWindow
 	                    && tmp_win == Scr->workSpaceMgr.occupyWindow->twm_win)) {
 		XBell(dpy, 0);
@@ -561,11 +570,13 @@ DFHANDLER(movetitlebar)
 	}
 
 	/* now move the mouse */
+#ifdef WINBOX
 	if(tmp_win->winbox) {
 		XTranslateCoordinates(dpy, Scr->Root, tmp_win->winbox->window,
 		                      eventp->xbutton.x_root, eventp->xbutton.y_root,
 		                      &eventp->xbutton.x_root, &eventp->xbutton.y_root, &JunkChild);
 	}
+#endif
 	/*
 	 * the event is always a button event, since key events
 	 * are "weeded out" - although incompletely only
@@ -598,9 +609,11 @@ DFHANDLER(movetitlebar)
 	}
 
 	grabwin = Scr->Root;
+#ifdef WINBOX
 	if(tmp_win->winbox) {
 		grabwin = tmp_win->winbox->window;
 	}
+#endif
 	XGrabPointer(dpy, grabwin, True,
 	             ButtonPressMask | ButtonReleaseMask |
 	             ButtonMotionMask | PointerMotionMask, /* PointerMotionHintMask */
@@ -681,11 +694,13 @@ DFHANDLER(movetitlebar)
 		              &JunkX, &JunkY, &JunkMask);
 
 		FixRootEvent(eventp);
+#ifdef WINBOX
 		if(tmp_win->winbox) {
 			XTranslateCoordinates(dpy, Scr->Root, tmp_win->winbox->window,
 			                      eventp->xmotion.x_root, eventp->xmotion.y_root,
 			                      &eventp->xmotion.x_root, &eventp->xmotion.y_root, &JunkChild);
 		}
+#endif
 
 		if(!Scr->NoRaiseMove && Scr->OpaqueMove && !WindowMoved) {
 			OtpRaise(tmp_win, WinWin);
diff --git a/functions_win_moveresize.c b/functions_win_moveresize.c
index 9c2bad9..ac63147 100644
--- a/functions_win_moveresize.c
+++ b/functions_win_moveresize.c
@@ -16,6 +16,8 @@
 #include "icons.h"
 #include "otp.h"
 #include "parse.h"
+#include "r_area.h"
+#include "r_layout.h"
 #include "screen.h"
 #include "util.h"
 #include "vscreen.h"
@@ -24,6 +26,7 @@
 #include "win_resize.h"
 #include "win_utils.h"
 #include "workspace_manager.h"
+#include "xparsegeometry.h"
 
 
 /*
@@ -149,12 +152,14 @@ movewindow(EF_FULLPROTO)
 		Scr->OpaqueMove = false;
 	}
 
+#ifdef WINBOX
 	/* If it's in a WindowBox, adjust coordinates as necessary */
 	if(tmp_win->winbox) {
 		XTranslateCoordinates(dpy, dragroot, tmp_win->winbox->window,
 		                      eventp->xbutton.x_root, eventp->xbutton.y_root,
 		                      &(eventp->xbutton.x_root), &(eventp->xbutton.y_root), &JunkChild);
 	}
+#endif
 
 	/*
 	 * XXX pulldown=true only when we're triggering from a ButtonRelease
@@ -184,9 +189,9 @@ movewindow(EF_FULLPROTO)
 	 * different size.
 	 */
 	Scr->SizeStringOffset = SIZE_HINDENT;
-	XResizeWindow(dpy, Scr->SizeWindow,
-	              Scr->SizeStringWidth + SIZE_HINDENT * 2,
-	              Scr->SizeFont.height + SIZE_VINDENT * 2);
+	MoveResizeSizeWindow(eventp->xbutton.x_root, eventp->xbutton.y_root,
+	                     Scr->SizeStringWidth + SIZE_HINDENT * 2,
+	                     Scr->SizeFont.height + SIZE_VINDENT * 2);
 	XMapRaised(dpy, Scr->SizeWindow);
 
 	/*
@@ -194,8 +199,12 @@ movewindow(EF_FULLPROTO)
 	 * reported relative to what root.
 	 */
 	{
+#ifdef WINBOX
 		const Window grabwin = (tmp_win->winbox ? tmp_win->winbox->window
 		                        : Scr->XineramaRoot);
+#else
+		const Window grabwin = Scr->XineramaRoot;
+#endif
 
 		XGrabPointer(dpy, grabwin, True,
 		             ButtonPressMask | ButtonReleaseMask |
@@ -447,11 +456,13 @@ movewindow(EF_FULLPROTO)
 		FixRootEvent(eventp);
 
 		/* Tweak for window box, if this is in one */
+#ifdef WINBOX
 		if(tmp_win->winbox) {
 			XTranslateCoordinates(dpy, dragroot, tmp_win->winbox->window,
 			                      eventp->xmotion.x_root, eventp->xmotion.y_root,
 			                      &(eventp->xmotion.x_root), &(eventp->xmotion.y_root), &JunkChild);
 		}
+#endif
 
 		/*
 		 * If we haven't moved MoveDelta yet, we're not yet sure we're
@@ -978,6 +989,39 @@ DFHANDLER(bottomzoom)
 	fullzoom(tmp_win, func);
 }
 
+DFHANDLER(xzoom)
+{
+	fullzoom(tmp_win, func);
+}
+DFHANDLER(xhorizoom)
+{
+	fullzoom(tmp_win, func);
+}
+DFHANDLER(xfullzoom)
+{
+	fullzoom(tmp_win, func);
+}
+DFHANDLER(xfullscreenzoom)
+{
+	fullzoom(tmp_win, func);
+}
+DFHANDLER(xleftzoom)
+{
+	fullzoom(tmp_win, func);
+}
+DFHANDLER(xrightzoom)
+{
+	fullzoom(tmp_win, func);
+}
+DFHANDLER(xtopzoom)
+{
+	fullzoom(tmp_win, func);
+}
+DFHANDLER(xbottomzoom)
+{
+	fullzoom(tmp_win, func);
+}
+
 
 /*
  * f.fill - resizing until collision
@@ -1188,7 +1232,7 @@ DFHANDLER(moveresize)
 	unsigned int width, height;
 	int px = 20, py = 30;
 
-	mask = XParseGeometry(action, &x, &y, &width, &height);
+	mask = RLayoutXParseGeometry(Scr->Layout, action, &x, &y, &width, &height);
 	if(!(mask &  WidthValue)) {
 		width = tmp_win->frame_width;
 	}
@@ -1281,36 +1325,42 @@ static int
 FindConstraint(TwmWindow *tmp_win, MoveFillDir direction)
 {
 	TwmWindow  *t;
-	int ret;
+	int ret, limit;
 	const int winx = tmp_win->frame_x;
 	const int winy = tmp_win->frame_y;
 	const int winw = tmp_win->frame_width  + 2 * tmp_win->frame_bw;
 	const int winh = tmp_win->frame_height + 2 * tmp_win->frame_bw;
 
+	RArea area = RAreaNew(winx, winy, winw, winh);
+
 	switch(direction) {
 		case MFD_LEFT:
-			if(winx < Scr->BorderLeft) {
+			limit = RLayoutFindMonitorLeftEdge(Scr->BorderedLayout, &area);
+			if(winx < limit) {
 				return -1;
 			}
-			ret = Scr->BorderLeft;
+			ret = limit;
 			break;
 		case MFD_RIGHT:
-			if(winx + winw > Scr->rootw - Scr->BorderRight) {
+			limit = RLayoutFindMonitorRightEdge(Scr->BorderedLayout, &area);
+			if(winx + winw > limit) {
 				return -1;
 			}
-			ret = Scr->rootw - Scr->BorderRight;
+			ret = limit + 1;
 			break;
 		case MFD_TOP:
-			if(winy < Scr->BorderTop) {
+			limit = RLayoutFindMonitorTopEdge(Scr->BorderedLayout, &area);
+			if(winy < limit) {
 				return -1;
 			}
-			ret = Scr->BorderTop;
+			ret = limit;
 			break;
 		case MFD_BOTTOM:
-			if(winy + winh > Scr->rooth - Scr->BorderBottom) {
+			limit = RLayoutFindMonitorBottomEdge(Scr->BorderedLayout, &area);
+			if(winy + winh > limit) {
 				return -1;
 			}
-			ret = Scr->rooth - Scr->BorderBottom;
+			ret = limit + 1;
 			break;
 		default:
 			return -1;
diff --git a/gen/ctwm.1 b/gen/ctwm.1
index 95ba589..a0e19fc 100644
--- a/gen/ctwm.1
+++ b/gen/ctwm.1
@@ -1,13 +1,13 @@
 '\" t
 .\"     Title: ctwm
 .\"    Author: [see the "AUTHOR(S)" section]
-.\" Generator: Asciidoctor 1.5.8
-.\"      Date: 2019-07-21
+.\" Generator: Asciidoctor 2.0.18
+.\"      Date: 2023-03-26
 .\"    Manual: \ \&
 .\"    Source: \ \&
 .\"  Language: English
 .\"
-.TH "CTWM" "1" "2019-07-21" "\ \&" "\ \&"
+.TH "CTWM" "1" "2023-03-26" "\ \&" "\ \&"
 .ie \n(.g .ds Aq \(aq
 .el       .ds Aq '
 .ss \n[.ss] 0
@@ -28,19 +28,20 @@
 .  LINKSTYLE blue R < >
 .\}
 .SH "NAME"
-ctwm \- Claude\(aqs Tab Window Manager for the X Window System
+ctwm \- Claude\*(Aqs Tab Window Manager for the X Window System
 .SH "SYNTAX"
 .sp
 .if n .RS 4
 .nf
+.fam C
 ctwm [(\-\-display | \-d) dpy]  [\-\-replace]  [\-\-single]
      [(\-\-file | \-f) initfile]  [\-\-cfgchk]  [\-\-dumpcfg]
      [\-\-nom4 | \-n]  [(\-\-keep\-defs | \-k)]  [(\-\-keep | \-K) m4file]
      [\-\-verbose | \-v]  [\-\-quiet | \-q]  [\-\-mono]  [\-\-xrm resource]
      [\-\-version]  [\-\-info]  [\-\-nowelcome | \-W]
-     [(\-\-window | \-w) [win\-id]]  [\-\-name name]
      [\-\-clientId clid]  [\-\-restore resfname]
      [\-\-help | \-h]
+.fam
 .fi
 .if n .RE
 .SH "DESCRIPTION"
@@ -63,7 +64,8 @@ This program is usually started by the user\(cqs session manager or
 startup script.  When used from \f(CRxdm(1)\fP or \f(CRxinit(1)\fP without
 a session manager, ctwm is frequently executed in the foreground
 as the last client.  When run this way, exiting ctwm causes the
-session to be terminated (i.e. logged out).
+X session to be terminated, shutting down the X server and killing off
+any other running clients.
 .sp
 By default, application windows are surrounded by a \(lqframe\(rq with a
 titlebar at the top and a special border around the window.  The titlebar
@@ -202,25 +204,6 @@ ctwm just prints its version number.
 ctwm prints its detailed version and compile time options.
 .RE
 .sp
-\-\-window[=\f(CRwin\-id\fP], \-w[\f(CRwin\-id\fP]
-.RS 4
-If \f(CR\-w\fP is specified without a win\-id value, ctwm does not take over
-the whole screen(s); instead it creates a new window that becomes its
-root window.  If the win\-id value is given, it is considered to be the
-id of an existing window, in which case, ctwm will try to use this
-window as root window.  You can run any number of instantiations of
-ctwm at the same time.  You can even have embedded ctwm instantiations.
-This is totally useless, but I like it.  The f.adoptwindow function can
-be used to capture an existing window belonging to another ctwm.  A
-possible use of such mode can be to test new configuration file without
-restarting ctwm.
-.RE
-.sp
-\-\-name=\f(CRname\fP, \-n \f(CRname\fP
-.RS 4
-Set the captivename when using \f(CR\-\-window\fP.
-.RE
-.sp
 \-\-nowelcome, \-W
 .RS 4
 This option tells ctwm not to display any welcome when starting.
@@ -240,14 +223,6 @@ ctwm uses \f(CRgetopt_long()\fP for parsing the command\-line options.  This
 means that args can be passed via \f(CR\-\-long=arg\fP and \f(CR\-\-long arg\fP, as well
 as \f(CR\-l arg\fP and \f(CR\-larg\fP, and short args can be bundled like \f(CR\-vnk\fP as
 well as \f(CR\-v \-n \-k\fP.
-.RS 4
-.sp
-Note, however, that the handling of optional args is slightly different;
-they \fBmust\fP be specified with = or no space.  e.g., \f(CR\-\-window=123\fP will
-set the win\-id to 123, but \f(CR\-\-window 123\fP will not; the 123 will be
-treated as a separate argument.  Similarly, it must be set as \f(CR\-w123\fP,
-not \f(CR\-w 123\fP.
-.RE
 .SH "CUSTOMIZATION"
 .sp
 Much of ctwm\(cqs appearance and behavior can be controlled by providing
@@ -322,8 +297,10 @@ you want.  For example:
 .sp
 .if n .RS 4
 .nf
+.fam C
 define(IRegion, translit(eval(WIDTH/3)*eval(HEIGHT/2)+eval(WIDTH\-WIDTH/3)\-0, *, x))
 IconRegion  "IRegion" SOUTH EAST 75 25
+.fam
 .fi
 .if n .RE
 .sp
@@ -502,25 +479,24 @@ Is defined if ctwm was compiled with I18N support.  This is no longer
 optional since 3.8 and is always compiled in.  The definition will be
 removed in a future version.
 .RE
-.SS "ctwm run\-time options"
+.SS "Obsolete options"
 .sp
-TWM_CAPTIVE
+GNOME
 .RS 4
-This will be either \(lqYes\(rq or \(lqNo\(rq. \(lqYes\(rq if the current ctwm is captive
-(flag \-w), \(lqNo\(rq in the other case.
+Defined when ctwm was compiled with GNOME1 support.  Removed after
+3.8.2.
 .RE
 .sp
-TWM_CAPTIVE_NAME
+TWM_CAPTIVE
 .RS 4
-Defined only if TWM_CAPTIVE is also defined. Contains the name of the captive
-ctwm (flag \-\-name).
+This was either \(lqYes\(rq or \(lqNo\(rq. \(lqYes\(rq if the current ctwm is captive
+(flag \-w), \(lqNo\(rq in the other case.  Removed in 4.1.0.
 .RE
-.SS "Obsolete options"
 .sp
-GNOME
+TWM_CAPTIVE_NAME
 .RS 4
-Defined when ctwm was compiled with GNOME1 support.  Removed after
-3.8.2.
+Defined only if TWM_CAPTIVE is also defined. Contains the name of the captive
+ctwm (flag \-\-name).  Removed in 4.1.0.
 .RE
 .SH "VARIABLES"
 .sp
@@ -534,7 +510,9 @@ whitespace or a newline.  For example:
 .sp
 .if n .RS 4
 .nf
+.fam C
 AutoRaise { "emacs" "XTerm" "Xmh" }
+.fam
 .fi
 .if n .RE
 .sp
@@ -542,12 +520,14 @@ or
 .sp
 .if n .RS 4
 .nf
+.fam C
 AutoRaise
 {
     "emacs"
     "XTerm"
     "Xmh"
 }
+.fam
 .fi
 .if n .RE
 .sp
@@ -722,11 +702,13 @@ different types of windows.  For example:
 .sp
 .if n .RS 4
 .nf
+.fam C
 BorderColor "gray50"
 {
     "XTerm" "red"
     "xmh"   "green"
 }
+.fam
 .fi
 .if n .RE
 .sp
@@ -819,7 +801,9 @@ The value is a comprised between 0 and 100. The formula used is :
 .sp
 .if n .RS 4
 .nf
+.fam C
     clear.{RGB} = (65535 \- color.{RGB}) * (contrast / 100).
+.fam
 .fi
 .if n .RE
 .sp
@@ -870,6 +854,7 @@ For example:
 .sp
 .if n .RS 4
 .nf
+.fam C
 Color
 {
     MenuBackground      "gray50"
@@ -878,6 +863,7 @@ Color
     TitleForeground     "yellow"
     TitleBackground     "blue"
 }
+.fam
 .fi
 .if n .RE
 .sp
@@ -910,7 +896,9 @@ Shapes from the \f(CRcursor\fP font may be specified directly as:
 .sp
 .if n .RS 4
 .nf
+.fam C
     cursorname "string"
+.fam
 .fi
 .if n .RE
 .sp
@@ -922,7 +910,9 @@ from bitmap files, the following syntax is used instead:
 .sp
 .if n .RS 4
 .nf
+.fam C
     cursorname "image" "mask"
+.fam
 .fi
 .if n .RE
 .sp
@@ -933,6 +923,7 @@ The following example shows the default cursor definitions:
 .sp
 .if n .RS 4
 .nf
+.fam C
 Cursors
 {
     Frame       "top_left_arrow"
@@ -947,6 +938,7 @@ Cursors
     Select      "dot"
     Destroy     "pirate"
 }
+.fam
 .fi
 .if n .RE
 .RE
@@ -958,7 +950,9 @@ The value is a comprised between 0 and 100. The formula used is :
 .sp
 .if n .RS 4
 .nf
+.fam C
     dark.{RGB}  = color.{RGB} * ((100 \- contrast) / 100),
+.fam
 .fi
 .if n .RE
 .sp
@@ -1059,6 +1053,7 @@ example shows all the valid options:
 .sp
 .if n .RS 4
 .nf
+.fam C
 EWMHIgnore
 {
     # Window states
@@ -1069,6 +1064,7 @@ EWMHIgnore
     "STATE_ABOVE"
     "STATE_BELOW"
 }
+.fam
 .fi
 .if n .RE
 .sp
@@ -1227,7 +1223,10 @@ This variable specifies the geometry of the icon manager window.  The
 the initial full size of the icon manager.  The icon manager window is
 then broken into \f(CRcolumns\fP pieces and scaled according to the number
 of entries in the icon manager.  Extra entries are wrapped to form
-additional rows.  The default number of columns is 1.
+additional rows.  The default number of columns is 1. When XrandR is
+compiled, the geometry can be relative to a monitor, by prefixing
+its name (visible with \f(CRxrandr(1)\fP command line) followed by \f(CR:\fP. This name
+is ignored when XrandR is not available.
 .RE
 .sp
 IconManagerHighlight \f(CRstring\fP [{ \f(CRwin\-list\fP }]
@@ -1249,23 +1248,30 @@ This variable specifies a list of icon managers to create.  Each item in the
 .sp
 .if n .RS 4
 .nf
+.fam C
     "winname" ["iconname"] "geometry" columns
+.fam
 .fi
 .if n .RE
 .sp
-where \f(CRwinname\fP is the name of the windows that should be put into this
-icon manager, \f(CRiconname\fP is the name of that icon manager window\(cqs icon,
-\f(CRgeometry\fP is a standard geometry specification, and \f(CRcolumns\fP is
-the number of columns in this icon manager as described in
-\f(CRIconManagerGeometry\fP.  For example:
+where \f(CRwinname\fP is the name of the windows that should be put into
+this icon manager, \f(CRiconname\fP is the name of that icon manager
+window\(cqs icon, \f(CRgeometry\fP is a standard geometry specification, and
+\f(CRcolumns\fP is the number of columns in this icon manager as described
+in \f(CRIconManagerGeometry\fP. When XrandR is compiled, the geometry can
+be relative to a monitor, by prefixing its name (visible with
+\f(CRxrandr(1)\fP command line) followed by \f(CR:\fP. This name is ignored when
+XrandR is not available.  For example:
 .sp
 .if n .RS 4
 .nf
+.fam C
 IconManagers
 {
-    "XTerm"  "=300x5+800+5"  5
-    "myhost" "=400x5+100+5"  2
+    "XTerm"  "=300x5+800+5"       5
+    "myhost" "HDMI2:400x5+100+5"  2
 }
+.fam
 .fi
 .if n .RE
 .sp
@@ -1296,7 +1302,10 @@ IconRegion \f(CRgeomstring\fP \f(CRvgrav hgrav gridwidth gridheight\fP [\f(CRico
 .RS 4
 This variable specifies an area on the root window in which icons are placed
 if no specific icon location is provided by the client.  The \f(CRgeomstring\fP
-is a quoted string containing a standard geometry specification. If more than
+is a quoted string containing a standard geometry specification. When XrandR
+is compiled, the geometry can be relative to a monitor, by prefixing
+its name (visible with \f(CRxrandr(1)\fP command line) followed by \f(CR:\fP.
+This name is ignored when XrandR is not available. If more than
 one \f(CRIconRegion\fP lines are given, icons will be put into the succeeding
 icon regions when the first is full. The \f(CRvgrav\fP argument should be either
 \f(CRNorth\fP or \f(CRSouth\fP  and is used to control whether icons are
@@ -1335,11 +1344,13 @@ should be used as their icons.  For example:
 .sp
 .if n .RS 4
 .nf
+.fam C
 Icons
 {
     "XTerm"  "xterm.icon"
     "xfd"    "xfd_icon"
 }
+.fam
 .fi
 .if n .RE
 .sp
@@ -1387,7 +1398,9 @@ num locks. You don\(cqt need IgnoreLockModifier any more with this option.
 .sp
 .if n .RS 4
 .nf
+.fam C
 IgnoreModifier { lock m2 }
+.fam
 .fi
 .if n .RE
 .RE
@@ -1398,7 +1411,9 @@ List of windows for which to ignore transients.
 .sp
 .if n .RS 4
 .nf
+.fam C
 IgnoreTransient { "Wine" }
+.fam
 .fi
 .if n .RE
 .RE
@@ -1410,6 +1425,7 @@ entry specified colors.  In the example below:
 .sp
 .if n .RS 4
 .nf
+.fam C
 Menu "mymenu"
 {
     "Title"     ("black":"red")     f.title
@@ -1419,6 +1435,7 @@ Menu "mymenu"
     "entry4"                        f.nop
     "entry5"    ("red":"white")     f.nop
 }
+.fam
 .fi
 .if n .RE
 .sp
@@ -1527,6 +1544,53 @@ can only be specified inside of a
 \f(CRColor\fP or \f(CRMonochrome\fP list.  The default is \(lqblack\(rq.
 .RE
 .sp
+MonitorLayout { \f(CRmonitor\-list\fP }
+.RS 4
+This allows manually configuring what ctwm will consider as the list of
+monitors.  If \f(CRXRANDR\fP support is compiled in (the default as of
+4.1.0), and the \f(CRRANDR\fP extension is available on your server, then
+\f(CRctwm\fP will use that to determine the size and layout of your monitors.
+.sp
+However, if either is not the case, or you want to override the results
+it returns, you can specify the names and layouts of your desired
+\(lqmonitors\(rq with this.  For instance, if you have a very wide monitor,
+and would prefer to treat it as several narrower side\-by\-side monitors,
+you could use this to tell ctwm to treat it that way.
+.sp
+.if n .RS 4
+.nf
+.fam C
+MonitorLayout
+{
+    # Imagine a 5000x1000 monitor, that we want to split into two 2k wide
+    # sections at the far left and right, with a 1k wide section in the
+    # middle.
+    "Left:2000x1000+0+0"
+    "1000x1000+2000+0"       # Middle section unnamed
+    "Right:2000x1000+3000+0"
+}
+.fam
+.fi
+.if n .RE
+.sp
+With \f(CRm4\fP support, you could even make it automatically split your full
+display into 2 \(lqmonitors\(rq:
+.sp
+.if n .RS 4
+.nf
+.fam C
+define(LeftWidth, eval(WIDTH / 2))dnl
+define(RightWidth, eval(WIDTH \- LeftWidth))dnl
+MonitorLayout
+{
+    "`AutoL:\*(AqLeftWidth`x\*(AqHEIGHT`+0+0\*(Aq"
+    "`AutoR:\*(AqRightWidth`x\*(AqHEIGHT`+\*(AqLeftWidth`+0\*(Aq"
+}
+.fam
+.fi
+.if n .RE
+.RE
+.sp
 Monochrome { \f(CRcolors\fP }
 .RS 4
 This variable specifies a list of color assignments that should be made if
@@ -1563,6 +1627,7 @@ example shows all the valid options:
 .sp
 .if n .RS 4
 .nf
+.fam C
 MWMIgnore
 {
     # en/disable window borders
@@ -1571,6 +1636,7 @@ MWMIgnore
     # en/disable titlebars
     "DECOR_TITLE"
 }
+.fam
 .fi
 .if n .RE
 .sp
@@ -1781,8 +1847,10 @@ This variable specifies which windows occupy which workspaces at startup.
 .sp
 .if n .RS 4
 .nf
+.fam C
         [Window]   win\-name  { wspc1 wspc2 ... }
 or      Workspace  wspc\-name { win1  win2 ...  }
+.fam
 .fi
 .if n .RE
 .sp
@@ -1790,6 +1858,7 @@ Example :
 .sp
 .if n .RS 4
 .nf
+.fam C
 Occupy
 {
                "xload"   {"all"}
@@ -1797,6 +1866,7 @@ Occupy
                "xv"      {"images"}
     WorkSpace  "images"  {"xloadimage"}
 }
+.fam
 .fi
 .if n .RE
 .sp
@@ -1822,12 +1892,14 @@ Example :
 .sp
 .if n .RS 4
 .nf
+.fam C
 OccupyAll
 {
     "xload"
     "xbiff"
     "xconsole"
 }
+.fam
 .fi
 .if n .RE
 .sp
@@ -1848,6 +1920,7 @@ Example:
 .sp
 .if n .RS 4
 .nf
+.fam C
 OnTopPriority Icons \-1  # place icons a little in the background
 OnTopPriority Icons 1   # place mail icons on top of normal windows
 {
@@ -1860,6 +1933,7 @@ OnTopPriority 8         # keep these always on top of other windows
     "Emacs Icon Manager" "WorkSpaceManager"
     "TWM Icon Manager" "XDaliClock"
 }
+.fam
 .fi
 .if n .RE
 .sp
@@ -1924,11 +1998,13 @@ may be specified:
 .sp
 .if n .RS 4
 .nf
+.fam C
 Pixmaps
 {
         TitleHighlight  "gray1"
 #       TitleHighlight  "supman%.xbm"
 }
+.fam
 .fi
 .if n .RE
 .sp
@@ -1947,12 +2023,14 @@ Example :
 .sp
 .if n .RS 4
 .nf
+.fam C
 PixmapDirectory  "/usr/lib/X11/twm"
 Icons
 {
     "Axe"    "xpm:edit.xpm"
     "xterm"  "xpm:ball%.xpm"
 }
+.fam
 .fi
 .if n .RE
 .sp
@@ -2076,7 +2154,9 @@ contains entries of the form
 .sp
 .if n .RS 4
 .nf
+.fam C
     "EventName"   "/file/to/play.wav"
+.fam
 .fi
 .if n .RE
 .sp
@@ -2099,6 +2179,7 @@ For example:
 .sp
 .if n .RS 4
 .nf
+.fam C
 SaveColor
 {
     BorderColor
@@ -2108,6 +2189,7 @@ SaveColor
     "green"
     "blue"
 }
+.fam
 .fi
 .if n .RE
 .sp
@@ -2177,7 +2259,9 @@ top of the window.  It contains entries of the form:
 .sp
 .if n .RS 4
 .nf
+.fam C
     "name"  justification num denom
+.fam
 .fi
 .if n .RE
 .sp
@@ -2197,6 +2281,7 @@ For example:
 .sp
 .if n .RS 4
 .nf
+.fam C
 SqueezeTitle
 {
     "XTerm"   left    0  0
@@ -2205,6 +2290,7 @@ SqueezeTitle
     "oclock"  center  1  2
     "emacs"   right   2  2
 }
+.fam
 .fi
 .if n .RE
 .sp
@@ -2437,29 +2523,6 @@ UseThreeDWMap
 Tells ctwm to use 3D for the small windows in the workspace map.
 .RE
 .sp
-VirtualScreens { \f(CRgeometries\-list\fP }
-.RS 4
-.sp
-This variable specifies a list of geometries for virtual screens. Virtual screens
-are designed to be used when you have several physical screens bound together
-with the Xinerama X extension.
-.sp
-\f(CRgeometries\-list\fP is a list of valid geometry strings, that correspond to
-your actual physical screens.
-.sp
-Example :
-.sp
-.if n .RS 4
-.nf
-VirtualScreens
-{
-    "1280x1024+0+0"
-    "1600x1200+1280+0"
-}
-.fi
-.if n .RE
-.RE
-.sp
 WarpCursor [{ \f(CRwin\-list\fP }]
 .RS 4
 This variable indicates that the pointer should be warped into windows when
@@ -2475,7 +2538,9 @@ into the current workspace, if necessary. For example
 .sp
 .if n .RS 4
 .nf
+.fam C
 WarpOnDeIconify { "Emacs" }
+.fam
 .fi
 .if n .RE
 .sp
@@ -2506,32 +2571,21 @@ binding that will pop a particular window (such as \f(CRxmh\fP), no matter
 where it is.  The default is for \f(CRf.warpto\fP to ignore iconified windows.
 .RE
 .sp
-WindowBox [{ \f(CRwin\-list\fP }]
-.RS 4
-creates a new window called a box, where
-all the client windows that match the windows list are opened in,
-instead of the root window. This is useful to group small windows
-in the same box (xload for instance)
-.sp
-.if n .RS 4
-.nf
-WindowBox "xloadbox" "320x100+0\-0" {
-    "xload"
-}
-.fi
-.if n .RE
-.RE
-.sp
 WindowGeometries { \f(CRwin\-list\fP }
 .RS 4
-Used to give a default geometry to some clients :
+Used to give a default geometry to some clients. When XrandR is
+compiled, the geometry can be relative to a monitor, by prefixing
+its name (visible with \f(CRxrandr(1)\fP command line) followed by \f(CR:\fP. This name
+is ignored when XrandR is not available:
 .sp
 .if n .RS 4
 .nf
+.fam C
 WindowGeometries {
     "Mozilla*"       "1000x800+10+10"
-    "jpilot*"        "800x600\-0\-0"
+    "jpilot*"        "HDMI1:800x600\-0\-0"
 }
+.fam
 .fi
 .if n .RE
 .RE
@@ -2588,12 +2642,18 @@ WorkSpaceManagerGeometry \f(CRstring\fP [ \f(CRcolumns\fP ]
 .RS 4
 This variable specifies the geometry of the workspace manager window. The
 \f(CRstring\fP argument is standard geometry specification that indicates
-the initial full size of the workspace manager. The \f(CRcolumns\fP argument
+the initial full size of the workspace manager. When XrandR is
+compiled, the geometry can be relative to a monitor, by prefixing
+its name (visible with \f(CRxrandr(1)\fP command line) followed by \f(CR:\fP. This name
+is ignored when XrandR is not available. The \f(CRcolumns\fP argument
 indicates the number of columns to use for the workspace manager window.
 .sp
 .if n .RS 4
 .nf
+.fam C
 WorkSpaceManagerGeometry        "360x60+60\-0" 8
+WorkSpaceManagerGeometry        "HDMI1:600x30+1235+0" 12
+.fam
 .fi
 .if n .RE
 .RE
@@ -2605,7 +2665,9 @@ Where \f(CRworkspace\-list\fP is :
 .sp
 .if n .RS 4
 .nf
+.fam C
 name [{bg\-button [fg\-button] [bg\-root] [fg\-root] [pixmap\-root]}]
+.fam
 .fi
 .if n .RE
 .sp
@@ -2642,6 +2704,7 @@ Example:
 .sp
 .if n .RS 4
 .nf
+.fam C
 WorkSpaces
 {
   "One"   {"#686B9F" "white" "DeepSkyBlue3" "white" "jpeg:shark.jpg"}
@@ -2655,6 +2718,7 @@ WorkSpaces
   "Seven" {"#8C5b7A" "white" "chartreuse4"}
   "Eight" {"#686B9F" "white" "MidnightBlue"}
 }
+.fam
 .fi
 .if n .RE
 .sp
@@ -2752,7 +2816,9 @@ pressed within them:
 .sp
 .if n .RS 4
 .nf
+.fam C
 LeftTitleButton "bitmapname" = function
+.fam
 .fi
 .if n .RE
 .sp
@@ -2760,11 +2826,13 @@ or
 .sp
 .if n .RS 4
 .nf
+.fam C
 LeftTitleButton "bitmapname" {
     Buttoni = modlist : function
     ...
     Buttonj = function
 }
+.fam
 .fi
 .if n .RE
 .sp
@@ -2772,7 +2840,9 @@ or
 .sp
 .if n .RS 4
 .nf
+.fam C
 RightTitleButton "bitmapname" = function
+.fam
 .fi
 .if n .RE
 .sp
@@ -2780,11 +2850,13 @@ or
 .sp
 .if n .RS 4
 .nf
+.fam C
 RightTitleButton "bitmapname" {
     Buttoni = modlist : function
     ...
     Buttonj = function
 }
+.fam
 .fi
 .if n .RE
 .sp
@@ -2799,13 +2871,17 @@ words, the following two lines are equivalent:
 .sp
 .if n .RS 4
 .nf
+.fam C
     Buttoni = function
+.fam
 .fi
 .if n .RE
 .sp
 .if n .RS 4
 .nf
+.fam C
     Buttoni = : function
+.fam
 .fi
 .if n .RE
 .SS "Key and pointer buttons"
@@ -2818,8 +2894,10 @@ keysym name; buttons are given as the keywords \f(CRButton1\fP\-\f(CRButton11\fP
 .sp
 .if n .RS 4
 .nf
+.fam C
 "FP1"   = modlist : context : function
 Button1 = modlist : context : function
+.fam
 .fi
 .if n .RE
 .sp
@@ -2828,6 +2906,7 @@ use the following bindings:
 .sp
 .if n .RS 4
 .nf
+.fam C
 "F1"    =       : all : f.iconify
 "F2"    =       : all : f.raiselower
 "F3"    =       : all : f.warpring "next"
@@ -2840,6 +2919,7 @@ use the following bindings:
 "Right" = m | s : all : f.forwiconmgr
 "Up"    = m     : all : f.upiconmgr
 "Down"  = m | s : all : f.downiconmgr
+.fam
 .fi
 .if n .RE
 .sp
@@ -2875,6 +2955,7 @@ a vertical bar (\f(CR|\fP).  For example:
 .sp
 .if n .RS 4
 .nf
+.fam C
 # Open the "top" menu with a Button1 (usually left mouse button) click in
 # the root window
 Button1 =        : root : f.menu "top"
@@ -2884,6 +2965,7 @@ Button1 =     s  : root : f.menu "top2"
 
 # And "top3" when control\-shift\-click
 Button1 = c | s  : root : f.menu "top3"
+.fam
 .fi
 .if n .RE
 .sp
@@ -2897,7 +2979,7 @@ modifiers refer to ctwm alternate keymaps; see the description of
 Note that if you\(cqre using the \f(CRm4\fP preprocessor, most implementations
 define a \f(CRshift\fP macro internally, so using that as a modifier will
 silently fail to work right.  To get around it, you\(cqll need to quote it
-so that \f(CRm4\fP passes it through as a literal string: \f(CR`shift\(aq\fP.
+so that \f(CRm4\fP passes it through as a literal string: \f(CR`shift\*(Aq\fP.
 .sp
 The \f(CRcontext\fP lets you specify which mappings apply based on where the
 pointer currently is on the screen.  The available options are
@@ -2925,9 +3007,11 @@ window context to all windows matching that name.  e.g.,
 .sp
 .if n .RS 4
 .nf
+.fam C
 # Pressing "F1" anywhere on the screen will cause all windows with name
 # "xterm" to raise themselves.
 "F1" = : "xterm" : f.raise
+.fam
 .fi
 .if n .RE
 .sp
@@ -2941,6 +3025,7 @@ For example, the default startup file contains the following bindings:
 .sp
 .if n .RS 4
 .nf
+.fam C
 Button1 =   : root          : f.menu "TwmWindows"
 Button1 = m : window | icon : f.function "move\-or\-lower"
 Button2 = m : window | icon : f.iconify
@@ -2951,6 +3036,7 @@ Button1 =   : icon          : f.function "move\-or\-iconify"
 Button2 =   : icon          : f.iconify
 Button1 =   : iconmgr       : f.iconify
 Button2 =   : iconmgr       : f.iconify
+.fam
 .fi
 .if n .RE
 .sp
@@ -2960,10 +3046,12 @@ example:
 .sp
 .if n .RS 4
 .nf
+.fam C
 Function "move\-or\-lower"    { f.move f.deltastop f.lower }
 Function "move\-or\-raise"    { f.move f.deltastop f.raise }
 Function "move\-or\-iconify"  { f.move f.deltastop f.iconify }
 Function "restore\-colormap" { f.colormap "default" f.lower }
+.fam
 .fi
 .if n .RE
 .sp
@@ -2986,13 +3074,6 @@ This function adds the selected window to the workspace whose name is
 \f(CRstring\fP.
 .RE
 .sp
-f.adoptwindow
-.RS 4
-This function asks for the user to select a window with the mouse, and then
-adopt this window if it doesn\(cqt belong to the current ctwm. Useful only
-with the \-w flag.
-.RE
-.sp
 f.altcontext
 .RS 4
 Set the alternate context. The next key or button event ctwm receives will
@@ -3002,9 +3083,11 @@ For example:
 .sp
 .if n .RS 4
 .nf
+.fam C
 "Return" = m : all   : f.altcontext
 "n" =        : alter : f.nextworkspace
 "p" =        : alter : f.prevworkspace
+.fam
 .fi
 .if n .RE
 .RE
@@ -3019,12 +3102,14 @@ field of the binding command. For example:
 .sp
 .if n .RS 4
 .nf
+.fam C
 "Return" = c : all                  : f.altkeymap "1"
 "i" =     a1 : window|icon|iconmgr  : f.iconify
 "z" =     a1 : window               : f.zoom
 "d" =     a1 : window|icon          : f.delete
 "o" =     a1 : window|icon          : f.occupy
 "r" =     a1 : window|icon          : f.refresh
+.fam
 .fi
 .if n .RE
 .sp
@@ -3065,8 +3150,8 @@ This function sounds the keyboard bell.
 f.bottomzoom
 .RS 4
 This function stretches the bottom side of the window out to the bottom
-edge of the screen, or restores the original size if the window was
-already bottomzoom\(cqd.
+edge of the current monitor, or restores the original size if the
+window was already bottomzoom\(cqd.
 .RE
 .sp
 f.changepriority \f(CRrel\-value\fP
@@ -3090,6 +3175,7 @@ can never be set/changed to less than 1.
 .sp
 .if n .RS 4
 .nf
+.fam C
 "Right"  = c|s : all : f.changesize "right +10"
 "Left"   = c|s : all : f.changesize "right \-10"
 "Down"   = c|s : all : f.changesize "bottom +10"
@@ -3098,6 +3184,7 @@ can never be set/changed to less than 1.
 "F1"     = c|s : all : f.changesize "640x480"
 "F2"     = c|s : all : f.changesize "800x600"
 "F3"     = c|s : all : f.changesize "1024x768"
+.fam
 .fi
 .if n .RE
 .RE
@@ -3195,12 +3282,6 @@ f.fill \(lqvertical\(rq sets the window status to \(lqzoomed\(rq and toggles, ie
 calling it again will reset the previous window size.
 .RE
 .sp
-f.fittocontent
-.RS 4
-Can be used only with window boxes. The result is to have the box have the
-minimal size that contains all its children windows.
-.RE
-.sp
 f.focus
 .RS 4
 This function toggles the keyboard focus of the server to the
@@ -3231,8 +3312,8 @@ f.fullscreenzoom
 .RS 4
 This function is similar to the \f(CRf.fullzoom\fP function, except that it
 makes the client window (the part inside the frame) the size of the
-screen, so the window decorations are off\-screen.  This gives the same
-visual effect as the window covering the whole screen with no
+current monitor, so the window decorations are off\-screen.  This gives
+the same visual effect as the window covering the whole screen with no
 decorations.  If the window is already fullscreenzoom\(cqd, it restores
 the original size.
 .RE
@@ -3240,8 +3321,8 @@ the original size.
 f.fullzoom
 .RS 4
 This function resizes the selected window to the full size of the
-screen, or restores the original size if the window was already
-fullzoom\(cqd.
+current monitor, or restores the original size if the window was
+already fullzoom\(cqd.
 .RE
 .sp
 f.function \f(CRstring\fP
@@ -3273,8 +3354,8 @@ Unmap the WorkSpace manager.
 f.horizoom
 .RS 4
 This function stretches the window so that it covers the whole width of
-the screen, or restores the original size if the window was already
-horizoom\(cqd.
+the current monitor, or restores the original size if the window was
+already horizoom\(cqd.
 .RE
 .sp
 f.htzoom
@@ -3282,13 +3363,6 @@ f.htzoom
 This function is a synonym for \f(CRf.topzoom\fP.
 .RE
 .sp
-f.hypermove
-.RS 4
-Use this function to \(lqmove\(rq a window between 2 captives ctwm (or between a
-captive and the root ctwm). Of course 2 ctwms are completely different
-universes. You have to go in hyperspace to achieve this, hence the name.
-.RE
-.sp
 f.hzoom
 .RS 4
 This function is a synonym for \f(CRf.horizoom\fP.
@@ -3352,8 +3426,8 @@ manager.
 f.leftzoom
 .RS 4
 This function stretches the left side of the window out to the left
-edge of the screen, or restores the original size if the window was
-already leftzoom\(cqd.
+edge of the current monitor, or restores the original size if the
+window was already leftzoom\(cqd.
 .RE
 .sp
 f.lower
@@ -3412,6 +3486,10 @@ Takes one string argument which is a geometry with the
 standard X geometry syntax (e.g. 200x300+150\-0). Sets the current window
 to the specified geometry. The width and height are to be given in pixel,
 no base size or resize increment are used.
+When XrandR is compiled, the geometry can be relative to a monitor,
+by prefixing its name (visible with \f(CRxrandr(1)\fP command line) followed by
+\f(CR:\fP (e.g. HDMI1:200x300+150\-0). This name is ignored when XrandR is not
+available.
 .RE
 .sp
 f.movetitlebar
@@ -3426,7 +3504,9 @@ The default positioning is left\-justified, absolute at 0 pixels.
 .sp
 .if n .RS 4
 .nf
+.fam C
 Button1 = m1 : title : f.movetitlebar
+.fam
 .fi
 .if n .RE
 .sp
@@ -3605,8 +3685,8 @@ manager.
 f.rightzoom
 .RS 4
 This function stretches the right side of the window out to the right
-edge of the screen, or restores the original size if the window was
-already rightzoom\(cqd.
+edge of the current monitor, or restores the original size if the
+window was already rightzoom\(cqd.
 .RE
 .sp
 f.ring
@@ -3759,8 +3839,8 @@ it is mapped, it will be unmapped and vice versa.
 .sp
 f.topzoom
 .RS 4
-This function stretches the top side of the window out to the top
-edge of the screen, or restores the original size if the window was
+This function stretches the top side of the window out to the top edge
+of the current monitor, or restores the original size if the window was
 already topzoom\(cqd.
 .RE
 .sp
@@ -3874,11 +3954,59 @@ This function is similar to the \f(CRf.refresh\fP function except that only the
 selected window is refreshed.
 .RE
 .sp
+f.xbottomzoom
+.RS 4
+This function is similar to the \f(CRf.bottomzoom\fP function, but will cross
+monitors.
+.RE
+.sp
+f.xfullscreenzoom
+.RS 4
+This function is similar to the \f(CRf.fullscreenzoom\fP function, but will
+cross monitors.
+.RE
+.sp
+f.xfullzoom
+.RS 4
+This function is similar to the \f(CRf.fullzoom\fP function, but will cross
+monitors.
+.RE
+.sp
+f.xhorizoom
+.RS 4
+This function is similar to the \f(CRf.horizoom\fP function, but will cross
+monitors.
+.RE
+.sp
+f.xleftzoom
+.RS 4
+This function is similar to the \f(CRf.leftzoom\fP function, but will cross
+monitors.
+.RE
+.sp
+f.xrightzoom
+.RS 4
+This function is similar to the \f(CRf.rightzoom\fP function, but will cross
+monitors.
+.RE
+.sp
+f.xtopzoom
+.RS 4
+This function is similar to the \f(CRf.topzoom\fP function, but will cross
+monitors.
+.RE
+.sp
+f.xzoom
+.RS 4
+This function is similar to the \f(CRf.zoom\fP function, but will cross
+monitors.
+.RE
+.sp
 f.zoom
 .RS 4
-This function stretches the window so that it covers the whole height of
-the screen, or restores the original size if the window was already
-zoom\(cqd.  It\(cqs the vertical counterpart fo \f(CRf.horizoom\fP; perhaps
+This function stretches the window so that it covers the whole height
+of the current monitor, or restores the original size if the window was
+already zoom\(cqd.  It\(cqs the vertical counterpart fo \f(CRf.horizoom\fP; perhaps
 \f(CRf.vertzoom\fP would be a better name...
 .RE
 .SH "MENUS"
@@ -3893,6 +4021,7 @@ individual items:
 .sp
 .if n .RS 4
 .nf
+.fam C
 Menu "menuname" [ ("deffg":"defbg") ]
 {
     string1  [ ("fg1":"bg1") ]  function1
@@ -3902,6 +4031,7 @@ Menu "menuname" [ ("deffg":"defbg") ]
         .
     stringN  [ ("fgN":"bgN") ]  functionN
 }
+.fam
 .fi
 .if n .RE
 .sp
@@ -4009,7 +4139,9 @@ normal), you could run a command like:
 .sp
 .if n .RS 4
 .nf
+.fam C
 xprop \-f CTWM_WM_ICON_NAME 8u \-set CTWM_WM_ICON_NAME "I hate this window"
+.fam
 .fi
 .if n .RE
 .sp
@@ -4037,7 +4169,9 @@ syntax:
 .sp
 .if n .RS 4
 .nf
+.fam C
 {X event}: {sound file}
+.fam
 .fi
 .if n .RE
 .sp
@@ -4050,6 +4184,7 @@ However configured, the currently known X events that can be given are:
 .sp
 .if n .RS 4
 .nf
+.fam C
 KeyPress
 KeyRelease
 ButtonPress
@@ -4083,6 +4218,7 @@ SelectionNotify
 ColormapNotify
 ClientMessage
 MappingNotify
+.fam
 .fi
 .if n .RE
 .sp
@@ -4091,8 +4227,10 @@ when ctwm is started or shut down:
 .sp
 .if n .RS 4
 .nf
+.fam C
 Startup
 Shutdown
+.fam
 .fi
 .if n .RE
 .SH "BUGS"
diff --git a/gen/ctwm.1.html b/gen/ctwm.1.html
index c63f429..253d745 100644
--- a/gen/ctwm.1.html
+++ b/gen/ctwm.1.html
@@ -2,28 +2,25 @@
 <html lang="en">
 <head>
 <meta charset="UTF-8">
-<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]-->
+<meta http-equiv="X-UA-Compatible" content="IE=edge">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
-<meta name="generator" content="Asciidoctor 1.5.8">
+<meta name="generator" content="Asciidoctor 2.0.18">
 <title>CTWM(1)</title>
 <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700">
 <style>
-/* Asciidoctor default stylesheet | MIT License | http://asciidoctor.org */
-/* Uncomment @import statement below to use as custom stylesheet */
-/*@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700";*/
-article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}
-audio,canvas,video{display:inline-block}
-audio:not([controls]){display:none;height:0}
-script{display:none!important}
-html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}
-a{background:transparent}
+/*! Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */
+/* Uncomment the following line when using as a custom stylesheet */
+/* @import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700"; */
+html{font-family:sans-serif;-webkit-text-size-adjust:100%}
+a{background:none}
 a:focus{outline:thin dotted}
 a:active,a:hover{outline:0}
 h1{font-size:2em;margin:.67em 0}
-abbr[title]{border-bottom:1px dotted}
 b,strong{font-weight:bold}
+abbr{font-size:.9em}
+abbr[title]{cursor:help;border-bottom:1px dotted #dddddf;text-decoration:none}
 dfn{font-style:italic}
-hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}
+hr{height:0}
 mark{background:#ff0;color:#000}
 code,kbd,pre,samp{font-family:monospace;font-size:1em}
 pre{white-space:pre-wrap}
@@ -35,20 +32,22 @@ sub{bottom:-.25em}
 img{border:0}
 svg:not(:root){overflow:hidden}
 figure{margin:0}
+audio,video{display:inline-block}
+audio:not([controls]){display:none;height:0}
 fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}
 legend{border:0;padding:0}
 button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}
 button,input{line-height:normal}
 button,select{text-transform:none}
-button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}
+button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}
 button[disabled],html input[disabled]{cursor:default}
-input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}
+input[type=checkbox],input[type=radio]{padding:0}
 button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
 textarea{overflow:auto;vertical-align:top}
 table{border-collapse:collapse;border-spacing:0}
-*,*::before,*::after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}
+*,::before,::after{box-sizing:border-box}
 html,body{font-size:100%}
-body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto;tab-size:4;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}
+body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;line-height:1;position:relative;cursor:auto;-moz-tab-size:4;-o-tab-size:4;tab-size:4;word-wrap:anywhere;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}
 a:hover{cursor:pointer}
 img,object,embed{max-width:100%;height:auto}
 object,embed{height:100%}
@@ -63,14 +62,12 @@ img{-ms-interpolation-mode:bicubic}
 img,object,svg{display:inline-block;vertical-align:middle}
 textarea{height:auto;min-height:50px}
 select{width:100%}
-.center{margin-left:auto;margin-right:auto}
-.stretch{width:100%}
 .subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}
-div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0;direction:ltr}
+div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0}
 a{color:#2156a5;text-decoration:underline;line-height:inherit}
 a:hover,a:focus{color:#1d4b8f}
-a img{border:none}
-p{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}
+a img{border:0}
+p{line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}
 p aside{font-size:.875em;line-height:1.35;font-style:italic}
 h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}
 h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}
@@ -79,51 +76,52 @@ h2{font-size:1.6875em}
 h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}
 h4,h5{font-size:1.125em}
 h6{font-size:1em}
-hr{border:solid #dddddf;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0}
+hr{border:solid #dddddf;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em}
 em,i{font-style:italic;line-height:inherit}
 strong,b{font-weight:bold;line-height:inherit}
 small{font-size:60%;line-height:inherit}
 code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}
-ul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}
+ul,ol,dl{line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}
 ul,ol{margin-left:1.5em}
-ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em}
-ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}
-ul.square{list-style-type:square}
+ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0}
 ul.circle{list-style-type:circle}
 ul.disc{list-style-type:disc}
+ul.square{list-style-type:square}
+ul.circle ul:not([class]),ul.disc ul:not([class]),ul.square ul:not([class]){list-style:inherit}
 ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}
 dl dt{margin-bottom:.3125em;font-weight:bold}
 dl dd{margin-bottom:1.25em}
-abbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help}
-abbr{text-transform:none}
 blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}
-blockquote cite{display:block;font-size:.9375em;color:rgba(0,0,0,.6)}
-blockquote cite::before{content:"\2014 \0020"}
-blockquote cite a,blockquote cite a:visited{color:rgba(0,0,0,.6)}
 blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}
 @media screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}
 h1{font-size:2.75em}
 h2{font-size:2.3125em}
 h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}
 h4{font-size:1.4375em}}
-table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede}
+table{background:#fff;margin-bottom:1.25em;border:1px solid #dedede;word-wrap:normal}
 table thead,table tfoot{background:#f7f8f7}
 table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}
 table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}
-table tr.even,table tr.alt,table tr:nth-of-type(even){background:#f8f8f7}
-table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{display:table-cell;line-height:1.6}
+table tr.even,table tr.alt{background:#f8f8f7}
+table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{line-height:1.6}
 h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}
 h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}
+.center{margin-left:auto;margin-right:auto}
+.stretch{width:100%}
 .clearfix::before,.clearfix::after,.float-group::before,.float-group::after{content:" ";display:table}
 .clearfix::after,.float-group::after{clear:both}
-*:not(pre)>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background-color:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed;word-wrap:break-word}
-*:not(pre)>code.nobreak{word-wrap:normal}
-*:not(pre)>code.nowrap{white-space:nowrap}
-pre,pre>code{line-height:1.45;color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;text-rendering:optimizeSpeed}
+:not(pre).nobreak{word-wrap:normal}
+:not(pre).nowrap{white-space:nowrap}
+:not(pre).pre-wrap{white-space:pre-wrap}
+:not(pre):not([class^=L])>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background:#f7f7f8;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed}
+pre{color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;line-height:1.45;text-rendering:optimizeSpeed}
+pre code,pre pre{color:inherit;font-size:inherit;line-height:inherit}
+pre>code{display:block}
+pre.nowrap,pre.nowrap pre{white-space:pre;word-wrap:normal}
 em em{font-style:normal}
 strong strong{font-weight:400}
 .keyseq{color:rgba(51,51,51,.8)}
-kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background-color:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}
+kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background:#f7f7f7;border:1px solid #ccc;border-radius:3px;box-shadow:0 1px 0 rgba(0,0,0,.2),inset 0 0 0 .1em #fff;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}
 .keyseq kbd:first-child{margin-left:0}
 .keyseq kbd:last-child{margin-right:0}
 .menuseq,.menuref{color:#000}
@@ -135,7 +133,7 @@ b.button::before,b.button::after{position:relative;top:-1px;font-weight:400}
 b.button::before{content:"[";padding:0 3px 0 2px}
 b.button::after{content:"]";padding:0 2px 0 3px}
 p a>code:hover{color:rgba(0,0,0,.9)}
-#header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}
+#header,#content,#footnotes,#footer{width:100%;margin:0 auto;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}
 #header::before,#header::after,#content::before,#content::after,#footnotes::before,#footnotes::after,#footer::before,#footer::after{content:" ";display:table}
 #header::after,#content::after,#footnotes::after,#footer::after{clear:both}
 #content{margin-top:1.25em}
@@ -143,7 +141,7 @@ p a>code:hover{color:rgba(0,0,0,.9)}
 #header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}
 #header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #dddddf}
 #header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px}
-#header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap}
+#header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:flex;flex-flow:row wrap}
 #header .details span:first-child{margin-left:-.125em}
 #header .details span.email a{color:rgba(0,0,0,.85)}
 #header .details br{display:none}
@@ -164,7 +162,7 @@ p a>code:hover{color:rgba(0,0,0,.9)}
 #toctitle{color:#7a2518;font-size:1.2em}
 @media screen and (min-width:768px){#toctitle{font-size:1.375em}
 body.toc2{padding-left:15em;padding-right:0}
-#toc.toc2{margin-top:0!important;background-color:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
+#toc.toc2{margin-top:0!important;background:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
 #toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}
 #toc.toc2>ul{font-size:.9em;margin-bottom:0}
 #toc.toc2 ul ul{margin-left:0;padding-left:1em}
@@ -177,11 +175,11 @@ body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #e7e7e9
 #toc.toc2>ul{font-size:.95em}
 #toc.toc2 ul ul{padding-left:1.25em}
 body.toc2.toc-right{padding-left:0;padding-right:20em}}
-#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
+#content #toc{border:1px solid #e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;border-radius:4px}
 #content #toc>:first-child{margin-top:0}
 #content #toc>:last-child{margin-bottom:0}
-#footer{max-width:100%;background-color:rgba(0,0,0,.8);padding:1.25em}
-#footer-text{color:rgba(255,255,255,.8);line-height:1.44}
+#footer{max-width:none;background:rgba(0,0,0,.8);padding:1.25em}
+#footer-text{color:hsla(0,0%,100%,.8);line-height:1.44}
 #content{margin-bottom:.625em}
 .sect1{padding-bottom:.625em}
 @media screen and (min-width:768px){#content{margin-bottom:1.25em}
@@ -193,55 +191,62 @@ body.toc2.toc-right{padding-left:0;padding-right:20em}}
 #content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}
 #content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}
 #content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}
-.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}
+details,.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}
+details{margin-left:1.25rem}
+details>summary{cursor:pointer;display:block;position:relative;line-height:1.6;margin-bottom:.625rem;outline:none;-webkit-tap-highlight-color:transparent}
+details>summary::-webkit-details-marker{display:none}
+details>summary::before{content:"";border:solid transparent;border-left:solid;border-width:.3em 0 .3em .5em;position:absolute;top:.5em;left:-1.25rem;transform:translateX(15%)}
+details[open]>summary::before{border:solid transparent;border-top:solid;border-width:.5em .3em 0;transform:translateY(15%)}
+details>summary::after{content:"";width:1.25rem;height:1em;position:absolute;top:.3em;left:-1.25rem}
 .admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}
 table.tableblock.fit-content>caption.title{white-space:nowrap;width:0}
-.paragraph.lead>p,#preamble>.sectionbody>[class="paragraph"]:first-of-type p{font-size:1.21875em;line-height:1.6;color:rgba(0,0,0,.85)}
-table.tableblock #preamble>.sectionbody>[class="paragraph"]:first-of-type p{font-size:inherit}
+.paragraph.lead>p,#preamble>.sectionbody>[class=paragraph]:first-of-type p{font-size:1.21875em;line-height:1.6;color:rgba(0,0,0,.85)}
 .admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%}
 .admonitionblock>table td.icon{text-align:center;width:80px}
 .admonitionblock>table td.icon img{max-width:none}
 .admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}
-.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #dddddf;color:rgba(0,0,0,.6)}
+.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #dddddf;color:rgba(0,0,0,.6);word-wrap:anywhere}
 .admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}
-.exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px}
+.exampleblock>.content{border:1px solid #e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;border-radius:4px}
 .exampleblock>.content>:first-child{margin-top:0}
 .exampleblock>.content>:last-child{margin-bottom:0}
-.sidebarblock{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
+.sidebarblock{border:1px solid #dbdbd6;margin-bottom:1.25em;padding:1.25em;background:#f3f3f2;border-radius:4px}
 .sidebarblock>:first-child{margin-top:0}
 .sidebarblock>:last-child{margin-bottom:0}
 .sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}
 .exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
-.literalblock pre,.listingblock pre:not(.highlight),.listingblock pre[class="highlight"],.listingblock pre[class^="highlight "],.listingblock pre.CodeRay,.listingblock pre.prettyprint{background:#f7f7f8}
-.sidebarblock .literalblock pre,.sidebarblock .listingblock pre:not(.highlight),.sidebarblock .listingblock pre[class="highlight"],.sidebarblock .listingblock pre[class^="highlight "],.sidebarblock .listingblock pre.CodeRay,.sidebarblock .listingblock pre.prettyprint{background:#f2f1f1}
-.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{-webkit-border-radius:4px;border-radius:4px;word-wrap:break-word;overflow-x:auto;padding:1em;font-size:.8125em}
-@media screen and (min-width:768px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:.90625em}}
-@media screen and (min-width:1280px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:1em}}
-.literalblock pre.nowrap,.literalblock pre.nowrap pre,.listingblock pre.nowrap,.listingblock pre.nowrap pre{white-space:pre;word-wrap:normal}
-.literalblock.output pre{color:#f7f7f8;background-color:rgba(0,0,0,.9)}
-.listingblock pre.highlightjs{padding:0}
-.listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px}
-.listingblock pre.prettyprint{border-width:0}
+.literalblock pre,.listingblock>.content>pre{border-radius:4px;overflow-x:auto;padding:1em;font-size:.8125em}
+@media screen and (min-width:768px){.literalblock pre,.listingblock>.content>pre{font-size:.90625em}}
+@media screen and (min-width:1280px){.literalblock pre,.listingblock>.content>pre{font-size:1em}}
+.literalblock pre,.listingblock>.content>pre:not(.highlight),.listingblock>.content>pre[class=highlight],.listingblock>.content>pre[class^="highlight "]{background:#f7f7f8}
+.literalblock.output pre{color:#f7f7f8;background:rgba(0,0,0,.9)}
 .listingblock>.content{position:relative}
-.listingblock code[data-lang]::before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:#999}
+.listingblock code[data-lang]::before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:inherit;opacity:.5}
 .listingblock:hover code[data-lang]::before{display:block}
-.listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:.5em;color:#999}
+.listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:.5em;color:inherit;opacity:.5}
 .listingblock.terminal pre .command:not([data-prompt])::before{content:"$"}
-table.pyhltable{border-collapse:separate;border:0;margin-bottom:0;background:none}
-table.pyhltable td{vertical-align:top;padding-top:0;padding-bottom:0;line-height:1.45}
-table.pyhltable td.code{padding-left:.75em;padding-right:0}
-pre.pygments .lineno,table.pyhltable td:not(.code){color:#999;padding-left:0;padding-right:.5em;border-right:1px solid #dddddf}
-pre.pygments .lineno{display:inline-block;margin-right:.25em}
-table.pyhltable .linenodiv{background:none!important;padding-right:0!important}
+.listingblock pre.highlightjs{padding:0}
+.listingblock pre.highlightjs>code{padding:1em;border-radius:4px}
+.listingblock pre.prettyprint{border-width:0}
+.prettyprint{background:#f7f7f8}
+pre.prettyprint .linenums{line-height:1.45;margin-left:2em}
+pre.prettyprint li{background:none;list-style-type:inherit;padding-left:0}
+pre.prettyprint li code[data-lang]::before{opacity:1}
+pre.prettyprint li:not(:first-child) code[data-lang]::before{display:none}
+table.linenotable{border-collapse:separate;border:0;margin-bottom:0;background:none}
+table.linenotable td[class]{color:inherit;vertical-align:top;padding:0;line-height:inherit;white-space:normal}
+table.linenotable td.code{padding-left:.75em}
+table.linenotable td.linenos,pre.pygments .linenos{border-right:1px solid;opacity:.35;padding-right:.5em;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
+pre.pygments span.linenos{display:inline-block;margin-right:.75em}
 .quoteblock{margin:0 1em 1.25em 1.5em;display:table}
-.quoteblock>.title{margin-left:-1.5em;margin-bottom:.75em}
+.quoteblock:not(.excerpt)>.title{margin-left:-1.5em;margin-bottom:.75em}
 .quoteblock blockquote,.quoteblock p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}
 .quoteblock blockquote{margin:0;padding:0;border:0}
 .quoteblock blockquote::before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}
 .quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}
 .quoteblock .attribution{margin-top:.75em;margin-right:.5ex;text-align:right}
 .verseblock{margin:0 1em 1.25em}
-.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}
+.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans-serif;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}
 .verseblock pre strong{font-weight:400}
 .verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}
 .quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}
@@ -251,25 +256,25 @@ table.pyhltable .linenodiv{background:none!important;padding-right:0!important}
 .quoteblock.abstract blockquote,.quoteblock.abstract p,.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{line-height:1.6;word-spacing:0}
 .quoteblock.abstract{margin:0 1em 1.25em;display:block}
 .quoteblock.abstract>.title{margin:0 0 .375em;font-size:1.15em;text-align:center}
-.quoteblock.excerpt,.quoteblock .quoteblock{margin:0 0 1.25em;padding:0 0 .25em 1em;border-left:.25em solid #dddddf}
+.quoteblock.excerpt>blockquote,.quoteblock .quoteblock{padding:0 0 .25em 1em;border-left:.25em solid #dddddf}
+.quoteblock.excerpt,.quoteblock .quoteblock{margin-left:0}
 .quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{color:inherit;font-size:1.0625rem}
-.quoteblock.excerpt .attribution,.quoteblock .quoteblock .attribution{color:inherit;text-align:left;margin-right:0}
-table.tableblock{max-width:100%;border-collapse:separate}
+.quoteblock.excerpt .attribution,.quoteblock .quoteblock .attribution{color:inherit;font-size:.85rem;text-align:left;margin-right:0}
 p.tableblock:last-child{margin-bottom:0}
-td.tableblock>.content{margin-bottom:-1.25em}
+td.tableblock>.content{margin-bottom:1.25em;word-wrap:anywhere}
+td.tableblock>.content>:last-child{margin-bottom:-1.25em}
 table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}
-table.grid-all>thead>tr>.tableblock,table.grid-all>tbody>tr>.tableblock{border-width:0 1px 1px 0}
-table.grid-all>tfoot>tr>.tableblock{border-width:1px 1px 0 0}
-table.grid-cols>*>tr>.tableblock{border-width:0 1px 0 0}
-table.grid-rows>thead>tr>.tableblock,table.grid-rows>tbody>tr>.tableblock{border-width:0 0 1px}
-table.grid-rows>tfoot>tr>.tableblock{border-width:1px 0 0}
-table.grid-all>*>tr>.tableblock:last-child,table.grid-cols>*>tr>.tableblock:last-child{border-right-width:0}
-table.grid-all>tbody>tr:last-child>.tableblock,table.grid-all>thead:last-child>tr>.tableblock,table.grid-rows>tbody>tr:last-child>.tableblock,table.grid-rows>thead:last-child>tr>.tableblock{border-bottom-width:0}
+table.grid-all>*>tr>*{border-width:1px}
+table.grid-cols>*>tr>*{border-width:0 1px}
+table.grid-rows>*>tr>*{border-width:1px 0}
 table.frame-all{border-width:1px}
+table.frame-ends{border-width:1px 0}
 table.frame-sides{border-width:0 1px}
-table.frame-topbot,table.frame-ends{border-width:1px 0}
-table.stripes-all tr,table.stripes-odd tr:nth-of-type(odd){background:#f8f8f7}
-table.stripes-none tr,table.stripes-odd tr:nth-of-type(even){background:none}
+table.frame-none>colgroup+*>:first-child>*,table.frame-sides>colgroup+*>:first-child>*{border-top-width:0}
+table.frame-none>:last-child>:last-child>*,table.frame-sides>:last-child>:last-child>*{border-bottom-width:0}
+table.frame-none>*>tr>:first-child,table.frame-ends>*>tr>:first-child{border-left-width:0}
+table.frame-none>*>tr>:last-child,table.frame-ends>*>tr>:last-child{border-right-width:0}
+table.stripes-all>*>tr,table.stripes-odd>*>tr:nth-of-type(odd),table.stripes-even>*>tr:nth-of-type(even),table.stripes-hover>*>tr:hover{background:#f8f8f7}
 th.halign-left,td.halign-left{text-align:left}
 th.halign-right,td.halign-right{text-align:right}
 th.halign-center,td.halign-center{text-align:center}
@@ -277,23 +282,23 @@ th.valign-top,td.valign-top{vertical-align:top}
 th.valign-bottom,td.valign-bottom{vertical-align:bottom}
 th.valign-middle,td.valign-middle{vertical-align:middle}
 table thead th,table tfoot th{font-weight:bold}
-tbody tr th{display:table-cell;line-height:1.6;background:#f7f8f7}
+tbody tr th{background:#f7f8f7}
 tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}
 p.tableblock>code:only-child{background:none;padding:0}
 p.tableblock{font-size:1em}
-td>div.verse{white-space:pre}
 ol{margin-left:1.75em}
 ul li ol{margin-left:1.5em}
 dl dd{margin-left:1.125em}
 dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}
-ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}
+li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}
 ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none}
 ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em}
 ul.unstyled,ol.unstyled{margin-left:0}
-ul.checklist{margin-left:.625em}
-ul.checklist li>p:first-child>.fa-square-o:first-child,ul.checklist li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em}
-ul.checklist li>p:first-child>input[type="checkbox"]:first-child{margin-right:.25em}
-ul.inline{display:-ms-flexbox;display:-webkit-box;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap;list-style:none;margin:0 0 .625em -1.25em}
+li>p:empty:only-child::before{content:"";display:inline-block}
+ul.checklist>li>p:first-child{margin-left:-1em}
+ul.checklist>li>p:first-child>.fa-square-o:first-child,ul.checklist>li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em}
+ul.checklist>li>p:first-child>input[type=checkbox]:first-child{margin-right:.25em}
+ul.inline{display:flex;flex-flow:row wrap;list-style:none;margin:0 0 .625em -1.25em}
 ul.inline>li{margin-left:1.25em}
 .unstyled dl dt{font-weight:400;font-style:normal}
 ol.arabic{list-style-type:decimal}
@@ -307,11 +312,12 @@ ol.lowergreek{list-style-type:lower-greek}
 .hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none}
 td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}
 td.hdlist1{font-weight:bold;padding-bottom:1.25em}
+td.hdlist2{word-wrap:anywhere}
 .literalblock+.colist,.listingblock+.colist{margin-top:-.5em}
 .colist td:not([class]):first-child{padding:.4em .75em 0;line-height:1;vertical-align:top}
 .colist td:not([class]):first-child img{max-width:none}
 .colist td:not([class]):last-child{padding:.25em 0}
-.thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd}
+.thumb,.th{line-height:0;display:inline-block;border:4px solid #fff;box-shadow:0 0 0 1px #ddd}
 .imageblock.left{margin:.25em .625em 1.25em 0}
 .imageblock.right{margin:.25em 0 1.25em .625em}
 .imageblock>.title{margin-bottom:0}
@@ -331,8 +337,6 @@ sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}
 #footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none;margin-left:-1.05em}
 #footnotes .footnote:last-of-type{margin-bottom:0}
 #content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}
-.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0}
-.gist .file-data>table td.line-data{width:99%}
 div.unbreakable{page-break-inside:avoid}
 .big{font-size:larger}
 .small{font-size:smaller}
@@ -340,37 +344,37 @@ div.unbreakable{page-break-inside:avoid}
 .overline{text-decoration:overline}
 .line-through{text-decoration:line-through}
 .aqua{color:#00bfbf}
-.aqua-background{background-color:#00fafa}
+.aqua-background{background:#00fafa}
 .black{color:#000}
-.black-background{background-color:#000}
+.black-background{background:#000}
 .blue{color:#0000bf}
-.blue-background{background-color:#0000fa}
+.blue-background{background:#0000fa}
 .fuchsia{color:#bf00bf}
-.fuchsia-background{background-color:#fa00fa}
+.fuchsia-background{background:#fa00fa}
 .gray{color:#606060}
-.gray-background{background-color:#7d7d7d}
+.gray-background{background:#7d7d7d}
 .green{color:#006000}
-.green-background{background-color:#007d00}
+.green-background{background:#007d00}
 .lime{color:#00bf00}
-.lime-background{background-color:#00fa00}
+.lime-background{background:#00fa00}
 .maroon{color:#600000}
-.maroon-background{background-color:#7d0000}
+.maroon-background{background:#7d0000}
 .navy{color:#000060}
-.navy-background{background-color:#00007d}
+.navy-background{background:#00007d}
 .olive{color:#606000}
-.olive-background{background-color:#7d7d00}
+.olive-background{background:#7d7d00}
 .purple{color:#600060}
-.purple-background{background-color:#7d007d}
+.purple-background{background:#7d007d}
 .red{color:#bf0000}
-.red-background{background-color:#fa0000}
+.red-background{background:#fa0000}
 .silver{color:#909090}
-.silver-background{background-color:#bcbcbc}
+.silver-background{background:#bcbcbc}
 .teal{color:#006060}
-.teal-background{background-color:#007d7d}
+.teal-background{background:#007d7d}
 .white{color:#bfbfbf}
-.white-background{background-color:#fafafa}
+.white-background{background:#fafafa}
 .yellow{color:#bfbf00}
-.yellow-background{background-color:#fafa00}
+.yellow-background{background:#fafa00}
 span.icon>.fa{cursor:default}
 a span.icon>.fa{cursor:inherit}
 .admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}
@@ -379,7 +383,7 @@ a span.icon>.fa{cursor:inherit}
 .admonitionblock td.icon .icon-warning::before{content:"\f071";color:#bf6900}
 .admonitionblock td.icon .icon-caution::before{content:"\f06d";color:#bf3400}
 .admonitionblock td.icon .icon-important::before{content:"\f06a";color:#bf0000}
-.conum[data-value]{display:inline-block;color:#fff!important;background-color:rgba(0,0,0,.8);-webkit-border-radius:100px;border-radius:100px;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}
+.conum[data-value]{display:inline-block;color:#fff!important;background:rgba(0,0,0,.8);border-radius:50%;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}
 .conum[data-value] *{color:#fff!important}
 .conum[data-value]+b{display:none}
 .conum[data-value]::after{content:attr(data-value)}
@@ -387,25 +391,27 @@ pre .conum[data-value]{position:relative;top:-.125em}
 b.conum *{color:inherit!important}
 .conum:not([data-value]):empty{display:none}
 dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}
-h1,h2,p,td.content,span.alt{letter-spacing:-.01em}
+h1,h2,p,td.content,span.alt,summary{letter-spacing:-.01em}
 p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}
-p,blockquote,dt,td.content,span.alt{font-size:1.0625rem}
+p,blockquote,dt,td.content,span.alt,summary{font-size:1.0625rem}
 p{margin-bottom:1.25rem}
 .sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}
-.exampleblock>.content{background-color:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc}
+.exampleblock>.content{background:#fffef7;border-color:#e0e0dc;box-shadow:0 1px 4px #e0e0dc}
 .print-only{display:none!important}
 @page{margin:1.25cm .75cm}
-@media print{*{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important}
+@media print{*{box-shadow:none!important;text-shadow:none!important}
 html{font-size:80%}
 a{color:inherit!important;text-decoration:underline!important}
 a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}
 a[href^="http:"]:not(.bare)::after,a[href^="https:"]:not(.bare)::after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}
+abbr[title]{border-bottom:1px dotted}
 abbr[title]::after{content:" (" attr(title) ")"}
 pre,blockquote,tr,img,object,svg{page-break-inside:avoid}
 thead{display:table-header-group}
 svg{max-width:100%}
 p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}
 h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}
+#header,#content,#footnotes,#footer{max-width:none}
 #toc,.sidebarblock,.exampleblock>.content{background:none!important}
 #toc{border-bottom:1px solid #dddddf!important;padding-bottom:0!important}
 body.book #header{text-align:center}
@@ -422,7 +428,7 @@ body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-b
 .print-only{display:block!important}
 .hide-for-print{display:none!important}
 .show-for-print{display:inherit!important}}
-@media print,amzn-kf8{#header>h1:first-child{margin-top:1.25rem}
+@media amzn-kf8,print{#header>h1:first-child{margin-top:1.25rem}
 .sect1{padding:0!important}
 .sect1+.sect1{border:0}
 #footer{background:none}
@@ -449,8 +455,7 @@ body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-b
 <li><a href="#_x_server_info">6.4. X server info</a></li>
 <li><a href="#_display_info">6.5. Display info</a></li>
 <li><a href="#_ctwm_compile_time_options">6.6. ctwm compile-time options</a></li>
-<li><a href="#_ctwm_run_time_options">6.7. ctwm run-time options</a></li>
-<li><a href="#_obsolete_options">6.8. Obsolete options</a></li>
+<li><a href="#_obsolete_options">6.7. Obsolete options</a></li>
 </ul>
 </li>
 <li><a href="#_variables">7. VARIABLES</a></li>
@@ -508,7 +513,6 @@ body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-b
      [--nom4 | -n]  [(--keep-defs | -k)]  [(--keep | -K) m4file]
      [--verbose | -v]  [--quiet | -q]  [--mono]  [--xrm resource]
      [--version]  [--info]  [--nowelcome | -W]
-     [(--window | -w) [win-id]]  [--name name]
      [--clientId clid]  [--restore resfname]
      [--help | -h]</pre>
 </div>
@@ -538,7 +542,8 @@ use any combination of the above icon/pixmap formats.</p>
 startup script.  When used from <code>xdm(1)</code> or <code>xinit(1)</code> without
 a session manager, ctwm is frequently executed in the foreground
 as the last client.  When run this way, exiting ctwm causes the
-session to be terminated (i.e. logged out).</p>
+X session to be terminated, shutting down the X server and killing off
+any other running clients.</p>
 </div>
 <div class="paragraph">
 <p>By default, application windows are surrounded by a &#8220;frame&#8221; with a
@@ -673,23 +678,6 @@ Available only if ctwm is built with the <code>USE_M4</code> flag.</p>
 <dd>
 <p>ctwm prints its detailed version and compile time options.</p>
 </dd>
-<dt class="hdlist1">--window[=<code>win-id</code>], -w[<code>win-id</code>]</dt>
-<dd>
-<p>If <code>-w</code> is specified without a win-id value, ctwm does not take over
-the whole screen(s); instead it creates a new window that becomes its
-root window.  If the win-id value is given, it is considered to be the
-id of an existing window, in which case, ctwm will try to use this
-window as root window.  You can run any number of instantiations of
-ctwm at the same time.  You can even have embedded ctwm instantiations.
-This is totally useless, but I like it.  The f.adoptwindow function can
-be used to capture an existing window belonging to another ctwm.  A
-possible use of such mode can be to test new configuration file without
-restarting ctwm.</p>
-</dd>
-<dt class="hdlist1">--name=<code>name</code>, -n <code>name</code></dt>
-<dd>
-<p>Set the captivename when using <code>--window</code>.</p>
-</dd>
 <dt class="hdlist1">--nowelcome, -W</dt>
 <dd>
 <p>This option tells ctwm not to display any welcome when starting.</p>
@@ -711,17 +699,6 @@ means that args can be passed via <code>--long=arg</code> and <code>--long arg</
 as <code>-l arg</code> and <code>-larg</code>, and short args can be bundled like <code>-vnk</code> as
 well as <code>-v -n -k</code>.</p>
 </div>
-<div class="exampleblock">
-<div class="content">
-<div class="paragraph">
-<p>Note, however, that the handling of optional args is slightly different;
-they <strong>must</strong> be specified with = or no space.  e.g., <code>--window=123</code> will
-set the win-id to 123, but <code>--window 123</code> will not; the 123 will be
-treated as a separate argument.  Similarly, it must be set as <code>-w123</code>,
-not <code>-w 123</code>.</p>
-</div>
-</div>
-</div>
 </div>
 </div>
 <div class="sect1">
@@ -1008,30 +985,23 @@ removed in a future version.</p>
 </div>
 </div>
 <div class="sect2">
-<h3 id="_ctwm_run_time_options">6.7. ctwm run-time options</h3>
+<h3 id="_obsolete_options">6.7. Obsolete options</h3>
 <div class="dlist">
 <dl>
+<dt class="hdlist1">GNOME</dt>
+<dd>
+<p>Defined when ctwm was compiled with GNOME1 support.  Removed after
+3.8.2.</p>
+</dd>
 <dt class="hdlist1">TWM_CAPTIVE</dt>
 <dd>
-<p>This will be either &#8220;Yes&#8221; or &#8220;No&#8221;. &#8220;Yes&#8221; if the current ctwm is captive
-(flag -w), &#8220;No&#8221; in the other case.</p>
+<p>This was either &#8220;Yes&#8221; or &#8220;No&#8221;. &#8220;Yes&#8221; if the current ctwm is captive
+(flag -w), &#8220;No&#8221; in the other case.  Removed in 4.1.0.</p>
 </dd>
 <dt class="hdlist1">TWM_CAPTIVE_NAME</dt>
 <dd>
 <p>Defined only if TWM_CAPTIVE is also defined. Contains the name of the captive
-ctwm (flag --name).</p>
-</dd>
-</dl>
-</div>
-</div>
-<div class="sect2">
-<h3 id="_obsolete_options">6.8. Obsolete options</h3>
-<div class="dlist">
-<dl>
-<dt class="hdlist1">GNOME</dt>
-<dd>
-<p>Defined when ctwm was compiled with GNOME1 support.  Removed after
-3.8.2.</p>
+ctwm (flag --name).  Removed in 4.1.0.</p>
 </dd>
 </dl>
 </div>
@@ -1688,7 +1658,10 @@ The default is &#8220;black&#8221;.</p>
 the initial full size of the icon manager.  The icon manager window is
 then broken into <code>columns</code> pieces and scaled according to the number
 of entries in the icon manager.  Extra entries are wrapped to form
-additional rows.  The default number of columns is 1.</p>
+additional rows.  The default number of columns is 1. When XrandR is
+compiled, the geometry can be relative to a monitor, by prefixing
+its name (visible with <code>xrandr(1)</code> command line) followed by <code>:</code>. This name
+is ignored when XrandR is not available.</p>
 </dd>
 <dt class="hdlist1">IconManagerHighlight <code>string</code> [{ <code>win-list</code> }]</dt>
 <dd>
@@ -1711,18 +1684,21 @@ The default is &#8220;black&#8221;.</p>
 </div>
 </div>
 <div class="paragraph">
-<p>where <code>winname</code> is the name of the windows that should be put into this
-icon manager, <code>iconname</code> is the name of that icon manager window&#8217;s icon,
-<code>geometry</code> is a standard geometry specification, and <code>columns</code> is
-the number of columns in this icon manager as described in
-<code>IconManagerGeometry</code>.  For example:</p>
+<p>where <code>winname</code> is the name of the windows that should be put into
+this icon manager, <code>iconname</code> is the name of that icon manager
+window&#8217;s icon, <code>geometry</code> is a standard geometry specification, and
+<code>columns</code> is the number of columns in this icon manager as described
+in <code>IconManagerGeometry</code>. When XrandR is compiled, the geometry can
+be relative to a monitor, by prefixing its name (visible with
+<code>xrandr(1)</code> command line) followed by <code>:</code>. This name is ignored when
+XrandR is not available.  For example:</p>
 </div>
 <div class="listingblock">
 <div class="content">
 <pre>IconManagers
 {
-    "XTerm"  "=300x5+800+5"  5
-    "myhost" "=400x5+100+5"  2
+    "XTerm"  "=300x5+800+5"       5
+    "myhost" "HDMI2:400x5+100+5"  2
 }</pre>
 </div>
 </div>
@@ -1751,7 +1727,10 @@ variable, only the windows in this list will be shown in the icon manager.</p>
 <dd>
 <p>This variable specifies an area on the root window in which icons are placed
 if no specific icon location is provided by the client.  The <code>geomstring</code>
-is a quoted string containing a standard geometry specification. If more than
+is a quoted string containing a standard geometry specification. When XrandR
+is compiled, the geometry can be relative to a monitor, by prefixing
+its name (visible with <code>xrandr(1)</code> command line) followed by <code>:</code>.
+This name is ignored when XrandR is not available. If more than
 one <code>IconRegion</code> lines are given, icons will be put into the succeeding
 icon regions when the first is full. The <code>vgrav</code> argument should be either
 <code>North</code> or <code>South</code>  and is used to control whether icons are
@@ -1957,6 +1936,48 @@ menus and
 can only be specified inside of a
 <code>Color</code> or <code>Monochrome</code> list.  The default is &#8220;black&#8221;.</p>
 </dd>
+<dt class="hdlist1">MonitorLayout { <code>monitor-list</code> }</dt>
+<dd>
+<p>This allows manually configuring what ctwm will consider as the list of
+monitors.  If <code>XRANDR</code> support is compiled in (the default as of
+4.1.0), and the <code>RANDR</code> extension is available on your server, then
+<code>ctwm</code> will use that to determine the size and layout of your monitors.</p>
+<div class="paragraph">
+<p>However, if either is not the case, or you want to override the results
+it returns, you can specify the names and layouts of your desired
+&#8220;monitors&#8221; with this.  For instance, if you have a very wide monitor,
+and would prefer to treat it as several narrower side-by-side monitors,
+you could use this to tell ctwm to treat it that way.</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre>MonitorLayout
+{
+    # Imagine a 5000x1000 monitor, that we want to split into two 2k wide
+    # sections at the far left and right, with a 1k wide section in the
+    # middle.
+    "Left:2000x1000+0+0"
+    "1000x1000+2000+0"       # Middle section unnamed
+    "Right:2000x1000+3000+0"
+}</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>With <code>m4</code> support, you could even make it automatically split your full
+display into 2 &#8220;monitors&#8221;:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre>define(LeftWidth, eval(WIDTH / 2))dnl
+define(RightWidth, eval(WIDTH - LeftWidth))dnl
+MonitorLayout
+{
+    "`AutoL:'LeftWidth`x'HEIGHT`+0+0'"
+    "`AutoR:'RightWidth`x'HEIGHT`+'LeftWidth`+0'"
+}</pre>
+</div>
+</div>
+</dd>
 <dt class="hdlist1">Monochrome { <code>colors</code> }</dt>
 <dd>
 <p>This variable specifies a list of color assignments that should be made if
@@ -2786,30 +2807,6 @@ Try them to see what they look like.</p>
 <dd>
 <p>Tells ctwm to use 3D for the small windows in the workspace map.</p>
 </dd>
-<dt class="hdlist1">VirtualScreens { <code>geometries-list</code> }</dt>
-<dd>
-<div class="paragraph">
-<p>This variable specifies a list of geometries for virtual screens. Virtual screens
-are designed to be used when you have several physical screens bound together
-with the Xinerama X extension.</p>
-</div>
-<div class="paragraph">
-<p><code>geometries-list</code> is a list of valid geometry strings, that correspond to
-your actual physical screens.</p>
-</div>
-<div class="paragraph">
-<p>Example :</p>
-</div>
-<div class="listingblock">
-<div class="content">
-<pre>VirtualScreens
-{
-    "1280x1024+0+0"
-    "1600x1200+1280+0"
-}</pre>
-</div>
-</div>
-</dd>
 <dt class="hdlist1">WarpCursor [{ <code>win-list</code> }]</dt>
 <dd>
 <p>This variable indicates that the pointer should be warped into windows when
@@ -2852,28 +2849,17 @@ any iconified windows it encounters.  This is typically used to make a key
 binding that will pop a particular window (such as <code>xmh</code>), no matter
 where it is.  The default is for <code>f.warpto</code> to ignore iconified windows.</p>
 </dd>
-<dt class="hdlist1">WindowBox [{ <code>win-list</code> }]</dt>
-<dd>
-<p>creates a new window called a box, where
-all the client windows that match the windows list are opened in,
-instead of the root window. This is useful to group small windows
-in the same box (xload for instance)</p>
-<div class="listingblock">
-<div class="content">
-<pre>WindowBox "xloadbox" "320x100+0-0" {
-    "xload"
-}</pre>
-</div>
-</div>
-</dd>
 <dt class="hdlist1">WindowGeometries { <code>win-list</code> }</dt>
 <dd>
-<p>Used to give a default geometry to some clients :</p>
+<p>Used to give a default geometry to some clients. When XrandR is
+compiled, the geometry can be relative to a monitor, by prefixing
+its name (visible with <code>xrandr(1)</code> command line) followed by <code>:</code>. This name
+is ignored when XrandR is not available:</p>
 <div class="listingblock">
 <div class="content">
 <pre>WindowGeometries {
     "Mozilla*"       "1000x800+10+10"
-    "jpilot*"        "800x600-0-0"
+    "jpilot*"        "HDMI1:800x600-0-0"
 }</pre>
 </div>
 </div>
@@ -2922,11 +2908,15 @@ workspace manager map. (Try <code>-adobe-times-*-r-*--10-*-*-*-*-*-*-*</code>).<
 <dd>
 <p>This variable specifies the geometry of the workspace manager window. The
 <code>string</code> argument is standard geometry specification that indicates
-the initial full size of the workspace manager. The <code>columns</code> argument
+the initial full size of the workspace manager. When XrandR is
+compiled, the geometry can be relative to a monitor, by prefixing
+its name (visible with <code>xrandr(1)</code> command line) followed by <code>:</code>. This name
+is ignored when XrandR is not available. The <code>columns</code> argument
 indicates the number of columns to use for the workspace manager window.</p>
 <div class="listingblock">
 <div class="content">
-<pre>WorkSpaceManagerGeometry        "360x60+60-0" 8</pre>
+<pre>WorkSpaceManagerGeometry        "360x60+60-0" 8
+WorkSpaceManagerGeometry        "HDMI1:600x30+1235+0" 12</pre>
 </div>
 </div>
 </dd>
@@ -3360,12 +3350,6 @@ be chosen:</p>
 <p>This function adds the selected window to the workspace whose name is
 <code>string</code>.</p>
 </dd>
-<dt class="hdlist1">f.adoptwindow</dt>
-<dd>
-<p>This function asks for the user to select a window with the mouse, and then
-adopt this window if it doesn&#8217;t belong to the current ctwm. Useful only
-with the -w flag.</p>
-</dd>
 <dt class="hdlist1">f.altcontext</dt>
 <dd>
 <p>Set the alternate context. The next key or button event ctwm receives will
@@ -3430,8 +3414,8 @@ but only stops at windows that are mapped.</p>
 <dt class="hdlist1">f.bottomzoom</dt>
 <dd>
 <p>This function stretches the bottom side of the window out to the bottom
-edge of the screen, or restores the original size if the window was
-already bottomzoom&#8217;d.</p>
+edge of the current monitor, or restores the original size if the
+window was already bottomzoom&#8217;d.</p>
 </dd>
 <dt class="hdlist1">f.changepriority <code>rel-value</code></dt>
 <dd>
@@ -3545,11 +3529,6 @@ reaches an obstacle (either another window, or the screen border).
 f.fill &#8220;vertical&#8221; sets the window status to &#8220;zoomed&#8221; and toggles, ie
 calling it again will reset the previous window size.</p>
 </dd>
-<dt class="hdlist1">f.fittocontent</dt>
-<dd>
-<p>Can be used only with window boxes. The result is to have the box have the
-minimal size that contains all its children windows.</p>
-</dd>
 <dt class="hdlist1">f.focus</dt>
 <dd>
 <p>This function toggles the keyboard focus of the server to the
@@ -3576,16 +3555,16 @@ but only stops at windows that are mapped.</p>
 <dd>
 <p>This function is similar to the <code>f.fullzoom</code> function, except that it
 makes the client window (the part inside the frame) the size of the
-screen, so the window decorations are off-screen.  This gives the same
-visual effect as the window covering the whole screen with no
+current monitor, so the window decorations are off-screen.  This gives
+the same visual effect as the window covering the whole screen with no
 decorations.  If the window is already fullscreenzoom&#8217;d, it restores
 the original size.</p>
 </dd>
 <dt class="hdlist1">f.fullzoom</dt>
 <dd>
 <p>This function resizes the selected window to the full size of the
-screen, or restores the original size if the window was already
-fullzoom&#8217;d.</p>
+current monitor, or restores the original size if the window was
+already fullzoom&#8217;d.</p>
 </dd>
 <dt class="hdlist1">f.function <code>string</code></dt>
 <dd>
@@ -3611,19 +3590,13 @@ by the argument <code>string</code>.</p>
 <dt class="hdlist1">f.horizoom</dt>
 <dd>
 <p>This function stretches the window so that it covers the whole width of
-the screen, or restores the original size if the window was already
-horizoom&#8217;d.</p>
+the current monitor, or restores the original size if the window was
+already horizoom&#8217;d.</p>
 </dd>
 <dt class="hdlist1">f.htzoom</dt>
 <dd>
 <p>This function is a synonym for <code>f.topzoom</code>.</p>
 </dd>
-<dt class="hdlist1">f.hypermove</dt>
-<dd>
-<p>Use this function to &#8220;move&#8221; a window between 2 captives ctwm (or between a
-captive and the root ctwm). Of course 2 ctwms are completely different
-universes. You have to go in hyperspace to achieve this, hence the name.</p>
-</dd>
 <dt class="hdlist1">f.hzoom</dt>
 <dd>
 <p>This function is a synonym for <code>f.horizoom</code>.</p>
@@ -3677,8 +3650,8 @@ manager.</p>
 <dt class="hdlist1">f.leftzoom</dt>
 <dd>
 <p>This function stretches the left side of the window out to the left
-edge of the screen, or restores the original size if the window was
-already leftzoom&#8217;d.</p>
+edge of the current monitor, or restores the original size if the
+window was already leftzoom&#8217;d.</p>
 </dd>
 <dt class="hdlist1">f.lower</dt>
 <dd>
@@ -3730,7 +3703,11 @@ active.</p>
 <p>Takes one string argument which is a geometry with the
 standard X geometry syntax (e.g. 200x300+150-0). Sets the current window
 to the specified geometry. The width and height are to be given in pixel,
-no base size or resize increment are used.</p>
+no base size or resize increment are used.
+When XrandR is compiled, the geometry can be relative to a monitor,
+by prefixing its name (visible with <code>xrandr(1)</code> command line) followed by
+<code>:</code> (e.g. HDMI1:200x300+150-0). This name is ignored when XrandR is not
+available.</p>
 </dd>
 <dt class="hdlist1">f.movetitlebar</dt>
 <dd>
@@ -3901,8 +3878,8 @@ manager.</p>
 <dt class="hdlist1">f.rightzoom</dt>
 <dd>
 <p>This function stretches the right side of the window out to the right
-edge of the screen, or restores the original size if the window was
-already rightzoom&#8217;d.</p>
+edge of the current monitor, or restores the original size if the
+window was already rightzoom&#8217;d.</p>
 </dd>
 <dt class="hdlist1">f.ring</dt>
 <dd>
@@ -4035,8 +4012,8 @@ it is mapped, it will be unmapped and vice versa.</p>
 </dd>
 <dt class="hdlist1">f.topzoom</dt>
 <dd>
-<p>This function stretches the top side of the window out to the top
-edge of the screen, or restores the original size if the window was
+<p>This function stretches the top side of the window out to the top edge
+of the current monitor, or restores the original size if the window was
 already topzoom&#8217;d.</p>
 </dd>
 <dt class="hdlist1">f.trace <code>string</code></dt>
@@ -4135,11 +4112,51 @@ any unmanaged screens), or the word
 <p>This function is similar to the <code>f.refresh</code> function except that only the
 selected window is refreshed.</p>
 </dd>
+<dt class="hdlist1">f.xbottomzoom</dt>
+<dd>
+<p>This function is similar to the <code>f.bottomzoom</code> function, but will cross
+monitors.</p>
+</dd>
+<dt class="hdlist1">f.xfullscreenzoom</dt>
+<dd>
+<p>This function is similar to the <code>f.fullscreenzoom</code> function, but will
+cross monitors.</p>
+</dd>
+<dt class="hdlist1">f.xfullzoom</dt>
+<dd>
+<p>This function is similar to the <code>f.fullzoom</code> function, but will cross
+monitors.</p>
+</dd>
+<dt class="hdlist1">f.xhorizoom</dt>
+<dd>
+<p>This function is similar to the <code>f.horizoom</code> function, but will cross
+monitors.</p>
+</dd>
+<dt class="hdlist1">f.xleftzoom</dt>
+<dd>
+<p>This function is similar to the <code>f.leftzoom</code> function, but will cross
+monitors.</p>
+</dd>
+<dt class="hdlist1">f.xrightzoom</dt>
+<dd>
+<p>This function is similar to the <code>f.rightzoom</code> function, but will cross
+monitors.</p>
+</dd>
+<dt class="hdlist1">f.xtopzoom</dt>
+<dd>
+<p>This function is similar to the <code>f.topzoom</code> function, but will cross
+monitors.</p>
+</dd>
+<dt class="hdlist1">f.xzoom</dt>
+<dd>
+<p>This function is similar to the <code>f.zoom</code> function, but will cross
+monitors.</p>
+</dd>
 <dt class="hdlist1">f.zoom</dt>
 <dd>
-<p>This function stretches the window so that it covers the whole height of
-the screen, or restores the original size if the window was already
-zoom&#8217;d.  It&#8217;s the vertical counterpart fo <code>f.horizoom</code>; perhaps
+<p>This function stretches the window so that it covers the whole height
+of the current monitor, or restores the original size if the window was
+already zoom&#8217;d.  It&#8217;s the vertical counterpart fo <code>f.horizoom</code>; perhaps
 <code>f.vertzoom</code> would be a better name&#8230;&#8203;</p>
 </dd>
 </dl>
@@ -4501,7 +4518,7 @@ and many other contributors.</p>
 </div>
 <div id="footer">
 <div id="footer-text">
-Last updated 2019-07-21 16:29:34 -0500
+Last updated 2023-03-26 17:37:34 -0500
 </div>
 </div>
 </body>
diff --git a/gen/gram.tab.c b/gen/gram.tab.c
index 58bed0f..0d454e2 100644
--- a/gen/gram.tab.c
+++ b/gen/gram.tab.c
@@ -1,8 +1,8 @@
-/* A Bison parser, made by GNU Bison 3.4.1.  */
+/* A Bison parser, made by GNU Bison 3.8.2.  */
 
 /* Bison implementation for Yacc-like parsers in C
 
-   Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2019 Free Software Foundation,
+   Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation,
    Inc.
 
    This program is free software: you can redistribute it and/or modify
@@ -16,7 +16,7 @@
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
 
 /* As a special exception, you may create a larger work that contains
    part or all of the Bison parser skeleton and distribute that work
@@ -34,6 +34,10 @@
 /* C LALR(1) parser skeleton written by Richard Stallman, by
    simplifying the original so-called "semantic" parser.  */
 
+/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual,
+   especially those whose name start with YY_ or yy_.  They are
+   private implementation details that can be changed or removed.  */
+
 /* All symbols defined below should begin with yy or YY, to avoid
    infringing on user name space.  This should be done even for local
    variables, as they might otherwise be expanded by user macros.
@@ -41,14 +45,11 @@
    define necessary library symbols; they are noted "INFRINGES ON
    USER NAME SPACE" below.  */
 
-/* Undocumented macros, especially those whose name start with YY_,
-   are private implementation details.  Do not rely on them.  */
-
-/* Identify Bison output.  */
-#define YYBISON 1
+/* Identify Bison output, and Bison version.  */
+#define YYBISON 30802
 
-/* Bison version.  */
-#define YYBISON_VERSION "3.4.1"
+/* Bison version string.  */
+#define YYBISON_VERSION "3.8.2"
 
 /* Skeleton name.  */
 #define YYSKELETON_NAME "yacc.c"
@@ -77,7 +78,9 @@
 #include "otp.h"
 #include "iconmgr.h"
 #include "icons.h"
+#ifdef WINBOX
 #include "windowbox.h"
+#endif
 #include "functions_defs.h"
 #include "list.h"
 #include "util.h"
@@ -104,8 +107,17 @@ extern char *yytext; // Have to manually pull this in
 
 int yylex(void);
 
-#line 108 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 111 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
 
+# ifndef YY_CAST
+#  ifdef __cplusplus
+#   define YY_CAST(Type, Val) static_cast<Type> (Val)
+#   define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast<Type> (Val)
+#  else
+#   define YY_CAST(Type, Val) ((Type) (Val))
+#   define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val))
+#  endif
+# endif
 # ifndef YY_NULLPTR
 #  if defined __cplusplus
 #   if 201103L <= __cplusplus
@@ -118,172 +130,302 @@ int yylex(void);
 #  endif
 # endif
 
-/* Enabling verbose error messages.  */
-#ifdef YYERROR_VERBOSE
-# undef YYERROR_VERBOSE
-# define YYERROR_VERBOSE 1
-#else
-# define YYERROR_VERBOSE 0
-#endif
-
-/* Use api.header.include to #include this header
-   instead of duplicating it here.  */
-#ifndef YY_YY_HOME_FULLERMD_WORK_CTWM_BZR_4_0_X_CTWM_MKTAR_ASYJVB_CTWM_4_0_3_BUILD_GRAM_TAB_H_INCLUDED
-# define YY_YY_HOME_FULLERMD_WORK_CTWM_BZR_4_0_X_CTWM_MKTAR_ASYJVB_CTWM_4_0_3_BUILD_GRAM_TAB_H_INCLUDED
-/* Debug traces.  */
-#ifndef YYDEBUG
-# define YYDEBUG 0
-#endif
-#if YYDEBUG
-extern int yydebug;
-#endif
-
-/* Token type.  */
-#ifndef YYTOKENTYPE
-# define YYTOKENTYPE
-  enum yytokentype
-  {
-    LB = 258,
-    RB = 259,
-    LP = 260,
-    RP = 261,
-    MENUS = 262,
-    MENU = 263,
-    BUTTON = 264,
-    DEFAULT_FUNCTION = 265,
-    PLUS = 266,
-    MINUS = 267,
-    ALL = 268,
-    OR = 269,
-    CURSORS = 270,
-    PIXMAPS = 271,
-    ICONS = 272,
-    COLOR = 273,
-    SAVECOLOR = 274,
-    MONOCHROME = 275,
-    FUNCTION = 276,
-    ICONMGR_SHOW = 277,
-    ICONMGR = 278,
-    ALTER = 279,
-    WINDOW_FUNCTION = 280,
-    ZOOM = 281,
-    ICONMGRS = 282,
-    ICONMGR_GEOMETRY = 283,
-    ICONMGR_NOSHOW = 284,
-    MAKE_TITLE = 285,
-    ICONIFY_BY_UNMAPPING = 286,
-    DONT_ICONIFY_BY_UNMAPPING = 287,
-    AUTO_POPUP = 288,
-    NO_BORDER = 289,
-    NO_ICON_TITLE = 290,
-    NO_TITLE = 291,
-    AUTO_RAISE = 292,
-    NO_HILITE = 293,
-    ICON_REGION = 294,
-    WINDOW_REGION = 295,
-    META = 296,
-    SHIFT = 297,
-    LOCK = 298,
-    CONTROL = 299,
-    WINDOW = 300,
-    TITLE = 301,
-    ICON = 302,
-    ROOT = 303,
-    FRAME = 304,
-    COLON = 305,
-    EQUALS = 306,
-    SQUEEZE_TITLE = 307,
-    DONT_SQUEEZE_TITLE = 308,
-    WARP_ON_DEICONIFY = 309,
-    START_ICONIFIED = 310,
-    NO_TITLE_HILITE = 311,
-    TITLE_HILITE = 312,
-    MOVE = 313,
-    RESIZE = 314,
-    WAITC = 315,
-    SELECT = 316,
-    KILL = 317,
-    LEFT_TITLEBUTTON = 318,
-    RIGHT_TITLEBUTTON = 319,
-    NUMBER = 320,
-    KEYWORD = 321,
-    NKEYWORD = 322,
-    CKEYWORD = 323,
-    CLKEYWORD = 324,
-    FKEYWORD = 325,
-    FSKEYWORD = 326,
-    FNKEYWORD = 327,
-    PRIORITY_SWITCHING = 328,
-    PRIORITY_NOT_SWITCHING = 329,
-    SKEYWORD = 330,
-    SSKEYWORD = 331,
-    WINDOW_RING = 332,
-    WINDOW_RING_EXCLUDE = 333,
-    WARP_CURSOR = 334,
-    ERRORTOKEN = 335,
-    GRAVITY = 336,
-    SIJENUM = 337,
-    NO_STACKMODE = 338,
-    ALWAYS_ON_TOP = 339,
-    WORKSPACE = 340,
-    WORKSPACES = 341,
-    WORKSPCMGR_GEOMETRY = 342,
-    OCCUPYALL = 343,
-    OCCUPYLIST = 344,
-    MAPWINDOWCURRENTWORKSPACE = 345,
-    MAPWINDOWDEFAULTWORKSPACE = 346,
-    ON_TOP_PRIORITY = 347,
-    UNMAPBYMOVINGFARAWAY = 348,
-    OPAQUEMOVE = 349,
-    NOOPAQUEMOVE = 350,
-    OPAQUERESIZE = 351,
-    NOOPAQUERESIZE = 352,
-    DONTSETINACTIVE = 353,
-    CHANGE_WORKSPACE_FUNCTION = 354,
-    DEICONIFY_FUNCTION = 355,
-    ICONIFY_FUNCTION = 356,
-    AUTOSQUEEZE = 357,
-    STARTSQUEEZED = 358,
-    DONT_SAVE = 359,
-    AUTO_LOWER = 360,
-    ICONMENU_DONTSHOW = 361,
-    WINDOW_BOX = 362,
-    IGNOREMODIFIER = 363,
-    WINDOW_GEOMETRIES = 364,
-    ALWAYSSQUEEZETOGRAVITY = 365,
-    VIRTUAL_SCREENS = 366,
-    IGNORE_TRANSIENT = 367,
-    EWMH_IGNORE = 368,
-    MWM_IGNORE = 369,
-    RPLAY_SOUNDS = 370,
-    FORCE_FOCUS = 371,
-    STRING = 372
-  };
-#endif
-
-/* Value type.  */
-#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
-union YYSTYPE
+#include "gram.tab.h"
+/* Symbol kind.  */
+enum yysymbol_kind_t
 {
-#line 62 "gram.y"
-
-    int num;
-    char *ptr;
-
-#line 274 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
-
+  YYSYMBOL_YYEMPTY = -2,
+  YYSYMBOL_YYEOF = 0,                      /* "end of file"  */
+  YYSYMBOL_YYerror = 1,                    /* error  */
+  YYSYMBOL_YYUNDEF = 2,                    /* "invalid token"  */
+  YYSYMBOL_LB = 3,                         /* LB  */
+  YYSYMBOL_RB = 4,                         /* RB  */
+  YYSYMBOL_LP = 5,                         /* LP  */
+  YYSYMBOL_RP = 6,                         /* RP  */
+  YYSYMBOL_MENUS = 7,                      /* MENUS  */
+  YYSYMBOL_MENU = 8,                       /* MENU  */
+  YYSYMBOL_BUTTON = 9,                     /* BUTTON  */
+  YYSYMBOL_DEFAULT_FUNCTION = 10,          /* DEFAULT_FUNCTION  */
+  YYSYMBOL_PLUS = 11,                      /* PLUS  */
+  YYSYMBOL_MINUS = 12,                     /* MINUS  */
+  YYSYMBOL_ALL = 13,                       /* ALL  */
+  YYSYMBOL_OR = 14,                        /* OR  */
+  YYSYMBOL_CURSORS = 15,                   /* CURSORS  */
+  YYSYMBOL_PIXMAPS = 16,                   /* PIXMAPS  */
+  YYSYMBOL_ICONS = 17,                     /* ICONS  */
+  YYSYMBOL_COLOR = 18,                     /* COLOR  */
+  YYSYMBOL_SAVECOLOR = 19,                 /* SAVECOLOR  */
+  YYSYMBOL_MONOCHROME = 20,                /* MONOCHROME  */
+  YYSYMBOL_FUNCTION = 21,                  /* FUNCTION  */
+  YYSYMBOL_ICONMGR_SHOW = 22,              /* ICONMGR_SHOW  */
+  YYSYMBOL_ICONMGR = 23,                   /* ICONMGR  */
+  YYSYMBOL_ALTER = 24,                     /* ALTER  */
+  YYSYMBOL_WINDOW_FUNCTION = 25,           /* WINDOW_FUNCTION  */
+  YYSYMBOL_ZOOM = 26,                      /* ZOOM  */
+  YYSYMBOL_ICONMGRS = 27,                  /* ICONMGRS  */
+  YYSYMBOL_ICONMGR_GEOMETRY = 28,          /* ICONMGR_GEOMETRY  */
+  YYSYMBOL_ICONMGR_NOSHOW = 29,            /* ICONMGR_NOSHOW  */
+  YYSYMBOL_MAKE_TITLE = 30,                /* MAKE_TITLE  */
+  YYSYMBOL_ICONIFY_BY_UNMAPPING = 31,      /* ICONIFY_BY_UNMAPPING  */
+  YYSYMBOL_DONT_ICONIFY_BY_UNMAPPING = 32, /* DONT_ICONIFY_BY_UNMAPPING  */
+  YYSYMBOL_AUTO_POPUP = 33,                /* AUTO_POPUP  */
+  YYSYMBOL_NO_BORDER = 34,                 /* NO_BORDER  */
+  YYSYMBOL_NO_ICON_TITLE = 35,             /* NO_ICON_TITLE  */
+  YYSYMBOL_NO_TITLE = 36,                  /* NO_TITLE  */
+  YYSYMBOL_AUTO_RAISE = 37,                /* AUTO_RAISE  */
+  YYSYMBOL_NO_HILITE = 38,                 /* NO_HILITE  */
+  YYSYMBOL_ICON_REGION = 39,               /* ICON_REGION  */
+  YYSYMBOL_WINDOW_REGION = 40,             /* WINDOW_REGION  */
+  YYSYMBOL_META = 41,                      /* META  */
+  YYSYMBOL_SHIFT = 42,                     /* SHIFT  */
+  YYSYMBOL_LOCK = 43,                      /* LOCK  */
+  YYSYMBOL_CONTROL = 44,                   /* CONTROL  */
+  YYSYMBOL_WINDOW = 45,                    /* WINDOW  */
+  YYSYMBOL_TITLE = 46,                     /* TITLE  */
+  YYSYMBOL_ICON = 47,                      /* ICON  */
+  YYSYMBOL_ROOT = 48,                      /* ROOT  */
+  YYSYMBOL_FRAME = 49,                     /* FRAME  */
+  YYSYMBOL_COLON = 50,                     /* COLON  */
+  YYSYMBOL_EQUALS = 51,                    /* EQUALS  */
+  YYSYMBOL_SQUEEZE_TITLE = 52,             /* SQUEEZE_TITLE  */
+  YYSYMBOL_DONT_SQUEEZE_TITLE = 53,        /* DONT_SQUEEZE_TITLE  */
+  YYSYMBOL_WARP_ON_DEICONIFY = 54,         /* WARP_ON_DEICONIFY  */
+  YYSYMBOL_START_ICONIFIED = 55,           /* START_ICONIFIED  */
+  YYSYMBOL_NO_TITLE_HILITE = 56,           /* NO_TITLE_HILITE  */
+  YYSYMBOL_TITLE_HILITE = 57,              /* TITLE_HILITE  */
+  YYSYMBOL_MOVE = 58,                      /* MOVE  */
+  YYSYMBOL_RESIZE = 59,                    /* RESIZE  */
+  YYSYMBOL_WAITC = 60,                     /* WAITC  */
+  YYSYMBOL_SELECT = 61,                    /* SELECT  */
+  YYSYMBOL_KILL = 62,                      /* KILL  */
+  YYSYMBOL_LEFT_TITLEBUTTON = 63,          /* LEFT_TITLEBUTTON  */
+  YYSYMBOL_RIGHT_TITLEBUTTON = 64,         /* RIGHT_TITLEBUTTON  */
+  YYSYMBOL_NUMBER = 65,                    /* NUMBER  */
+  YYSYMBOL_KEYWORD = 66,                   /* KEYWORD  */
+  YYSYMBOL_NKEYWORD = 67,                  /* NKEYWORD  */
+  YYSYMBOL_CKEYWORD = 68,                  /* CKEYWORD  */
+  YYSYMBOL_CLKEYWORD = 69,                 /* CLKEYWORD  */
+  YYSYMBOL_FKEYWORD = 70,                  /* FKEYWORD  */
+  YYSYMBOL_FSKEYWORD = 71,                 /* FSKEYWORD  */
+  YYSYMBOL_FNKEYWORD = 72,                 /* FNKEYWORD  */
+  YYSYMBOL_PRIORITY_SWITCHING = 73,        /* PRIORITY_SWITCHING  */
+  YYSYMBOL_PRIORITY_NOT_SWITCHING = 74,    /* PRIORITY_NOT_SWITCHING  */
+  YYSYMBOL_SKEYWORD = 75,                  /* SKEYWORD  */
+  YYSYMBOL_SSKEYWORD = 76,                 /* SSKEYWORD  */
+  YYSYMBOL_WINDOW_RING = 77,               /* WINDOW_RING  */
+  YYSYMBOL_WINDOW_RING_EXCLUDE = 78,       /* WINDOW_RING_EXCLUDE  */
+  YYSYMBOL_WARP_CURSOR = 79,               /* WARP_CURSOR  */
+  YYSYMBOL_ERRORTOKEN = 80,                /* ERRORTOKEN  */
+  YYSYMBOL_GRAVITY = 81,                   /* GRAVITY  */
+  YYSYMBOL_SIJENUM = 82,                   /* SIJENUM  */
+  YYSYMBOL_NO_STACKMODE = 83,              /* NO_STACKMODE  */
+  YYSYMBOL_ALWAYS_ON_TOP = 84,             /* ALWAYS_ON_TOP  */
+  YYSYMBOL_WORKSPACE = 85,                 /* WORKSPACE  */
+  YYSYMBOL_WORKSPACES = 86,                /* WORKSPACES  */
+  YYSYMBOL_WORKSPCMGR_GEOMETRY = 87,       /* WORKSPCMGR_GEOMETRY  */
+  YYSYMBOL_OCCUPYALL = 88,                 /* OCCUPYALL  */
+  YYSYMBOL_OCCUPYLIST = 89,                /* OCCUPYLIST  */
+  YYSYMBOL_MAPWINDOWCURRENTWORKSPACE = 90, /* MAPWINDOWCURRENTWORKSPACE  */
+  YYSYMBOL_MAPWINDOWDEFAULTWORKSPACE = 91, /* MAPWINDOWDEFAULTWORKSPACE  */
+  YYSYMBOL_ON_TOP_PRIORITY = 92,           /* ON_TOP_PRIORITY  */
+  YYSYMBOL_UNMAPBYMOVINGFARAWAY = 93,      /* UNMAPBYMOVINGFARAWAY  */
+  YYSYMBOL_OPAQUEMOVE = 94,                /* OPAQUEMOVE  */
+  YYSYMBOL_NOOPAQUEMOVE = 95,              /* NOOPAQUEMOVE  */
+  YYSYMBOL_OPAQUERESIZE = 96,              /* OPAQUERESIZE  */
+  YYSYMBOL_NOOPAQUERESIZE = 97,            /* NOOPAQUERESIZE  */
+  YYSYMBOL_DONTSETINACTIVE = 98,           /* DONTSETINACTIVE  */
+  YYSYMBOL_CHANGE_WORKSPACE_FUNCTION = 99, /* CHANGE_WORKSPACE_FUNCTION  */
+  YYSYMBOL_DEICONIFY_FUNCTION = 100,       /* DEICONIFY_FUNCTION  */
+  YYSYMBOL_ICONIFY_FUNCTION = 101,         /* ICONIFY_FUNCTION  */
+  YYSYMBOL_AUTOSQUEEZE = 102,              /* AUTOSQUEEZE  */
+  YYSYMBOL_STARTSQUEEZED = 103,            /* STARTSQUEEZED  */
+  YYSYMBOL_DONT_SAVE = 104,                /* DONT_SAVE  */
+  YYSYMBOL_AUTO_LOWER = 105,               /* AUTO_LOWER  */
+  YYSYMBOL_ICONMENU_DONTSHOW = 106,        /* ICONMENU_DONTSHOW  */
+  YYSYMBOL_WINDOW_BOX = 107,               /* WINDOW_BOX  */
+  YYSYMBOL_IGNOREMODIFIER = 108,           /* IGNOREMODIFIER  */
+  YYSYMBOL_WINDOW_GEOMETRIES = 109,        /* WINDOW_GEOMETRIES  */
+  YYSYMBOL_ALWAYSSQUEEZETOGRAVITY = 110,   /* ALWAYSSQUEEZETOGRAVITY  */
+  YYSYMBOL_VIRTUAL_SCREENS = 111,          /* VIRTUAL_SCREENS  */
+  YYSYMBOL_IGNORE_TRANSIENT = 112,         /* IGNORE_TRANSIENT  */
+  YYSYMBOL_EWMH_IGNORE = 113,              /* EWMH_IGNORE  */
+  YYSYMBOL_MWM_IGNORE = 114,               /* MWM_IGNORE  */
+  YYSYMBOL_MONITOR_LAYOUT = 115,           /* MONITOR_LAYOUT  */
+  YYSYMBOL_RPLAY_SOUNDS = 116,             /* RPLAY_SOUNDS  */
+  YYSYMBOL_FORCE_FOCUS = 117,              /* FORCE_FOCUS  */
+  YYSYMBOL_STRING = 118,                   /* STRING  */
+  YYSYMBOL_YYACCEPT = 119,                 /* $accept  */
+  YYSYMBOL_twmrc = 120,                    /* twmrc  */
+  YYSYMBOL_121_1 = 121,                    /* $@1  */
+  YYSYMBOL_stmts = 122,                    /* stmts  */
+  YYSYMBOL_stmt = 123,                     /* stmt  */
+  YYSYMBOL_124_2 = 124,                    /* $@2  */
+  YYSYMBOL_125_3 = 125,                    /* $@3  */
+  YYSYMBOL_126_4 = 126,                    /* $@4  */
+  YYSYMBOL_127_5 = 127,                    /* $@5  */
+  YYSYMBOL_128_6 = 128,                    /* $@6  */
+  YYSYMBOL_129_7 = 129,                    /* $@7  */
+  YYSYMBOL_130_8 = 130,                    /* $@8  */
+  YYSYMBOL_131_9 = 131,                    /* $@9  */
+  YYSYMBOL_132_10 = 132,                   /* $@10  */
+  YYSYMBOL_133_11 = 133,                   /* $@11  */
+  YYSYMBOL_134_12 = 134,                   /* $@12  */
+  YYSYMBOL_135_13 = 135,                   /* $@13  */
+  YYSYMBOL_136_14 = 136,                   /* $@14  */
+  YYSYMBOL_137_15 = 137,                   /* $@15  */
+  YYSYMBOL_138_16 = 138,                   /* $@16  */
+  YYSYMBOL_139_17 = 139,                   /* $@17  */
+  YYSYMBOL_140_18 = 140,                   /* $@18  */
+  YYSYMBOL_141_19 = 141,                   /* $@19  */
+  YYSYMBOL_142_20 = 142,                   /* $@20  */
+  YYSYMBOL_143_21 = 143,                   /* $@21  */
+  YYSYMBOL_144_22 = 144,                   /* $@22  */
+  YYSYMBOL_145_23 = 145,                   /* $@23  */
+  YYSYMBOL_146_24 = 146,                   /* $@24  */
+  YYSYMBOL_147_25 = 147,                   /* $@25  */
+  YYSYMBOL_148_26 = 148,                   /* $@26  */
+  YYSYMBOL_149_27 = 149,                   /* $@27  */
+  YYSYMBOL_150_28 = 150,                   /* $@28  */
+  YYSYMBOL_151_29 = 151,                   /* $@29  */
+  YYSYMBOL_152_30 = 152,                   /* $@30  */
+  YYSYMBOL_153_31 = 153,                   /* $@31  */
+  YYSYMBOL_154_32 = 154,                   /* $@32  */
+  YYSYMBOL_155_33 = 155,                   /* $@33  */
+  YYSYMBOL_156_34 = 156,                   /* $@34  */
+  YYSYMBOL_157_35 = 157,                   /* $@35  */
+  YYSYMBOL_158_36 = 158,                   /* $@36  */
+  YYSYMBOL_159_37 = 159,                   /* $@37  */
+  YYSYMBOL_160_38 = 160,                   /* $@38  */
+  YYSYMBOL_161_39 = 161,                   /* $@39  */
+  YYSYMBOL_162_40 = 162,                   /* $@40  */
+  YYSYMBOL_163_41 = 163,                   /* $@41  */
+  YYSYMBOL_164_42 = 164,                   /* $@42  */
+  YYSYMBOL_165_43 = 165,                   /* $@43  */
+  YYSYMBOL_166_44 = 166,                   /* $@44  */
+  YYSYMBOL_167_45 = 167,                   /* $@45  */
+  YYSYMBOL_168_46 = 168,                   /* $@46  */
+  YYSYMBOL_169_47 = 169,                   /* $@47  */
+  YYSYMBOL_170_48 = 170,                   /* $@48  */
+  YYSYMBOL_171_49 = 171,                   /* $@49  */
+  YYSYMBOL_172_50 = 172,                   /* $@50  */
+  YYSYMBOL_173_51 = 173,                   /* $@51  */
+  YYSYMBOL_174_52 = 174,                   /* $@52  */
+  YYSYMBOL_175_53 = 175,                   /* $@53  */
+  YYSYMBOL_176_54 = 176,                   /* $@54  */
+  YYSYMBOL_177_55 = 177,                   /* $@55  */
+  YYSYMBOL_178_56 = 178,                   /* $@56  */
+  YYSYMBOL_179_57 = 179,                   /* $@57  */
+  YYSYMBOL_180_58 = 180,                   /* $@58  */
+  YYSYMBOL_181_59 = 181,                   /* $@59  */
+  YYSYMBOL_182_60 = 182,                   /* $@60  */
+  YYSYMBOL_183_61 = 183,                   /* $@61  */
+  YYSYMBOL_184_62 = 184,                   /* $@62  */
+  YYSYMBOL_185_63 = 185,                   /* $@63  */
+  YYSYMBOL_186_64 = 186,                   /* $@64  */
+  YYSYMBOL_187_65 = 187,                   /* $@65  */
+  YYSYMBOL_188_66 = 188,                   /* $@66  */
+  YYSYMBOL_189_67 = 189,                   /* $@67  */
+  YYSYMBOL_190_68 = 190,                   /* $@68  */
+  YYSYMBOL_191_69 = 191,                   /* $@69  */
+  YYSYMBOL_noarg = 192,                    /* noarg  */
+  YYSYMBOL_sarg = 193,                     /* sarg  */
+  YYSYMBOL_narg = 194,                     /* narg  */
+  YYSYMBOL_keyaction = 195,                /* keyaction  */
+  YYSYMBOL_full = 196,                     /* full  */
+  YYSYMBOL_fullkey = 197,                  /* fullkey  */
+  YYSYMBOL_keys = 198,                     /* keys  */
+  YYSYMBOL_key = 199,                      /* key  */
+  YYSYMBOL_vgrav = 200,                    /* vgrav  */
+  YYSYMBOL_hgrav = 201,                    /* hgrav  */
+  YYSYMBOL_contexts = 202,                 /* contexts  */
+  YYSYMBOL_context = 203,                  /* context  */
+  YYSYMBOL_contextkeys = 204,              /* contextkeys  */
+  YYSYMBOL_contextkey = 205,               /* contextkey  */
+  YYSYMBOL_binding_list = 206,             /* binding_list  */
+  YYSYMBOL_binding_entries = 207,          /* binding_entries  */
+  YYSYMBOL_binding_entry = 208,            /* binding_entry  */
+  YYSYMBOL_pixmap_list = 209,              /* pixmap_list  */
+  YYSYMBOL_pixmap_entries = 210,           /* pixmap_entries  */
+  YYSYMBOL_pixmap_entry = 211,             /* pixmap_entry  */
+  YYSYMBOL_cursor_list = 212,              /* cursor_list  */
+  YYSYMBOL_cursor_entries = 213,           /* cursor_entries  */
+  YYSYMBOL_cursor_entry = 214,             /* cursor_entry  */
+  YYSYMBOL_color_list = 215,               /* color_list  */
+  YYSYMBOL_color_entries = 216,            /* color_entries  */
+  YYSYMBOL_color_entry = 217,              /* color_entry  */
+  YYSYMBOL_218_70 = 218,                   /* $@70  */
+  YYSYMBOL_save_color_list = 219,          /* save_color_list  */
+  YYSYMBOL_s_color_entries = 220,          /* s_color_entries  */
+  YYSYMBOL_s_color_entry = 221,            /* s_color_entry  */
+  YYSYMBOL_win_color_list = 222,           /* win_color_list  */
+  YYSYMBOL_win_color_entries = 223,        /* win_color_entries  */
+  YYSYMBOL_win_color_entry = 224,          /* win_color_entry  */
+  YYSYMBOL_wingeom_list = 225,             /* wingeom_list  */
+  YYSYMBOL_wingeom_entries = 226,          /* wingeom_entries  */
+  YYSYMBOL_wingeom_entry = 227,            /* wingeom_entry  */
+  YYSYMBOL_vscreen_geom_list = 228,        /* vscreen_geom_list  */
+  YYSYMBOL_vscreen_geom_entries = 229,     /* vscreen_geom_entries  */
+  YYSYMBOL_vscreen_geom_entry = 230,       /* vscreen_geom_entry  */
+  YYSYMBOL_ewmh_ignore_list = 231,         /* ewmh_ignore_list  */
+  YYSYMBOL_ewmh_ignore_entries = 232,      /* ewmh_ignore_entries  */
+  YYSYMBOL_ewmh_ignore_entry = 233,        /* ewmh_ignore_entry  */
+  YYSYMBOL_mwm_ignore_list = 234,          /* mwm_ignore_list  */
+  YYSYMBOL_mwm_ignore_entries = 235,       /* mwm_ignore_entries  */
+  YYSYMBOL_mwm_ignore_entry = 236,         /* mwm_ignore_entry  */
+  YYSYMBOL_layout_geom_list = 237,         /* layout_geom_list  */
+  YYSYMBOL_layout_geom_entries = 238,      /* layout_geom_entries  */
+  YYSYMBOL_layout_geom_entry = 239,        /* layout_geom_entry  */
+  YYSYMBOL_squeeze = 240,                  /* squeeze  */
+  YYSYMBOL_241_71 = 241,                   /* $@71  */
+  YYSYMBOL_242_72 = 242,                   /* $@72  */
+  YYSYMBOL_win_sqz_entries = 243,          /* win_sqz_entries  */
+  YYSYMBOL_iconm_list = 244,               /* iconm_list  */
+  YYSYMBOL_iconm_entries = 245,            /* iconm_entries  */
+  YYSYMBOL_iconm_entry = 246,              /* iconm_entry  */
+  YYSYMBOL_workspc_list = 247,             /* workspc_list  */
+  YYSYMBOL_workspc_entries = 248,          /* workspc_entries  */
+  YYSYMBOL_workspc_entry = 249,            /* workspc_entry  */
+  YYSYMBOL_250_73 = 250,                   /* $@73  */
+  YYSYMBOL_workapp_list = 251,             /* workapp_list  */
+  YYSYMBOL_workapp_entries = 252,          /* workapp_entries  */
+  YYSYMBOL_workapp_entry = 253,            /* workapp_entry  */
+  YYSYMBOL_curwork = 254,                  /* curwork  */
+  YYSYMBOL_defwork = 255,                  /* defwork  */
+  YYSYMBOL_win_list = 256,                 /* win_list  */
+  YYSYMBOL_win_entries = 257,              /* win_entries  */
+  YYSYMBOL_win_entry = 258,                /* win_entry  */
+  YYSYMBOL_occupy_list = 259,              /* occupy_list  */
+  YYSYMBOL_occupy_entries = 260,           /* occupy_entries  */
+  YYSYMBOL_occupy_entry = 261,             /* occupy_entry  */
+  YYSYMBOL_262_74 = 262,                   /* $@74  */
+  YYSYMBOL_263_75 = 263,                   /* $@75  */
+  YYSYMBOL_264_76 = 264,                   /* $@76  */
+  YYSYMBOL_occupy_workspc_list = 265,      /* occupy_workspc_list  */
+  YYSYMBOL_occupy_workspc_entries = 266,   /* occupy_workspc_entries  */
+  YYSYMBOL_occupy_workspc_entry = 267,     /* occupy_workspc_entry  */
+  YYSYMBOL_occupy_window_list = 268,       /* occupy_window_list  */
+  YYSYMBOL_occupy_window_entries = 269,    /* occupy_window_entries  */
+  YYSYMBOL_occupy_window_entry = 270,      /* occupy_window_entry  */
+  YYSYMBOL_icon_list = 271,                /* icon_list  */
+  YYSYMBOL_icon_entries = 272,             /* icon_entries  */
+  YYSYMBOL_icon_entry = 273,               /* icon_entry  */
+  YYSYMBOL_rplay_sounds_list = 274,        /* rplay_sounds_list  */
+  YYSYMBOL_rplay_sounds_entries = 275,     /* rplay_sounds_entries  */
+  YYSYMBOL_rplay_sounds_entry = 276,       /* rplay_sounds_entry  */
+  YYSYMBOL_function = 277,                 /* function  */
+  YYSYMBOL_function_entries = 278,         /* function_entries  */
+  YYSYMBOL_function_entry = 279,           /* function_entry  */
+  YYSYMBOL_menu = 280,                     /* menu  */
+  YYSYMBOL_menu_entries = 281,             /* menu_entries  */
+  YYSYMBOL_menu_entry = 282,               /* menu_entry  */
+  YYSYMBOL_action = 283,                   /* action  */
+  YYSYMBOL_signed_number = 284,            /* signed_number  */
+  YYSYMBOL_button = 285,                   /* button  */
+  YYSYMBOL_string = 286,                   /* string  */
+  YYSYMBOL_number = 287                    /* number  */
 };
-typedef union YYSTYPE YYSTYPE;
-# define YYSTYPE_IS_TRIVIAL 1
-# define YYSTYPE_IS_DECLARED 1
-#endif
-
+typedef enum yysymbol_kind_t yysymbol_kind_t;
 
-extern YYSTYPE yylval;
-
-int yyparse (void);
-
-#endif /* !YY_YY_HOME_FULLERMD_WORK_CTWM_BZR_4_0_X_CTWM_MKTAR_ASYJVB_CTWM_4_0_3_BUILD_GRAM_TAB_H_INCLUDED  */
 
 
 
@@ -291,28 +433,87 @@ int yyparse (void);
 # undef short
 #endif
 
-#ifdef YYTYPE_UINT8
-typedef YYTYPE_UINT8 yytype_uint8;
-#else
-typedef unsigned char yytype_uint8;
+/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure
+   <limits.h> and (if available) <stdint.h> are included
+   so that the code can choose integer types of a good width.  */
+
+#ifndef __PTRDIFF_MAX__
+# include <limits.h> /* INFRINGES ON USER NAME SPACE */
+# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__
+#  include <stdint.h> /* INFRINGES ON USER NAME SPACE */
+#  define YY_STDINT_H
+# endif
 #endif
 
-#ifdef YYTYPE_INT8
-typedef YYTYPE_INT8 yytype_int8;
+/* Narrow types that promote to a signed type and that can represent a
+   signed or unsigned integer of at least N bits.  In tables they can
+   save space and decrease cache pressure.  Promoting to a signed type
+   helps avoid bugs in integer arithmetic.  */
+
+#ifdef __INT_LEAST8_MAX__
+typedef __INT_LEAST8_TYPE__ yytype_int8;
+#elif defined YY_STDINT_H
+typedef int_least8_t yytype_int8;
 #else
 typedef signed char yytype_int8;
 #endif
 
-#ifdef YYTYPE_UINT16
-typedef YYTYPE_UINT16 yytype_uint16;
+#ifdef __INT_LEAST16_MAX__
+typedef __INT_LEAST16_TYPE__ yytype_int16;
+#elif defined YY_STDINT_H
+typedef int_least16_t yytype_int16;
 #else
-typedef unsigned short yytype_uint16;
+typedef short yytype_int16;
+#endif
+
+/* Work around bug in HP-UX 11.23, which defines these macros
+   incorrectly for preprocessor constants.  This workaround can likely
+   be removed in 2023, as HPE has promised support for HP-UX 11.23
+   (aka HP-UX 11i v2) only through the end of 2022; see Table 2 of
+   <https://h20195.www2.hpe.com/V2/getpdf.aspx/4AA4-7673ENW.pdf>.  */
+#ifdef __hpux
+# undef UINT_LEAST8_MAX
+# undef UINT_LEAST16_MAX
+# define UINT_LEAST8_MAX 255
+# define UINT_LEAST16_MAX 65535
 #endif
 
-#ifdef YYTYPE_INT16
-typedef YYTYPE_INT16 yytype_int16;
+#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__
+typedef __UINT_LEAST8_TYPE__ yytype_uint8;
+#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \
+       && UINT_LEAST8_MAX <= INT_MAX)
+typedef uint_least8_t yytype_uint8;
+#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX
+typedef unsigned char yytype_uint8;
 #else
-typedef short yytype_int16;
+typedef short yytype_uint8;
+#endif
+
+#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__
+typedef __UINT_LEAST16_TYPE__ yytype_uint16;
+#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \
+       && UINT_LEAST16_MAX <= INT_MAX)
+typedef uint_least16_t yytype_uint16;
+#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX
+typedef unsigned short yytype_uint16;
+#else
+typedef int yytype_uint16;
+#endif
+
+#ifndef YYPTRDIFF_T
+# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__
+#  define YYPTRDIFF_T __PTRDIFF_TYPE__
+#  define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__
+# elif defined PTRDIFF_MAX
+#  ifndef ptrdiff_t
+#   include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  endif
+#  define YYPTRDIFF_T ptrdiff_t
+#  define YYPTRDIFF_MAXIMUM PTRDIFF_MAX
+# else
+#  define YYPTRDIFF_T long
+#  define YYPTRDIFF_MAXIMUM LONG_MAX
+# endif
 #endif
 
 #ifndef YYSIZE_T
@@ -320,7 +521,7 @@ typedef short yytype_int16;
 #  define YYSIZE_T __SIZE_TYPE__
 # elif defined size_t
 #  define YYSIZE_T size_t
-# elif ! defined YYSIZE_T
+# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__
 #  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
 #  define YYSIZE_T size_t
 # else
@@ -328,7 +529,20 @@ typedef short yytype_int16;
 # endif
 #endif
 
-#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+#define YYSIZE_MAXIMUM                                  \
+  YY_CAST (YYPTRDIFF_T,                                 \
+           (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1)  \
+            ? YYPTRDIFF_MAXIMUM                         \
+            : YY_CAST (YYSIZE_T, -1)))
+
+#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X))
+
+
+/* Stored state numbers (used for stacks). */
+typedef yytype_int16 yy_state_t;
+
+/* State numbers in computations.  */
+typedef int yy_state_fast_t;
 
 #ifndef YY_
 # if defined YYENABLE_NLS && YYENABLE_NLS
@@ -342,38 +556,43 @@ typedef short yytype_int16;
 # endif
 #endif
 
-#ifndef YY_ATTRIBUTE
-# if (defined __GNUC__                                               \
-      && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__)))  \
-     || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C
-#  define YY_ATTRIBUTE(Spec) __attribute__(Spec)
-# else
-#  define YY_ATTRIBUTE(Spec) /* empty */
-# endif
-#endif
 
 #ifndef YY_ATTRIBUTE_PURE
-# define YY_ATTRIBUTE_PURE   YY_ATTRIBUTE ((__pure__))
+# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__)
+#  define YY_ATTRIBUTE_PURE __attribute__ ((__pure__))
+# else
+#  define YY_ATTRIBUTE_PURE
+# endif
 #endif
 
 #ifndef YY_ATTRIBUTE_UNUSED
-# define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__))
+# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__)
+#  define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__))
+# else
+#  define YY_ATTRIBUTE_UNUSED
+# endif
 #endif
 
 /* Suppress unused-variable warnings by "using" E.  */
 #if ! defined lint || defined __GNUC__
-# define YYUSE(E) ((void) (E))
+# define YY_USE(E) ((void) (E))
 #else
-# define YYUSE(E) /* empty */
+# define YY_USE(E) /* empty */
 #endif
 
-#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__
 /* Suppress an incorrect diagnostic about yylval being uninitialized.  */
-# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \
-    _Pragma ("GCC diagnostic push") \
-    _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\
+#if defined __GNUC__ && ! defined __ICC && 406 <= __GNUC__ * 100 + __GNUC_MINOR__
+# if __GNUC__ * 100 + __GNUC_MINOR__ < 407
+#  define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN                           \
+    _Pragma ("GCC diagnostic push")                                     \
+    _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")
+# else
+#  define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN                           \
+    _Pragma ("GCC diagnostic push")                                     \
+    _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")              \
     _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
-# define YY_IGNORE_MAYBE_UNINITIALIZED_END \
+# endif
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END      \
     _Pragma ("GCC diagnostic pop")
 #else
 # define YY_INITIAL_VALUE(Value) Value
@@ -386,10 +605,22 @@ typedef short yytype_int16;
 # define YY_INITIAL_VALUE(Value) /* Nothing. */
 #endif
 
+#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__
+# define YY_IGNORE_USELESS_CAST_BEGIN                          \
+    _Pragma ("GCC diagnostic push")                            \
+    _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"")
+# define YY_IGNORE_USELESS_CAST_END            \
+    _Pragma ("GCC diagnostic pop")
+#endif
+#ifndef YY_IGNORE_USELESS_CAST_BEGIN
+# define YY_IGNORE_USELESS_CAST_BEGIN
+# define YY_IGNORE_USELESS_CAST_END
+#endif
+
 
 #define YY_ASSERT(E) ((void) (0 && (E)))
 
-#if ! defined yyoverflow || YYERROR_VERBOSE
+#if !defined yyoverflow
 
 /* The parser invokes alloca or malloc; define the necessary symbols.  */
 
@@ -454,8 +685,7 @@ void free (void *); /* INFRINGES ON USER NAME SPACE */
 #   endif
 #  endif
 # endif
-#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
-
+#endif /* !defined yyoverflow */
 
 #if (! defined yyoverflow \
      && (! defined __cplusplus \
@@ -464,17 +694,17 @@ void free (void *); /* INFRINGES ON USER NAME SPACE */
 /* A type that is properly aligned for any stack member.  */
 union yyalloc
 {
-  yytype_int16 yyss_alloc;
+  yy_state_t yyss_alloc;
   YYSTYPE yyvs_alloc;
 };
 
 /* The size of the maximum gap between one aligned stack and the next.  */
-# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1)
 
 /* The size of an array large to enough to hold all stacks, each with
    N elements.  */
 # define YYSTACK_BYTES(N) \
-     ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+     ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE)) \
       + YYSTACK_GAP_MAXIMUM)
 
 # define YYCOPY_NEEDED 1
@@ -487,11 +717,11 @@ union yyalloc
 # define YYSTACK_RELOCATE(Stack_alloc, Stack)                           \
     do                                                                  \
       {                                                                 \
-        YYSIZE_T yynewbytes;                                            \
+        YYPTRDIFF_T yynewbytes;                                         \
         YYCOPY (&yyptr->Stack_alloc, Stack, yysize);                    \
         Stack = &yyptr->Stack_alloc;                                    \
-        yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
-        yyptr += yynewbytes / sizeof (*yyptr);                          \
+        yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \
+        yyptr += yynewbytes / YYSIZEOF (*yyptr);                        \
       }                                                                 \
     while (0)
 
@@ -503,12 +733,12 @@ union yyalloc
 # ifndef YYCOPY
 #  if defined __GNUC__ && 1 < __GNUC__
 #   define YYCOPY(Dst, Src, Count) \
-      __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src)))
+      __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src)))
 #  else
 #   define YYCOPY(Dst, Src, Count)              \
       do                                        \
         {                                       \
-          YYSIZE_T yyi;                         \
+          YYPTRDIFF_T yyi;                      \
           for (yyi = 0; yyi < (Count); yyi++)   \
             (Dst)[yyi] = (Src)[yyi];            \
         }                                       \
@@ -520,28 +750,31 @@ union yyalloc
 /* YYFINAL -- State number of the termination state.  */
 #define YYFINAL  3
 /* YYLAST -- Last index in YYTABLE.  */
-#define YYLAST   710
+#define YYLAST   977
 
 /* YYNTOKENS -- Number of terminals.  */
-#define YYNTOKENS  118
+#define YYNTOKENS  119
 /* YYNNTS -- Number of nonterminals.  */
-#define YYNNTS  165
+#define YYNNTS  169
 /* YYNRULES -- Number of rules.  */
-#define YYNRULES  382
+#define YYNRULES  388
 /* YYNSTATES -- Number of states.  */
-#define YYNSTATES  546
+#define YYNSTATES  554
+
+/* YYMAXUTOK -- Last valid token kind.  */
+#define YYMAXUTOK   373
 
-#define YYUNDEFTOK  2
-#define YYMAXUTOK   372
 
 /* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM
    as returned by yylex, with out-of-bounds checking.  */
-#define YYTRANSLATE(YYX)                                                \
-  ((unsigned) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+#define YYTRANSLATE(YYX)                                \
+  (0 <= (YYX) && (YYX) <= YYMAXUTOK                     \
+   ? YY_CAST (yysymbol_kind_t, yytranslate[YYX])        \
+   : YYSYMBOL_YYUNDEF)
 
 /* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM
    as returned by yylex.  */
-static const yytype_uint8 yytranslate[] =
+static const yytype_int8 yytranslate[] =
 {
        0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
        2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
@@ -580,65 +813,72 @@ static const yytype_uint8 yytranslate[] =
       85,    86,    87,    88,    89,    90,    91,    92,    93,    94,
       95,    96,    97,    98,    99,   100,   101,   102,   103,   104,
      105,   106,   107,   108,   109,   110,   111,   112,   113,   114,
-     115,   116,   117
+     115,   116,   117,   118
 };
 
 #if YYDEBUG
-  /* YYRLINE[YYN] -- Source line where rule number YYN was defined.  */
-static const yytype_uint16 yyrline[] =
+/* YYRLINE[YYN] -- Source line where rule number YYN was defined.  */
+static const yytype_int16 yyrline[] =
 {
-       0,   106,   106,   106,   110,   111,   114,   115,   116,   117,
-     118,   119,   122,   125,   128,   131,   131,   135,   135,   139,
-     139,   143,   143,   148,   148,   153,   153,   158,   164,   167,
-     173,   176,   176,   179,   179,   182,   188,   190,   191,   192,
-     192,   194,   197,   197,   199,   200,   200,   202,   203,   203,
-     205,   206,   206,   208,   210,   213,   216,   216,   218,   218,
-     220,   224,   240,   241,   243,   243,   245,   245,   248,   250,
-     247,   252,   252,   254,   254,   256,   256,   258,   258,   260,
-     260,   262,   262,   264,   265,   265,   267,   267,   269,   269,
-     271,   272,   272,   274,   274,   276,   276,   278,   280,   280,
-     282,   284,   286,   289,   288,   292,   291,   294,   294,   296,
-     296,   299,   299,   303,   302,   307,   306,   312,   312,   314,
-     316,   316,   318,   319,   319,   321,   321,   323,   323,   325,
-     327,   327,   329,   331,   331,   333,   333,   335,   335,   337,
-     337,   339,   340,   340,   342,   342,   344,   345,   345,   348,
-     348,   350,   350,   352,   352,   354,   354,   356,   356,   358,
-     358,   360,   376,   384,   392,   400,   408,   408,   410,   412,
-     412,   414,   415,   415,   419,   419,   421,   421,   423,   423,
-     425,   425,   427,   427,   429,   430,   430,   434,   444,   452,
-     462,   471,   479,   489,   501,   504,   507,   510,   511,   514,
-     515,   516,   517,   518,   528,   538,   541,   556,   571,   572,
-     575,   576,   577,   578,   579,   580,   581,   582,   583,   584,
-     585,   588,   589,   592,   593,   594,   595,   596,   597,   598,
-     599,   600,   601,   602,   603,   607,   610,   611,   614,   615,
-     616,   624,   627,   628,   631,   635,   638,   639,   642,   644,
-     646,   648,   650,   652,   654,   656,   658,   660,   662,   664,
-     666,   668,   670,   672,   674,   676,   678,   680,   682,   684,
-     688,   692,   693,   696,   705,   705,   716,   727,   730,   731,
-     734,   735,   738,   741,   742,   745,   750,   753,   754,   757,
-     760,   763,   764,   767,   771,   774,   775,   778,   782,   785,
-     786,   789,   793,   796,   796,   801,   802,   802,   806,   807,
-     815,   818,   819,   822,   827,   835,   838,   839,   842,   845,
-     845,   851,   854,   855,   858,   861,   864,   867,   870,   875,
-     878,   881,   884,   889,   892,   895,   898,   903,   906,   907,
-     910,   915,   918,   919,   922,   922,   924,   924,   926,   926,
-     930,   933,   934,   937,   945,   948,   949,   952,   960,   963,
-     964,   967,   970,   981,   982,   985,   996,   999,  1000,  1003,
-    1009,  1012,  1013,  1016,  1026,  1038,  1039,  1080,  1081,  1082,
-    1085,  1097,  1103
+       0,   109,   109,   109,   113,   114,   117,   118,   119,   120,
+     121,   122,   125,   128,   131,   134,   134,   138,   138,   142,
+     142,   146,   146,   151,   151,   156,   156,   163,   169,   172,
+     178,   181,   181,   184,   184,   187,   193,   195,   196,   197,
+     197,   199,   202,   202,   204,   205,   205,   207,   208,   208,
+     210,   211,   211,   213,   215,   218,   221,   221,   223,   223,
+     225,   229,   245,   246,   248,   248,   250,   250,   253,   255,
+     252,   257,   257,   259,   259,   261,   261,   263,   263,   265,
+     265,   267,   267,   269,   270,   270,   272,   272,   274,   274,
+     276,   277,   277,   279,   279,   281,   281,   283,   285,   285,
+     287,   289,   291,   294,   293,   297,   296,   299,   299,   301,
+     301,   304,   304,   308,   307,   312,   311,   317,   317,   319,
+     321,   321,   323,   324,   324,   326,   326,   335,   335,   337,
+     339,   339,   341,   343,   343,   345,   345,   347,   347,   349,
+     349,   351,   352,   352,   354,   354,   356,   357,   357,   360,
+     360,   362,   362,   364,   364,   366,   366,   368,   368,   370,
+     370,   372,   388,   396,   404,   412,   420,   420,   422,   424,
+     424,   426,   427,   427,   431,   431,   433,   433,   435,   435,
+     437,   437,   439,   439,   441,   441,   443,   444,   444,   448,
+     458,   466,   476,   485,   493,   503,   515,   518,   521,   524,
+     525,   528,   529,   530,   531,   532,   542,   552,   555,   570,
+     585,   586,   589,   590,   591,   592,   593,   594,   595,   596,
+     597,   598,   599,   602,   603,   606,   607,   608,   609,   610,
+     611,   612,   613,   614,   615,   616,   617,   621,   624,   625,
+     628,   629,   630,   638,   641,   642,   645,   649,   652,   653,
+     656,   658,   660,   662,   664,   666,   668,   670,   672,   674,
+     676,   678,   680,   682,   684,   686,   688,   690,   692,   694,
+     696,   698,   702,   706,   707,   710,   719,   719,   730,   741,
+     744,   745,   748,   749,   752,   755,   756,   759,   764,   767,
+     768,   771,   774,   777,   778,   781,   789,   792,   793,   796,
+     800,   803,   804,   807,   811,   814,   815,   818,   822,   825,
+     825,   830,   831,   831,   835,   836,   844,   847,   848,   851,
+     856,   864,   867,   868,   871,   874,   874,   880,   883,   884,
+     887,   890,   893,   896,   899,   904,   907,   910,   913,   918,
+     921,   924,   927,   932,   935,   936,   939,   944,   947,   948,
+     951,   951,   953,   953,   955,   955,   959,   962,   963,   966,
+     974,   977,   978,   981,   989,   992,   993,   996,   999,  1010,
+    1011,  1014,  1025,  1028,  1029,  1032,  1038,  1041,  1042,  1045,
+    1055,  1067,  1068,  1110,  1111,  1112,  1115,  1127,  1133
 };
 #endif
 
-#if YYDEBUG || YYERROR_VERBOSE || 0
+/** Accessing symbol of state STATE.  */
+#define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State])
+
+#if YYDEBUG || 0
+/* The user-facing name of the symbol whose (internal) number is
+   YYSYMBOL.  No bounds checking.  */
+static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED;
+
 /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
    First, the terminals, then, starting at YYNTOKENS, nonterminals.  */
 static const char *const yytname[] =
 {
-  "$end", "error", "$undefined", "LB", "RB", "LP", "RP", "MENUS", "MENU",
-  "BUTTON", "DEFAULT_FUNCTION", "PLUS", "MINUS", "ALL", "OR", "CURSORS",
-  "PIXMAPS", "ICONS", "COLOR", "SAVECOLOR", "MONOCHROME", "FUNCTION",
-  "ICONMGR_SHOW", "ICONMGR", "ALTER", "WINDOW_FUNCTION", "ZOOM",
-  "ICONMGRS", "ICONMGR_GEOMETRY", "ICONMGR_NOSHOW", "MAKE_TITLE",
+  "\"end of file\"", "error", "\"invalid token\"", "LB", "RB", "LP", "RP",
+  "MENUS", "MENU", "BUTTON", "DEFAULT_FUNCTION", "PLUS", "MINUS", "ALL",
+  "OR", "CURSORS", "PIXMAPS", "ICONS", "COLOR", "SAVECOLOR", "MONOCHROME",
+  "FUNCTION", "ICONMGR_SHOW", "ICONMGR", "ALTER", "WINDOW_FUNCTION",
+  "ZOOM", "ICONMGRS", "ICONMGR_GEOMETRY", "ICONMGR_NOSHOW", "MAKE_TITLE",
   "ICONIFY_BY_UNMAPPING", "DONT_ICONIFY_BY_UNMAPPING", "AUTO_POPUP",
   "NO_BORDER", "NO_ICON_TITLE", "NO_TITLE", "AUTO_RAISE", "NO_HILITE",
   "ICON_REGION", "WINDOW_REGION", "META", "SHIFT", "LOCK", "CONTROL",
@@ -658,32 +898,33 @@ static const char *const yytname[] =
   "ICONIFY_FUNCTION", "AUTOSQUEEZE", "STARTSQUEEZED", "DONT_SAVE",
   "AUTO_LOWER", "ICONMENU_DONTSHOW", "WINDOW_BOX", "IGNOREMODIFIER",
   "WINDOW_GEOMETRIES", "ALWAYSSQUEEZETOGRAVITY", "VIRTUAL_SCREENS",
-  "IGNORE_TRANSIENT", "EWMH_IGNORE", "MWM_IGNORE", "RPLAY_SOUNDS",
-  "FORCE_FOCUS", "STRING", "$accept", "twmrc", "$@1", "stmts", "stmt",
-  "$@2", "$@3", "$@4", "$@5", "$@6", "$@7", "$@8", "$@9", "$@10", "$@11",
-  "$@12", "$@13", "$@14", "$@15", "$@16", "$@17", "$@18", "$@19", "$@20",
-  "$@21", "$@22", "$@23", "$@24", "$@25", "$@26", "$@27", "$@28", "$@29",
-  "$@30", "$@31", "$@32", "$@33", "$@34", "$@35", "$@36", "$@37", "$@38",
-  "$@39", "$@40", "$@41", "$@42", "$@43", "$@44", "$@45", "$@46", "$@47",
-  "$@48", "$@49", "$@50", "$@51", "$@52", "$@53", "$@54", "$@55", "$@56",
-  "$@57", "$@58", "$@59", "$@60", "$@61", "$@62", "$@63", "$@64", "$@65",
-  "$@66", "$@67", "$@68", "noarg", "sarg", "narg", "keyaction", "full",
-  "fullkey", "keys", "key", "vgrav", "hgrav", "contexts", "context",
-  "contextkeys", "contextkey", "binding_list", "binding_entries",
-  "binding_entry", "pixmap_list", "pixmap_entries", "pixmap_entry",
-  "cursor_list", "cursor_entries", "cursor_entry", "color_list",
-  "color_entries", "color_entry", "$@69", "save_color_list",
+  "IGNORE_TRANSIENT", "EWMH_IGNORE", "MWM_IGNORE", "MONITOR_LAYOUT",
+  "RPLAY_SOUNDS", "FORCE_FOCUS", "STRING", "$accept", "twmrc", "$@1",
+  "stmts", "stmt", "$@2", "$@3", "$@4", "$@5", "$@6", "$@7", "$@8", "$@9",
+  "$@10", "$@11", "$@12", "$@13", "$@14", "$@15", "$@16", "$@17", "$@18",
+  "$@19", "$@20", "$@21", "$@22", "$@23", "$@24", "$@25", "$@26", "$@27",
+  "$@28", "$@29", "$@30", "$@31", "$@32", "$@33", "$@34", "$@35", "$@36",
+  "$@37", "$@38", "$@39", "$@40", "$@41", "$@42", "$@43", "$@44", "$@45",
+  "$@46", "$@47", "$@48", "$@49", "$@50", "$@51", "$@52", "$@53", "$@54",
+  "$@55", "$@56", "$@57", "$@58", "$@59", "$@60", "$@61", "$@62", "$@63",
+  "$@64", "$@65", "$@66", "$@67", "$@68", "$@69", "noarg", "sarg", "narg",
+  "keyaction", "full", "fullkey", "keys", "key", "vgrav", "hgrav",
+  "contexts", "context", "contextkeys", "contextkey", "binding_list",
+  "binding_entries", "binding_entry", "pixmap_list", "pixmap_entries",
+  "pixmap_entry", "cursor_list", "cursor_entries", "cursor_entry",
+  "color_list", "color_entries", "color_entry", "$@70", "save_color_list",
   "s_color_entries", "s_color_entry", "win_color_list",
   "win_color_entries", "win_color_entry", "wingeom_list",
-  "wingeom_entries", "wingeom_entry", "geom_list", "geom_entries",
-  "geom_entry", "ewmh_ignore_list", "ewmh_ignore_entries",
-  "ewmh_ignore_entry", "mwm_ignore_list", "mwm_ignore_entries",
-  "mwm_ignore_entry", "squeeze", "$@70", "$@71", "win_sqz_entries",
-  "iconm_list", "iconm_entries", "iconm_entry", "workspc_list",
-  "workspc_entries", "workspc_entry", "$@72", "workapp_list",
-  "workapp_entries", "workapp_entry", "curwork", "defwork", "win_list",
-  "win_entries", "win_entry", "occupy_list", "occupy_entries",
-  "occupy_entry", "$@73", "$@74", "$@75", "occupy_workspc_list",
+  "wingeom_entries", "wingeom_entry", "vscreen_geom_list",
+  "vscreen_geom_entries", "vscreen_geom_entry", "ewmh_ignore_list",
+  "ewmh_ignore_entries", "ewmh_ignore_entry", "mwm_ignore_list",
+  "mwm_ignore_entries", "mwm_ignore_entry", "layout_geom_list",
+  "layout_geom_entries", "layout_geom_entry", "squeeze", "$@71", "$@72",
+  "win_sqz_entries", "iconm_list", "iconm_entries", "iconm_entry",
+  "workspc_list", "workspc_entries", "workspc_entry", "$@73",
+  "workapp_list", "workapp_entries", "workapp_entry", "curwork", "defwork",
+  "win_list", "win_entries", "win_entry", "occupy_list", "occupy_entries",
+  "occupy_entry", "$@74", "$@75", "$@76", "occupy_workspc_list",
   "occupy_workspc_entries", "occupy_workspc_entry", "occupy_window_list",
   "occupy_window_entries", "occupy_window_entry", "icon_list",
   "icon_entries", "icon_entry", "rplay_sounds_list",
@@ -691,365 +932,405 @@ static const char *const yytname[] =
   "function_entries", "function_entry", "menu", "menu_entries",
   "menu_entry", "action", "signed_number", "button", "string", "number", YY_NULLPTR
 };
-#endif
 
-# ifdef YYPRINT
-/* YYTOKNUM[NUM] -- (External) token number corresponding to the
-   (internal) symbol number NUM (which must be that of a token).  */
-static const yytype_uint16 yytoknum[] =
+static const char *
+yysymbol_name (yysymbol_kind_t yysymbol)
 {
-       0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
-     265,   266,   267,   268,   269,   270,   271,   272,   273,   274,
-     275,   276,   277,   278,   279,   280,   281,   282,   283,   284,
-     285,   286,   287,   288,   289,   290,   291,   292,   293,   294,
-     295,   296,   297,   298,   299,   300,   301,   302,   303,   304,
-     305,   306,   307,   308,   309,   310,   311,   312,   313,   314,
-     315,   316,   317,   318,   319,   320,   321,   322,   323,   324,
-     325,   326,   327,   328,   329,   330,   331,   332,   333,   334,
-     335,   336,   337,   338,   339,   340,   341,   342,   343,   344,
-     345,   346,   347,   348,   349,   350,   351,   352,   353,   354,
-     355,   356,   357,   358,   359,   360,   361,   362,   363,   364,
-     365,   366,   367,   368,   369,   370,   371,   372
-};
-# endif
+  return yytname[yysymbol];
+}
+#endif
 
-#define YYPACT_NINF -242
+#define YYPACT_NINF (-244)
 
-#define yypact_value_is_default(Yystate) \
-  (!!((Yystate) == (-242)))
+#define yypact_value_is_default(Yyn) \
+  ((Yyn) == YYPACT_NINF)
 
-#define YYTABLE_NINF -320
+#define YYTABLE_NINF (-326)
 
-#define yytable_value_is_error(Yytable_value) \
+#define yytable_value_is_error(Yyn) \
   0
 
-  /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
-     STATE-NUM.  */
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+   STATE-NUM.  */
 static const yytype_int16 yypact[] =
 {
-    -242,    34,  -242,  -242,   593,  -242,  -109,    -3,   -13,    62,
-      67,  -242,  -242,  -242,  -242,  -109,  -242,   -13,    -3,  -242,
-    -109,    68,  -242,    72,  -242,    76,  -242,    78,    80,    82,
-      86,  -109,  -109,    87,    91,  -242,  -242,    93,  -109,  -109,
-    -242,    -3,    89,    90,  -109,  -109,    95,  -242,   107,   119,
-    -242,  -242,  -109,  -242,  -242,  -242,  -242,    44,  -242,   124,
-     127,   135,   142,  -242,   -13,   -13,   -13,  -242,  -242,  -242,
-     150,  -242,  -109,  -242,  -242,   155,  -242,  -242,  -242,  -242,
-    -242,   156,  -242,  -242,  -242,  -242,  -242,  -242,     3,   110,
-      79,  -242,  -242,  -242,  -109,  -242,  -242,  -242,  -242,  -242,
-     159,   160,   161,   160,  -242,   162,  -242,  -242,   163,    -3,
-     162,   162,   162,   162,   162,   162,   162,   162,   162,   162,
-      88,    88,   164,   162,   162,   162,   162,   117,   121,  -242,
-    -242,   162,  -242,   162,  -242,  -109,   162,   162,   162,   162,
-     162,   167,    -3,   162,   170,   171,   172,    -3,    -3,    48,
-     174,  -242,   162,   162,   162,   162,   162,   162,  -242,  -242,
-    -242,   162,   162,   162,   162,   162,  -109,   175,   176,   162,
-     177,   162,   178,   179,   180,   162,  -242,  -242,  -242,  -242,
-    -242,  -242,  -109,   191,  -242,   401,    12,  -242,  -242,  -242,
-    -242,  -242,  -242,  -242,   192,  -242,  -242,  -242,  -242,  -242,
-    -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,
-    -242,   115,   115,  -242,  -242,  -242,  -242,  -242,   -13,   198,
-     -13,   198,   162,  -242,   162,  -242,  -242,  -242,  -242,  -242,
-    -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -109,  -242,
-    -109,  -242,  -242,  -242,   200,   162,  -242,  -242,  -242,  -242,
-    -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,
-    -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,
-    -242,  -242,   209,   372,   158,  -242,  -242,  -242,  -109,  -109,
-    -109,  -109,  -109,  -109,  -109,  -109,  -109,  -109,  -109,  -242,
-    -242,  -109,  -242,     8,    24,     7,  -242,  -242,     9,    11,
-    -242,    -3,  -242,    14,  -242,  -242,  -242,  -242,  -242,  -242,
-     162,    15,     6,    16,    17,   162,  -242,   162,    73,    18,
-      19,    20,    23,    25,  -242,    -3,    -3,  -242,  -242,  -242,
-    -242,  -242,  -242,  -109,    26,  -109,  -109,  -109,  -109,  -109,
-    -109,  -109,  -109,  -109,  -109,  -109,  -242,  -242,  -242,  -109,
-    -242,  -109,  -109,  -242,  -242,  -242,  -242,  -242,    41,  -242,
-    -242,  -242,  -242,  -242,  -109,    -3,   162,  -242,   133,    43,
-    -242,  -242,  -242,   203,  -242,  -109,  -109,  -242,  -242,  -242,
-      27,  -242,    31,  -242,  -242,   205,  -242,  -242,  -109,  -242,
-    -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,
-    -109,  -242,  -242,   354,    54,   210,  -242,  -242,    81,  -242,
-    -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,
-    -242,  -242,   215,  -242,  -242,  -242,   -12,     1,  -242,    48,
-    -242,  -242,    13,   216,  -242,  -242,   217,  -242,    32,  -242,
-      33,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,
-    -242,  -242,  -242,  -242,   -13,  -242,  -242,  -242,  -242,  -242,
-    -242,  -242,  -242,  -242,  -242,  -242,  -242,   -13,  -242,  -242,
-    -242,  -242,  -109,  -242,   218,    -3,  -242,   162,     2,    -3,
-     -13,   -13,  -242,  -242,  -242,   217,   219,  -242,  -242,  -242,
-     220,  -242,   221,  -242,  -242,   191,   190,  -242,  -242,  -242,
-    -242,   162,     4,  -242,  -242,   443,  -242,    37,  -242,  -242,
-    -242,    38,  -242,  -242,  -242,  -109,    39,  -242,   162,   238,
-     -13,  -242,  -242,  -109,    40,  -242,  -242,  -242,   236,  -242,
-    -242,  -109,  -242,   162,  -242,  -109,  -242,  -242,  -242,   -13,
-    -242,  -242,  -109,  -242,  -109,  -242
+    -244,    62,  -244,  -244,   611,  -244,   -53,    21,   -10,    67,
+      68,  -244,  -244,  -244,  -244,   -53,  -244,   -10,    21,  -244,
+     -53,    69,  -244,    73,  -244,    74,  -244,    79,    81,    85,
+      87,   -53,   -53,    89,    90,  -244,  -244,    94,   -53,   -53,
+    -244,    21,    82,    91,   -53,   -53,   104,  -244,   108,   111,
+    -244,  -244,   -53,  -244,  -244,  -244,  -244,    41,  -244,   112,
+     114,   115,   118,  -244,   -10,   -10,   -10,  -244,  -244,  -244,
+     125,  -244,   -53,  -244,  -244,   128,  -244,  -244,  -244,  -244,
+    -244,  -244,   136,  -244,  -244,  -244,  -244,  -244,  -244,     4,
+      95,   149,  -244,  -244,  -244,   -53,  -244,  -244,  -244,  -244,
+    -244,   163,   165,   166,   165,  -244,   167,  -244,  -244,   171,
+      21,   167,   167,   167,   167,   167,   167,   167,   167,   167,
+     167,    78,    78,   172,   167,   167,   167,   167,   121,   131,
+    -244,  -244,   167,  -244,   167,  -244,   -53,   167,   167,   167,
+     167,   167,   173,    21,   167,   187,   188,   189,    21,    21,
+      45,   205,  -244,   167,   167,   167,   167,   167,   167,  -244,
+    -244,  -244,   167,   167,   167,   167,   167,   -53,   218,   219,
+     167,   220,   167,   222,   223,   224,   225,   167,  -244,  -244,
+    -244,  -244,  -244,  -244,   -53,   226,  -244,   156,    30,  -244,
+    -244,  -244,  -244,  -244,  -244,  -244,   228,  -244,  -244,  -244,
+    -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,
+    -244,  -244,  -244,   151,   151,  -244,  -244,  -244,  -244,  -244,
+     -10,   230,   -10,   230,   167,  -244,   167,  -244,  -244,  -244,
+    -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,
+     -53,  -244,   -53,  -244,  -244,  -244,   232,   167,  -244,  -244,
+    -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,
+    -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,
+    -244,  -244,  -244,  -244,  -244,  -244,   157,   169,   159,  -244,
+    -244,  -244,   -53,   -53,   -53,   -53,   -53,   -53,   -53,   -53,
+     -53,   -53,   -53,  -244,  -244,   -53,  -244,     7,    12,    -2,
+    -244,  -244,     8,     9,  -244,    21,  -244,    11,  -244,  -244,
+    -244,  -244,  -244,  -244,   167,    14,     6,    15,    16,   167,
+    -244,   167,   153,    17,    18,    19,    20,    23,    25,  -244,
+      21,    21,  -244,  -244,  -244,  -244,  -244,  -244,   -53,    26,
+     -53,   -53,   -53,   -53,   -53,   -53,   -53,   -53,   -53,   -53,
+     -53,  -244,  -244,  -244,   -53,  -244,   -53,   -53,  -244,  -244,
+    -244,  -244,  -244,    24,  -244,  -244,  -244,  -244,  -244,   -53,
+      21,   167,  -244,   155,    50,  -244,  -244,  -244,   235,  -244,
+     -53,   -53,  -244,  -244,  -244,    27,  -244,    31,  -244,  -244,
+     236,  -244,  -244,   -53,  -244,  -244,  -244,  -244,  -244,  -244,
+    -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,   -53,  -244,
+    -244,   139,    55,   233,  -244,  -244,    42,  -244,  -244,  -244,
+    -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,
+     238,  -244,  -244,  -244,   -20,     1,  -244,    45,  -244,  -244,
+      13,   239,  -244,  -244,   240,  -244,    32,  -244,    33,  -244,
+    -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,
+    -244,  -244,   -10,  -244,  -244,  -244,  -244,  -244,  -244,  -244,
+    -244,  -244,  -244,  -244,  -244,   -10,  -244,  -244,  -244,  -244,
+     -53,  -244,   241,    21,  -244,   167,     2,    21,   -10,   -10,
+    -244,  -244,  -244,   240,   242,  -244,  -244,  -244,   247,  -244,
+     248,  -244,  -244,   226,   196,  -244,  -244,  -244,  -244,   167,
+       5,  -244,  -244,   206,  -244,    37,  -244,  -244,  -244,    38,
+    -244,  -244,  -244,   -53,    39,  -244,   167,   250,   -10,  -244,
+    -244,   -53,    40,  -244,  -244,  -244,   249,  -244,  -244,   -53,
+    -244,   167,  -244,   -53,  -244,  -244,  -244,   -10,  -244,  -244,
+     -53,  -244,   -53,  -244
 };
 
-  /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
-     Performed when YYTABLE does not specify something else to do.  Zero
-     means the default is an error.  */
-static const yytype_uint16 yydefact[] =
+/* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
+   Performed when YYTABLE does not specify something else to do.  Zero
+   means the default is an error.  */
+static const yytype_int16 yydefact[] =
 {
        2,     0,     4,     1,     0,     6,     0,     0,     0,     0,
        0,   153,   155,   157,   159,     0,    93,     0,    36,    91,
        0,    90,   135,    41,    64,   122,   120,   129,   132,   141,
-     100,     0,     0,   302,   305,   142,   137,    97,     0,     0,
-     187,     0,   109,   111,   189,   192,   171,   172,   168,   119,
+     100,     0,     0,   308,   311,   142,   137,    97,     0,     0,
+     189,     0,   109,   111,   191,   194,   171,   172,   168,   119,
      107,    66,     0,    71,    75,    31,    33,     0,    77,    44,
       47,    50,    53,    86,     0,     0,     0,    79,    81,   125,
      146,    73,     0,    68,   174,    83,   176,   133,   178,   180,
-     182,   184,   381,     5,     7,     8,     9,    10,     0,     0,
-     149,   382,   380,   375,     0,   161,   246,    38,   242,    37,
-       0,     0,     0,     0,   151,     0,   162,    35,     0,    28,
-       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
-       0,     0,     0,     0,     0,     0,     0,    56,    58,   193,
-     113,     0,   115,     0,   188,   191,     0,     0,     0,     0,
-       0,     0,    30,     0,     0,     0,     0,     0,     0,     0,
-     101,   377,     0,     0,     0,     0,     0,     0,   163,   164,
-     165,     0,     0,     0,     0,     0,     0,     0,     0,     0,
-       0,     0,     0,     0,     0,     0,   197,    63,    61,    60,
-     197,    62,     0,     0,   376,     0,     0,   359,   154,   271,
-     156,   278,   158,   160,     0,   338,    94,   311,    92,    27,
-      89,   136,    40,    65,   124,   121,   128,   131,   140,    99,
-     206,     0,     0,   308,   307,   143,   138,    96,     0,     0,
-       0,     0,     0,   110,     0,   112,   190,   170,   173,   167,
-     118,   108,   316,    67,    29,    72,   342,    76,     0,    32,
-       0,    34,   378,   379,   102,     0,    78,    43,    46,    49,
-      52,    87,    80,    82,   126,   145,    74,    25,   197,   287,
-     175,    85,   291,   177,   134,   295,   179,   299,   181,   363,
-     183,   186,     0,     0,     0,   371,   150,   245,     0,     0,
-       0,     0,     0,     0,     0,     0,     0,     0,     0,   247,
-     241,     0,   243,     0,     0,     0,   367,   152,     0,     0,
-     207,     0,    23,     0,    54,   236,    57,    55,    59,   114,
-       0,     0,     0,     0,     0,     0,   104,     0,    69,     0,
-       0,     0,     0,     0,   205,     0,   199,   200,   201,   202,
-     208,   198,   221,     0,     0,   265,   257,   255,   251,   253,
-     249,   259,   261,   263,   267,   269,   244,   358,   360,     0,
-     270,     0,     0,   272,   277,   281,   279,   280,     0,   337,
-     339,   340,   310,   312,     0,     0,     0,   304,     0,     0,
-     116,   315,   317,   318,   341,     0,     0,   343,   344,   329,
-       0,   333,     0,   106,    26,     0,   286,   288,     0,   290,
-     292,   293,   294,   296,   297,   298,   300,   301,   362,   364,
-       0,   203,   204,     0,     0,     0,   370,   372,     0,   264,
-     256,   254,   250,   252,   248,   258,   260,   262,   266,   268,
-     361,   276,   273,   366,   368,   369,     0,    11,    24,     0,
-     235,   237,     0,     0,   346,   348,     0,   330,     0,   334,
-       0,    70,   289,   365,   219,   220,   216,   218,   217,   210,
-     211,   212,   213,   214,     0,   215,   209,   232,   233,   229,
-     231,   230,   223,   224,   225,   226,   227,     0,   228,   222,
-     234,   147,     0,   373,     0,     0,   313,     0,    12,     0,
-       0,   197,   238,   322,   320,     0,     0,   351,   345,   331,
-       0,   335,     0,   195,   196,     0,     0,   283,   275,   314,
-      16,     0,    13,   309,   240,     0,   239,     0,   347,   355,
-     349,     0,   332,   336,   148,     0,     0,    18,     0,    14,
-       0,   321,   323,   324,     0,   350,   352,   353,     0,   282,
-     284,     0,    20,     0,   194,   325,   354,   356,   357,     0,
-     285,    22,   326,   374,   327,   328
+     182,   184,   186,   387,     5,     7,     8,     9,    10,     0,
+       0,   149,   388,   386,   381,     0,   161,   248,    38,   244,
+      37,     0,     0,     0,     0,   151,     0,   162,    35,     0,
+      28,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,    56,    58,
+     195,   113,     0,   115,     0,   190,   193,     0,     0,     0,
+       0,     0,     0,    30,     0,     0,     0,     0,     0,     0,
+       0,   101,   383,     0,     0,     0,     0,     0,     0,   163,
+     164,   165,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,   199,    63,
+      61,    60,   199,    62,     0,     0,   382,     0,     0,   365,
+     154,   273,   156,   280,   158,   160,     0,   344,    94,   317,
+      92,    27,    89,   136,    40,    65,   124,   121,   128,   131,
+     140,    99,   208,     0,     0,   314,   313,   143,   138,    96,
+       0,     0,     0,     0,     0,   110,     0,   112,   192,   170,
+     173,   167,   118,   108,   322,    67,    29,    72,   348,    76,
+       0,    32,     0,    34,   384,   385,   102,     0,    78,    43,
+      46,    49,    52,    87,    80,    82,   126,   145,    74,    25,
+     199,   289,   175,    85,   293,   177,   134,   297,   179,   301,
+     181,   305,   183,   369,   185,   188,     0,     0,     0,   377,
+     150,   247,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,   249,   243,     0,   245,     0,     0,     0,
+     373,   152,     0,     0,   209,     0,    23,     0,    54,   238,
+      57,    55,    59,   114,     0,     0,     0,     0,     0,     0,
+     104,     0,    69,     0,     0,     0,     0,     0,     0,   207,
+       0,   201,   202,   203,   204,   210,   200,   223,     0,     0,
+     267,   259,   257,   253,   255,   251,   261,   263,   265,   269,
+     271,   246,   364,   366,     0,   272,     0,     0,   274,   279,
+     283,   281,   282,     0,   343,   345,   346,   316,   318,     0,
+       0,     0,   310,     0,     0,   116,   321,   323,   324,   347,
+       0,     0,   349,   350,   335,     0,   339,     0,   106,    26,
+       0,   288,   290,     0,   292,   294,   295,   296,   298,   299,
+     300,   302,   303,   304,   306,   307,   368,   370,     0,   205,
+     206,     0,     0,     0,   376,   378,     0,   266,   258,   256,
+     252,   254,   250,   260,   262,   264,   268,   270,   367,   278,
+     275,   372,   374,   375,     0,    11,    24,     0,   237,   239,
+       0,     0,   352,   354,     0,   336,     0,   340,     0,    70,
+     291,   371,   221,   222,   218,   220,   219,   212,   213,   214,
+     215,   216,     0,   217,   211,   234,   235,   231,   233,   232,
+     225,   226,   227,   228,   229,     0,   230,   224,   236,   147,
+       0,   379,     0,     0,   319,     0,    12,     0,     0,   199,
+     240,   328,   326,     0,     0,   357,   351,   337,     0,   341,
+       0,   197,   198,     0,     0,   285,   277,   320,    16,     0,
+      13,   315,   242,     0,   241,     0,   353,   361,   355,     0,
+     338,   342,   148,     0,     0,    18,     0,    14,     0,   327,
+     329,   330,     0,   356,   358,   359,     0,   284,   286,     0,
+      20,     0,   196,   331,   360,   362,   363,     0,   287,    22,
+     332,   380,   333,   334
 };
 
-  /* YYPGOTO[NTERM-NUM].  */
+/* YYPGOTO[NTERM-NUM].  */
 static const yytype_int16 yypgoto[] =
 {
-    -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,
-    -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,
-    -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,
-    -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,
-    -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,
-    -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,
-    -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,
-    -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -178,  -242,
-     123,    35,  -242,  -242,  -242,  -242,    36,  -242,  -242,  -242,
-    -242,  -242,  -242,  -242,  -242,   143,  -242,  -242,  -242,  -242,
-    -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,
-    -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,
-    -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,  -242,
-    -242,  -242,  -242,    74,  -242,  -242,  -242,  -242,  -242,  -242,
-    -242,  -242,  -237,  -242,  -242,  -242,  -242,  -242,  -242,  -242,
-    -242,  -242,  -242,  -242,  -242,  -242,  -242,  -241,  -242,  -242,
-     -16,  -146,  -114,    -6,    -1
+    -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,
+    -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,
+    -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,
+    -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,
+    -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,
+    -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,
+    -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,
+    -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -175,
+    -244,   132,    43,  -244,  -244,  -244,  -244,    35,  -244,  -244,
+    -244,  -244,  -244,  -244,  -244,  -244,   158,  -244,  -244,  -244,
+    -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,
+    -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,
+    -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,
+    -244,  -244,  -244,  -244,  -244,  -244,  -244,   436,  -244,  -244,
+    -244,  -244,  -244,  -244,  -244,  -244,  -234,  -244,  -244,  -244,
+    -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,  -244,
+    -244,  -243,  -244,  -244,   -16,  -147,  -113,    -6,    -1
 };
 
-  /* YYDEFGOTO[NTERM-NUM].  */
+/* YYDEFGOTO[NTERM-NUM].  */
 static const yytype_int16 yydefgoto[] =
 {
-      -1,     1,     2,     4,    83,   477,   501,   518,   533,   366,
-     317,   145,   146,   112,   153,   154,   155,   156,   219,   221,
-     113,   141,   167,   385,   143,   165,   144,   152,   161,   162,
-     169,   157,   110,   108,   105,   126,   119,   245,   315,   140,
-     131,   133,   222,   224,   139,   115,   114,   163,   116,   117,
-     171,   111,   125,   118,   124,   164,   495,   183,   194,   100,
-     101,   102,   103,   138,   136,   137,   168,   170,   172,   173,
-     174,   175,    84,    85,    86,   482,   177,   181,   272,   331,
-     211,   301,   403,   456,   404,   469,   306,   369,   431,    99,
-     186,   292,    97,   185,   289,   190,   294,   353,   474,   192,
-     295,   356,   498,   516,   530,   260,   319,   387,   263,   320,
-     390,   266,   321,   393,   268,   322,   396,    87,   122,   123,
-     303,   198,   299,   363,   233,   311,   372,   433,   484,   507,
-     522,   239,   241,   196,   298,   360,   237,   312,   377,   436,
-     485,   486,   488,   511,   526,   510,   524,   537,   188,   293,
-     348,   270,   323,   399,   297,   358,   424,   276,   334,   407,
-      95,   150,    88,    89,   151
+       0,     1,     2,     4,    84,   485,   509,   526,   541,   371,
+     321,   146,   147,   113,   154,   155,   156,   157,   221,   223,
+     114,   142,   168,   390,   144,   166,   145,   153,   162,   163,
+     170,   158,   111,   109,   106,   127,   120,   247,   319,   141,
+     132,   134,   224,   226,   140,   116,   115,   164,   117,   118,
+     172,   112,   126,   119,   125,   165,   503,   185,   196,   101,
+     102,   103,   104,   139,   137,   138,   169,   171,   173,   174,
+     175,   176,   177,    85,    86,    87,   490,   179,   183,   276,
+     336,   213,   305,   411,   464,   412,   477,   310,   374,   439,
+     100,   188,   296,    98,   187,   293,   192,   298,   358,   482,
+     194,   299,   361,   506,   524,   538,   262,   323,   392,   265,
+     324,   395,   268,   325,   398,   270,   326,   401,   272,   327,
+     404,    88,   123,   124,   307,   200,   303,   368,   235,   315,
+     377,   441,   492,   515,   530,   241,   243,   198,   302,   365,
+     239,   316,   382,   444,   493,   494,   496,   519,   534,   518,
+     532,   545,   190,   297,   353,   274,   328,   407,   301,   363,
+     432,   280,   339,   415,    96,   151,    89,    90,   152
 };
 
-  /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM.  If
-     positive, shift that token.  If negative, reduce the rule whose
-     number is the opposite.  If YYTABLE_NINF, syntax error.  */
+/* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM.  If
+   positive, shift that token.  If negative, reduce the rule whose
+   number is the opposite.  If YYTABLE_NINF, syntax error.  */
 static const yytype_int16 yytable[] =
 {
-      90,   106,   273,   244,   -15,   -17,    92,   -19,    82,   104,
-     374,   354,   347,   359,   109,   362,   290,   107,   367,   371,
-     379,   381,   386,   389,   392,   120,   121,   395,   350,   398,
-     406,   437,   127,   128,     3,   439,   489,   491,   134,   135,
-     129,   521,   525,   529,   536,   423,   142,   430,   158,   159,
-     160,   375,     7,    91,   176,   147,   148,    93,    94,   147,
-     148,   149,    91,   480,   481,    96,   166,   457,   458,   291,
-      98,   -88,   178,    93,    94,   -39,   355,   459,   460,  -123,
-     318,  -127,   179,  -130,   182,  -139,   472,   324,   184,   -98,
-    -303,   376,   351,   352,  -306,   461,   -95,   325,  -169,   462,
-     463,   464,   465,   466,   467,    82,   130,   132,   199,    91,
-    -166,    93,    94,    91,   326,   327,   328,   329,    82,    82,
-      82,    82,  -117,    82,    82,    82,    82,   -42,    82,   226,
-     -45,    82,    82,    82,    82,    82,    82,    82,   -48,   468,
-      82,   234,    82,    82,    82,   -51,   242,   243,    82,    82,
-      82,    93,    94,  -144,    82,    82,    82,    82,   -84,  -185,
-     257,   180,   187,   189,   191,   195,   197,   213,   218,   210,
-     232,    82,   220,   236,   238,   240,   274,  -103,   258,   259,
-     262,   265,   267,   269,   200,   201,   202,   203,   204,   205,
-     206,   207,   208,   209,   275,   296,   300,   214,   215,   216,
-     217,   305,   304,  -105,   307,   223,  -319,   225,   333,   441,
-     227,   228,   229,   230,   231,   429,   471,   235,  -274,   483,
-     487,   497,   509,   324,   512,   513,   246,   247,   248,   249,
-     250,   251,   313,   325,   314,   252,   253,   254,   255,   256,
-     515,   -21,   539,   261,   212,   264,   193,   302,   508,   271,
-     326,   327,   328,   329,   514,   432,     0,   308,     0,   330,
+      91,   107,   359,   246,   -15,   -17,    93,   277,   -19,   105,
+     379,   352,   364,   367,   110,   372,   355,   108,   376,   384,
+     386,   391,   394,   397,   400,   121,   122,   403,   431,   406,
+     414,   445,   128,   129,   294,   447,   497,   499,   135,   136,
+     130,   529,   533,   537,   544,    92,   143,   480,   159,   160,
+     161,   380,   148,   149,   438,   178,   148,   149,   150,     7,
+      94,    95,     3,   488,   489,    83,   167,   360,   465,   466,
+      97,    99,   -88,   180,    94,    95,   -39,  -123,   467,   468,
+     356,   357,  -127,   181,  -130,   322,    92,   295,  -139,   186,
+     -98,   381,  -309,  -312,    94,    95,   469,   -95,    83,   131,
+     470,   471,   472,   473,   474,   475,    92,  -169,   133,   201,
+      92,  -166,    94,    95,  -117,   -42,    83,   -45,   -48,    83,
+      83,   -51,    83,    83,    83,    83,    83,    83,  -144,    83,
+     228,   -84,    83,    83,    83,    83,    83,    83,    83,  -187,
+     476,    83,   236,    83,    83,    83,   182,   244,   245,    83,
+      83,    83,   452,   453,   184,    83,    83,    83,    83,   212,
+     281,   259,   454,   455,   282,   283,   189,   329,   191,   193,
+     197,   329,   220,    83,   199,   215,   234,   330,   278,   284,
+     456,   330,   222,   329,   457,   458,   459,   460,   461,   462,
+     238,   240,   242,   330,   331,   332,   333,   334,   331,   332,
+     333,   334,   285,   286,   308,   287,   311,   335,  -103,   338,
+     331,   332,   333,   334,   288,   289,   290,   291,   292,   337,
+     329,   260,   261,   264,   463,   267,   269,   271,   273,   279,
+     330,   300,   304,   309,   317,  -105,   318,   437,  -325,   479,
+     449,  -276,   491,   495,   505,   517,   523,   331,   332,   333,
+     334,   520,   521,   -21,   214,   547,   528,   306,   312,   516,
+     522,   440,   195,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,   340,   341,   342,   343,
+     344,   345,   346,   347,   348,   349,   350,     0,     0,   351,
+     487,   354,     0,   362,     0,     0,   366,   369,     0,     0,
+       0,   373,     0,     0,   370,     0,     0,     0,     0,   378,
+     383,   385,   387,     0,   513,     0,     0,   393,   396,   399,
+     402,   405,   408,     0,     0,     0,     0,     0,     0,   409,
+     410,     0,   413,   416,   417,   418,   419,   420,   421,   422,
+     423,   424,   425,   426,   427,     0,     0,   433,   428,     0,
+     429,   430,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,   434,     0,     0,     0,     0,     0,   435,
+       0,     0,     0,     0,   442,   443,     0,     0,     0,   446,
+       0,   448,     0,     0,     0,     0,     0,   450,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+     481,     0,   451,     0,     0,     0,   478,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,   483,   486,
+       0,     0,     0,   484,     0,     0,     0,     0,     0,     0,
+     498,     0,   500,     0,     0,     0,   501,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,   502,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,   512,   514,   504,     0,     0,     0,     0,     0,
+     510,     0,   507,     0,     0,     0,   511,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,   527,     0,     0,     0,     0,   531,
+       0,     0,   542,   535,     0,     0,     0,   536,   539,     0,
+       0,     0,     0,     0,     0,   543,   546,     0,     0,     0,
+       0,   551,     0,   548,     0,     0,     0,   550,     0,     0,
+       0,     0,     0,     0,   552,     0,   553,   202,   203,   204,
+     205,   206,   207,   208,   209,   210,   211,     0,     0,     0,
+     216,   217,   218,   219,     0,     0,     0,     0,   225,     0,
+     227,     0,     0,   229,   230,   231,   232,   233,     0,     0,
+     237,     0,     0,     0,     0,     0,     0,     0,     0,   248,
+     249,   250,   251,   252,   253,     0,     0,     0,   254,   255,
+     256,   257,   258,     0,     0,     0,   263,     0,   266,     0,
+       0,    -3,     5,   275,     0,     0,     0,     0,     0,     6,
+       7,     8,     0,     0,     0,     0,     9,    10,    11,    12,
+      13,    14,    15,    16,     0,     0,    17,    18,    19,    20,
+      21,    22,    23,    24,    25,    26,    27,    28,    29,    30,
+      31,    32,     0,     0,     0,     0,     0,     0,     0,     0,
+     313,     0,   314,    33,    34,    35,    36,    37,     0,     0,
+       0,     0,     0,     0,    38,    39,     0,    40,    41,     0,
+       0,     0,     0,   320,    42,    43,    44,    45,    46,    47,
+      48,     0,     0,     0,    49,    50,     0,    51,    52,    53,
+      54,    55,    56,    57,    58,    59,    60,    61,    62,    63,
+      64,    65,    66,    67,    68,    69,    70,    71,    72,    73,
+      74,    75,    76,    77,    78,    79,    80,    81,    82,    83,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+     375,     0,     0,     0,     0,   388,     0,   389,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,   436,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
        0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
-       0,     0,   335,   336,   337,   338,   339,   340,   341,   342,
-     343,   344,   345,   479,     0,   346,     0,   349,     0,   357,
-       0,     0,   361,   364,     0,     0,   309,   368,   310,     0,
-     365,     0,     0,   505,     0,   373,   378,   380,   382,     0,
-       0,     0,     0,   388,   391,   394,   397,   400,     0,   316,
-       0,     0,     0,     0,   401,   402,     0,   405,   408,   409,
-     410,   411,   412,   413,   414,   415,   416,   417,   418,   419,
-       0,     0,   425,   420,     0,   421,   422,     0,     0,     0,
-       0,     0,     0,     0,     0,     0,     0,     0,   426,     0,
-       0,     0,     0,     0,   427,     0,     0,   444,   445,   434,
-     435,     0,     0,     0,   438,     0,   440,   446,   447,     0,
-       0,     0,   442,     0,   370,     0,   324,     0,     0,   383,
-       0,   384,   473,     0,   443,   448,   325,     0,   470,   449,
-     450,   451,   452,   453,   454,   277,     0,     0,     0,   278,
-     279,     0,     0,   326,   327,   328,   329,     0,     0,     0,
-     475,   478,   332,     0,   280,   476,     0,     0,     0,     0,
-       0,     0,   490,     0,   492,     0,     0,     0,   493,   455,
-     428,     0,     0,     0,     0,     0,     0,   281,   282,     0,
-     283,   494,     0,     0,     0,     0,     0,   324,     0,   284,
-     285,   286,   287,   288,   504,   506,   496,   325,     0,     0,
-       0,     0,   502,     0,   499,     0,     0,     0,   503,     0,
-       0,     0,     0,     0,   326,   327,   328,   329,     0,     0,
-       0,     0,     0,   520,     0,     0,   519,     0,     0,     0,
-       0,   523,     0,     0,   534,   527,     0,     0,     0,   528,
-     531,     0,     0,     0,     0,     0,     0,   535,   538,     0,
-       0,     0,     0,   543,     0,   540,     0,     0,     0,   542,
-       0,     0,     0,     0,     0,     0,   544,     0,   545,     0,
        0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
-       0,   500,     0,     0,     0,     0,     0,     0,     0,     0,
        0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
-       0,     0,     0,     0,     0,   517,     0,     0,     0,     0,
        0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
-       0,     0,   532,    -3,     5,     0,     0,     0,     0,     0,
-       0,     6,     7,     8,     0,     0,     0,   541,     9,    10,
-      11,    12,    13,    14,    15,    16,     0,     0,    17,    18,
-      19,    20,    21,    22,    23,    24,    25,    26,    27,    28,
-      29,    30,    31,    32,     0,     0,     0,     0,     0,     0,
-       0,     0,     0,     0,     0,    33,    34,    35,    36,    37,
-       0,     0,     0,     0,     0,     0,    38,    39,     0,    40,
-      41,     0,     0,     0,     0,     0,    42,    43,    44,    45,
-      46,    47,    48,     0,     0,     0,    49,    50,     0,    51,
-      52,    53,    54,    55,    56,    57,    58,    59,    60,    61,
-      62,    63,    64,    65,    66,    67,    68,    69,    70,    71,
-      72,    73,    74,    75,    76,    77,    78,    79,    80,    81,
-      82
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,   508,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,   525,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,   540,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,   549
 };
 
 static const yytype_int16 yycheck[] =
 {
-       6,    17,   180,   149,     3,     3,     7,     3,   117,    15,
+       6,    17,     4,   150,     3,     3,     7,   182,     3,    15,
        4,     4,     4,     4,    20,     4,     4,    18,     4,     4,
        4,     4,     4,     4,     4,    31,    32,     4,     4,     4,
-       4,     4,    38,    39,     0,     4,     4,     4,    44,    45,
-      41,     4,     4,     4,     4,     4,    52,     4,    64,    65,
-      66,    45,     9,    65,    51,    11,    12,    70,    71,    11,
-      12,    17,    65,    50,    51,     3,    72,    13,    14,    57,
-       3,     3,    88,    70,    71,     3,    69,    23,    24,     3,
-     258,     3,    88,     3,     5,     3,     5,    14,    94,     3,
-       3,    85,    68,    69,     3,    41,     3,    24,     3,    45,
-      46,    47,    48,    49,    50,   117,    17,    17,   109,    65,
-       3,    70,    71,    65,    41,    42,    43,    44,   117,   117,
-     117,   117,     3,   117,   117,   117,   117,     3,   117,   135,
-       3,   117,   117,   117,   117,   117,   117,   117,     3,    85,
-     117,   142,   117,   117,   117,     3,   147,   148,   117,   117,
-     117,    70,    71,     3,   117,   117,   117,   117,     3,     3,
-     166,    51,     3,     3,     3,     3,     3,     3,    51,    81,
-       3,   117,    51,     3,     3,     3,   182,     3,     3,     3,
-       3,     3,     3,     3,   110,   111,   112,   113,   114,   115,
-     116,   117,   118,   119,     3,     3,    81,   123,   124,   125,
-     126,     3,   218,     3,   220,   131,     3,   133,    50,     4,
-     136,   137,   138,   139,   140,    82,     6,   143,     3,     3,
-       3,     3,     3,    14,     4,     4,   152,   153,   154,   155,
-     156,   157,   238,    24,   240,   161,   162,   163,   164,   165,
-      50,     3,     6,   169,   121,   171,   103,   212,   485,   175,
-      41,    42,    43,    44,   495,   369,    -1,   221,    -1,    50,
+       4,     4,    38,    39,     4,     4,     4,     4,    44,    45,
+      41,     4,     4,     4,     4,    65,    52,     5,    64,    65,
+      66,    45,    11,    12,     4,    51,    11,    12,    17,     9,
+      70,    71,     0,    50,    51,   118,    72,    69,    13,    14,
+       3,     3,     3,    89,    70,    71,     3,     3,    23,    24,
+      68,    69,     3,    89,     3,   260,    65,    57,     3,    95,
+       3,    85,     3,     3,    70,    71,    41,     3,   118,    17,
+      45,    46,    47,    48,    49,    50,    65,     3,    17,   110,
+      65,     3,    70,    71,     3,     3,   118,     3,     3,   118,
+     118,     3,   118,   118,   118,   118,   118,   118,     3,   118,
+     136,     3,   118,   118,   118,   118,   118,   118,   118,     3,
+      85,   118,   143,   118,   118,   118,    51,   148,   149,   118,
+     118,   118,    13,    14,     5,   118,   118,   118,   118,    81,
+       4,   167,    23,    24,     8,     9,     3,    14,     3,     3,
+       3,    14,    51,   118,     3,     3,     3,    24,   184,    23,
+      41,    24,    51,    14,    45,    46,    47,    48,    49,    50,
+       3,     3,     3,    24,    41,    42,    43,    44,    41,    42,
+      43,    44,    46,    47,   220,    49,   222,    50,     3,    50,
+      41,    42,    43,    44,    58,    59,    60,    61,    62,    50,
+      14,     3,     3,     3,    85,     3,     3,     3,     3,     3,
+      24,     3,    81,     3,   240,     3,   242,    82,     3,     6,
+       4,     3,     3,     3,     3,     3,    50,    41,    42,    43,
+      44,     4,     4,     3,   122,     6,    50,   214,   223,   493,
+     503,   374,   104,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,   282,   283,   284,   285,
+     286,   287,   288,   289,   290,   291,   292,    -1,    -1,   295,
+     437,   297,    -1,   299,    -1,    -1,   302,   303,    -1,    -1,
+      -1,   307,    -1,    -1,   305,    -1,    -1,    -1,    -1,   315,
+     316,   317,   318,    -1,   489,    -1,    -1,   323,   324,   325,
+     326,   327,   328,    -1,    -1,    -1,    -1,    -1,    -1,   330,
+     331,    -1,   338,   339,   340,   341,   342,   343,   344,   345,
+     346,   347,   348,   349,   350,    -1,    -1,   363,   354,    -1,
+     356,   357,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,   369,    -1,    -1,    -1,    -1,    -1,   370,
+      -1,    -1,    -1,    -1,   380,   381,    -1,    -1,    -1,   385,
+      -1,   387,    -1,    -1,    -1,    -1,    -1,   393,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+     416,    -1,   408,    -1,    -1,    -1,   412,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,   434,   435,
+      -1,    -1,    -1,   434,    -1,    -1,    -1,    -1,    -1,    -1,
+     446,    -1,   448,    -1,    -1,    -1,   462,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,   475,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,   488,   489,   480,    -1,    -1,    -1,    -1,    -1,
+     486,    -1,   483,    -1,    -1,    -1,   487,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,   510,    -1,    -1,    -1,    -1,   515,
+      -1,    -1,   528,   519,    -1,    -1,    -1,   523,   524,    -1,
+      -1,    -1,    -1,    -1,    -1,   531,   532,    -1,    -1,    -1,
+      -1,   547,    -1,   539,    -1,    -1,    -1,   543,    -1,    -1,
+      -1,    -1,    -1,    -1,   550,    -1,   552,   111,   112,   113,
+     114,   115,   116,   117,   118,   119,   120,    -1,    -1,    -1,
+     124,   125,   126,   127,    -1,    -1,    -1,    -1,   132,    -1,
+     134,    -1,    -1,   137,   138,   139,   140,   141,    -1,    -1,
+     144,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,   153,
+     154,   155,   156,   157,   158,    -1,    -1,    -1,   162,   163,
+     164,   165,   166,    -1,    -1,    -1,   170,    -1,   172,    -1,
+      -1,     0,     1,   177,    -1,    -1,    -1,    -1,    -1,     8,
+       9,    10,    -1,    -1,    -1,    -1,    15,    16,    17,    18,
+      19,    20,    21,    22,    -1,    -1,    25,    26,    27,    28,
+      29,    30,    31,    32,    33,    34,    35,    36,    37,    38,
+      39,    40,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+     224,    -1,   226,    52,    53,    54,    55,    56,    -1,    -1,
+      -1,    -1,    -1,    -1,    63,    64,    -1,    66,    67,    -1,
+      -1,    -1,    -1,   247,    73,    74,    75,    76,    77,    78,
+      79,    -1,    -1,    -1,    83,    84,    -1,    86,    87,    88,
+      89,    90,    91,    92,    93,    94,    95,    96,    97,    98,
+      99,   100,   101,   102,   103,   104,   105,   106,   107,   108,
+     109,   110,   111,   112,   113,   114,   115,   116,   117,   118,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+     314,    -1,    -1,    -1,    -1,   319,    -1,   321,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
       -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
-      -1,    -1,   278,   279,   280,   281,   282,   283,   284,   285,
-     286,   287,   288,   429,    -1,   291,    -1,   293,    -1,   295,
-      -1,    -1,   298,   299,    -1,    -1,   222,   303,   224,    -1,
-     301,    -1,    -1,   481,    -1,   311,   312,   313,   314,    -1,
-      -1,    -1,    -1,   319,   320,   321,   322,   323,    -1,   245,
-      -1,    -1,    -1,    -1,   325,   326,    -1,   333,   334,   335,
-     336,   337,   338,   339,   340,   341,   342,   343,   344,   345,
-      -1,    -1,   358,   349,    -1,   351,   352,    -1,    -1,    -1,
-      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,   364,    -1,
-      -1,    -1,    -1,    -1,   365,    -1,    -1,    13,    14,   375,
-     376,    -1,    -1,    -1,   380,    -1,   382,    23,    24,    -1,
-      -1,    -1,   388,    -1,   310,    -1,    14,    -1,    -1,   315,
-      -1,   317,   408,    -1,   400,    41,    24,    -1,   404,    45,
-      46,    47,    48,    49,    50,     4,    -1,    -1,    -1,     8,
-       9,    -1,    -1,    41,    42,    43,    44,    -1,    -1,    -1,
-     426,   427,    50,    -1,    23,   426,    -1,    -1,    -1,    -1,
-      -1,    -1,   438,    -1,   440,    -1,    -1,    -1,   454,    85,
-     366,    -1,    -1,    -1,    -1,    -1,    -1,    46,    47,    -1,
-      49,   467,    -1,    -1,    -1,    -1,    -1,    14,    -1,    58,
-      59,    60,    61,    62,   480,   481,   472,    24,    -1,    -1,
-      -1,    -1,   478,    -1,   475,    -1,    -1,    -1,   479,    -1,
-      -1,    -1,    -1,    -1,    41,    42,    43,    44,    -1,    -1,
-      -1,    -1,    -1,    50,    -1,    -1,   502,    -1,    -1,    -1,
-      -1,   507,    -1,    -1,   520,   511,    -1,    -1,    -1,   515,
-     516,    -1,    -1,    -1,    -1,    -1,    -1,   523,   524,    -1,
-      -1,    -1,    -1,   539,    -1,   531,    -1,    -1,    -1,   535,
-      -1,    -1,    -1,    -1,    -1,    -1,   542,    -1,   544,    -1,
       -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
-      -1,   477,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,   371,    -1,    -1,
       -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
-      -1,    -1,    -1,    -1,    -1,   501,    -1,    -1,    -1,    -1,
       -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
-      -1,    -1,   518,     0,     1,    -1,    -1,    -1,    -1,    -1,
-      -1,     8,     9,    10,    -1,    -1,    -1,   533,    15,    16,
-      17,    18,    19,    20,    21,    22,    -1,    -1,    25,    26,
-      27,    28,    29,    30,    31,    32,    33,    34,    35,    36,
-      37,    38,    39,    40,    -1,    -1,    -1,    -1,    -1,    -1,
-      -1,    -1,    -1,    -1,    -1,    52,    53,    54,    55,    56,
-      -1,    -1,    -1,    -1,    -1,    -1,    63,    64,    -1,    66,
-      67,    -1,    -1,    -1,    -1,    -1,    73,    74,    75,    76,
-      77,    78,    79,    -1,    -1,    -1,    83,    84,    -1,    86,
-      87,    88,    89,    90,    91,    92,    93,    94,    95,    96,
-      97,    98,    99,   100,   101,   102,   103,   104,   105,   106,
-     107,   108,   109,   110,   111,   112,   113,   114,   115,   116,
-     117
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,   485,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,   509,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,   526,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,   541
 };
 
-  /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
-     symbol of state STATE-NUM.  */
-static const yytype_uint16 yystos[] =
+/* YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of
+   state STATE-NUM.  */
+static const yytype_int16 yystos[] =
 {
-       0,   119,   120,     0,   121,     1,     8,     9,    10,    15,
+       0,   120,   121,     0,   122,     1,     8,     9,    10,    15,
       16,    17,    18,    19,    20,    21,    22,    25,    26,    27,
       28,    29,    30,    31,    32,    33,    34,    35,    36,    37,
       38,    39,    40,    52,    53,    54,    55,    56,    63,    64,
@@ -1057,101 +1338,102 @@ static const yytype_uint16 yystos[] =
       84,    86,    87,    88,    89,    90,    91,    92,    93,    94,
       95,    96,    97,    98,    99,   100,   101,   102,   103,   104,
      105,   106,   107,   108,   109,   110,   111,   112,   113,   114,
-     115,   116,   117,   122,   190,   191,   192,   235,   280,   281,
-     281,    65,   282,    70,    71,   278,     3,   210,     3,   207,
-     177,   178,   179,   180,   281,   152,   278,   282,   151,   281,
-     150,   169,   131,   138,   164,   163,   166,   167,   171,   154,
-     281,   281,   236,   237,   172,   170,   153,   281,   281,   282,
-      17,   158,    17,   159,   281,   281,   182,   183,   181,   162,
-     157,   139,   281,   142,   144,   129,   130,    11,    12,    17,
-     279,   282,   145,   132,   133,   134,   135,   149,   278,   278,
-     278,   146,   147,   165,   173,   143,   281,   140,   184,   148,
-     185,   168,   186,   187,   188,   189,    51,   194,   278,   281,
-      51,   195,     5,   175,   281,   211,   208,     3,   266,     3,
-     213,     3,   217,   213,   176,     3,   251,     3,   239,   282,
-     251,   251,   251,   251,   251,   251,   251,   251,   251,   251,
-      81,   198,   198,     3,   251,   251,   251,   251,    51,   136,
-      51,   137,   160,   251,   161,   251,   281,   251,   251,   251,
-     251,   251,     3,   242,   282,   251,     3,   254,     3,   249,
-       3,   250,   282,   282,   279,   155,   251,   251,   251,   251,
-     251,   251,   251,   251,   251,   251,   251,   281,     3,     3,
-     223,   251,     3,   226,   251,     3,   229,     3,   232,     3,
-     269,   251,   196,   196,   281,     3,   275,     4,     8,     9,
-      23,    46,    47,    49,    58,    59,    60,    61,    62,   212,
-       4,    57,   209,   267,   214,   218,     3,   272,   252,   240,
-      81,   199,   199,   238,   278,     3,   204,   278,   204,   251,
-     251,   243,   255,   281,   281,   156,   251,   128,   196,   224,
-     227,   230,   233,   270,    14,    24,    41,    42,    43,    44,
-      50,   197,    50,    50,   276,   281,   281,   281,   281,   281,
-     281,   281,   281,   281,   281,   281,   281,     4,   268,   281,
-       4,    68,    69,   215,     4,    69,   219,   281,   273,     4,
-     253,   281,     4,   241,   281,   282,   127,     4,   281,   205,
-     251,     4,   244,   281,     4,    45,    85,   256,   281,     4,
-     281,     4,   281,   251,   251,   141,     4,   225,   281,     4,
-     228,   281,     4,   231,   281,     4,   234,   281,     4,   271,
-     281,   282,   282,   200,   202,   281,     4,   277,   281,   281,
-     281,   281,   281,   281,   281,   281,   281,   281,   281,   281,
-     281,   281,   281,     4,   274,   278,   281,   282,   251,    82,
-       4,   206,   280,   245,   281,   281,   257,     4,   281,     4,
-     281,     4,   281,   281,    13,    14,    23,    24,    41,    45,
-      46,    47,    48,    49,    50,    85,   201,    13,    14,    23,
-      24,    41,    45,    46,    47,    48,    49,    50,    85,   203,
-     281,     6,     5,   278,   216,   281,   282,   123,   281,   279,
-      50,    51,   193,     3,   246,   258,   259,     3,   260,     4,
-     281,     4,   281,   278,   278,   174,   281,     3,   220,   282,
-     251,   124,   281,   282,   278,   196,   278,   247,   260,     3,
-     263,   261,     4,     4,   275,    50,   221,   251,   125,   281,
-      50,     4,   248,   281,   264,     4,   262,   281,   281,     4,
-     222,   281,   251,   126,   278,   281,     4,   265,   281,     6,
-     281,   251,   281,   278,   281,   281
+     115,   116,   117,   118,   123,   192,   193,   194,   240,   285,
+     286,   286,    65,   287,    70,    71,   283,     3,   212,     3,
+     209,   178,   179,   180,   181,   286,   153,   283,   287,   152,
+     286,   151,   170,   132,   139,   165,   164,   167,   168,   172,
+     155,   286,   286,   241,   242,   173,   171,   154,   286,   286,
+     287,    17,   159,    17,   160,   286,   286,   183,   184,   182,
+     163,   158,   140,   286,   143,   145,   130,   131,    11,    12,
+      17,   284,   287,   146,   133,   134,   135,   136,   150,   283,
+     283,   283,   147,   148,   166,   174,   144,   286,   141,   185,
+     149,   186,   169,   187,   188,   189,   190,   191,    51,   196,
+     283,   286,    51,   197,     5,   176,   286,   213,   210,     3,
+     271,     3,   215,     3,   219,   215,   177,     3,   256,     3,
+     244,   287,   256,   256,   256,   256,   256,   256,   256,   256,
+     256,   256,    81,   200,   200,     3,   256,   256,   256,   256,
+      51,   137,    51,   138,   161,   256,   162,   256,   286,   256,
+     256,   256,   256,   256,     3,   247,   287,   256,     3,   259,
+       3,   254,     3,   255,   287,   287,   284,   156,   256,   256,
+     256,   256,   256,   256,   256,   256,   256,   256,   256,   286,
+       3,     3,   225,   256,     3,   228,   256,     3,   231,     3,
+     234,     3,   237,     3,   274,   256,   198,   198,   286,     3,
+     280,     4,     8,     9,    23,    46,    47,    49,    58,    59,
+      60,    61,    62,   214,     4,    57,   211,   272,   216,   220,
+       3,   277,   257,   245,    81,   201,   201,   243,   283,     3,
+     206,   283,   206,   256,   256,   248,   260,   286,   286,   157,
+     256,   129,   198,   226,   229,   232,   235,   238,   275,    14,
+      24,    41,    42,    43,    44,    50,   199,    50,    50,   281,
+     286,   286,   286,   286,   286,   286,   286,   286,   286,   286,
+     286,   286,     4,   273,   286,     4,    68,    69,   217,     4,
+      69,   221,   286,   278,     4,   258,   286,     4,   246,   286,
+     287,   128,     4,   286,   207,   256,     4,   249,   286,     4,
+      45,    85,   261,   286,     4,   286,     4,   286,   256,   256,
+     142,     4,   227,   286,     4,   230,   286,     4,   233,   286,
+       4,   236,   286,     4,   239,   286,     4,   276,   286,   287,
+     287,   202,   204,   286,     4,   282,   286,   286,   286,   286,
+     286,   286,   286,   286,   286,   286,   286,   286,   286,   286,
+     286,     4,   279,   283,   286,   287,   256,    82,     4,   208,
+     285,   250,   286,   286,   262,     4,   286,     4,   286,     4,
+     286,   286,    13,    14,    23,    24,    41,    45,    46,    47,
+      48,    49,    50,    85,   203,    13,    14,    23,    24,    41,
+      45,    46,    47,    48,    49,    50,    85,   205,   286,     6,
+       5,   283,   218,   286,   287,   124,   286,   284,    50,    51,
+     195,     3,   251,   263,   264,     3,   265,     4,   286,     4,
+     286,   283,   283,   175,   286,     3,   222,   287,   256,   125,
+     286,   287,   283,   198,   283,   252,   265,     3,   268,   266,
+       4,     4,   280,    50,   223,   256,   126,   286,    50,     4,
+     253,   286,   269,     4,   267,   286,   286,     4,   224,   286,
+     256,   127,   283,   286,     4,   270,   286,     6,   286,   256,
+     286,   283,   286,   286
 };
 
-  /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
-static const yytype_uint16 yyr1[] =
+/* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM.  */
+static const yytype_int16 yyr1[] =
 {
-       0,   118,   120,   119,   121,   121,   122,   122,   122,   122,
-     122,   122,   122,   122,   122,   123,   122,   124,   122,   125,
-     122,   126,   122,   127,   122,   128,   122,   122,   122,   122,
-     122,   129,   122,   130,   122,   122,   122,   122,   122,   131,
-     122,   122,   132,   122,   122,   133,   122,   122,   134,   122,
-     122,   135,   122,   122,   122,   122,   136,   122,   137,   122,
-     122,   122,   122,   122,   138,   122,   139,   122,   140,   141,
-     122,   142,   122,   143,   122,   144,   122,   145,   122,   146,
-     122,   147,   122,   122,   148,   122,   149,   122,   150,   122,
-     122,   151,   122,   152,   122,   153,   122,   122,   154,   122,
-     122,   122,   122,   155,   122,   156,   122,   157,   122,   158,
-     122,   159,   122,   160,   122,   161,   122,   162,   122,   122,
-     163,   122,   122,   164,   122,   165,   122,   166,   122,   122,
-     167,   122,   122,   168,   122,   169,   122,   170,   122,   171,
-     122,   122,   172,   122,   173,   122,   122,   174,   122,   175,
-     122,   176,   122,   177,   122,   178,   122,   179,   122,   180,
-     122,   122,   122,   122,   122,   122,   181,   122,   122,   182,
-     122,   122,   183,   122,   184,   122,   185,   122,   186,   122,
-     187,   122,   188,   122,   122,   189,   122,   190,   191,   191,
-     191,   191,   191,   192,   193,   194,   195,   196,   196,   197,
-     197,   197,   197,   197,   197,   197,   198,   199,   200,   200,
-     201,   201,   201,   201,   201,   201,   201,   201,   201,   201,
-     201,   202,   202,   203,   203,   203,   203,   203,   203,   203,
-     203,   203,   203,   203,   203,   204,   205,   205,   206,   206,
-     206,   207,   208,   208,   209,   210,   211,   211,   212,   212,
-     212,   212,   212,   212,   212,   212,   212,   212,   212,   212,
-     212,   212,   212,   212,   212,   212,   212,   212,   212,   212,
-     213,   214,   214,   215,   216,   215,   215,   217,   218,   218,
-     219,   219,   220,   221,   221,   222,   223,   224,   224,   225,
-     226,   227,   227,   228,   229,   230,   230,   231,   232,   233,
-     233,   234,   235,   236,   235,   235,   237,   235,   238,   238,
-     239,   240,   240,   241,   241,   242,   243,   243,   244,   245,
-     244,   246,   247,   247,   248,   248,   248,   248,   248,   249,
-     249,   249,   249,   250,   250,   250,   250,   251,   252,   252,
-     253,   254,   255,   255,   257,   256,   258,   256,   259,   256,
-     260,   261,   261,   262,   263,   264,   264,   265,   266,   267,
-     267,   268,   269,   270,   270,   271,   272,   273,   273,   274,
-     275,   276,   276,   277,   277,   278,   278,   279,   279,   279,
-     280,   281,   282
+       0,   119,   121,   120,   122,   122,   123,   123,   123,   123,
+     123,   123,   123,   123,   123,   124,   123,   125,   123,   126,
+     123,   127,   123,   128,   123,   129,   123,   123,   123,   123,
+     123,   130,   123,   131,   123,   123,   123,   123,   123,   132,
+     123,   123,   133,   123,   123,   134,   123,   123,   135,   123,
+     123,   136,   123,   123,   123,   123,   137,   123,   138,   123,
+     123,   123,   123,   123,   139,   123,   140,   123,   141,   142,
+     123,   143,   123,   144,   123,   145,   123,   146,   123,   147,
+     123,   148,   123,   123,   149,   123,   150,   123,   151,   123,
+     123,   152,   123,   153,   123,   154,   123,   123,   155,   123,
+     123,   123,   123,   156,   123,   157,   123,   158,   123,   159,
+     123,   160,   123,   161,   123,   162,   123,   163,   123,   123,
+     164,   123,   123,   165,   123,   166,   123,   167,   123,   123,
+     168,   123,   123,   169,   123,   170,   123,   171,   123,   172,
+     123,   123,   173,   123,   174,   123,   123,   175,   123,   176,
+     123,   177,   123,   178,   123,   179,   123,   180,   123,   181,
+     123,   123,   123,   123,   123,   123,   182,   123,   123,   183,
+     123,   123,   184,   123,   185,   123,   186,   123,   187,   123,
+     188,   123,   189,   123,   190,   123,   123,   191,   123,   192,
+     193,   193,   193,   193,   193,   194,   195,   196,   197,   198,
+     198,   199,   199,   199,   199,   199,   199,   199,   200,   201,
+     202,   202,   203,   203,   203,   203,   203,   203,   203,   203,
+     203,   203,   203,   204,   204,   205,   205,   205,   205,   205,
+     205,   205,   205,   205,   205,   205,   205,   206,   207,   207,
+     208,   208,   208,   209,   210,   210,   211,   212,   213,   213,
+     214,   214,   214,   214,   214,   214,   214,   214,   214,   214,
+     214,   214,   214,   214,   214,   214,   214,   214,   214,   214,
+     214,   214,   215,   216,   216,   217,   218,   217,   217,   219,
+     220,   220,   221,   221,   222,   223,   223,   224,   225,   226,
+     226,   227,   228,   229,   229,   230,   231,   232,   232,   233,
+     234,   235,   235,   236,   237,   238,   238,   239,   240,   241,
+     240,   240,   242,   240,   243,   243,   244,   245,   245,   246,
+     246,   247,   248,   248,   249,   250,   249,   251,   252,   252,
+     253,   253,   253,   253,   253,   254,   254,   254,   254,   255,
+     255,   255,   255,   256,   257,   257,   258,   259,   260,   260,
+     262,   261,   263,   261,   264,   261,   265,   266,   266,   267,
+     268,   269,   269,   270,   271,   272,   272,   273,   274,   275,
+     275,   276,   277,   278,   278,   279,   280,   281,   281,   282,
+     282,   283,   283,   284,   284,   284,   285,   286,   287
 };
 
-  /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN.  */
-static const yytype_uint8 yyr2[] =
+/* YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM.  */
+static const yytype_int8 yyr2[] =
 {
        0,     2,     0,     2,     0,     2,     1,     1,     1,     1,
        1,     6,     7,     8,     9,     0,     8,     0,     9,     0,
@@ -1171,38 +1453,39 @@ static const yytype_uint8 yyr2[] =
        4,     0,     4,     0,     3,     0,     3,     0,     3,     0,
        3,     2,     2,     2,     2,     2,     0,     3,     1,     0,
        3,     1,     0,     3,     0,     3,     0,     3,     0,     3,
-       0,     3,     0,     3,     1,     0,     3,     1,     2,     1,
-       3,     2,     1,     2,     4,     6,     6,     0,     2,     1,
-       1,     1,     1,     2,     2,     1,     1,     1,     0,     2,
-       1,     1,     1,     1,     1,     1,     1,     1,     1,     1,
-       1,     0,     2,     1,     1,     1,     1,     1,     1,     1,
-       1,     1,     1,     1,     1,     3,     0,     2,     2,     3,
-       3,     3,     0,     2,     2,     3,     0,     2,     3,     2,
+       0,     3,     0,     3,     0,     3,     1,     0,     3,     1,
+       2,     1,     3,     2,     1,     2,     4,     6,     6,     0,
+       2,     1,     1,     1,     1,     2,     2,     1,     1,     1,
+       0,     2,     1,     1,     1,     1,     1,     1,     1,     1,
+       1,     1,     1,     0,     2,     1,     1,     1,     1,     1,
+       1,     1,     1,     1,     1,     1,     1,     3,     0,     2,
+       2,     3,     3,     3,     0,     2,     2,     3,     0,     2,
        3,     2,     3,     2,     3,     2,     3,     2,     3,     2,
        3,     2,     3,     2,     3,     2,     3,     2,     3,     2,
-       3,     0,     2,     2,     0,     4,     2,     3,     0,     2,
-       1,     1,     3,     0,     2,     2,     3,     0,     2,     2,
-       3,     0,     2,     1,     3,     0,     2,     1,     3,     0,
-       2,     1,     1,     0,     5,     1,     0,     3,     0,     5,
-       3,     0,     2,     3,     4,     3,     0,     2,     1,     0,
-       3,     3,     0,     2,     1,     2,     3,     4,     5,     3,
-       4,     5,     6,     3,     4,     5,     6,     3,     0,     2,
-       1,     3,     0,     2,     0,     3,     0,     4,     0,     4,
-       3,     0,     2,     1,     3,     0,     2,     1,     3,     0,
-       2,     2,     3,     0,     2,     2,     3,     0,     2,     1,
-       3,     0,     2,     2,     7,     1,     2,     1,     2,     2,
-       2,     1,     1
+       3,     2,     3,     0,     2,     2,     0,     4,     2,     3,
+       0,     2,     1,     1,     3,     0,     2,     2,     3,     0,
+       2,     2,     3,     0,     2,     1,     3,     0,     2,     1,
+       3,     0,     2,     1,     3,     0,     2,     1,     1,     0,
+       5,     1,     0,     3,     0,     5,     3,     0,     2,     3,
+       4,     3,     0,     2,     1,     0,     3,     3,     0,     2,
+       1,     2,     3,     4,     5,     3,     4,     5,     6,     3,
+       4,     5,     6,     3,     0,     2,     1,     3,     0,     2,
+       0,     3,     0,     4,     0,     4,     3,     0,     2,     1,
+       3,     0,     2,     1,     3,     0,     2,     2,     3,     0,
+       2,     2,     3,     0,     2,     1,     3,     0,     2,     2,
+       7,     1,     2,     1,     2,     2,     2,     1,     1
 };
 
 
+enum { YYENOMEM = -2 };
+
 #define yyerrok         (yyerrstatus = 0)
 #define yyclearin       (yychar = YYEMPTY)
-#define YYEMPTY         (-2)
-#define YYEOF           0
 
 #define YYACCEPT        goto yyacceptlab
 #define YYABORT         goto yyabortlab
 #define YYERROR         goto yyerrorlab
+#define YYNOMEM         goto yyexhaustedlab
 
 
 #define YYRECOVERING()  (!!yyerrstatus)
@@ -1224,10 +1507,9 @@ static const yytype_uint8 yyr2[] =
       }                                                           \
   while (0)
 
-/* Error token number */
-#define YYTERROR        1
-#define YYERRCODE       256
-
+/* Backward compatibility with an undocumented macro.
+   Use YYerror or YYUNDEF. */
+#define YYERRCODE YYUNDEF
 
 
 /* Enable debugging if requested.  */
@@ -1244,19 +1526,16 @@ do {                                            \
     YYFPRINTF Args;                             \
 } while (0)
 
-/* This macro is provided for backward compatibility. */
-#ifndef YY_LOCATION_PRINT
-# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
-#endif
 
 
-# define YY_SYMBOL_PRINT(Title, Type, Value, Location)                    \
+
+# define YY_SYMBOL_PRINT(Title, Kind, Value, Location)                    \
 do {                                                                      \
   if (yydebug)                                                            \
     {                                                                     \
       YYFPRINTF (stderr, "%s ", Title);                                   \
       yy_symbol_print (stderr,                                            \
-                  Type, Value); \
+                  Kind, Value); \
       YYFPRINTF (stderr, "\n");                                           \
     }                                                                     \
 } while (0)
@@ -1267,17 +1546,16 @@ do {                                                                      \
 `-----------------------------------*/
 
 static void
-yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep)
+yy_symbol_value_print (FILE *yyo,
+                       yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep)
 {
   FILE *yyoutput = yyo;
-  YYUSE (yyoutput);
+  YY_USE (yyoutput);
   if (!yyvaluep)
     return;
-# ifdef YYPRINT
-  if (yytype < YYNTOKENS)
-    YYPRINT (yyo, yytoknum[yytype], *yyvaluep);
-# endif
-  YYUSE (yytype);
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  YY_USE (yykind);
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
 }
 
 
@@ -1286,12 +1564,13 @@ yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep)
 `---------------------------*/
 
 static void
-yy_symbol_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep)
+yy_symbol_print (FILE *yyo,
+                 yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep)
 {
   YYFPRINTF (yyo, "%s %s (",
-             yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]);
+             yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind));
 
-  yy_symbol_value_print (yyo, yytype, yyvaluep);
+  yy_symbol_value_print (yyo, yykind, yyvaluep);
   YYFPRINTF (yyo, ")");
 }
 
@@ -1301,7 +1580,7 @@ yy_symbol_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep)
 `------------------------------------------------------------------*/
 
 static void
-yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop)
 {
   YYFPRINTF (stderr, "Stack now");
   for (; yybottom <= yytop; yybottom++)
@@ -1324,21 +1603,21 @@ do {                                                            \
 `------------------------------------------------*/
 
 static void
-yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule)
+yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp,
+                 int yyrule)
 {
-  unsigned long yylno = yyrline[yyrule];
+  int yylno = yyrline[yyrule];
   int yynrhs = yyr2[yyrule];
   int yyi;
-  YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n",
              yyrule - 1, yylno);
   /* The symbols being reduced.  */
   for (yyi = 0; yyi < yynrhs; yyi++)
     {
       YYFPRINTF (stderr, "   $%d = ", yyi + 1);
       yy_symbol_print (stderr,
-                       yystos[yyssp[yyi + 1 - yynrhs]],
-                       &yyvsp[(yyi + 1) - (yynrhs)]
-                                              );
+                       YY_ACCESSING_SYMBOL (+yyssp[yyi + 1 - yynrhs]),
+                       &yyvsp[(yyi + 1) - (yynrhs)]);
       YYFPRINTF (stderr, "\n");
     }
 }
@@ -1353,8 +1632,8 @@ do {                                    \
    multiple parsers can coexist.  */
 int yydebug;
 #else /* !YYDEBUG */
-# define YYDPRINTF(Args)
-# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YYDPRINTF(Args) ((void) 0)
+# define YY_SYMBOL_PRINT(Title, Kind, Value, Location)
 # define YY_STACK_PRINT(Bottom, Top)
 # define YY_REDUCE_PRINT(Rule)
 #endif /* !YYDEBUG */
@@ -1377,254 +1656,30 @@ int yydebug;
 #endif
 
 
-#if YYERROR_VERBOSE
-
-# ifndef yystrlen
-#  if defined __GLIBC__ && defined _STRING_H
-#   define yystrlen strlen
-#  else
-/* Return the length of YYSTR.  */
-static YYSIZE_T
-yystrlen (const char *yystr)
-{
-  YYSIZE_T yylen;
-  for (yylen = 0; yystr[yylen]; yylen++)
-    continue;
-  return yylen;
-}
-#  endif
-# endif
-
-# ifndef yystpcpy
-#  if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
-#   define yystpcpy stpcpy
-#  else
-/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
-   YYDEST.  */
-static char *
-yystpcpy (char *yydest, const char *yysrc)
-{
-  char *yyd = yydest;
-  const char *yys = yysrc;
-
-  while ((*yyd++ = *yys++) != '\0')
-    continue;
-
-  return yyd - 1;
-}
-#  endif
-# endif
 
-# ifndef yytnamerr
-/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
-   quotes and backslashes, so that it's suitable for yyerror.  The
-   heuristic is that double-quoting is unnecessary unless the string
-   contains an apostrophe, a comma, or backslash (other than
-   backslash-backslash).  YYSTR is taken from yytname.  If YYRES is
-   null, do not copy; instead, return the length of what the result
-   would have been.  */
-static YYSIZE_T
-yytnamerr (char *yyres, const char *yystr)
-{
-  if (*yystr == '"')
-    {
-      YYSIZE_T yyn = 0;
-      char const *yyp = yystr;
-
-      for (;;)
-        switch (*++yyp)
-          {
-          case '\'':
-          case ',':
-            goto do_not_strip_quotes;
-
-          case '\\':
-            if (*++yyp != '\\')
-              goto do_not_strip_quotes;
-            else
-              goto append;
-
-          append:
-          default:
-            if (yyres)
-              yyres[yyn] = *yyp;
-            yyn++;
-            break;
-
-          case '"':
-            if (yyres)
-              yyres[yyn] = '\0';
-            return yyn;
-          }
-    do_not_strip_quotes: ;
-    }
 
-  if (! yyres)
-    return yystrlen (yystr);
 
-  return (YYSIZE_T) (yystpcpy (yyres, yystr) - yyres);
-}
-# endif
-
-/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
-   about the unexpected token YYTOKEN for the state stack whose top is
-   YYSSP.
-
-   Return 0 if *YYMSG was successfully written.  Return 1 if *YYMSG is
-   not large enough to hold the message.  In that case, also set
-   *YYMSG_ALLOC to the required number of bytes.  Return 2 if the
-   required number of bytes is too large to store.  */
-static int
-yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
-                yytype_int16 *yyssp, int yytoken)
-{
-  YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]);
-  YYSIZE_T yysize = yysize0;
-  enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
-  /* Internationalized format string. */
-  const char *yyformat = YY_NULLPTR;
-  /* Arguments of yyformat. */
-  char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
-  /* Number of reported tokens (one for the "unexpected", one per
-     "expected"). */
-  int yycount = 0;
-
-  /* There are many possibilities here to consider:
-     - If this state is a consistent state with a default action, then
-       the only way this function was invoked is if the default action
-       is an error action.  In that case, don't check for expected
-       tokens because there are none.
-     - The only way there can be no lookahead present (in yychar) is if
-       this state is a consistent state with a default action.  Thus,
-       detecting the absence of a lookahead is sufficient to determine
-       that there is no unexpected or expected token to report.  In that
-       case, just report a simple "syntax error".
-     - Don't assume there isn't a lookahead just because this state is a
-       consistent state with a default action.  There might have been a
-       previous inconsistent state, consistent state with a non-default
-       action, or user semantic action that manipulated yychar.
-     - Of course, the expected token list depends on states to have
-       correct lookahead information, and it depends on the parser not
-       to perform extra reductions after fetching a lookahead from the
-       scanner and before detecting a syntax error.  Thus, state merging
-       (from LALR or IELR) and default reductions corrupt the expected
-       token list.  However, the list is correct for canonical LR with
-       one exception: it will still contain any token that will not be
-       accepted due to an error action in a later state.
-  */
-  if (yytoken != YYEMPTY)
-    {
-      int yyn = yypact[*yyssp];
-      yyarg[yycount++] = yytname[yytoken];
-      if (!yypact_value_is_default (yyn))
-        {
-          /* Start YYX at -YYN if negative to avoid negative indexes in
-             YYCHECK.  In other words, skip the first -YYN actions for
-             this state because they are default actions.  */
-          int yyxbegin = yyn < 0 ? -yyn : 0;
-          /* Stay within bounds of both yycheck and yytname.  */
-          int yychecklim = YYLAST - yyn + 1;
-          int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
-          int yyx;
-
-          for (yyx = yyxbegin; yyx < yyxend; ++yyx)
-            if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
-                && !yytable_value_is_error (yytable[yyx + yyn]))
-              {
-                if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
-                  {
-                    yycount = 1;
-                    yysize = yysize0;
-                    break;
-                  }
-                yyarg[yycount++] = yytname[yyx];
-                {
-                  YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]);
-                  if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)
-                    yysize = yysize1;
-                  else
-                    return 2;
-                }
-              }
-        }
-    }
-
-  switch (yycount)
-    {
-# define YYCASE_(N, S)                      \
-      case N:                               \
-        yyformat = S;                       \
-      break
-    default: /* Avoid compiler warnings. */
-      YYCASE_(0, YY_("syntax error"));
-      YYCASE_(1, YY_("syntax error, unexpected %s"));
-      YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
-      YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
-      YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
-      YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
-# undef YYCASE_
-    }
-
-  {
-    YYSIZE_T yysize1 = yysize + yystrlen (yyformat);
-    if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)
-      yysize = yysize1;
-    else
-      return 2;
-  }
-
-  if (*yymsg_alloc < yysize)
-    {
-      *yymsg_alloc = 2 * yysize;
-      if (! (yysize <= *yymsg_alloc
-             && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
-        *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
-      return 1;
-    }
-
-  /* Avoid sprintf, as that infringes on the user's name space.
-     Don't have undefined behavior even if the translation
-     produced a string with the wrong number of "%s"s.  */
-  {
-    char *yyp = *yymsg;
-    int yyi = 0;
-    while ((*yyp = *yyformat) != '\0')
-      if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
-        {
-          yyp += yytnamerr (yyp, yyarg[yyi++]);
-          yyformat += 2;
-        }
-      else
-        {
-          yyp++;
-          yyformat++;
-        }
-  }
-  return 0;
-}
-#endif /* YYERROR_VERBOSE */
 
 /*-----------------------------------------------.
 | Release the memory associated to this symbol.  |
 `-----------------------------------------------*/
 
 static void
-yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+yydestruct (const char *yymsg,
+            yysymbol_kind_t yykind, YYSTYPE *yyvaluep)
 {
-  YYUSE (yyvaluep);
+  YY_USE (yyvaluep);
   if (!yymsg)
     yymsg = "Deleting";
-  YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+  YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp);
 
   YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
-  YYUSE (yytype);
+  YY_USE (yykind);
   YY_IGNORE_MAYBE_UNINITIALIZED_END
 }
 
 
-
-
-/* The lookahead symbol.  */
+/* Lookahead token kind.  */
 int yychar;
 
 /* The semantic value of the lookahead symbol.  */
@@ -1633,6 +1688,8 @@ YYSTYPE yylval;
 int yynerrs;
 
 
+
+
 /*----------.
 | yyparse.  |
 `----------*/
@@ -1640,43 +1697,36 @@ int yynerrs;
 int
 yyparse (void)
 {
-    int yystate;
+    yy_state_fast_t yystate = 0;
     /* Number of tokens to shift before error messages enabled.  */
-    int yyerrstatus;
-
-    /* The stacks and their tools:
-       'yyss': related to states.
-       'yyvs': related to semantic values.
+    int yyerrstatus = 0;
 
-       Refer to the stacks through separate pointers, to allow yyoverflow
+    /* Refer to the stacks through separate pointers, to allow yyoverflow
        to reallocate them elsewhere.  */
 
-    /* The state stack.  */
-    yytype_int16 yyssa[YYINITDEPTH];
-    yytype_int16 *yyss;
-    yytype_int16 *yyssp;
+    /* Their size.  */
+    YYPTRDIFF_T yystacksize = YYINITDEPTH;
 
-    /* The semantic value stack.  */
-    YYSTYPE yyvsa[YYINITDEPTH];
-    YYSTYPE *yyvs;
-    YYSTYPE *yyvsp;
+    /* The state stack: array, bottom, top.  */
+    yy_state_t yyssa[YYINITDEPTH];
+    yy_state_t *yyss = yyssa;
+    yy_state_t *yyssp = yyss;
 
-    YYSIZE_T yystacksize;
+    /* The semantic value stack: array, bottom, top.  */
+    YYSTYPE yyvsa[YYINITDEPTH];
+    YYSTYPE *yyvs = yyvsa;
+    YYSTYPE *yyvsp = yyvs;
 
   int yyn;
+  /* The return value of yyparse.  */
   int yyresult;
-  /* Lookahead token as an internal (translated) token number.  */
-  int yytoken = 0;
+  /* Lookahead symbol kind.  */
+  yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY;
   /* The variables used to return semantic value and location from the
      action routines.  */
   YYSTYPE yyval;
 
-#if YYERROR_VERBOSE
-  /* Buffer for error messages, and its allocated size.  */
-  char yymsgbuf[128];
-  char *yymsg = yymsgbuf;
-  YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
-#endif
+
 
 #define YYPOPSTACK(N)   (yyvsp -= (N), yyssp -= (N))
 
@@ -1684,16 +1734,10 @@ yyparse (void)
      Keep to zero when no symbol should be popped.  */
   int yylen = 0;
 
-  yyssp = yyss = yyssa;
-  yyvsp = yyvs = yyvsa;
-  yystacksize = YYINITDEPTH;
-
   YYDPRINTF ((stderr, "Starting parse\n"));
 
-  yystate = 0;
-  yyerrstatus = 0;
-  yynerrs = 0;
   yychar = YYEMPTY; /* Cause a token to be read.  */
+
   goto yysetstate;
 
 
@@ -1707,36 +1751,39 @@ yynewstate:
 
 
 /*--------------------------------------------------------------------.
-| yynewstate -- set current state (the top of the stack) to yystate.  |
+| yysetstate -- set current state (the top of the stack) to yystate.  |
 `--------------------------------------------------------------------*/
 yysetstate:
   YYDPRINTF ((stderr, "Entering state %d\n", yystate));
   YY_ASSERT (0 <= yystate && yystate < YYNSTATES);
-  *yyssp = (yytype_int16) yystate;
+  YY_IGNORE_USELESS_CAST_BEGIN
+  *yyssp = YY_CAST (yy_state_t, yystate);
+  YY_IGNORE_USELESS_CAST_END
+  YY_STACK_PRINT (yyss, yyssp);
 
   if (yyss + yystacksize - 1 <= yyssp)
 #if !defined yyoverflow && !defined YYSTACK_RELOCATE
-    goto yyexhaustedlab;
+    YYNOMEM;
 #else
     {
       /* Get the current used size of the three stacks, in elements.  */
-      YYSIZE_T yysize = (YYSIZE_T) (yyssp - yyss + 1);
+      YYPTRDIFF_T yysize = yyssp - yyss + 1;
 
 # if defined yyoverflow
       {
         /* Give user a chance to reallocate the stack.  Use copies of
            these so that the &'s don't force the real ones into
            memory.  */
+        yy_state_t *yyss1 = yyss;
         YYSTYPE *yyvs1 = yyvs;
-        yytype_int16 *yyss1 = yyss;
 
         /* Each stack pointer address is followed by the size of the
            data in use in that stack, in bytes.  This used to be a
            conditional around just the two extra args, but that might
            be undefined if yyoverflow is a macro.  */
         yyoverflow (YY_("memory exhausted"),
-                    &yyss1, yysize * sizeof (*yyssp),
-                    &yyvs1, yysize * sizeof (*yyvsp),
+                    &yyss1, yysize * YYSIZEOF (*yyssp),
+                    &yyvs1, yysize * YYSIZEOF (*yyvsp),
                     &yystacksize);
         yyss = yyss1;
         yyvs = yyvs1;
@@ -1744,20 +1791,21 @@ yysetstate:
 # else /* defined YYSTACK_RELOCATE */
       /* Extend the stack our own way.  */
       if (YYMAXDEPTH <= yystacksize)
-        goto yyexhaustedlab;
+        YYNOMEM;
       yystacksize *= 2;
       if (YYMAXDEPTH < yystacksize)
         yystacksize = YYMAXDEPTH;
 
       {
-        yytype_int16 *yyss1 = yyss;
+        yy_state_t *yyss1 = yyss;
         union yyalloc *yyptr =
-          (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+          YY_CAST (union yyalloc *,
+                   YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize))));
         if (! yyptr)
-          goto yyexhaustedlab;
+          YYNOMEM;
         YYSTACK_RELOCATE (yyss_alloc, yyss);
         YYSTACK_RELOCATE (yyvs_alloc, yyvs);
-# undef YYSTACK_RELOCATE
+#  undef YYSTACK_RELOCATE
         if (yyss1 != yyssa)
           YYSTACK_FREE (yyss1);
       }
@@ -1766,14 +1814,17 @@ yysetstate:
       yyssp = yyss + yysize - 1;
       yyvsp = yyvs + yysize - 1;
 
-      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
-                  (unsigned long) yystacksize));
+      YY_IGNORE_USELESS_CAST_BEGIN
+      YYDPRINTF ((stderr, "Stack size increased to %ld\n",
+                  YY_CAST (long, yystacksize)));
+      YY_IGNORE_USELESS_CAST_END
 
       if (yyss + yystacksize - 1 <= yyssp)
         YYABORT;
     }
 #endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */
 
+
   if (yystate == YYFINAL)
     YYACCEPT;
 
@@ -1794,18 +1845,29 @@ yybackup:
 
   /* Not known => get a lookahead token if don't already have one.  */
 
-  /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol.  */
+  /* YYCHAR is either empty, or end-of-input, or a valid lookahead.  */
   if (yychar == YYEMPTY)
     {
-      YYDPRINTF ((stderr, "Reading a token: "));
+      YYDPRINTF ((stderr, "Reading a token\n"));
       yychar = yylex ();
     }
 
   if (yychar <= YYEOF)
     {
-      yychar = yytoken = YYEOF;
+      yychar = YYEOF;
+      yytoken = YYSYMBOL_YYEOF;
       YYDPRINTF ((stderr, "Now at end of input.\n"));
     }
+  else if (yychar == YYerror)
+    {
+      /* The scanner already issued an error message, process directly
+         to error recovery.  But do not keep the error token as
+         lookahead, it is too special and may lead us to an endless
+         loop in error recovery. */
+      yychar = YYUNDEF;
+      yytoken = YYSYMBOL_YYerror;
+      goto yyerrlab1;
+    }
   else
     {
       yytoken = YYTRANSLATE (yychar);
@@ -1833,14 +1895,13 @@ yybackup:
 
   /* Shift the lookahead token.  */
   YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
-
-  /* Discard the shifted token.  */
-  yychar = YYEMPTY;
-
   yystate = yyn;
   YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
   *++yyvsp = yylval;
   YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+  /* Discard the shifted token.  */
+  yychar = YYEMPTY;
   goto yynewstate;
 
 
@@ -1875,273 +1936,275 @@ yyreduce:
   YY_REDUCE_PRINT (yyn);
   switch (yyn)
     {
-  case 2:
-#line 106 "gram.y"
-    { InitGramVariables(); }
-#line 1882 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 2: /* $@1: %empty  */
+#line 109 "gram.y"
+                  { InitGramVariables(); }
+#line 1943 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 11:
-#line 119 "gram.y"
-    {
+  case 11: /* stmt: ICON_REGION string vgrav hgrav number number  */
+#line 122 "gram.y"
+                                                               {
 		      AddIconRegion((yyvsp[-4].ptr), (yyvsp[-3].num), (yyvsp[-2].num), (yyvsp[-1].num), (yyvsp[0].num), "undef", "undef", "undef");
 		  }
-#line 1890 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 1951 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 12:
-#line 122 "gram.y"
-    {
+  case 12: /* stmt: ICON_REGION string vgrav hgrav number number string  */
+#line 125 "gram.y"
+                                                                      {
 		      AddIconRegion((yyvsp[-5].ptr), (yyvsp[-4].num), (yyvsp[-3].num), (yyvsp[-2].num), (yyvsp[-1].num), (yyvsp[0].ptr), "undef", "undef");
 		  }
-#line 1898 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 1959 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 13:
-#line 125 "gram.y"
-    {
+  case 13: /* stmt: ICON_REGION string vgrav hgrav number number string string  */
+#line 128 "gram.y"
+                                                                             {
 		      AddIconRegion((yyvsp[-6].ptr), (yyvsp[-5].num), (yyvsp[-4].num), (yyvsp[-3].num), (yyvsp[-2].num), (yyvsp[-1].ptr), (yyvsp[0].ptr), "undef");
 		  }
-#line 1906 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 1967 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 14:
-#line 128 "gram.y"
-    {
+  case 14: /* stmt: ICON_REGION string vgrav hgrav number number string string string  */
+#line 131 "gram.y"
+                                                                                    {
 		      AddIconRegion((yyvsp[-7].ptr), (yyvsp[-6].num), (yyvsp[-5].num), (yyvsp[-4].num), (yyvsp[-3].num), (yyvsp[-2].ptr), (yyvsp[-1].ptr), (yyvsp[0].ptr));
 		  }
-#line 1914 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 1975 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 15:
-#line 131 "gram.y"
-    {
+  case 15: /* $@2: %empty  */
+#line 134 "gram.y"
+                                                               {
 		      curplist = AddIconRegion((yyvsp[-4].ptr), (yyvsp[-3].num), (yyvsp[-2].num), (yyvsp[-1].num), (yyvsp[0].num), "undef", "undef", "undef");
 		  }
-#line 1922 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 1983 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 17:
-#line 135 "gram.y"
-    {
+  case 17: /* $@3: %empty  */
+#line 138 "gram.y"
+                                                                      {
 		      curplist = AddIconRegion((yyvsp[-5].ptr), (yyvsp[-4].num), (yyvsp[-3].num), (yyvsp[-2].num), (yyvsp[-1].num), (yyvsp[0].ptr), "undef", "undef");
 		  }
-#line 1930 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 1991 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 19:
-#line 139 "gram.y"
-    {
+  case 19: /* $@4: %empty  */
+#line 142 "gram.y"
+                                                                             {
 		      curplist = AddIconRegion((yyvsp[-6].ptr), (yyvsp[-5].num), (yyvsp[-4].num), (yyvsp[-3].num), (yyvsp[-2].num), (yyvsp[-1].ptr), (yyvsp[0].ptr), "undef");
 		  }
-#line 1938 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 1999 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 21:
-#line 143 "gram.y"
-    {
+  case 21: /* $@5: %empty  */
+#line 146 "gram.y"
+                                                                                    {
 		      curplist = AddIconRegion((yyvsp[-7].ptr), (yyvsp[-6].num), (yyvsp[-5].num), (yyvsp[-4].num), (yyvsp[-3].num), (yyvsp[-2].ptr), (yyvsp[-1].ptr), (yyvsp[0].ptr));
 		  }
-#line 1946 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2007 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 23:
-#line 148 "gram.y"
-    {
+  case 23: /* $@6: %empty  */
+#line 151 "gram.y"
+                                                   {
 		      curplist = AddWindowRegion ((yyvsp[-2].ptr), (yyvsp[-1].num), (yyvsp[0].num));
 		  }
-#line 1954 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2015 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 25:
-#line 153 "gram.y"
-    {
+  case 25: /* $@7: %empty  */
+#line 156 "gram.y"
+                                           {
+#ifdef WINBOX
 		      curplist = addWindowBox ((yyvsp[-1].ptr), (yyvsp[0].ptr));
+#endif
 		  }
-#line 1962 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2025 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 27:
-#line 158 "gram.y"
-    { if (Scr->FirstTime)
+  case 27: /* stmt: ICONMGR_GEOMETRY string number  */
+#line 163 "gram.y"
+                                                        { if (Scr->FirstTime)
 						  {
 						    Scr->iconmgr->geometry= (char*)(yyvsp[-1].ptr);
 						    Scr->iconmgr->columns=(yyvsp[0].num);
 						  }
 						}
-#line 1973 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2036 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 28:
-#line 164 "gram.y"
-    { if (Scr->FirstTime)
+  case 28: /* stmt: ICONMGR_GEOMETRY string  */
+#line 169 "gram.y"
+                                                { if (Scr->FirstTime)
 						    Scr->iconmgr->geometry = (char*)(yyvsp[0].ptr);
 						}
-#line 1981 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2044 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 29:
-#line 167 "gram.y"
-    { if (Scr->FirstTime)
+  case 29: /* stmt: WORKSPCMGR_GEOMETRY string number  */
+#line 172 "gram.y"
+                                                        { if (Scr->FirstTime)
 				{
 				    Scr->workSpaceMgr.geometry= (char*)(yyvsp[-1].ptr);
 				    Scr->workSpaceMgr.columns=(yyvsp[0].num);
 				}
 						}
-#line 1992 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2055 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 30:
-#line 173 "gram.y"
-    { if (Scr->FirstTime)
+  case 30: /* stmt: WORKSPCMGR_GEOMETRY string  */
+#line 178 "gram.y"
+                                                { if (Scr->FirstTime)
 				    Scr->workSpaceMgr.geometry = (char*)(yyvsp[0].ptr);
 						}
-#line 2000 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2063 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 31:
-#line 176 "gram.y"
-    {}
-#line 2006 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 31: /* $@8: %empty  */
+#line 181 "gram.y"
+                                            {}
+#line 2069 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 33:
-#line 179 "gram.y"
-    {}
-#line 2012 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 33: /* $@9: %empty  */
+#line 184 "gram.y"
+                                            {}
+#line 2075 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 35:
-#line 182 "gram.y"
-    { if (Scr->FirstTime)
+  case 35: /* stmt: ZOOM number  */
+#line 187 "gram.y"
+                                        { if (Scr->FirstTime)
 					  {
 						Scr->DoZoom = true;
 						Scr->ZoomCount = (yyvsp[0].num);
 					  }
 					}
-#line 2023 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2086 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 36:
-#line 188 "gram.y"
-    { if (Scr->FirstTime)
+  case 36: /* stmt: ZOOM  */
+#line 193 "gram.y"
+                                        { if (Scr->FirstTime)
 						Scr->DoZoom = true; }
-#line 2030 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2093 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 37:
-#line 190 "gram.y"
-    {}
-#line 2036 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 37: /* stmt: PIXMAPS pixmap_list  */
+#line 195 "gram.y"
+                                        {}
+#line 2099 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 38:
-#line 191 "gram.y"
-    {}
-#line 2042 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 38: /* stmt: CURSORS cursor_list  */
+#line 196 "gram.y"
+                                        {}
+#line 2105 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 39:
-#line 192 "gram.y"
-    { curplist = &Scr->IconifyByUn; }
-#line 2048 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 39: /* $@10: %empty  */
+#line 197 "gram.y"
+                                        { curplist = &Scr->IconifyByUn; }
+#line 2111 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 41:
-#line 194 "gram.y"
-    { if (Scr->FirstTime)
+  case 41: /* stmt: ICONIFY_BY_UNMAPPING  */
+#line 199 "gram.y"
+                                        { if (Scr->FirstTime)
 		    Scr->IconifyByUnmapping = true; }
-#line 2055 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2118 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 42:
-#line 197 "gram.y"
-    { curplist = &Scr->OpaqueMoveList; }
-#line 2061 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 42: /* $@11: %empty  */
+#line 202 "gram.y"
+                                { curplist = &Scr->OpaqueMoveList; }
+#line 2124 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 44:
-#line 199 "gram.y"
-    { if (Scr->FirstTime) Scr->DoOpaqueMove = true; }
-#line 2067 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 44: /* stmt: OPAQUEMOVE  */
+#line 204 "gram.y"
+                                { if (Scr->FirstTime) Scr->DoOpaqueMove = true; }
+#line 2130 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 45:
-#line 200 "gram.y"
-    { curplist = &Scr->NoOpaqueMoveList; }
-#line 2073 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 45: /* $@12: %empty  */
+#line 205 "gram.y"
+                                { curplist = &Scr->NoOpaqueMoveList; }
+#line 2136 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 47:
-#line 202 "gram.y"
-    { if (Scr->FirstTime) Scr->DoOpaqueMove = false; }
-#line 2079 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 47: /* stmt: NOOPAQUEMOVE  */
+#line 207 "gram.y"
+                                { if (Scr->FirstTime) Scr->DoOpaqueMove = false; }
+#line 2142 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 48:
-#line 203 "gram.y"
-    { curplist = &Scr->OpaqueMoveList; }
-#line 2085 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 48: /* $@13: %empty  */
+#line 208 "gram.y"
+                                { curplist = &Scr->OpaqueMoveList; }
+#line 2148 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 50:
-#line 205 "gram.y"
-    { if (Scr->FirstTime) Scr->DoOpaqueResize = true; }
-#line 2091 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 50: /* stmt: OPAQUERESIZE  */
+#line 210 "gram.y"
+                                { if (Scr->FirstTime) Scr->DoOpaqueResize = true; }
+#line 2154 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 51:
-#line 206 "gram.y"
-    { curplist = &Scr->NoOpaqueResizeList; }
-#line 2097 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 51: /* $@14: %empty  */
+#line 211 "gram.y"
+                                        { curplist = &Scr->NoOpaqueResizeList; }
+#line 2160 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 53:
-#line 208 "gram.y"
-    { if (Scr->FirstTime) Scr->DoOpaqueResize = false; }
-#line 2103 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 53: /* stmt: NOOPAQUERESIZE  */
+#line 213 "gram.y"
+                                        { if (Scr->FirstTime) Scr->DoOpaqueResize = false; }
+#line 2166 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 54:
-#line 210 "gram.y"
-    {
+  case 54: /* stmt: LEFT_TITLEBUTTON string EQUALS action  */
+#line 215 "gram.y"
+                                                        {
 					  GotTitleButton ((yyvsp[-2].ptr), (yyvsp[0].num), false);
 					}
-#line 2111 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2174 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 55:
-#line 213 "gram.y"
-    {
+  case 55: /* stmt: RIGHT_TITLEBUTTON string EQUALS action  */
+#line 218 "gram.y"
+                                                         {
 					  GotTitleButton ((yyvsp[-2].ptr), (yyvsp[0].num), true);
 					}
-#line 2119 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2182 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 56:
-#line 216 "gram.y"
-    { CreateTitleButton((yyvsp[0].ptr), 0, NULL, NULL, false, true); }
-#line 2125 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 56: /* $@15: %empty  */
+#line 221 "gram.y"
+                                          { CreateTitleButton((yyvsp[0].ptr), 0, NULL, NULL, false, true); }
+#line 2188 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 58:
-#line 218 "gram.y"
-    { CreateTitleButton((yyvsp[0].ptr), 0, NULL, NULL, true, true); }
-#line 2131 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 58: /* $@16: %empty  */
+#line 223 "gram.y"
+                                           { CreateTitleButton((yyvsp[0].ptr), 0, NULL, NULL, true, true); }
+#line 2194 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 60:
-#line 220 "gram.y"
-    {
+  case 60: /* stmt: button string  */
+#line 225 "gram.y"
+                                        {
 		    root = GetRoot((yyvsp[0].ptr), NULL, NULL);
 		    AddFuncButton ((yyvsp[-1].num), C_ROOT, 0, F_MENU, root, NULL);
 		}
-#line 2140 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2203 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 61:
-#line 224 "gram.y"
-    {
+  case 61: /* stmt: button action  */
+#line 229 "gram.y"
+                                        {
 			if ((yyvsp[0].num) == F_MENU) {
 			    pull->prev = NULL;
 			    AddFuncButton ((yyvsp[-1].num), C_ROOT, 0, (yyvsp[0].num), pull, NULL);
@@ -2157,376 +2220,383 @@ yyreduce:
 			Action = "";
 			pull = NULL;
 		}
-#line 2161 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
-    break;
-
-  case 62:
-#line 240 "gram.y"
-    { GotKey((yyvsp[-1].ptr), (yyvsp[0].num)); }
-#line 2167 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2224 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 63:
-#line 241 "gram.y"
-    { GotButton((yyvsp[-1].num), (yyvsp[0].num)); }
-#line 2173 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
-    break;
-
-  case 64:
-#line 243 "gram.y"
-    { curplist = &Scr->DontIconify; }
-#line 2179 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 62: /* stmt: string fullkey  */
+#line 245 "gram.y"
+                                        { GotKey((yyvsp[-1].ptr), (yyvsp[0].num)); }
+#line 2230 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 66:
-#line 245 "gram.y"
-    {}
-#line 2185 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 63: /* stmt: button full  */
+#line 246 "gram.y"
+                                        { GotButton((yyvsp[-1].num), (yyvsp[0].num)); }
+#line 2236 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 68:
+  case 64: /* $@17: %empty  */
 #line 248 "gram.y"
-    { mods = 0; }
-#line 2191 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+                                            { curplist = &Scr->DontIconify; }
+#line 2242 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 69:
+  case 66: /* $@18: %empty  */
 #line 250 "gram.y"
-    { Scr->IgnoreModifier |= mods; mods = 0; }
-#line 2197 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+                             {}
+#line 2248 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 71:
-#line 252 "gram.y"
-    { curplist = &Scr->OccupyAll; }
-#line 2203 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 68: /* $@19: %empty  */
+#line 253 "gram.y"
+                        { mods = 0; }
+#line 2254 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 73:
-#line 254 "gram.y"
-    { curplist = &Scr->IconMenuDontShow; }
-#line 2209 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 69: /* $@20: %empty  */
+#line 255 "gram.y"
+                        { Scr->IgnoreModifier |= mods; mods = 0; }
+#line 2260 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 75:
-#line 256 "gram.y"
-    {}
-#line 2215 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 71: /* $@21: %empty  */
+#line 257 "gram.y"
+                                        { curplist = &Scr->OccupyAll; }
+#line 2266 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 77:
-#line 258 "gram.y"
-    { curplist = &Scr->UnmapByMovingFarAway; }
-#line 2221 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 73: /* $@22: %empty  */
+#line 259 "gram.y"
+                                        { curplist = &Scr->IconMenuDontShow; }
+#line 2272 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 79:
-#line 260 "gram.y"
-    { curplist = &Scr->AutoSqueeze; }
-#line 2227 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 75: /* $@23: %empty  */
+#line 261 "gram.y"
+                             {}
+#line 2278 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 81:
-#line 262 "gram.y"
-    { curplist = &Scr->StartSqueezed; }
-#line 2233 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 77: /* $@24: %empty  */
+#line 263 "gram.y"
+                                        { curplist = &Scr->UnmapByMovingFarAway; }
+#line 2284 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 83:
-#line 264 "gram.y"
-    { Scr->AlwaysSqueezeToGravity = true; }
-#line 2239 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
-    break;
-
-  case 84:
+  case 79: /* $@25: %empty  */
 #line 265 "gram.y"
-    { curplist = &Scr->AlwaysSqueezeToGravityL; }
-#line 2245 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+                                        { curplist = &Scr->AutoSqueeze; }
+#line 2290 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 86:
+  case 81: /* $@26: %empty  */
 #line 267 "gram.y"
-    { curplist = &Scr->DontSetInactive; }
-#line 2251 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+                                        { curplist = &Scr->StartSqueezed; }
+#line 2296 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 88:
+  case 83: /* stmt: ALWAYSSQUEEZETOGRAVITY  */
 #line 269 "gram.y"
-    { curplist = &Scr->IconMgrNoShow; }
-#line 2257 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+                                                { Scr->AlwaysSqueezeToGravity = true; }
+#line 2302 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 90:
-#line 271 "gram.y"
-    { Scr->IconManagerDontShow = true; }
-#line 2263 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 84: /* $@27: %empty  */
+#line 270 "gram.y"
+                                                { curplist = &Scr->AlwaysSqueezeToGravityL; }
+#line 2308 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 91:
+  case 86: /* $@28: %empty  */
 #line 272 "gram.y"
-    { curplist = &Scr->IconMgrs; }
-#line 2269 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+                                        { curplist = &Scr->DontSetInactive; }
+#line 2314 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 93:
+  case 88: /* $@29: %empty  */
 #line 274 "gram.y"
-    { curplist = &Scr->IconMgrShow; }
-#line 2275 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+                                        { curplist = &Scr->IconMgrNoShow; }
+#line 2320 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 95:
+  case 90: /* stmt: ICONMGR_NOSHOW  */
 #line 276 "gram.y"
-    { curplist = &Scr->NoTitleHighlight; }
-#line 2281 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+                                        { Scr->IconManagerDontShow = true; }
+#line 2326 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 97:
-#line 278 "gram.y"
-    { if (Scr->FirstTime)
-						Scr->TitleHighlight = false; }
-#line 2288 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 91: /* $@30: %empty  */
+#line 277 "gram.y"
+                                        { curplist = &Scr->IconMgrs; }
+#line 2332 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 98:
-#line 280 "gram.y"
-    { curplist = &Scr->NoHighlight; }
-#line 2294 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 93: /* $@31: %empty  */
+#line 279 "gram.y"
+                                        { curplist = &Scr->IconMgrShow; }
+#line 2338 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 100:
-#line 282 "gram.y"
-    { if (Scr->FirstTime)
-						Scr->Highlight = false; }
-#line 2301 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 95: /* $@32: %empty  */
+#line 281 "gram.y"
+                                        { curplist = &Scr->NoTitleHighlight; }
+#line 2344 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
+    break;
+
+  case 97: /* stmt: NO_TITLE_HILITE  */
+#line 283 "gram.y"
+                                        { if (Scr->FirstTime)
+						Scr->TitleHighlight = false; }
+#line 2351 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 101:
+  case 98: /* $@33: %empty  */
 #line 285 "gram.y"
-    { OtpScrSetZero(Scr, WinWin, (yyvsp[0].num)); }
-#line 2307 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+                                        { curplist = &Scr->NoHighlight; }
+#line 2357 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 102:
+  case 100: /* stmt: NO_HILITE  */
 #line 287 "gram.y"
-    { OtpScrSetZero(Scr, IconWin, (yyvsp[0].num)); }
-#line 2313 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+                                        { if (Scr->FirstTime)
+						Scr->Highlight = false; }
+#line 2364 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 103:
-#line 289 "gram.y"
-    { curplist = OtpScrPriorityL(Scr, WinWin, (yyvsp[0].num)); }
-#line 2319 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 101: /* stmt: ON_TOP_PRIORITY signed_number  */
+#line 290 "gram.y"
+                                        { OtpScrSetZero(Scr, WinWin, (yyvsp[0].num)); }
+#line 2370 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 105:
+  case 102: /* stmt: ON_TOP_PRIORITY ICONS signed_number  */
 #line 292 "gram.y"
-    { curplist = OtpScrPriorityL(Scr, IconWin, (yyvsp[0].num)); }
-#line 2325 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+                                        { OtpScrSetZero(Scr, IconWin, (yyvsp[0].num)); }
+#line 2376 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 107:
+  case 103: /* $@34: %empty  */
 #line 294 "gram.y"
-    { curplist = OtpScrPriorityL(Scr, WinWin, 8); }
-#line 2331 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+                                        { curplist = OtpScrPriorityL(Scr, WinWin, (yyvsp[0].num)); }
+#line 2382 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 109:
-#line 296 "gram.y"
-    { OtpScrSetSwitching(Scr, WinWin, false);
-		                          curplist = OtpScrSwitchingL(Scr, WinWin); }
-#line 2338 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 105: /* $@35: %empty  */
+#line 297 "gram.y"
+                                        { curplist = OtpScrPriorityL(Scr, IconWin, (yyvsp[0].num)); }
+#line 2388 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 111:
+  case 107: /* $@36: %empty  */
 #line 299 "gram.y"
-    { OtpScrSetSwitching(Scr, WinWin, true);
+                                        { curplist = OtpScrPriorityL(Scr, WinWin, 8); }
+#line 2394 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
+    break;
+
+  case 109: /* $@37: %empty  */
+#line 301 "gram.y"
+                                        { OtpScrSetSwitching(Scr, WinWin, false);
+		                          curplist = OtpScrSwitchingL(Scr, WinWin); }
+#line 2401 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
+    break;
+
+  case 111: /* $@38: %empty  */
+#line 304 "gram.y"
+                                         { OtpScrSetSwitching(Scr, WinWin, true);
 		                          curplist = OtpScrSwitchingL(Scr, WinWin); }
-#line 2345 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2408 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 113:
-#line 303 "gram.y"
-    { OtpScrSetSwitching(Scr, IconWin, false);
+  case 113: /* $@39: %empty  */
+#line 308 "gram.y"
+                                        { OtpScrSetSwitching(Scr, IconWin, false);
                                         curplist = OtpScrSwitchingL(Scr, IconWin); }
-#line 2352 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2415 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 115:
-#line 307 "gram.y"
-    { OtpScrSetSwitching(Scr, IconWin, true);
+  case 115: /* $@40: %empty  */
+#line 312 "gram.y"
+                                        { OtpScrSetSwitching(Scr, IconWin, true);
 		                          curplist = OtpScrSwitchingL(Scr, IconWin); }
-#line 2359 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2422 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 117:
-#line 312 "gram.y"
-    { curplist = &Scr->NoStackModeL; }
-#line 2365 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 117: /* $@41: %empty  */
+#line 317 "gram.y"
+                                        { curplist = &Scr->NoStackModeL; }
+#line 2428 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 119:
-#line 314 "gram.y"
-    { if (Scr->FirstTime)
+  case 119: /* stmt: NO_STACKMODE  */
+#line 319 "gram.y"
+                                        { if (Scr->FirstTime)
 						Scr->StackMode = false; }
-#line 2372 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2435 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 120:
-#line 316 "gram.y"
-    { curplist = &Scr->NoBorder; }
-#line 2378 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 120: /* $@42: %empty  */
+#line 321 "gram.y"
+                                        { curplist = &Scr->NoBorder; }
+#line 2441 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 122:
-#line 318 "gram.y"
-    { Scr->AutoPopup = true; }
-#line 2384 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 122: /* stmt: AUTO_POPUP  */
+#line 323 "gram.y"
+                                        { Scr->AutoPopup = true; }
+#line 2447 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 123:
-#line 319 "gram.y"
-    { curplist = &Scr->AutoPopupL; }
-#line 2390 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 123: /* $@43: %empty  */
+#line 324 "gram.y"
+                                        { curplist = &Scr->AutoPopupL; }
+#line 2453 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 125:
-#line 321 "gram.y"
-    { curplist = &Scr->DontSave; }
-#line 2396 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 125: /* $@44: %empty  */
+#line 326 "gram.y"
+                                        {
+#ifndef SESSION
+			twmrc_error_prefix();
+			fprintf(stderr, "DontSave ignored; session support "
+					"disabled.\n");
+#endif
+				curplist = &Scr->DontSave;
+			}
+#line 2466 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 127:
-#line 323 "gram.y"
-    { curplist = &Scr->NoIconTitle; }
-#line 2402 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 127: /* $@45: %empty  */
+#line 335 "gram.y"
+                                        { curplist = &Scr->NoIconTitle; }
+#line 2472 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 129:
-#line 325 "gram.y"
-    { if (Scr->FirstTime)
+  case 129: /* stmt: NO_ICON_TITLE  */
+#line 337 "gram.y"
+                                        { if (Scr->FirstTime)
 						Scr->NoIconTitlebar = true; }
-#line 2409 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2479 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 130:
-#line 327 "gram.y"
-    { curplist = &Scr->NoTitle; }
-#line 2415 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 130: /* $@46: %empty  */
+#line 339 "gram.y"
+                                        { curplist = &Scr->NoTitle; }
+#line 2485 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 132:
-#line 329 "gram.y"
-    { if (Scr->FirstTime)
+  case 132: /* stmt: NO_TITLE  */
+#line 341 "gram.y"
+                                        { if (Scr->FirstTime)
 						Scr->NoTitlebar = true; }
-#line 2422 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2492 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 133:
-#line 331 "gram.y"
-    { curplist = &Scr->IgnoreTransientL; }
-#line 2428 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 133: /* $@47: %empty  */
+#line 343 "gram.y"
+                                        { curplist = &Scr->IgnoreTransientL; }
+#line 2498 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 135:
-#line 333 "gram.y"
-    { curplist = &Scr->MakeTitle; }
-#line 2434 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 135: /* $@48: %empty  */
+#line 345 "gram.y"
+                                        { curplist = &Scr->MakeTitle; }
+#line 2504 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 137:
-#line 335 "gram.y"
-    { curplist = &Scr->StartIconified; }
-#line 2440 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 137: /* $@49: %empty  */
+#line 347 "gram.y"
+                                        { curplist = &Scr->StartIconified; }
+#line 2510 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 139:
-#line 337 "gram.y"
-    { curplist = &Scr->AutoRaise; }
-#line 2446 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 139: /* $@50: %empty  */
+#line 349 "gram.y"
+                                        { curplist = &Scr->AutoRaise; }
+#line 2516 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 141:
-#line 339 "gram.y"
-    { Scr->AutoRaiseDefault = true; }
-#line 2452 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 141: /* stmt: AUTO_RAISE  */
+#line 351 "gram.y"
+                                        { Scr->AutoRaiseDefault = true; }
+#line 2522 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 142:
-#line 340 "gram.y"
-    { curplist = &Scr->WarpOnDeIconify; }
-#line 2458 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 142: /* $@51: %empty  */
+#line 352 "gram.y"
+                                        { curplist = &Scr->WarpOnDeIconify; }
+#line 2528 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 144:
-#line 342 "gram.y"
-    { curplist = &Scr->AutoLower; }
-#line 2464 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 144: /* $@52: %empty  */
+#line 354 "gram.y"
+                                        { curplist = &Scr->AutoLower; }
+#line 2534 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 146:
-#line 344 "gram.y"
-    { Scr->AutoLowerDefault = true; }
-#line 2470 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 146: /* stmt: AUTO_LOWER  */
+#line 356 "gram.y"
+                                        { Scr->AutoLowerDefault = true; }
+#line 2540 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 147:
-#line 345 "gram.y"
-    {
+  case 147: /* $@53: %empty  */
+#line 357 "gram.y"
+                                                        {
 					root = GetRoot((yyvsp[-5].ptr), (yyvsp[-3].ptr), (yyvsp[-1].ptr)); }
-#line 2477 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2547 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 148:
-#line 347 "gram.y"
-    { root->real_menu = true;}
-#line 2483 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 148: /* stmt: MENU string LP string COLON string RP $@53 menu  */
+#line 359 "gram.y"
+                                        { root->real_menu = true;}
+#line 2553 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 149:
-#line 348 "gram.y"
-    { root = GetRoot((yyvsp[0].ptr), NULL, NULL); }
-#line 2489 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 149: /* $@54: %empty  */
+#line 360 "gram.y"
+                                        { root = GetRoot((yyvsp[0].ptr), NULL, NULL); }
+#line 2559 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 150:
-#line 349 "gram.y"
-    { root->real_menu = true; }
-#line 2495 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 150: /* stmt: MENU string $@54 menu  */
+#line 361 "gram.y"
+                                        { root->real_menu = true; }
+#line 2565 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 151:
-#line 350 "gram.y"
-    { root = GetRoot((yyvsp[0].ptr), NULL, NULL); }
-#line 2501 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 151: /* $@55: %empty  */
+#line 362 "gram.y"
+                                        { root = GetRoot((yyvsp[0].ptr), NULL, NULL); }
+#line 2571 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 153:
-#line 352 "gram.y"
-    { curplist = &Scr->IconNames; }
-#line 2507 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 153: /* $@56: %empty  */
+#line 364 "gram.y"
+                                        { curplist = &Scr->IconNames; }
+#line 2577 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 155:
-#line 354 "gram.y"
-    { color = COLOR; }
-#line 2513 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 155: /* $@57: %empty  */
+#line 366 "gram.y"
+                                        { color = COLOR; }
+#line 2583 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 157:
-#line 356 "gram.y"
-    {}
-#line 2519 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 157: /* $@58: %empty  */
+#line 368 "gram.y"
+                                        {}
+#line 2589 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 159:
-#line 358 "gram.y"
-    { color = MONOCHROME; }
-#line 2525 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 159: /* $@59: %empty  */
+#line 370 "gram.y"
+                                        { color = MONOCHROME; }
+#line 2595 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 161:
-#line 360 "gram.y"
-    { Scr->DefaultFunction.func = (yyvsp[0].num);
+  case 161: /* stmt: DEFAULT_FUNCTION action  */
+#line 372 "gram.y"
+                                          { Scr->DefaultFunction.func = (yyvsp[0].num);
 					  if ((yyvsp[0].num) == F_MENU)
 					  {
 					    pull->prev = NULL;
@@ -2542,12 +2612,12 @@ yyreduce:
 					  Action = "";
 					  pull = NULL;
 					}
-#line 2546 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2616 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 162:
-#line 376 "gram.y"
-    { Scr->WindowFunction.func = (yyvsp[0].num);
+  case 162: /* stmt: WINDOW_FUNCTION action  */
+#line 388 "gram.y"
+                                         { Scr->WindowFunction.func = (yyvsp[0].num);
 					   root = GetRoot(TWM_ROOT,NULL,NULL);
 					   Scr->WindowFunction.item =
 						AddToMenu(root,"x",Action,
@@ -2555,12 +2625,12 @@ yyreduce:
 					   Action = "";
 					   pull = NULL;
 					}
-#line 2559 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2629 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 163:
-#line 384 "gram.y"
-    { Scr->ChangeWorkspaceFunction.func = (yyvsp[0].num);
+  case 163: /* stmt: CHANGE_WORKSPACE_FUNCTION action  */
+#line 396 "gram.y"
+                                                   { Scr->ChangeWorkspaceFunction.func = (yyvsp[0].num);
 					   root = GetRoot(TWM_ROOT,NULL,NULL);
 					   Scr->ChangeWorkspaceFunction.item =
 						AddToMenu(root,"x",Action,
@@ -2568,12 +2638,12 @@ yyreduce:
 					   Action = "";
 					   pull = NULL;
 					}
-#line 2572 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2642 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 164:
-#line 392 "gram.y"
-    { Scr->DeIconifyFunction.func = (yyvsp[0].num);
+  case 164: /* stmt: DEICONIFY_FUNCTION action  */
+#line 404 "gram.y"
+                                            { Scr->DeIconifyFunction.func = (yyvsp[0].num);
 					   root = GetRoot(TWM_ROOT,NULL,NULL);
 					   Scr->DeIconifyFunction.item =
 						AddToMenu(root,"x",Action,
@@ -2581,12 +2651,12 @@ yyreduce:
 					   Action = "";
 					   pull = NULL;
 					}
-#line 2585 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2655 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 165:
-#line 400 "gram.y"
-    { Scr->IconifyFunction.func = (yyvsp[0].num);
+  case 165: /* stmt: ICONIFY_FUNCTION action  */
+#line 412 "gram.y"
+                                          { Scr->IconifyFunction.func = (yyvsp[0].num);
 					   root = GetRoot(TWM_ROOT,NULL,NULL);
 					   Scr->IconifyFunction.item =
 						AddToMenu(root,"x",Action,
@@ -2594,87 +2664,93 @@ yyreduce:
 					   Action = "";
 					   pull = NULL;
 					}
-#line 2598 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2668 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 166:
-#line 408 "gram.y"
-    { curplist = &Scr->WarpCursorL; }
-#line 2604 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 166: /* $@60: %empty  */
+#line 420 "gram.y"
+                                        { curplist = &Scr->WarpCursorL; }
+#line 2674 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 168:
-#line 410 "gram.y"
-    { if (Scr->FirstTime)
+  case 168: /* stmt: WARP_CURSOR  */
+#line 422 "gram.y"
+                                        { if (Scr->FirstTime)
 					    Scr->WarpCursor = true; }
-#line 2611 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2681 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 169:
-#line 412 "gram.y"
-    { curplist = &Scr->WindowRingL; }
-#line 2617 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 169: /* $@61: %empty  */
+#line 424 "gram.y"
+                                        { curplist = &Scr->WindowRingL; }
+#line 2687 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 171:
-#line 414 "gram.y"
-    { Scr->WindowRingAll = true; }
-#line 2623 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 171: /* stmt: WINDOW_RING  */
+#line 426 "gram.y"
+                                        { Scr->WindowRingAll = true; }
+#line 2693 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 172:
-#line 415 "gram.y"
-    { if (!Scr->WindowRingL)
+  case 172: /* $@62: %empty  */
+#line 427 "gram.y"
+                                        { if (!Scr->WindowRingL)
 					    Scr->WindowRingAll = true;
 					  curplist = &Scr->WindowRingExcludeL; }
-#line 2631 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2701 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 174:
-#line 419 "gram.y"
-    {  }
-#line 2637 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 174: /* $@63: %empty  */
+#line 431 "gram.y"
+                                        {  }
+#line 2707 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 176:
-#line 421 "gram.y"
-    { }
-#line 2643 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 176: /* $@64: %empty  */
+#line 433 "gram.y"
+                                        { }
+#line 2713 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 178:
-#line 423 "gram.y"
-    { }
-#line 2649 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 178: /* $@65: %empty  */
+#line 435 "gram.y"
+                                        { }
+#line 2719 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 180:
-#line 425 "gram.y"
-    { }
-#line 2655 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 180: /* $@66: %empty  */
+#line 437 "gram.y"
+                                        { }
+#line 2725 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 182:
-#line 427 "gram.y"
-    { }
-#line 2661 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 182: /* $@67: %empty  */
+#line 439 "gram.y"
+                                 { init_layout_override(); }
+#line 2731 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 184:
-#line 429 "gram.y"
-    { Scr->ForceFocus = true; }
-#line 2667 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 184: /* $@68: %empty  */
+#line 441 "gram.y"
+                               { }
+#line 2737 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 185:
-#line 430 "gram.y"
-    { curplist = &Scr->ForceFocusL; }
-#line 2673 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 186: /* stmt: FORCE_FOCUS  */
+#line 443 "gram.y"
+                              { Scr->ForceFocus = true; }
+#line 2743 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 187:
-#line 434 "gram.y"
-    { if (!do_single_keyword ((yyvsp[0].num))) {
+  case 187: /* $@69: %empty  */
+#line 444 "gram.y"
+                              { curplist = &Scr->ForceFocusL; }
+#line 2749 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
+    break;
+
+  case 189: /* noarg: KEYWORD  */
+#line 448 "gram.y"
+                                        { if (!do_single_keyword ((yyvsp[0].num))) {
 					    twmrc_error_prefix();
 					    fprintf (stderr,
 					"unknown singleton keyword %d\n",
@@ -2682,12 +2758,12 @@ yyreduce:
 					    ParseError = true;
 					  }
 					}
-#line 2686 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2762 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 188:
-#line 444 "gram.y"
-    { if (!do_string_keyword ((yyvsp[-1].num), (yyvsp[0].ptr))) {
+  case 190: /* sarg: SKEYWORD string  */
+#line 458 "gram.y"
+                                        { if (!do_string_keyword ((yyvsp[-1].num), (yyvsp[0].ptr))) {
 					    twmrc_error_prefix();
 					    fprintf (stderr,
 				"unknown string keyword %d (value \"%s\")\n",
@@ -2695,12 +2771,12 @@ yyreduce:
 					    ParseError = true;
 					  }
 					}
-#line 2699 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2775 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 189:
-#line 452 "gram.y"
-    { if (!do_string_keyword ((yyvsp[0].num), DEFSTRING)) {
+  case 191: /* sarg: SKEYWORD  */
+#line 466 "gram.y"
+                                        { if (!do_string_keyword ((yyvsp[0].num), DEFSTRING)) {
 					    twmrc_error_prefix();
 					    fprintf (stderr,
 				"unknown string keyword %d (no value)\n",
@@ -2708,12 +2784,12 @@ yyreduce:
 					    ParseError = true;
 					  }
 					}
-#line 2712 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2788 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 190:
-#line 463 "gram.y"
-    { if (!do_string_string_keyword ((yyvsp[-2].num), (yyvsp[-1].ptr), (yyvsp[0].ptr))) {
+  case 192: /* sarg: SSKEYWORD string string  */
+#line 477 "gram.y"
+                                        { if (!do_string_string_keyword ((yyvsp[-2].num), (yyvsp[-1].ptr), (yyvsp[0].ptr))) {
 					    twmrc_error_prefix();
 					    fprintf (stderr,
 				"unknown strings keyword %d (value \"%s\" and \"%s\")\n",
@@ -2721,12 +2797,12 @@ yyreduce:
 					    ParseError = true;
 					  }
 					}
-#line 2725 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2801 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 191:
-#line 471 "gram.y"
-    { if (!do_string_string_keyword ((yyvsp[-1].num), (yyvsp[0].ptr), NULL)) {
+  case 193: /* sarg: SSKEYWORD string  */
+#line 485 "gram.y"
+                                        { if (!do_string_string_keyword ((yyvsp[-1].num), (yyvsp[0].ptr), NULL)) {
 					    twmrc_error_prefix();
 					    fprintf (stderr,
 				"unknown string keyword %d (value \"%s\")\n",
@@ -2734,12 +2810,12 @@ yyreduce:
 					    ParseError = true;
 					  }
 					}
-#line 2738 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2814 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 192:
-#line 479 "gram.y"
-    { if (!do_string_string_keyword ((yyvsp[0].num), NULL, NULL)) {
+  case 194: /* sarg: SSKEYWORD  */
+#line 493 "gram.y"
+                                        { if (!do_string_string_keyword ((yyvsp[0].num), NULL, NULL)) {
 					    twmrc_error_prefix();
 					    fprintf (stderr,
 				"unknown string keyword %d (no value)\n",
@@ -2747,12 +2823,12 @@ yyreduce:
 					    ParseError = true;
 					  }
 					}
-#line 2751 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2827 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 193:
-#line 489 "gram.y"
-    { if (!do_number_keyword ((yyvsp[-1].num), (yyvsp[0].num))) {
+  case 195: /* narg: NKEYWORD number  */
+#line 503 "gram.y"
+                                        { if (!do_number_keyword ((yyvsp[-1].num), (yyvsp[0].num))) {
 					    twmrc_error_prefix();
 					    fprintf (stderr,
 				"unknown numeric keyword %d (value %d)\n",
@@ -2760,54 +2836,54 @@ yyreduce:
 					    ParseError = true;
 					  }
 					}
-#line 2764 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2840 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 194:
-#line 501 "gram.y"
-    { (yyval.num) = (yyvsp[0].num); }
-#line 2770 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 196: /* keyaction: EQUALS keys COLON action  */
+#line 515 "gram.y"
+                                            { (yyval.num) = (yyvsp[0].num); }
+#line 2846 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 195:
-#line 504 "gram.y"
-    { (yyval.num) = (yyvsp[0].num); }
-#line 2776 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 197: /* full: EQUALS keys COLON contexts COLON action  */
+#line 518 "gram.y"
+                                                           { (yyval.num) = (yyvsp[0].num); }
+#line 2852 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 196:
-#line 507 "gram.y"
-    { (yyval.num) = (yyvsp[0].num); }
-#line 2782 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 198: /* fullkey: EQUALS keys COLON contextkeys COLON action  */
+#line 521 "gram.y"
+                                                              { (yyval.num) = (yyvsp[0].num); }
+#line 2858 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 199:
-#line 514 "gram.y"
-    { mods |= Mod1Mask; }
-#line 2788 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 201: /* key: META  */
+#line 528 "gram.y"
+                                        { mods |= Mod1Mask; }
+#line 2864 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 200:
-#line 515 "gram.y"
-    { mods |= ShiftMask; }
-#line 2794 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 202: /* key: SHIFT  */
+#line 529 "gram.y"
+                                        { mods |= ShiftMask; }
+#line 2870 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 201:
-#line 516 "gram.y"
-    { mods |= LockMask; }
-#line 2800 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 203: /* key: LOCK  */
+#line 530 "gram.y"
+                                        { mods |= LockMask; }
+#line 2876 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 202:
-#line 517 "gram.y"
-    { mods |= ControlMask; }
-#line 2806 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 204: /* key: CONTROL  */
+#line 531 "gram.y"
+                                        { mods |= ControlMask; }
+#line 2882 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 203:
-#line 518 "gram.y"
-    { if ((yyvsp[0].num) < 1 || (yyvsp[0].num) > 5) {
+  case 205: /* key: ALTER number  */
+#line 532 "gram.y"
+                                        { if ((yyvsp[0].num) < 1 || (yyvsp[0].num) > 5) {
 					     twmrc_error_prefix();
 					     fprintf (stderr,
 				"bad altkeymap number (%d), must be 1-5\n",
@@ -2817,12 +2893,12 @@ yyreduce:
 					     mods |= (Alt1Mask << ((yyvsp[0].num) - 1));
 					  }
 					}
-#line 2821 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2897 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 204:
-#line 528 "gram.y"
-    { if ((yyvsp[0].num) < 1 || (yyvsp[0].num) > 5) {
+  case 206: /* key: META number  */
+#line 542 "gram.y"
+                                        { if ((yyvsp[0].num) < 1 || (yyvsp[0].num) > 5) {
 					     twmrc_error_prefix();
 					     fprintf (stderr,
 				"bad modifier number (%d), must be 1-5\n",
@@ -2832,18 +2908,18 @@ yyreduce:
 					     mods |= (Mod1Mask << ((yyvsp[0].num) - 1));
 					  }
 					}
-#line 2836 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2912 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 205:
-#line 538 "gram.y"
-    { }
-#line 2842 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 207: /* key: OR  */
+#line 552 "gram.y"
+                                        { }
+#line 2918 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 206:
-#line 541 "gram.y"
-    {
+  case 208: /* vgrav: GRAVITY  */
+#line 555 "gram.y"
+                  {
 			switch((yyvsp[0].num)) {
 				case GRAV_NORTH:
 				case GRAV_SOUTH:
@@ -2857,12 +2933,12 @@ yyreduce:
 					YYERROR;
 			}
 		}
-#line 2861 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2937 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 207:
-#line 556 "gram.y"
-    {
+  case 209: /* hgrav: GRAVITY  */
+#line 570 "gram.y"
+                  {
 			switch((yyvsp[0].num)) {
 				case GRAV_EAST:
 				case GRAV_WEST:
@@ -2876,356 +2952,356 @@ yyreduce:
 					YYERROR;
 			}
 		}
-#line 2880 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 2956 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 210:
-#line 575 "gram.y"
-    { cont |= C_WINDOW_BIT; }
-#line 2886 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 212: /* context: WINDOW  */
+#line 589 "gram.y"
+                                        { cont |= C_WINDOW_BIT; }
+#line 2962 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 211:
-#line 576 "gram.y"
-    { cont |= C_TITLE_BIT; }
-#line 2892 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 213: /* context: TITLE  */
+#line 590 "gram.y"
+                                        { cont |= C_TITLE_BIT; }
+#line 2968 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 212:
-#line 577 "gram.y"
-    { cont |= C_ICON_BIT; }
-#line 2898 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 214: /* context: ICON  */
+#line 591 "gram.y"
+                                        { cont |= C_ICON_BIT; }
+#line 2974 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 213:
-#line 578 "gram.y"
-    { cont |= C_ROOT_BIT; }
-#line 2904 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 215: /* context: ROOT  */
+#line 592 "gram.y"
+                                        { cont |= C_ROOT_BIT; }
+#line 2980 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 214:
-#line 579 "gram.y"
-    { cont |= C_FRAME_BIT; }
-#line 2910 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 216: /* context: FRAME  */
+#line 593 "gram.y"
+                                        { cont |= C_FRAME_BIT; }
+#line 2986 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 215:
-#line 580 "gram.y"
-    { cont |= C_WORKSPACE_BIT; }
-#line 2916 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 217: /* context: WORKSPACE  */
+#line 594 "gram.y"
+                                        { cont |= C_WORKSPACE_BIT; }
+#line 2992 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 216:
-#line 581 "gram.y"
-    { cont |= C_ICONMGR_BIT; }
-#line 2922 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 218: /* context: ICONMGR  */
+#line 595 "gram.y"
+                                        { cont |= C_ICONMGR_BIT; }
+#line 2998 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 217:
-#line 582 "gram.y"
-    { cont |= C_ICONMGR_BIT; }
-#line 2928 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 219: /* context: META  */
+#line 596 "gram.y"
+                                        { cont |= C_ICONMGR_BIT; }
+#line 3004 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 218:
-#line 583 "gram.y"
-    { cont |= C_ALTER_BIT; }
-#line 2934 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 220: /* context: ALTER  */
+#line 597 "gram.y"
+                                        { cont |= C_ALTER_BIT; }
+#line 3010 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 219:
-#line 584 "gram.y"
-    { cont |= C_ALL_BITS; }
-#line 2940 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 221: /* context: ALL  */
+#line 598 "gram.y"
+                                        { cont |= C_ALL_BITS; }
+#line 3016 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 220:
-#line 585 "gram.y"
-    { }
-#line 2946 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 222: /* context: OR  */
+#line 599 "gram.y"
+                                        { }
+#line 3022 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 223:
-#line 592 "gram.y"
-    { cont |= C_WINDOW_BIT; }
-#line 2952 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 225: /* contextkey: WINDOW  */
+#line 606 "gram.y"
+                                        { cont |= C_WINDOW_BIT; }
+#line 3028 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 224:
-#line 593 "gram.y"
-    { cont |= C_TITLE_BIT; }
-#line 2958 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 226: /* contextkey: TITLE  */
+#line 607 "gram.y"
+                                        { cont |= C_TITLE_BIT; }
+#line 3034 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 225:
-#line 594 "gram.y"
-    { cont |= C_ICON_BIT; }
-#line 2964 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 227: /* contextkey: ICON  */
+#line 608 "gram.y"
+                                        { cont |= C_ICON_BIT; }
+#line 3040 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 226:
-#line 595 "gram.y"
-    { cont |= C_ROOT_BIT; }
-#line 2970 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 228: /* contextkey: ROOT  */
+#line 609 "gram.y"
+                                        { cont |= C_ROOT_BIT; }
+#line 3046 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 227:
-#line 596 "gram.y"
-    { cont |= C_FRAME_BIT; }
-#line 2976 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 229: /* contextkey: FRAME  */
+#line 610 "gram.y"
+                                        { cont |= C_FRAME_BIT; }
+#line 3052 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 228:
-#line 597 "gram.y"
-    { cont |= C_WORKSPACE_BIT; }
-#line 2982 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 230: /* contextkey: WORKSPACE  */
+#line 611 "gram.y"
+                                        { cont |= C_WORKSPACE_BIT; }
+#line 3058 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 229:
-#line 598 "gram.y"
-    { cont |= C_ICONMGR_BIT; }
-#line 2988 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 231: /* contextkey: ICONMGR  */
+#line 612 "gram.y"
+                                        { cont |= C_ICONMGR_BIT; }
+#line 3064 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 230:
-#line 599 "gram.y"
-    { cont |= C_ICONMGR_BIT; }
-#line 2994 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 232: /* contextkey: META  */
+#line 613 "gram.y"
+                                        { cont |= C_ICONMGR_BIT; }
+#line 3070 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 231:
-#line 600 "gram.y"
-    { cont |= C_ALTER_BIT; }
-#line 3000 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 233: /* contextkey: ALTER  */
+#line 614 "gram.y"
+                                        { cont |= C_ALTER_BIT; }
+#line 3076 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 232:
-#line 601 "gram.y"
-    { cont |= C_ALL_BITS; }
-#line 3006 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 234: /* contextkey: ALL  */
+#line 615 "gram.y"
+                                        { cont |= C_ALL_BITS; }
+#line 3082 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 233:
-#line 602 "gram.y"
-    { }
-#line 3012 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 235: /* contextkey: OR  */
+#line 616 "gram.y"
+                                        { }
+#line 3088 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 234:
-#line 603 "gram.y"
-    { Name = (char*)(yyvsp[0].ptr); cont |= C_NAME_BIT; }
-#line 3018 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 236: /* contextkey: string  */
+#line 617 "gram.y"
+                                        { Name = (char*)(yyvsp[0].ptr); cont |= C_NAME_BIT; }
+#line 3094 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 235:
-#line 607 "gram.y"
-    {}
-#line 3024 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 237: /* binding_list: LB binding_entries RB  */
+#line 621 "gram.y"
+                                        {}
+#line 3100 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 238:
-#line 614 "gram.y"
-    { SetCurrentTBAction((yyvsp[-1].num), mods, (yyvsp[0].num), Action, pull); mods = 0;}
-#line 3030 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 240: /* binding_entry: button keyaction  */
+#line 628 "gram.y"
+                                   { SetCurrentTBAction((yyvsp[-1].num), mods, (yyvsp[0].num), Action, pull); mods = 0;}
+#line 3106 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 239:
-#line 615 "gram.y"
-    { SetCurrentTBAction((yyvsp[-2].num), 0, (yyvsp[0].num), Action, pull);}
-#line 3036 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 241: /* binding_entry: button EQUALS action  */
+#line 629 "gram.y"
+                                       { SetCurrentTBAction((yyvsp[-2].num), 0, (yyvsp[0].num), Action, pull);}
+#line 3112 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 240:
-#line 616 "gram.y"
-    {
+  case 242: /* binding_entry: button COLON action  */
+#line 630 "gram.y"
+                                      {
 			/* Deprecated since 3.8, no longer supported */
 			yyerror("Title buttons specifications without = are no "
 			        "longer supported.");
 		}
-#line 3046 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3122 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 241:
-#line 624 "gram.y"
-    {}
-#line 3052 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 243: /* pixmap_list: LB pixmap_entries RB  */
+#line 638 "gram.y"
+                                       {}
+#line 3128 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 244:
-#line 631 "gram.y"
-    { Scr->HighlightPixmapName = strdup((yyvsp[0].ptr)); }
-#line 3058 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 246: /* pixmap_entry: TITLE_HILITE string  */
+#line 645 "gram.y"
+                                      { Scr->HighlightPixmapName = strdup((yyvsp[0].ptr)); }
+#line 3134 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 245:
-#line 635 "gram.y"
-    {}
-#line 3064 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 247: /* cursor_list: LB cursor_entries RB  */
+#line 649 "gram.y"
+                                       {}
+#line 3140 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 248:
-#line 642 "gram.y"
-    {
+  case 250: /* cursor_entry: FRAME string string  */
+#line 656 "gram.y"
+                                      {
 			NewBitmapCursor(&Scr->FrameCursor, (yyvsp[-1].ptr), (yyvsp[0].ptr)); }
-#line 3071 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3147 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 249:
-#line 644 "gram.y"
-    {
+  case 251: /* cursor_entry: FRAME string  */
+#line 658 "gram.y"
+                                {
 			NewFontCursor(&Scr->FrameCursor, (yyvsp[0].ptr)); }
-#line 3078 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3154 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 250:
-#line 646 "gram.y"
-    {
+  case 252: /* cursor_entry: TITLE string string  */
+#line 660 "gram.y"
+                                      {
 			NewBitmapCursor(&Scr->TitleCursor, (yyvsp[-1].ptr), (yyvsp[0].ptr)); }
-#line 3085 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3161 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 251:
-#line 648 "gram.y"
-    {
+  case 253: /* cursor_entry: TITLE string  */
+#line 662 "gram.y"
+                               {
 			NewFontCursor(&Scr->TitleCursor, (yyvsp[0].ptr)); }
-#line 3092 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3168 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 252:
-#line 650 "gram.y"
-    {
+  case 254: /* cursor_entry: ICON string string  */
+#line 664 "gram.y"
+                                     {
 			NewBitmapCursor(&Scr->IconCursor, (yyvsp[-1].ptr), (yyvsp[0].ptr)); }
-#line 3099 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3175 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 253:
-#line 652 "gram.y"
-    {
+  case 255: /* cursor_entry: ICON string  */
+#line 666 "gram.y"
+                              {
 			NewFontCursor(&Scr->IconCursor, (yyvsp[0].ptr)); }
-#line 3106 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3182 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 254:
-#line 654 "gram.y"
-    {
+  case 256: /* cursor_entry: ICONMGR string string  */
+#line 668 "gram.y"
+                                        {
 			NewBitmapCursor(&Scr->IconMgrCursor, (yyvsp[-1].ptr), (yyvsp[0].ptr)); }
-#line 3113 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3189 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 255:
-#line 656 "gram.y"
-    {
+  case 257: /* cursor_entry: ICONMGR string  */
+#line 670 "gram.y"
+                                 {
 			NewFontCursor(&Scr->IconMgrCursor, (yyvsp[0].ptr)); }
-#line 3120 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3196 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 256:
-#line 658 "gram.y"
-    {
+  case 258: /* cursor_entry: BUTTON string string  */
+#line 672 "gram.y"
+                                       {
 			NewBitmapCursor(&Scr->ButtonCursor, (yyvsp[-1].ptr), (yyvsp[0].ptr)); }
-#line 3127 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3203 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 257:
-#line 660 "gram.y"
-    {
+  case 259: /* cursor_entry: BUTTON string  */
+#line 674 "gram.y"
+                                {
 			NewFontCursor(&Scr->ButtonCursor, (yyvsp[0].ptr)); }
-#line 3134 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3210 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 258:
-#line 662 "gram.y"
-    {
+  case 260: /* cursor_entry: MOVE string string  */
+#line 676 "gram.y"
+                                     {
 			NewBitmapCursor(&Scr->MoveCursor, (yyvsp[-1].ptr), (yyvsp[0].ptr)); }
-#line 3141 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3217 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 259:
-#line 664 "gram.y"
-    {
+  case 261: /* cursor_entry: MOVE string  */
+#line 678 "gram.y"
+                              {
 			NewFontCursor(&Scr->MoveCursor, (yyvsp[0].ptr)); }
-#line 3148 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3224 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 260:
-#line 666 "gram.y"
-    {
+  case 262: /* cursor_entry: RESIZE string string  */
+#line 680 "gram.y"
+                                       {
 			NewBitmapCursor(&Scr->ResizeCursor, (yyvsp[-1].ptr), (yyvsp[0].ptr)); }
-#line 3155 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3231 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 261:
-#line 668 "gram.y"
-    {
+  case 263: /* cursor_entry: RESIZE string  */
+#line 682 "gram.y"
+                                {
 			NewFontCursor(&Scr->ResizeCursor, (yyvsp[0].ptr)); }
-#line 3162 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3238 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 262:
-#line 670 "gram.y"
-    {
+  case 264: /* cursor_entry: WAITC string string  */
+#line 684 "gram.y"
+                                      {
 			NewBitmapCursor(&Scr->WaitCursor, (yyvsp[-1].ptr), (yyvsp[0].ptr)); }
-#line 3169 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3245 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 263:
-#line 672 "gram.y"
-    {
+  case 265: /* cursor_entry: WAITC string  */
+#line 686 "gram.y"
+                               {
 			NewFontCursor(&Scr->WaitCursor, (yyvsp[0].ptr)); }
-#line 3176 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3252 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 264:
-#line 674 "gram.y"
-    {
+  case 266: /* cursor_entry: MENU string string  */
+#line 688 "gram.y"
+                                     {
 			NewBitmapCursor(&Scr->MenuCursor, (yyvsp[-1].ptr), (yyvsp[0].ptr)); }
-#line 3183 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3259 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 265:
-#line 676 "gram.y"
-    {
+  case 267: /* cursor_entry: MENU string  */
+#line 690 "gram.y"
+                              {
 			NewFontCursor(&Scr->MenuCursor, (yyvsp[0].ptr)); }
-#line 3190 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3266 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 266:
-#line 678 "gram.y"
-    {
+  case 268: /* cursor_entry: SELECT string string  */
+#line 692 "gram.y"
+                                       {
 			NewBitmapCursor(&Scr->SelectCursor, (yyvsp[-1].ptr), (yyvsp[0].ptr)); }
-#line 3197 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3273 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 267:
-#line 680 "gram.y"
-    {
+  case 269: /* cursor_entry: SELECT string  */
+#line 694 "gram.y"
+                                {
 			NewFontCursor(&Scr->SelectCursor, (yyvsp[0].ptr)); }
-#line 3204 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3280 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 268:
-#line 682 "gram.y"
-    {
+  case 270: /* cursor_entry: KILL string string  */
+#line 696 "gram.y"
+                                     {
 			NewBitmapCursor(&Scr->DestroyCursor, (yyvsp[-1].ptr), (yyvsp[0].ptr)); }
-#line 3211 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3287 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 269:
-#line 684 "gram.y"
-    {
+  case 271: /* cursor_entry: KILL string  */
+#line 698 "gram.y"
+                              {
 			NewFontCursor(&Scr->DestroyCursor, (yyvsp[0].ptr)); }
-#line 3218 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3294 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 270:
-#line 688 "gram.y"
-    {}
-#line 3224 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 272: /* color_list: LB color_entries RB  */
+#line 702 "gram.y"
+                                      {}
+#line 3300 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 273:
-#line 696 "gram.y"
-    { if (!do_colorlist_keyword ((yyvsp[-1].num), color,
+  case 275: /* color_entry: CLKEYWORD string  */
+#line 710 "gram.y"
+                                        { if (!do_colorlist_keyword ((yyvsp[-1].num), color,
 								     (yyvsp[0].ptr))) {
 					    twmrc_error_prefix();
 					    fprintf (stderr,
@@ -3234,12 +3310,12 @@ yyreduce:
 					    ParseError = true;
 					  }
 					}
-#line 3238 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3314 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 274:
-#line 705 "gram.y"
-    { curplist = do_colorlist_keyword((yyvsp[-1].num),color,
+  case 276: /* $@70: %empty  */
+#line 719 "gram.y"
+                                        { curplist = do_colorlist_keyword((yyvsp[-1].num),color,
 								      (yyvsp[0].ptr));
 					  if (!curplist) {
 					    twmrc_error_prefix();
@@ -3249,12 +3325,12 @@ yyreduce:
 					    ParseError = true;
 					  }
 					}
-#line 3253 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3329 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 276:
-#line 716 "gram.y"
-    { if (!do_color_keyword ((yyvsp[-1].num), color,
+  case 278: /* color_entry: CKEYWORD string  */
+#line 730 "gram.y"
+                                        { if (!do_color_keyword ((yyvsp[-1].num), color,
 								 (yyvsp[0].ptr))) {
 					    twmrc_error_prefix();
 					    fprintf (stderr,
@@ -3263,373 +3339,389 @@ yyreduce:
 					    ParseError = true;
 					  }
 					}
-#line 3267 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3343 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 277:
-#line 727 "gram.y"
-    {}
-#line 3273 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 279: /* save_color_list: LB s_color_entries RB  */
+#line 741 "gram.y"
+                                        {}
+#line 3349 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 280:
-#line 734 "gram.y"
-    { do_string_savecolor(color, (yyvsp[0].ptr)); }
-#line 3279 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 282: /* s_color_entry: string  */
+#line 748 "gram.y"
+                                        { do_string_savecolor(color, (yyvsp[0].ptr)); }
+#line 3355 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 281:
-#line 735 "gram.y"
-    { do_var_savecolor((yyvsp[0].num)); }
-#line 3285 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 283: /* s_color_entry: CLKEYWORD  */
+#line 749 "gram.y"
+                                        { do_var_savecolor((yyvsp[0].num)); }
+#line 3361 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 282:
-#line 738 "gram.y"
-    {}
-#line 3291 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 284: /* win_color_list: LB win_color_entries RB  */
+#line 752 "gram.y"
+                                          {}
+#line 3367 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 285:
-#line 745 "gram.y"
-    { if (Scr->FirstTime &&
+  case 287: /* win_color_entry: string string  */
+#line 759 "gram.y"
+                                        { if (Scr->FirstTime &&
 					      color == Scr->Monochrome)
 					    AddToList(curplist, (yyvsp[-1].ptr), (yyvsp[0].ptr)); }
-#line 3299 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3375 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 286:
-#line 750 "gram.y"
-    {}
-#line 3305 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 288: /* wingeom_list: LB wingeom_entries RB  */
+#line 764 "gram.y"
+                                        {}
+#line 3381 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 289:
-#line 757 "gram.y"
-    { AddToList (&Scr->WindowGeometries, (yyvsp[-1].ptr), (yyvsp[0].ptr)); }
-#line 3311 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 291: /* wingeom_entry: string string  */
+#line 771 "gram.y"
+                                { AddToList (&Scr->WindowGeometries, (yyvsp[-1].ptr), (yyvsp[0].ptr)); }
+#line 3387 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 290:
-#line 760 "gram.y"
-    {}
-#line 3317 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 292: /* vscreen_geom_list: LB vscreen_geom_entries RB  */
+#line 774 "gram.y"
+                                                     {}
+#line 3393 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 293:
-#line 767 "gram.y"
-    { AddToList (&Scr->VirtualScreens, (yyvsp[0].ptr), ""); }
-#line 3323 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 295: /* vscreen_geom_entry: string  */
+#line 781 "gram.y"
+                                 {
+#ifdef VSCREEN
+				   AddToList (&Scr->VirtualScreens, (yyvsp[0].ptr), "");
+#endif
+				   }
+#line 3403 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 294:
-#line 771 "gram.y"
-    { proc_ewmh_ignore(); }
-#line 3329 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 296: /* ewmh_ignore_list: LB ewmh_ignore_entries RB  */
+#line 789 "gram.y"
+                                                    { proc_ewmh_ignore(); }
+#line 3409 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 297:
-#line 778 "gram.y"
-    { add_ewmh_ignore((yyvsp[0].ptr)); }
-#line 3335 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 299: /* ewmh_ignore_entry: string  */
+#line 796 "gram.y"
+                                 { add_ewmh_ignore((yyvsp[0].ptr)); }
+#line 3415 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 298:
-#line 782 "gram.y"
-    { proc_mwm_ignore(); }
-#line 3341 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 300: /* mwm_ignore_list: LB mwm_ignore_entries RB  */
+#line 800 "gram.y"
+                                           { proc_mwm_ignore(); }
+#line 3421 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 301:
-#line 789 "gram.y"
-    { add_mwm_ignore((yyvsp[0].ptr)); }
-#line 3347 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 303: /* mwm_ignore_entry: string  */
+#line 807 "gram.y"
+                                 { add_mwm_ignore((yyvsp[0].ptr)); }
+#line 3427 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 302:
-#line 793 "gram.y"
-    {
+  case 304: /* layout_geom_list: LB layout_geom_entries RB  */
+#line 811 "gram.y"
+                                                    { proc_layout_override(); }
+#line 3433 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
+    break;
+
+  case 307: /* layout_geom_entry: string  */
+#line 818 "gram.y"
+                                 { add_layout_override_entry((yyvsp[0].ptr)); }
+#line 3439 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
+    break;
+
+  case 308: /* squeeze: SQUEEZE_TITLE  */
+#line 822 "gram.y"
+                                {
 				    if (HasShape) Scr->SqueezeTitle = true;
 				}
-#line 3355 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3447 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 303:
-#line 796 "gram.y"
-    { curplist = &Scr->SqueezeTitleL;
+  case 309: /* $@71: %empty  */
+#line 825 "gram.y"
+                                { curplist = &Scr->SqueezeTitleL;
 				  if (HasShape)
 				    Scr->SqueezeTitle = true;
 				}
-#line 3364 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3456 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 305:
-#line 801 "gram.y"
-    { Scr->SqueezeTitle = false; }
-#line 3370 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 311: /* squeeze: DONT_SQUEEZE_TITLE  */
+#line 830 "gram.y"
+                                     { Scr->SqueezeTitle = false; }
+#line 3462 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 306:
-#line 802 "gram.y"
-    { curplist = &Scr->DontSqueezeTitleL; }
-#line 3376 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 312: /* $@72: %empty  */
+#line 831 "gram.y"
+                                     { curplist = &Scr->DontSqueezeTitleL; }
+#line 3468 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 309:
-#line 807 "gram.y"
-    {
+  case 315: /* win_sqz_entries: win_sqz_entries string SIJENUM signed_number number  */
+#line 836 "gram.y"
+                                                                        {
 				if (Scr->FirstTime) {
 				   do_squeeze_entry (curplist, (yyvsp[-3].ptr), (yyvsp[-2].num), (yyvsp[-1].num), (yyvsp[0].num));
 				}
 			}
-#line 3386 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3478 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 310:
-#line 815 "gram.y"
-    {}
-#line 3392 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 316: /* iconm_list: LB iconm_entries RB  */
+#line 844 "gram.y"
+                                      {}
+#line 3484 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 313:
-#line 822 "gram.y"
-    { if (Scr->FirstTime)
+  case 319: /* iconm_entry: string string number  */
+#line 851 "gram.y"
+                                        { if (Scr->FirstTime)
 					    AddToList(curplist, (yyvsp[-2].ptr),
 						AllocateIconManager((yyvsp[-2].ptr), NULL,
 							(yyvsp[-1].ptr),(yyvsp[0].num)));
 					}
-#line 3402 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3494 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 314:
-#line 828 "gram.y"
-    { if (Scr->FirstTime)
+  case 320: /* iconm_entry: string string string number  */
+#line 857 "gram.y"
+                                        { if (Scr->FirstTime)
 					    AddToList(curplist, (yyvsp[-3].ptr),
 						AllocateIconManager((yyvsp[-3].ptr),(yyvsp[-2].ptr),
 						(yyvsp[-1].ptr), (yyvsp[0].num)));
 					}
-#line 3412 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3504 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 315:
-#line 835 "gram.y"
-    {}
-#line 3418 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 321: /* workspc_list: LB workspc_entries RB  */
+#line 864 "gram.y"
+                                        {}
+#line 3510 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 318:
-#line 842 "gram.y"
-    {
+  case 324: /* workspc_entry: string  */
+#line 871 "gram.y"
+                                {
 			AddWorkSpace ((yyvsp[0].ptr), NULL, NULL, NULL, NULL, NULL);
 		}
-#line 3426 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3518 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 319:
-#line 845 "gram.y"
-    {
+  case 325: /* $@73: %empty  */
+#line 874 "gram.y"
+                                {
 			curWorkSpc = (char*)(yyvsp[0].ptr);
 		}
-#line 3434 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3526 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 321:
-#line 851 "gram.y"
-    {}
-#line 3440 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 327: /* workapp_list: LB workapp_entries RB  */
+#line 880 "gram.y"
+                                        {}
+#line 3532 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 324:
-#line 858 "gram.y"
-    {
+  case 330: /* workapp_entry: string  */
+#line 887 "gram.y"
+                                        {
 			AddWorkSpace (curWorkSpc, (yyvsp[0].ptr), NULL, NULL, NULL, NULL);
 		}
-#line 3448 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3540 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 325:
-#line 861 "gram.y"
-    {
+  case 331: /* workapp_entry: string string  */
+#line 890 "gram.y"
+                                        {
 			AddWorkSpace (curWorkSpc, (yyvsp[-1].ptr), (yyvsp[0].ptr), NULL, NULL, NULL);
 		}
-#line 3456 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3548 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 326:
-#line 864 "gram.y"
-    {
+  case 332: /* workapp_entry: string string string  */
+#line 893 "gram.y"
+                                        {
 			AddWorkSpace (curWorkSpc, (yyvsp[-2].ptr), (yyvsp[-1].ptr), (yyvsp[0].ptr), NULL, NULL);
 		}
-#line 3464 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3556 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 327:
-#line 867 "gram.y"
-    {
+  case 333: /* workapp_entry: string string string string  */
+#line 896 "gram.y"
+                                                {
 			AddWorkSpace (curWorkSpc, (yyvsp[-3].ptr), (yyvsp[-2].ptr), (yyvsp[-1].ptr), (yyvsp[0].ptr), NULL);
 		}
-#line 3472 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3564 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 328:
-#line 870 "gram.y"
-    {
+  case 334: /* workapp_entry: string string string string string  */
+#line 899 "gram.y"
+                                                        {
 			AddWorkSpace (curWorkSpc, (yyvsp[-4].ptr), (yyvsp[-3].ptr), (yyvsp[-2].ptr), (yyvsp[-1].ptr), (yyvsp[0].ptr));
 		}
-#line 3480 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3572 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 329:
-#line 875 "gram.y"
-    {
+  case 335: /* curwork: LB string RB  */
+#line 904 "gram.y"
+                               {
 		    WMapCreateCurrentBackGround ((yyvsp[-1].ptr), NULL, NULL, NULL);
 		}
-#line 3488 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3580 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 330:
-#line 878 "gram.y"
-    {
+  case 336: /* curwork: LB string string RB  */
+#line 907 "gram.y"
+                                      {
 		    WMapCreateCurrentBackGround ((yyvsp[-2].ptr), (yyvsp[-1].ptr), NULL, NULL);
 		}
-#line 3496 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3588 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 331:
-#line 881 "gram.y"
-    {
+  case 337: /* curwork: LB string string string RB  */
+#line 910 "gram.y"
+                                             {
 		    WMapCreateCurrentBackGround ((yyvsp[-3].ptr), (yyvsp[-2].ptr), (yyvsp[-1].ptr), NULL);
 		}
-#line 3504 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3596 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 332:
-#line 884 "gram.y"
-    {
+  case 338: /* curwork: LB string string string string RB  */
+#line 913 "gram.y"
+                                                    {
 		    WMapCreateCurrentBackGround ((yyvsp[-4].ptr), (yyvsp[-3].ptr), (yyvsp[-2].ptr), (yyvsp[-1].ptr));
 		}
-#line 3512 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3604 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 333:
-#line 889 "gram.y"
-    {
+  case 339: /* defwork: LB string RB  */
+#line 918 "gram.y"
+                               {
 		    WMapCreateDefaultBackGround ((yyvsp[-1].ptr), NULL, NULL, NULL);
 		}
-#line 3520 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3612 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 334:
-#line 892 "gram.y"
-    {
+  case 340: /* defwork: LB string string RB  */
+#line 921 "gram.y"
+                                      {
 		    WMapCreateDefaultBackGround ((yyvsp[-2].ptr), (yyvsp[-1].ptr), NULL, NULL);
 		}
-#line 3528 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3620 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 335:
-#line 895 "gram.y"
-    {
+  case 341: /* defwork: LB string string string RB  */
+#line 924 "gram.y"
+                                             {
 		    WMapCreateDefaultBackGround ((yyvsp[-3].ptr), (yyvsp[-2].ptr), (yyvsp[-1].ptr), NULL);
 		}
-#line 3536 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3628 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 336:
-#line 898 "gram.y"
-    {
+  case 342: /* defwork: LB string string string string RB  */
+#line 927 "gram.y"
+                                                    {
 		    WMapCreateDefaultBackGround ((yyvsp[-4].ptr), (yyvsp[-3].ptr), (yyvsp[-2].ptr), (yyvsp[-1].ptr));
 		}
-#line 3544 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3636 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 337:
-#line 903 "gram.y"
-    {}
-#line 3550 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 343: /* win_list: LB win_entries RB  */
+#line 932 "gram.y"
+                                    {}
+#line 3642 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 340:
-#line 910 "gram.y"
-    { if (Scr->FirstTime)
+  case 346: /* win_entry: string  */
+#line 939 "gram.y"
+                                        { if (Scr->FirstTime)
 					    AddToList(curplist, (yyvsp[0].ptr), 0);
 					}
-#line 3558 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3650 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 341:
-#line 915 "gram.y"
-    {}
-#line 3564 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 347: /* occupy_list: LB occupy_entries RB  */
+#line 944 "gram.y"
+                                       {}
+#line 3656 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 344:
-#line 922 "gram.y"
-    {client = (char*)(yyvsp[0].ptr);}
-#line 3570 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 350: /* $@74: %empty  */
+#line 951 "gram.y"
+                         {client = (char*)(yyvsp[0].ptr);}
+#line 3662 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 346:
-#line 924 "gram.y"
-    {client = (char*)(yyvsp[0].ptr);}
-#line 3576 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 352: /* $@75: %empty  */
+#line 953 "gram.y"
+                                   {client = (char*)(yyvsp[0].ptr);}
+#line 3668 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 348:
-#line 926 "gram.y"
-    {workspace = (char*)(yyvsp[0].ptr);}
-#line 3582 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 354: /* $@76: %empty  */
+#line 955 "gram.y"
+                                   {workspace = (char*)(yyvsp[0].ptr);}
+#line 3674 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 350:
-#line 930 "gram.y"
-    {}
-#line 3588 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 356: /* occupy_workspc_list: LB occupy_workspc_entries RB  */
+#line 959 "gram.y"
+                                                       {}
+#line 3680 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 353:
-#line 937 "gram.y"
-    {
+  case 359: /* occupy_workspc_entry: string  */
+#line 966 "gram.y"
+                                 {
 				if(!AddToClientsList ((yyvsp[0].ptr), client)) {
 					twmrc_error_prefix();
 					fprintf(stderr, "unknown workspace '%s'\n", (yyvsp[0].ptr));
 				}
 			  }
-#line 3599 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3691 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 354:
-#line 945 "gram.y"
-    {}
-#line 3605 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 360: /* occupy_window_list: LB occupy_window_entries RB  */
+#line 974 "gram.y"
+                                                      {}
+#line 3697 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 357:
-#line 952 "gram.y"
-    {
+  case 363: /* occupy_window_entry: string  */
+#line 981 "gram.y"
+                                 {
 				if(!AddToClientsList (workspace, (yyvsp[0].ptr))) {
 					twmrc_error_prefix();
 					fprintf(stderr, "unknown workspace '%s'\n", workspace);
 				}
 			  }
-#line 3616 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3708 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 358:
-#line 960 "gram.y"
-    {}
-#line 3622 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 364: /* icon_list: LB icon_entries RB  */
+#line 989 "gram.y"
+                                     {}
+#line 3714 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 361:
-#line 967 "gram.y"
-    { if (Scr->FirstTime) AddToList(curplist, (yyvsp[-1].ptr), (yyvsp[0].ptr)); }
-#line 3628 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 367: /* icon_entry: string string  */
+#line 996 "gram.y"
+                                        { if (Scr->FirstTime) AddToList(curplist, (yyvsp[-1].ptr), (yyvsp[0].ptr)); }
+#line 3720 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 362:
-#line 970 "gram.y"
-    {
+  case 368: /* rplay_sounds_list: LB rplay_sounds_entries RB  */
+#line 999 "gram.y"
+                                                     {
 #ifndef SOUNDS
 			twmrc_error_prefix();
 			fprintf(stderr, "RplaySounds ignored; rplay support "
@@ -3638,12 +3730,12 @@ yyreduce:
 			sound_set_from_config();
 #endif
 		}
-#line 3642 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3734 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 365:
-#line 985 "gram.y"
-    {
+  case 371: /* rplay_sounds_entry: string string  */
+#line 1014 "gram.y"
+                                        {
 #ifdef SOUNDS
 			if(set_sound_event_name((yyvsp[-1].ptr), (yyvsp[0].ptr)) != 0) {
 				twmrc_error_prefix();
@@ -3652,33 +3744,33 @@ yyreduce:
 			}
 #endif
 		}
-#line 3656 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3748 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 366:
-#line 996 "gram.y"
-    {}
-#line 3662 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 372: /* function: LB function_entries RB  */
+#line 1025 "gram.y"
+                                         {}
+#line 3754 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 369:
-#line 1003 "gram.y"
-    { AddToMenu(root, "", Action, NULL, (yyvsp[0].num),
+  case 375: /* function_entry: action  */
+#line 1032 "gram.y"
+                                        { AddToMenu(root, "", Action, NULL, (yyvsp[0].num),
 						    NULL, NULL);
 					  Action = "";
 					}
-#line 3671 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3763 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 370:
-#line 1009 "gram.y"
-    {lastmenuitem = NULL;}
-#line 3677 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 376: /* menu: LB menu_entries RB  */
+#line 1038 "gram.y"
+                                     {lastmenuitem = NULL;}
+#line 3769 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 373:
-#line 1016 "gram.y"
-    {
+  case 379: /* menu_entry: string action  */
+#line 1045 "gram.y"
+                                        {
 			if ((yyvsp[0].num) == F_SEPARATOR) {
 			    if (lastmenuitem) lastmenuitem->separated = true;
 			}
@@ -3688,12 +3780,12 @@ yyreduce:
 			    pull = NULL;
 			}
 		}
-#line 3692 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3784 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 374:
-#line 1026 "gram.y"
-    {
+  case 380: /* menu_entry: string LP string COLON string RP action  */
+#line 1055 "gram.y"
+                                                          {
 			if ((yyvsp[0].num) == F_SEPARATOR) {
 			    if (lastmenuitem) lastmenuitem->separated = true;
 			}
@@ -3703,18 +3795,18 @@ yyreduce:
 			    pull = NULL;
 			}
 		}
-#line 3707 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3799 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 375:
-#line 1038 "gram.y"
-    { (yyval.num) = (yyvsp[0].num); }
-#line 3713 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 381: /* action: FKEYWORD  */
+#line 1067 "gram.y"
+                                { (yyval.num) = (yyvsp[0].num); }
+#line 3805 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 376:
-#line 1039 "gram.y"
-    {
+  case 382: /* action: FSKEYWORD string  */
+#line 1068 "gram.y"
+                                   {
 				(yyval.num) = (yyvsp[-1].num);
 				Action = (char*)(yyvsp[0].ptr);
 				switch ((yyvsp[-1].num)) {
@@ -3730,6 +3822,7 @@ yyreduce:
 						 Action);
 					(yyval.num) = F_NOP;
 				    }
+				    break;
 				  case F_WARPTOSCREEN:
 				    if (!CheckWarpScreenArg (Action)) {
 					twmrc_error_prefix();
@@ -3752,30 +3845,30 @@ yyreduce:
 				    break;
 				} /* end switch */
 				   }
-#line 3756 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3849 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 377:
-#line 1080 "gram.y"
-    { (yyval.num) = (yyvsp[0].num); }
-#line 3762 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 383: /* signed_number: number  */
+#line 1110 "gram.y"
+                                        { (yyval.num) = (yyvsp[0].num); }
+#line 3855 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 378:
-#line 1081 "gram.y"
-    { (yyval.num) = (yyvsp[0].num); }
-#line 3768 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 384: /* signed_number: PLUS number  */
+#line 1111 "gram.y"
+                                        { (yyval.num) = (yyvsp[0].num); }
+#line 3861 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 379:
-#line 1082 "gram.y"
-    { (yyval.num) = -((yyvsp[0].num)); }
-#line 3774 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 385: /* signed_number: MINUS number  */
+#line 1112 "gram.y"
+                                        { (yyval.num) = -((yyvsp[0].num)); }
+#line 3867 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 380:
-#line 1085 "gram.y"
-    { (yyval.num) = (yyvsp[0].num);
+  case 386: /* button: BUTTON number  */
+#line 1115 "gram.y"
+                                        { (yyval.num) = (yyvsp[0].num);
 					  if ((yyvsp[0].num) == 0)
 						yyerror("bad button 0");
 
@@ -3785,26 +3878,26 @@ yyreduce:
 						yyerror("button number too large");
 					  }
 					}
-#line 3789 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3882 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 381:
-#line 1097 "gram.y"
-    { char *ptr = strdup((yyvsp[0].ptr));
+  case 387: /* string: STRING  */
+#line 1127 "gram.y"
+                                        { char *ptr = strdup((yyvsp[0].ptr));
 					  RemoveDQuote(ptr);
 					  (yyval.ptr) = ptr;
 					}
-#line 3798 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3891 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
-  case 382:
-#line 1103 "gram.y"
-    { (yyval.num) = (yyvsp[0].num); }
-#line 3804 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+  case 388: /* number: NUMBER  */
+#line 1133 "gram.y"
+                                        { (yyval.num) = (yyvsp[0].num); }
+#line 3897 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
     break;
 
 
-#line 3808 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.c"
+#line 3901 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.c"
 
       default: break;
     }
@@ -3819,11 +3912,10 @@ yyreduce:
      case of YYERROR or YYBACKUP, subsequent parser actions might lead
      to an incorrect destructor call or verbose syntax error message
      before the lookahead is translated.  */
-  YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+  YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc);
 
   YYPOPSTACK (yylen);
   yylen = 0;
-  YY_STACK_PRINT (yyss, yyssp);
 
   *++yyvsp = yyval;
 
@@ -3847,50 +3939,14 @@ yyreduce:
 yyerrlab:
   /* Make sure we have latest lookahead translation.  See comments at
      user semantic actions for why this is necessary.  */
-  yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
-
+  yytoken = yychar == YYEMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar);
   /* If not already recovering from an error, report this error.  */
   if (!yyerrstatus)
     {
       ++yynerrs;
-#if ! YYERROR_VERBOSE
       yyerror (YY_("syntax error"));
-#else
-# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
-                                        yyssp, yytoken)
-      {
-        char const *yymsgp = YY_("syntax error");
-        int yysyntax_error_status;
-        yysyntax_error_status = YYSYNTAX_ERROR;
-        if (yysyntax_error_status == 0)
-          yymsgp = yymsg;
-        else if (yysyntax_error_status == 1)
-          {
-            if (yymsg != yymsgbuf)
-              YYSTACK_FREE (yymsg);
-            yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
-            if (!yymsg)
-              {
-                yymsg = yymsgbuf;
-                yymsg_alloc = sizeof yymsgbuf;
-                yysyntax_error_status = 2;
-              }
-            else
-              {
-                yysyntax_error_status = YYSYNTAX_ERROR;
-                yymsgp = yymsg;
-              }
-          }
-        yyerror (yymsgp);
-        if (yysyntax_error_status == 2)
-          goto yyexhaustedlab;
-      }
-# undef YYSYNTAX_ERROR
-#endif
     }
 
-
-
   if (yyerrstatus == 3)
     {
       /* If just tried and failed to reuse lookahead token after an
@@ -3923,6 +3979,7 @@ yyerrorlab:
      label yyerrorlab therefore never appears in user code.  */
   if (0)
     YYERROR;
+  ++yynerrs;
 
   /* Do not reclaim the symbols of the rule whose action triggered
      this YYERROR.  */
@@ -3939,13 +3996,14 @@ yyerrorlab:
 yyerrlab1:
   yyerrstatus = 3;      /* Each real token shifted decrements this.  */
 
+  /* Pop stack until we find a state that shifts the error token.  */
   for (;;)
     {
       yyn = yypact[yystate];
       if (!yypact_value_is_default (yyn))
         {
-          yyn += YYTERROR;
-          if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+          yyn += YYSYMBOL_YYerror;
+          if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror)
             {
               yyn = yytable[yyn];
               if (0 < yyn)
@@ -3959,7 +4017,7 @@ yyerrlab1:
 
 
       yydestruct ("Error: popping",
-                  yystos[yystate], yyvsp);
+                  YY_ACCESSING_SYMBOL (yystate), yyvsp);
       YYPOPSTACK (1);
       yystate = *yyssp;
       YY_STACK_PRINT (yyss, yyssp);
@@ -3971,7 +4029,7 @@ yyerrlab1:
 
 
   /* Shift the error token.  */
-  YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+  YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (yyn), yyvsp, yylsp);
 
   yystate = yyn;
   goto yynewstate;
@@ -3982,7 +4040,7 @@ yyerrlab1:
 `-------------------------------------*/
 yyacceptlab:
   yyresult = 0;
-  goto yyreturn;
+  goto yyreturnlab;
 
 
 /*-----------------------------------.
@@ -3990,24 +4048,22 @@ yyacceptlab:
 `-----------------------------------*/
 yyabortlab:
   yyresult = 1;
-  goto yyreturn;
+  goto yyreturnlab;
 
 
-#if !defined yyoverflow || YYERROR_VERBOSE
-/*-------------------------------------------------.
-| yyexhaustedlab -- memory exhaustion comes here.  |
-`-------------------------------------------------*/
+/*-----------------------------------------------------------.
+| yyexhaustedlab -- YYNOMEM (memory exhaustion) comes here.  |
+`-----------------------------------------------------------*/
 yyexhaustedlab:
   yyerror (YY_("memory exhausted"));
   yyresult = 2;
-  /* Fall through.  */
-#endif
+  goto yyreturnlab;
 
 
-/*-----------------------------------------------------.
-| yyreturn -- parsing is finished, return the result.  |
-`-----------------------------------------------------*/
-yyreturn:
+/*----------------------------------------------------------.
+| yyreturnlab -- parsing is finished, clean up and return.  |
+`----------------------------------------------------------*/
+yyreturnlab:
   if (yychar != YYEMPTY)
     {
       /* Make sure we have latest lookahead translation.  See comments at
@@ -4023,18 +4079,16 @@ yyreturn:
   while (yyssp != yyss)
     {
       yydestruct ("Cleanup: popping",
-                  yystos[*yyssp], yyvsp);
+                  YY_ACCESSING_SYMBOL (+*yyssp), yyvsp);
       YYPOPSTACK (1);
     }
 #ifndef yyoverflow
   if (yyss != yyssa)
     YYSTACK_FREE (yyss);
 #endif
-#if YYERROR_VERBOSE
-  if (yymsg != yymsgbuf)
-    YYSTACK_FREE (yymsg);
-#endif
+
   return yyresult;
 }
-#line 1106 "gram.y"
+
+#line 1136 "gram.y"
 
diff --git a/gen/gram.tab.h b/gen/gram.tab.h
index e280f89..3dc5334 100644
--- a/gen/gram.tab.h
+++ b/gen/gram.tab.h
@@ -1,8 +1,8 @@
-/* A Bison parser, made by GNU Bison 3.4.1.  */
+/* A Bison parser, made by GNU Bison 3.8.2.  */
 
 /* Bison interface for Yacc-like parsers in C
 
-   Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2019 Free Software Foundation,
+   Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation,
    Inc.
 
    This program is free software: you can redistribute it and/or modify
@@ -16,7 +16,7 @@
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
 
 /* As a special exception, you may create a larger work that contains
    part or all of the Bison parser skeleton and distribute that work
@@ -31,11 +31,12 @@
    This special exception was added by the Free Software Foundation in
    version 2.2 of Bison.  */
 
-/* Undocumented macros, especially those whose name start with YY_,
-   are private implementation details.  Do not rely on them.  */
+/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual,
+   especially those whose name start with YY_ or yy_.  They are
+   private implementation details that can be changed or removed.  */
 
-#ifndef YY_YY_HOME_FULLERMD_WORK_CTWM_BZR_4_0_X_CTWM_MKTAR_ASYJVB_CTWM_4_0_3_BUILD_GRAM_TAB_H_INCLUDED
-# define YY_YY_HOME_FULLERMD_WORK_CTWM_BZR_4_0_X_CTWM_MKTAR_ASYJVB_CTWM_4_0_3_BUILD_GRAM_TAB_H_INCLUDED
+#ifndef YY_YY_HOME_FULLERMD_WORK_CTWM_BZR_DEV_CTWM_MKTAR_4Y0T7B_CTWM_4_1_0_BUILD_GRAM_TAB_H_INCLUDED
+# define YY_YY_HOME_FULLERMD_WORK_CTWM_BZR_DEV_CTWM_MKTAR_4Y0T7B_CTWM_4_1_0_BUILD_GRAM_TAB_H_INCLUDED
 /* Debug traces.  */
 #ifndef YYDEBUG
 # define YYDEBUG 0
@@ -44,139 +45,145 @@
 extern int yydebug;
 #endif
 
-/* Token type.  */
+/* Token kinds.  */
 #ifndef YYTOKENTYPE
 # define YYTOKENTYPE
   enum yytokentype
   {
-    LB = 258,
-    RB = 259,
-    LP = 260,
-    RP = 261,
-    MENUS = 262,
-    MENU = 263,
-    BUTTON = 264,
-    DEFAULT_FUNCTION = 265,
-    PLUS = 266,
-    MINUS = 267,
-    ALL = 268,
-    OR = 269,
-    CURSORS = 270,
-    PIXMAPS = 271,
-    ICONS = 272,
-    COLOR = 273,
-    SAVECOLOR = 274,
-    MONOCHROME = 275,
-    FUNCTION = 276,
-    ICONMGR_SHOW = 277,
-    ICONMGR = 278,
-    ALTER = 279,
-    WINDOW_FUNCTION = 280,
-    ZOOM = 281,
-    ICONMGRS = 282,
-    ICONMGR_GEOMETRY = 283,
-    ICONMGR_NOSHOW = 284,
-    MAKE_TITLE = 285,
-    ICONIFY_BY_UNMAPPING = 286,
-    DONT_ICONIFY_BY_UNMAPPING = 287,
-    AUTO_POPUP = 288,
-    NO_BORDER = 289,
-    NO_ICON_TITLE = 290,
-    NO_TITLE = 291,
-    AUTO_RAISE = 292,
-    NO_HILITE = 293,
-    ICON_REGION = 294,
-    WINDOW_REGION = 295,
-    META = 296,
-    SHIFT = 297,
-    LOCK = 298,
-    CONTROL = 299,
-    WINDOW = 300,
-    TITLE = 301,
-    ICON = 302,
-    ROOT = 303,
-    FRAME = 304,
-    COLON = 305,
-    EQUALS = 306,
-    SQUEEZE_TITLE = 307,
-    DONT_SQUEEZE_TITLE = 308,
-    WARP_ON_DEICONIFY = 309,
-    START_ICONIFIED = 310,
-    NO_TITLE_HILITE = 311,
-    TITLE_HILITE = 312,
-    MOVE = 313,
-    RESIZE = 314,
-    WAITC = 315,
-    SELECT = 316,
-    KILL = 317,
-    LEFT_TITLEBUTTON = 318,
-    RIGHT_TITLEBUTTON = 319,
-    NUMBER = 320,
-    KEYWORD = 321,
-    NKEYWORD = 322,
-    CKEYWORD = 323,
-    CLKEYWORD = 324,
-    FKEYWORD = 325,
-    FSKEYWORD = 326,
-    FNKEYWORD = 327,
-    PRIORITY_SWITCHING = 328,
-    PRIORITY_NOT_SWITCHING = 329,
-    SKEYWORD = 330,
-    SSKEYWORD = 331,
-    WINDOW_RING = 332,
-    WINDOW_RING_EXCLUDE = 333,
-    WARP_CURSOR = 334,
-    ERRORTOKEN = 335,
-    GRAVITY = 336,
-    SIJENUM = 337,
-    NO_STACKMODE = 338,
-    ALWAYS_ON_TOP = 339,
-    WORKSPACE = 340,
-    WORKSPACES = 341,
-    WORKSPCMGR_GEOMETRY = 342,
-    OCCUPYALL = 343,
-    OCCUPYLIST = 344,
-    MAPWINDOWCURRENTWORKSPACE = 345,
-    MAPWINDOWDEFAULTWORKSPACE = 346,
-    ON_TOP_PRIORITY = 347,
-    UNMAPBYMOVINGFARAWAY = 348,
-    OPAQUEMOVE = 349,
-    NOOPAQUEMOVE = 350,
-    OPAQUERESIZE = 351,
-    NOOPAQUERESIZE = 352,
-    DONTSETINACTIVE = 353,
-    CHANGE_WORKSPACE_FUNCTION = 354,
-    DEICONIFY_FUNCTION = 355,
-    ICONIFY_FUNCTION = 356,
-    AUTOSQUEEZE = 357,
-    STARTSQUEEZED = 358,
-    DONT_SAVE = 359,
-    AUTO_LOWER = 360,
-    ICONMENU_DONTSHOW = 361,
-    WINDOW_BOX = 362,
-    IGNOREMODIFIER = 363,
-    WINDOW_GEOMETRIES = 364,
-    ALWAYSSQUEEZETOGRAVITY = 365,
-    VIRTUAL_SCREENS = 366,
-    IGNORE_TRANSIENT = 367,
-    EWMH_IGNORE = 368,
-    MWM_IGNORE = 369,
-    RPLAY_SOUNDS = 370,
-    FORCE_FOCUS = 371,
-    STRING = 372
+    YYEMPTY = -2,
+    YYEOF = 0,                     /* "end of file"  */
+    YYerror = 256,                 /* error  */
+    YYUNDEF = 257,                 /* "invalid token"  */
+    LB = 258,                      /* LB  */
+    RB = 259,                      /* RB  */
+    LP = 260,                      /* LP  */
+    RP = 261,                      /* RP  */
+    MENUS = 262,                   /* MENUS  */
+    MENU = 263,                    /* MENU  */
+    BUTTON = 264,                  /* BUTTON  */
+    DEFAULT_FUNCTION = 265,        /* DEFAULT_FUNCTION  */
+    PLUS = 266,                    /* PLUS  */
+    MINUS = 267,                   /* MINUS  */
+    ALL = 268,                     /* ALL  */
+    OR = 269,                      /* OR  */
+    CURSORS = 270,                 /* CURSORS  */
+    PIXMAPS = 271,                 /* PIXMAPS  */
+    ICONS = 272,                   /* ICONS  */
+    COLOR = 273,                   /* COLOR  */
+    SAVECOLOR = 274,               /* SAVECOLOR  */
+    MONOCHROME = 275,              /* MONOCHROME  */
+    FUNCTION = 276,                /* FUNCTION  */
+    ICONMGR_SHOW = 277,            /* ICONMGR_SHOW  */
+    ICONMGR = 278,                 /* ICONMGR  */
+    ALTER = 279,                   /* ALTER  */
+    WINDOW_FUNCTION = 280,         /* WINDOW_FUNCTION  */
+    ZOOM = 281,                    /* ZOOM  */
+    ICONMGRS = 282,                /* ICONMGRS  */
+    ICONMGR_GEOMETRY = 283,        /* ICONMGR_GEOMETRY  */
+    ICONMGR_NOSHOW = 284,          /* ICONMGR_NOSHOW  */
+    MAKE_TITLE = 285,              /* MAKE_TITLE  */
+    ICONIFY_BY_UNMAPPING = 286,    /* ICONIFY_BY_UNMAPPING  */
+    DONT_ICONIFY_BY_UNMAPPING = 287, /* DONT_ICONIFY_BY_UNMAPPING  */
+    AUTO_POPUP = 288,              /* AUTO_POPUP  */
+    NO_BORDER = 289,               /* NO_BORDER  */
+    NO_ICON_TITLE = 290,           /* NO_ICON_TITLE  */
+    NO_TITLE = 291,                /* NO_TITLE  */
+    AUTO_RAISE = 292,              /* AUTO_RAISE  */
+    NO_HILITE = 293,               /* NO_HILITE  */
+    ICON_REGION = 294,             /* ICON_REGION  */
+    WINDOW_REGION = 295,           /* WINDOW_REGION  */
+    META = 296,                    /* META  */
+    SHIFT = 297,                   /* SHIFT  */
+    LOCK = 298,                    /* LOCK  */
+    CONTROL = 299,                 /* CONTROL  */
+    WINDOW = 300,                  /* WINDOW  */
+    TITLE = 301,                   /* TITLE  */
+    ICON = 302,                    /* ICON  */
+    ROOT = 303,                    /* ROOT  */
+    FRAME = 304,                   /* FRAME  */
+    COLON = 305,                   /* COLON  */
+    EQUALS = 306,                  /* EQUALS  */
+    SQUEEZE_TITLE = 307,           /* SQUEEZE_TITLE  */
+    DONT_SQUEEZE_TITLE = 308,      /* DONT_SQUEEZE_TITLE  */
+    WARP_ON_DEICONIFY = 309,       /* WARP_ON_DEICONIFY  */
+    START_ICONIFIED = 310,         /* START_ICONIFIED  */
+    NO_TITLE_HILITE = 311,         /* NO_TITLE_HILITE  */
+    TITLE_HILITE = 312,            /* TITLE_HILITE  */
+    MOVE = 313,                    /* MOVE  */
+    RESIZE = 314,                  /* RESIZE  */
+    WAITC = 315,                   /* WAITC  */
+    SELECT = 316,                  /* SELECT  */
+    KILL = 317,                    /* KILL  */
+    LEFT_TITLEBUTTON = 318,        /* LEFT_TITLEBUTTON  */
+    RIGHT_TITLEBUTTON = 319,       /* RIGHT_TITLEBUTTON  */
+    NUMBER = 320,                  /* NUMBER  */
+    KEYWORD = 321,                 /* KEYWORD  */
+    NKEYWORD = 322,                /* NKEYWORD  */
+    CKEYWORD = 323,                /* CKEYWORD  */
+    CLKEYWORD = 324,               /* CLKEYWORD  */
+    FKEYWORD = 325,                /* FKEYWORD  */
+    FSKEYWORD = 326,               /* FSKEYWORD  */
+    FNKEYWORD = 327,               /* FNKEYWORD  */
+    PRIORITY_SWITCHING = 328,      /* PRIORITY_SWITCHING  */
+    PRIORITY_NOT_SWITCHING = 329,  /* PRIORITY_NOT_SWITCHING  */
+    SKEYWORD = 330,                /* SKEYWORD  */
+    SSKEYWORD = 331,               /* SSKEYWORD  */
+    WINDOW_RING = 332,             /* WINDOW_RING  */
+    WINDOW_RING_EXCLUDE = 333,     /* WINDOW_RING_EXCLUDE  */
+    WARP_CURSOR = 334,             /* WARP_CURSOR  */
+    ERRORTOKEN = 335,              /* ERRORTOKEN  */
+    GRAVITY = 336,                 /* GRAVITY  */
+    SIJENUM = 337,                 /* SIJENUM  */
+    NO_STACKMODE = 338,            /* NO_STACKMODE  */
+    ALWAYS_ON_TOP = 339,           /* ALWAYS_ON_TOP  */
+    WORKSPACE = 340,               /* WORKSPACE  */
+    WORKSPACES = 341,              /* WORKSPACES  */
+    WORKSPCMGR_GEOMETRY = 342,     /* WORKSPCMGR_GEOMETRY  */
+    OCCUPYALL = 343,               /* OCCUPYALL  */
+    OCCUPYLIST = 344,              /* OCCUPYLIST  */
+    MAPWINDOWCURRENTWORKSPACE = 345, /* MAPWINDOWCURRENTWORKSPACE  */
+    MAPWINDOWDEFAULTWORKSPACE = 346, /* MAPWINDOWDEFAULTWORKSPACE  */
+    ON_TOP_PRIORITY = 347,         /* ON_TOP_PRIORITY  */
+    UNMAPBYMOVINGFARAWAY = 348,    /* UNMAPBYMOVINGFARAWAY  */
+    OPAQUEMOVE = 349,              /* OPAQUEMOVE  */
+    NOOPAQUEMOVE = 350,            /* NOOPAQUEMOVE  */
+    OPAQUERESIZE = 351,            /* OPAQUERESIZE  */
+    NOOPAQUERESIZE = 352,          /* NOOPAQUERESIZE  */
+    DONTSETINACTIVE = 353,         /* DONTSETINACTIVE  */
+    CHANGE_WORKSPACE_FUNCTION = 354, /* CHANGE_WORKSPACE_FUNCTION  */
+    DEICONIFY_FUNCTION = 355,      /* DEICONIFY_FUNCTION  */
+    ICONIFY_FUNCTION = 356,        /* ICONIFY_FUNCTION  */
+    AUTOSQUEEZE = 357,             /* AUTOSQUEEZE  */
+    STARTSQUEEZED = 358,           /* STARTSQUEEZED  */
+    DONT_SAVE = 359,               /* DONT_SAVE  */
+    AUTO_LOWER = 360,              /* AUTO_LOWER  */
+    ICONMENU_DONTSHOW = 361,       /* ICONMENU_DONTSHOW  */
+    WINDOW_BOX = 362,              /* WINDOW_BOX  */
+    IGNOREMODIFIER = 363,          /* IGNOREMODIFIER  */
+    WINDOW_GEOMETRIES = 364,       /* WINDOW_GEOMETRIES  */
+    ALWAYSSQUEEZETOGRAVITY = 365,  /* ALWAYSSQUEEZETOGRAVITY  */
+    VIRTUAL_SCREENS = 366,         /* VIRTUAL_SCREENS  */
+    IGNORE_TRANSIENT = 367,        /* IGNORE_TRANSIENT  */
+    EWMH_IGNORE = 368,             /* EWMH_IGNORE  */
+    MWM_IGNORE = 369,              /* MWM_IGNORE  */
+    MONITOR_LAYOUT = 370,          /* MONITOR_LAYOUT  */
+    RPLAY_SOUNDS = 371,            /* RPLAY_SOUNDS  */
+    FORCE_FOCUS = 372,             /* FORCE_FOCUS  */
+    STRING = 373                   /* STRING  */
   };
+  typedef enum yytokentype yytoken_kind_t;
 #endif
 
 /* Value type.  */
 #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
 union YYSTYPE
 {
-#line 62 "gram.y"
+#line 64 "gram.y"
 
     int num;
     char *ptr;
 
-#line 180 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/gram.tab.h"
+#line 187 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/gram.tab.h"
 
 };
 typedef union YYSTYPE YYSTYPE;
@@ -187,6 +194,8 @@ typedef union YYSTYPE YYSTYPE;
 
 extern YYSTYPE yylval;
 
+
 int yyparse (void);
 
-#endif /* !YY_YY_HOME_FULLERMD_WORK_CTWM_BZR_4_0_X_CTWM_MKTAR_ASYJVB_CTWM_4_0_3_BUILD_GRAM_TAB_H_INCLUDED  */
+
+#endif /* !YY_YY_HOME_FULLERMD_WORK_CTWM_BZR_DEV_CTWM_MKTAR_4Y0T7B_CTWM_4_1_0_BUILD_GRAM_TAB_H_INCLUDED  */
diff --git a/gen/lex.c b/gen/lex.c
index c7f08d3..8ced847 100644
--- a/gen/lex.c
+++ b/gen/lex.c
@@ -1,6 +1,6 @@
-#line 1 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/lex.c"
+#line 1 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/lex.c"
 
-#line 3 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/lex.c"
+#line 3 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/lex.c"
 
 #define  YY_INT_ALIGNED short int
 
@@ -513,11 +513,11 @@ char *yytext;
 #define YY_NO_UNPUT
 #define YY_NO_INPUT
 
-#line 516 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/lex.c"
+#line 516 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/lex.c"
 #line 55 "lex.l"
  /* Requires flex 2.5.1 (28Mar95) */
  /* Requires flex 2.5.2 (25Apr95) */
-#line 520 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/lex.c"
+#line 520 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/lex.c"
 
 #define INITIAL 0
 
@@ -734,7 +734,7 @@ YY_DECL
 	{
 #line 60 "lex.l"
 
-#line 737 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/lex.c"
+#line 737 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/lex.c"
 
 	while ( /*CONSTCOND*/1 )		/* loops until end-of-file is reached */
 		{
@@ -896,7 +896,7 @@ YY_RULE_SETUP
 #line 97 "lex.l"
 ECHO;
 	YY_BREAK
-#line 899 "/home/fullermd/work/ctwm/bzr/4.0.x/ctwm-mktar.asYJVb/ctwm-4.0.3/build/lex.c"
+#line 899 "/home/fullermd/work/ctwm/bzr/dev/ctwm-mktar.4Y0T7b/ctwm-4.1.0/build/lex.c"
 case YY_STATE_EOF(INITIAL):
 	yyterminate();
 
diff --git a/gen/version.c.in b/gen/version.c.in
index 07e9066..1559b4d 100644
--- a/gen/version.c.in
+++ b/gen/version.c.in
@@ -26,7 +26,7 @@ const char *VersionNumber_minor = VERSION_MINOR;
 const char *VersionNumber_patch = VERSION_PATCH;
 const char *VersionNumber_addl  = VERSION_ADDL;
 const char *VCSType     = "bzr";
-const char *VCSRevision = "fullermd@over-yonder.net-20190721212859-1hko50q7rrvqttfb";
+const char *VCSRevision = "fullermd@over-yonder.net-20230326223622-nydwio13vncluzwi";
 
 #ifdef BUILD_VERSION_BIN
 #include <stdio.h>
diff --git a/gram.y b/gram.y
index db03436..f022450 100644
--- a/gram.y
+++ b/gram.y
@@ -30,7 +30,9 @@
 #include "otp.h"
 #include "iconmgr.h"
 #include "icons.h"
+#ifdef WINBOX
 #include "windowbox.h"
+#endif
 #include "functions_defs.h"
 #include "list.h"
 #include "util.h"
@@ -92,6 +94,7 @@ int yylex(void);
 %token <num> IGNORE_TRANSIENT
 %token <num> EWMH_IGNORE
 %token <num> MWM_IGNORE
+%token <num> MONITOR_LAYOUT
 %token <num> RPLAY_SOUNDS
 %token <num> FORCE_FOCUS
 %token <ptr> STRING
@@ -151,7 +154,9 @@ stmt		: error
 		  win_list
 
 		| WINDOW_BOX string string {
+#ifdef WINBOX
 		      curplist = addWindowBox ($2, $3);
+#endif
 		  }
 		  win_list
 
@@ -318,7 +323,14 @@ stmt		: error
 		| AUTO_POPUP		{ Scr->AutoPopup = true; }
 		| AUTO_POPUP		{ curplist = &Scr->AutoPopupL; }
 		  win_list
-		| DONT_SAVE		{ curplist = &Scr->DontSave; }
+		| DONT_SAVE		{
+#ifndef SESSION
+			twmrc_error_prefix();
+			fprintf(stderr, "DontSave ignored; session support "
+					"disabled.\n");
+#endif
+				curplist = &Scr->DontSave;
+			}
 		  win_list
 		| NO_ICON_TITLE		{ curplist = &Scr->NoIconTitle; }
 		  win_list
@@ -419,11 +431,13 @@ stmt		: error
 		| WINDOW_GEOMETRIES	{  }
 		  wingeom_list
 		| VIRTUAL_SCREENS	{ }
-		  geom_list
+		  vscreen_geom_list
 		| EWMH_IGNORE		{ }
 		  ewmh_ignore_list
 		| MWM_IGNORE		{ }
 		  mwm_ignore_list
+		| MONITOR_LAYOUT { init_layout_override(); }
+			layout_geom_list
 		| RPLAY_SOUNDS { }
 		  rplay_sounds_list
 		| FORCE_FOCUS { Scr->ForceFocus = true; }
@@ -757,14 +771,18 @@ wingeom_entries	: /* Empty */
 wingeom_entry	: string string	{ AddToList (&Scr->WindowGeometries, $1, $2); }
 		;
 
-geom_list	: LB geom_entries RB {}
+vscreen_geom_list	: LB vscreen_geom_entries RB {}
 		;
 
-geom_entries	: /* Empty */
-		| geom_entries geom_entry
+vscreen_geom_entries	: /* Empty */
+		| vscreen_geom_entries vscreen_geom_entry
 		;
 
-geom_entry	: string { AddToList (&Scr->VirtualScreens, $1, ""); }
+vscreen_geom_entry	: string {
+#ifdef VSCREEN
+				   AddToList (&Scr->VirtualScreens, $1, "");
+#endif
+				   }
 		;
 
 
@@ -790,6 +808,17 @@ mwm_ignore_entry	: string { add_mwm_ignore($1); }
 		;
 
 
+layout_geom_list	: LB layout_geom_entries RB { proc_layout_override(); }
+		;
+
+layout_geom_entries	: /* Empty */
+		| layout_geom_entries layout_geom_entry
+		;
+
+layout_geom_entry	: string { add_layout_override_entry($1); }
+		;
+
+
 squeeze		: SQUEEZE_TITLE {
 				    if (HasShape) Scr->SqueezeTitle = true;
 				}
@@ -1052,6 +1081,7 @@ action		: FKEYWORD	{ $$ = $1; }
 						 Action);
 					$$ = F_NOP;
 				    }
+				    break;
 				  case F_WARPTOSCREEN:
 				    if (!CheckWarpScreenArg (Action)) {
 					twmrc_error_prefix();
diff --git a/iconmgr.c b/iconmgr.c
index ad05a22..8c36b75 100644
--- a/iconmgr.c
+++ b/iconmgr.c
@@ -39,9 +39,11 @@
 #include "otp.h"
 #include "add_window.h"
 #include "gram.tab.h"
+#include "vscreen.h"
 #include "win_decorations.h"
 #include "win_resize.h"
 #include "win_utils.h"
+#include "xparsegeometry.h"
 
 
 /* Where we start drawing the name in the icon manager */
@@ -89,6 +91,11 @@ void CreateIconManagers(void)
 		Scr->siconifyPm = Create2DIconManagerIcon();
 	}
 
+	// This loop is confusing.  The inner for() loops p over the ->next
+	// elements in the list, which is all the iconmgr's in the workspace.
+	// The outer for() loops q over the ->nextv (<-- extra 'v' on the
+	// end), which is a link to the head of the iconmgr list for the
+	// _next_ workspace.
 	ws = Scr->workSpaceMgr.workSpaceList;
 	for(IconMgr *q = Scr->iconmgr; q != NULL; q = q->nextv) {
 		for(IconMgr *p = q; p != NULL; p = p->next) {
@@ -104,8 +111,9 @@ void CreateIconManagers(void)
 			if(!p->geometry || !strlen(p->geometry)) {
 				p->geometry = "+0+0";
 			}
-			mask = XParseGeometry(p->geometry, &gx, &gy,
-			                      (unsigned int *) &p->width, (unsigned int *)&p->height);
+			mask = RLayoutXParseGeometry(Scr->Layout, p->geometry,
+			                             &gx, &gy,
+			                             (unsigned int *) &p->width, (unsigned int *)&p->height);
 
 			bw = LookInList(Scr->NoBorder, imname, NULL) ? 0 :
 			     (Scr->ThreeDBorderWidth ? Scr->ThreeDBorderWidth : Scr->BorderWidth);
@@ -165,28 +173,30 @@ void CreateIconManagers(void)
 
 
 			p->twm_win = AddWindow(p->w, AWT_ICON_MANAGER, p, Scr->currentvs);
-			/*
-			 * SetupOccupation() called from AddWindow() doesn't setup
-			 * occupation for icon managers, nor clear vs if occupation lacks.
-			 *
-			 * There is no Scr->currentvs->wsw->currentwspc set up this
-			 * early, so we can't check with that; the best check we can do
-			 * is use ws->number.  This may be incorrect when re-starting
-			 * ctwm.
-			 */
+
+			// SetupOccupation() called from AddWindow() doesn't setup
+			// occupation for icon managers, nor clear vs if occupation
+			// lacks.  So make it occupy the one we're setting up, or the
+			// 1st if we ran out somehow...
 			if(ws) {
 				p->twm_win->occupation = 1 << ws->number;
-				if(ws->number > 0) {
+
+				// ConfigureWorkSpaceManager() ran before us, so we can
+				// tell whether we're in the ws to reveal this IM.
+				if(ws->number != Scr->currentvs->wsw->currentwspc->number) {
 					p->twm_win->vs = NULL;
 				}
 			}
 			else {
 				p->twm_win->occupation = 1;
 			}
+
 #ifdef DEBUG_ICONMGR
 			fprintf(stderr,
-			        "CreateIconManagers: IconMgr %p: x=%d y=%d w=%d h=%d occupation=%x\n",
-			        p, gx, gy,  p->width, p->height, p->twm_win->occupation);
+			        "CreateIconManagers: IconMgr %p: twm_win=%p win=0x%lx "
+			        "name='%s' x=%d y=%d w=%d h=%d occupation=%x\n",
+			        p, p->twm_win, p->twm_win->w, p->name,
+			        gx, gy,  p->width, p->height, p->twm_win->occupation);
 #endif
 
 			{
@@ -1196,8 +1206,8 @@ void PackIconManager(IconMgr *ip)
 
 	XResizeWindow(dpy, ip->w, newwidth, ip->height);
 
-	mask = XParseGeometry(ip->geometry, &JunkX, &JunkY,
-	                      &JunkWidth, &JunkHeight);
+	mask = RLayoutXParseGeometry(Scr->Layout, ip->geometry, &JunkX, &JunkY,
+	                             &JunkWidth, &JunkHeight);
 	if(mask & XNegative) {
 		ip->twm_win->frame_x += ip->twm_win->frame_width - newwidth -
 		                        2 * ip->twm_win->frame_bw3D;
@@ -1247,8 +1257,7 @@ DrawIconManagerIconName(TwmWindow *tmp_win)
 		PackIconManagers();
 	}
 
-	DrawIconManagerBorder(iconmanagerlist, true);
-
+	// Write in the title
 	FB(iconmanagerlist->cp.fore, iconmanagerlist->cp.back);
 
 	/* XXX This is a completely absurd way of writing this */
@@ -1265,6 +1274,13 @@ DrawIconManagerIconName(TwmWindow *tmp_win)
 	 + ICON_MGR_IBORDER,
 	 tmp_win->icon_name,
 	 strlen(tmp_win->icon_name));
+
+	// Draw the border around it.  Our "border" isn't an X border, it's
+	// just our own drawing inside the X window.  Since XmbDrawString()
+	// believes it has all the space in the window to fill, it might
+	// scribble into the space where we're drawing the border, so draw
+	// the border after the text to cover it up.
+	DrawIconManagerBorder(iconmanagerlist, false);
 }
 
 
diff --git a/iconmgr.h b/iconmgr.h
index d036f4e..0865b36 100644
--- a/iconmgr.h
+++ b/iconmgr.h
@@ -34,10 +34,11 @@ struct WList {
 };
 
 struct IconMgr {
-	struct IconMgr *next;               /* ptr to the next icon manager */
-	struct IconMgr *prev;               /* ptr to the previous icon mgr */
-	struct IconMgr *lasti;              /* ptr to the last icon mgr */
-	struct IconMgr *nextv;              /* ptr to the next virt icon mgr */
+	struct IconMgr *next;  ///< Next iconmgr in this workspace
+	struct IconMgr *prev;  ///< Prev iconmgr in this workspace
+	struct IconMgr *lasti; ///< Last iconmgr in this workspace
+	struct IconMgr *nextv; ///< Next workspace's icon manager head
+
 	struct WList *first;                /* first window in the list */
 	struct WList *last;                 /* last window in the list */
 	struct WList *active;               /* the active entry */
diff --git a/icons.c b/icons.c
index 793858e..2c3a07e 100644
--- a/icons.c
+++ b/icons.c
@@ -38,6 +38,8 @@
 #include "image.h"
 #include "win_utils.h"
 #include "workspace_manager.h"
+#include "xparsegeometry.h"
+
 
 static void splitIconRegionEntry(IconEntry *ie, RegGravity grav1,
                                  RegGravity grav2, int w, int h);
@@ -162,8 +164,8 @@ AddIconRegion(const char *geom, RegGravity grav1, RegGravity grav2,
 	ir->stepy = stepy;
 	ir->x = ir->y = ir->w = ir->h = 0;
 
-	mask = XParseGeometry(geom, &ir->x, &ir->y, (unsigned int *)&ir->w,
-	                      (unsigned int *)&ir->h);
+	mask = RLayoutXParseGeometry(Scr->Layout, geom, &ir->x, &ir->y,
+	                             (unsigned int *)&ir->w, (unsigned int *)&ir->h);
 
 	if(mask & XNegative) {
 		ir->x += Scr->rootw - ir->w;
diff --git a/image.c b/image.c
index 66e2bae..abae241 100644
--- a/image.c
+++ b/image.c
@@ -44,6 +44,10 @@ GetImage(const char *name, ColorPair cp)
 	if(name == NULL) {
 		return NULL;
 	}
+	if(dpy == NULL) {
+		// May happen in special cases like --cfgchk with no $DISPLAY
+		return NULL;
+	}
 	image = NULL;
 
 	list = &Scr->ImageCache;
diff --git a/list.c b/list.c
index 3b2bb9f..27511b4 100644
--- a/list.c
+++ b/list.c
@@ -29,6 +29,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include "ctwm_shutdown.h"
 #include "screen.h"
 #include "list.h"
 #include "util.h"
@@ -71,7 +72,7 @@ void AddToList(name_list **list_head, const char *name, void *ptr)
 	if(nptr == NULL) {
 		fprintf(stderr, "unable to allocate %lu bytes for name_list\n",
 		        (unsigned long) sizeof(name_list));
-		Done(0);
+		DoShutdown();
 	}
 
 	nptr->next = *list_head;
diff --git a/mask_screen.c b/mask_screen.c
index ae552db..b683f5f 100644
--- a/mask_screen.c
+++ b/mask_screen.c
@@ -37,7 +37,6 @@ MaskScreen(char *file)
 	XEvent event;
 	Cursor waitcursor;
 	int x, y;
-	ColorPair WelcomeCp;
 	XColor black;
 
 	NewFontCursor(&waitcursor, "watch");
@@ -61,8 +60,10 @@ MaskScreen(char *file)
 		return;
 	}
 
-	WelcomeCp.fore = Scr->Black;
-	WelcomeCp.back = Scr->White;
+	ColorPair WelcomeCp = {
+		.fore = Scr->Black,
+		.back = Scr->White,
+	};
 	Scr->WelcomeCmap  = XCreateColormap(dpy, Scr->WindowMask, Scr->d_visual,
 	                                    AllocNone);
 	if(! Scr->WelcomeCmap) {
@@ -91,10 +92,15 @@ MaskScreen(char *file)
 		return;
 	}
 
-	if(CLarg.is_captive) {
+	if(0) {
+		// Dummy
+	}
+#ifdef CAPTIVE
+	else if(CLarg.is_captive) {
 		XSetWindowColormap(dpy, Scr->WindowMask, Scr->WelcomeCmap);
 		XSetWMColormapWindows(dpy, Scr->Root, &(Scr->WindowMask), 1);
 	}
+#endif
 	else {
 		XInstallColormap(dpy, Scr->WelcomeCmap);
 	}
@@ -173,9 +179,14 @@ UnmaskScreen(void)
 	}
 	XStoreColors(dpy, cmap, colors, 256);
 
-	if(CLarg.is_captive) {
+	if(0) {
+		// Dummy
+	}
+#ifdef CAPTIVE
+	else if(CLarg.is_captive) {
 		XSetWindowColormap(dpy, Scr->Root, cmap);
 	}
+#endif
 	else {
 		XInstallColormap(dpy, cmap);
 	}
@@ -201,9 +212,14 @@ UnmaskScreen(void)
 		select(0, NULL, NULL, NULL, &timeout);
 	}
 
-	if(CLarg.is_captive) {
+	if(0) {
+		// Dummy
+	}
+#ifdef CAPTIVE
+	else if(CLarg.is_captive) {
 		XSetWindowColormap(dpy, Scr->Root, stdcmap);
 	}
+#endif
 	else {
 		XInstallColormap(dpy, stdcmap);
 	}
diff --git a/menus.c b/menus.c
index 1d2c4a6..230aecf 100644
--- a/menus.c
+++ b/menus.c
@@ -103,15 +103,23 @@ AddFuncKey(char *name, int cont, int nmods, int func,
            MenuRoot *menu, char *win_name, char *action)
 {
 	FuncKey *tmp;
-	KeySym keysym;
-	KeyCode keycode;
+	KeySym keysym = NoSymbol;
+	KeyCode keycode = 0;
 
 	/*
 	 * Don't let a 0 keycode go through, since that means AnyKey to the
-	 * XGrabKey call in GrabKeys().
+	 * XGrabKey call in GrabKeys().  Conditionalize on dpy to handle
+	 * special cases where we don't have a server to talk to.
 	 */
-	if((keysym = XStringToKeysym(name)) == NoSymbol ||
-	                (keycode = XKeysymToKeycode(dpy, keysym)) == 0) {
+	keysym = XStringToKeysym(name);
+	if(dpy) {
+		keycode = XKeysymToKeycode(dpy, keysym);
+	}
+	if(keysym == NoSymbol || (dpy && keycode == 0)) {
+		fprintf(stderr, "ignore %s key binding (%s)\n", name,
+		        keysym == NoSymbol
+		        ? "key symbol not found"
+		        : "key code not found");
 		return false;
 	}
 
@@ -471,7 +479,7 @@ void MakeWorkspacesMenu(void)
 
 static bool fromMenu;
 bool
-cur_fromMenu()
+cur_fromMenu(void)
 {
 	return fromMenu;
 }
@@ -523,7 +531,7 @@ void UpdateMenu(void)
 		              &x_root, &y_root, &x, &y, &JunkMask);
 
 		/* if we haven't received the enter notify yet, wait */
-		if(ActiveMenu && !ActiveMenu->entered) {
+		if(!ActiveMenu->entered) {
 			continue;
 		}
 
@@ -783,10 +791,16 @@ MenuItem *AddToMenu(MenuRoot *menu, char *item, char *action,
 		CreateFonts(Scr);
 	}
 
-	XmbTextExtents(Scr->MenuFont.font_set,
-	               itemname, tmp->strlen,
-	               &ink_rect, &logical_rect);
-	width = logical_rect.width;
+	if(dpy) {
+		XmbTextExtents(Scr->MenuFont.font_set,
+		               itemname, tmp->strlen,
+		               &ink_rect, &logical_rect);
+		width = logical_rect.width;
+	}
+	else {
+		// Fake for non-dpy cases
+		width = 25;
+	}
 
 	if(width <= 0) {
 		width = 1;
@@ -836,11 +850,10 @@ void MakeMenus(void)
 
 void MakeMenu(MenuRoot *mr)
 {
-	MenuItem *start, *end, *cur, *tmp;
+	MenuItem *start, *tmp;
 	XColor f1, f2, f3;
 	XColor b1, b2, b3;
 	XColor save_fore, save_back;
-	int num, i;
 	int fred, fgreen, fblue;
 	int bred, bgreen, bblue;
 	int width, borderwidth;
@@ -860,7 +873,7 @@ void MakeMenu(MenuRoot *mr)
 			mr->width += 16 + 10;
 		}
 		width = mr->width + 10;
-		for(cur = mr->first; cur != NULL; cur = cur->next) {
+		for(MenuItem *cur = mr->first; cur != NULL; cur = cur->next) {
 			XmbTextExtents(Scr->MenuFont.font_set, cur->item, cur->strlen,
 			               &ink_rect, &logical_rect);
 			max_entry_height = MAX(max_entry_height, logical_rect.height);
@@ -1015,6 +1028,7 @@ void MakeMenu(MenuRoot *mr)
 		return;
 	}
 
+	// Do InterpolateMenuColors magic
 	start = mr->first;
 	while(1) {
 		for(; start != NULL; start = start->next) {
@@ -1026,6 +1040,7 @@ void MakeMenu(MenuRoot *mr)
 			break;
 		}
 
+		MenuItem *end;
 		for(end = start->next; end != NULL; end = end->next) {
 			if(end->user_colors) {
 				break;
@@ -1036,7 +1051,7 @@ void MakeMenu(MenuRoot *mr)
 		}
 
 		/* we have a start and end to interpolate between */
-		num = end->item_num - start->item_num;
+		int num = end->item_num - start->item_num;
 
 		f1.pixel = start->normal.fore;
 		XQueryColor(dpy, cmap, &f1);
@@ -1065,7 +1080,12 @@ void MakeMenu(MenuRoot *mr)
 		start->highlight.back = start->normal.fore;
 		start->highlight.fore = start->normal.back;
 		num -= 1;
-		for(i = 0, cur = start->next; i < num; i++, cur = cur->next) {
+		int i = 0;
+		MenuItem *cur = start->next;
+		// XXX Should be impossible to run out of cur's before num's,
+		// unless the item_num's are wrong (which would break other
+		// stuff), but add condition to quiet static analysis.
+		for(; cur != NULL && i < num ; i++, cur = cur->next) {
 			f3.red += fred;
 			f3.green += fgreen;
 			f3.blue += fblue;
@@ -1205,7 +1225,15 @@ PopUpMenu(MenuRoot *menu, int x, int y, bool center)
 			}
 			WindowNameCount++;
 		}
+
+		// Hack: always pretend there's at least one window, even if
+		// there are none; that lets us skip special cases for empty
+		// lists...
+		if(WindowNameCount == 0) {
+			WindowNameCount = 1;
+		}
 		WindowNames = calloc(WindowNameCount, sizeof(TwmWindow *));
+
 		WindowNameCount = 0;
 		for(tmp_win = Scr->FirstWindow;
 		                tmp_win != NULL;
@@ -1276,9 +1304,6 @@ PopUpMenu(MenuRoot *menu, int x, int y, bool center)
 	/* Keys added by dl */
 
 	if(menu == Scr->Keys) {
-		FuncKey *tmpKey;
-		char *tmpStr;
-		char *modStr;
 		char *oldact = 0;
 		int oldmod = 0;
 
@@ -1294,37 +1319,21 @@ PopUpMenu(MenuRoot *menu, int x, int y, bool center)
 
 		AddToMenu(menu, "Twm Keys", NULL, NULL, F_TITLE, NULL, NULL);
 
-		for(tmpKey = Scr->FuncKeyRoot.next; tmpKey != NULL;  tmpKey = tmpKey->next) {
+		for(const FuncKey *tmpKey = Scr->FuncKeyRoot.next; tmpKey != NULL;
+		                tmpKey = tmpKey->next) {
+			char *tmpStr;
+
 			if(tmpKey->func != F_EXEC) {
 				continue;
 			}
 			if((tmpKey->action == oldact) && (tmpKey->mods == oldmod)) {
 				continue;
 			}
-			switch(tmpKey->mods) {
-				case  1:
-					modStr = "S";
-					break;
-				case  4:
-					modStr = "C";
-					break;
-				case  5:
-					modStr = "S + C";
-					break;
-				case  8:
-					modStr = "M";
-					break;
-				case  9:
-					modStr = "S + M";
-					break;
-				case 12:
-					modStr = "C + M";
-					break;
-				default:
-					modStr = "";
-					break;
+
+			tmpStr = mk_twmkeys_entry(tmpKey);
+			if(tmpStr == NULL) {
+				tmpStr = strdup("(error)");
 			}
-			asprintf(&tmpStr, "[%s + %s] %s", tmpKey->name, modStr, tmpKey->action);
 
 			AddToMenu(menu, tmpStr, tmpKey->action, NULL, tmpKey->func, NULL, NULL);
 			oldact = tmpKey->action;
@@ -1381,23 +1390,7 @@ PopUpMenu(MenuRoot *menu, int x, int y, bool center)
 	/*
 	* clip to screen
 	*/
-	clipped = false;
-	if(x + menu->width > Scr->rootw) {
-		x = Scr->rootw - menu->width;
-		clipped = true;
-	}
-	if(x < 0) {
-		x = 0;
-		clipped = true;
-	}
-	if(y + menu->height > Scr->rooth) {
-		y = Scr->rooth - menu->height;
-		clipped = true;
-	}
-	if(y < 0) {
-		y = 0;
-		clipped = true;
-	}
+	clipped = ConstrainByLayout(Scr->Layout, -1, &x, menu->width, &y, menu->height);
 	MenuOrigins[MenuDepth].x = x;
 	MenuOrigins[MenuDepth].y = y;
 	MenuDepth++;
@@ -1654,3 +1647,72 @@ void WarpCursorToDefaultEntry(MenuRoot *menu)
 	             menu->width, menu->height, xl, yt);
 }
 
+
+
+/**
+ * Generate up a string representation of a keybinding->action.
+ * Internally used in generating TwmKeys menu.
+ */
+char *
+mk_twmkeys_entry(const FuncKey *key)
+{
+	char *ret;
+	//         S+  C+  5(Mx+)  5(Ax+)
+#define MSLEN (2 + 2 + 5 * 3 + 5 * 3)
+	char modStr[MSLEN + 1];
+	char *modStrCur = modStr;
+
+	// Init
+	*modStrCur = '\0';
+
+	// Check and add prefixes for each modifier
+#define DO(mask, str) do { \
+                if(key->mods & mask##Mask) { \
+                        const int tslen = sizeof(str) - 1; \
+                        if((modStrCur - modStr + tslen) >= MSLEN) { \
+                                fprintf(stderr, "BUG: No space to add '%s' " \
+                                                "in %s()\n", str, __func__); \
+                                return NULL; \
+                        } \
+                        strcpy(modStrCur, str); \
+                        modStrCur += tslen; \
+                } \
+        } while(0)
+
+	// Mod1 is Meta (== Alt), so is special and comes first, apart and
+	// differing from the other more generic ModX's.
+	DO(Mod1, "M+");
+
+	// Shift/Ctrl are normal common bits.
+	DO(Shift,   "S+");
+	DO(Control, "C+");
+
+	// Other Mod's and Alt's are weirder, but possible.
+	DO(Mod2, "M2+");
+	DO(Mod3, "M3+");
+	DO(Mod4, "M4+");
+	DO(Mod5, "M5+");
+
+	DO(Alt1, "A1+");
+	DO(Alt2, "A2+");
+	DO(Alt3, "A3+");
+	DO(Alt4, "A4+");
+	DO(Alt5, "A5+");
+
+	// Overflows for test.  Watch out for colliding with X or our *Mask
+	// defs.
+	// +1 when combined with above, should be enough
+#define Over1Mask (1<<30)
+	DO(Over1, "a");
+	// Way too big no matter what
+#define OverAllMask (1<<31)
+	DO(OverAll, "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz");
+
+#undef OverAllMask
+#undef Over1Mask
+
+#undef DO
+
+	asprintf(&ret, "[%s%s] %s", modStr, key->name, key->action);
+	return ret;
+}
diff --git a/menus.h b/menus.h
index e953301..bd53b6c 100644
--- a/menus.h
+++ b/menus.h
@@ -158,4 +158,7 @@ void MakeMenu(MenuRoot *mr);
 void MoveMenu(XEvent *eventp);
 void WarpCursorToDefaultEntry(MenuRoot *menu);
 
+// Mostly internal; exposed for testing
+char *mk_twmkeys_entry(const FuncKey *key);
+
 #endif /* _CTWM_MENUS_H */
diff --git a/minibuild/main.mk b/minibuild/main.mk
index 4404225..c76156d 100644
--- a/minibuild/main.mk
+++ b/minibuild/main.mk
@@ -27,6 +27,12 @@ OFILES+=${BDIR}/ewmh.o ${BDIR}/ewmh_atoms.o
 STDSRC+=${RTDIR}/ewmh.c
 GENSRC+=${BDIR}/ewmh_atoms.c
 
+# RANDR
+OPTDEFS+=XRANDR
+_LFLAGS+=-lXrandr
+OFILES+=${BDIR}/xrandr.o
+STDSRC+=${RTDIR}/xrandr.c
+
 
 
 ### Rules for generating various files
diff --git a/minibuild/mkmk.sh b/minibuild/mkmk.sh
index 53ea148..2af2ebe 100755
--- a/minibuild/mkmk.sh
+++ b/minibuild/mkmk.sh
@@ -30,6 +30,7 @@ ongfiles="${ongfiles} image_xpm.c"
 ongfiles="${ongfiles} image_jpeg.c"
 ongfiles="${ongfiles} parse_m4.c"
 ongfiles="${ongfiles} ewmh.c"
+ongfiles="${ongfiles} xrandr.c"
 
 
 # Top boilerplate
diff --git a/occupation.c b/occupation.c
index eedc4af..7f17720 100644
--- a/occupation.c
+++ b/occupation.c
@@ -202,11 +202,12 @@ SetupOccupation(TwmWindow *twm_win, int occupation_hint)
 	 * If it could be shown in one of the other vscreens, change the vscreen.
 	 */
 	if(!OCCUPY(twm_win, twm_win->vs->wsw->currentwspc)) {
-		VirtualScreen *vs;
 
 		twm_win->vs = NULL;
 
+#ifdef VSCREEN
 		if(Scr->numVscreens > 1) {
+			VirtualScreen *vs;
 			for(vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
 				if(OCCUPY(twm_win, vs->wsw->currentwspc)) {
 					twm_win->vs = vs;
@@ -215,6 +216,7 @@ SetupOccupation(TwmWindow *twm_win, int occupation_hint)
 				}
 			}
 		}
+#endif
 	}
 
 
@@ -1072,7 +1074,6 @@ Occupy(TwmWindow *twm_win)
 {
 	int          x, y;
 	unsigned int width, height;
-	int          xoffset, yoffset;
 	Window       w;
 	struct OccupyWindow    *occupyWindow;
 	TwmWindow *occupy_twm;
@@ -1092,28 +1093,18 @@ Occupy(TwmWindow *twm_win)
 	             &JunkBW, &JunkDepth);
 	XQueryPointer(dpy, Scr->Root, &JunkRoot, &JunkRoot, &JunkX, &JunkY,
 	              &x, &y, &JunkMask);
-	x -= (width  / 2);
-	y -= (height / 2);
-	if(x < 0) {
-		x = 0;
-	}
-	if(y < 0) {
-		y = 0;
-	}
-	xoffset = width  + 2 * Scr->BorderWidth;
-	yoffset = height + 2 * Scr->BorderWidth + Scr->TitleHeight;
-
-	/* ... (but not off the screen!) */
-	if((x + xoffset) > Scr->rootw) {
-		x = Scr->rootw - xoffset;
-	}
-	if((y + yoffset) > Scr->rooth) {
-		y = Scr->rooth - yoffset;
-	}
 
 	occupy_twm = occupyWindow->twm_win;
 	occupy_twm->occupation = twm_win->occupation;
 
+	width += 2 * (occupy_twm->frame_bw3D + occupy_twm->frame_bw);
+	height += 2 * (occupy_twm->frame_bw3D + occupy_twm->frame_bw);
+	x -= (width  / 2);
+	y -= (height / 2);
+
+	/* Clip to screen */
+	ConstrainByLayout(Scr->BorderedLayout, -1, &x, width, &y, height);
+
 	/* Move the occupy window to where it should be */
 	if(occupy_twm->parent_vs != twm_win->parent_vs) {
 		occupy_twm->vs = twm_win->parent_vs;
diff --git a/otp.c b/otp.c
index 7c53a63..e8977c1 100644
--- a/otp.c
+++ b/otp.c
@@ -80,6 +80,7 @@ typedef struct Box {
 
 
 static bool OtpCheckConsistencyVS(VirtualScreen *currentvs, Window vroot);
+static void OwlPrettyPrint(const OtpWinList *start);
 static void OwlSetAflagMask(OtpWinList *owl, unsigned mask, unsigned setto);
 static void OwlSetAflag(OtpWinList *owl, unsigned flag);
 static void OwlClearAflag(OtpWinList *owl, unsigned flag);
@@ -245,8 +246,19 @@ static bool OtpCheckConsistencyVS(VirtualScreen *currentvs, Window vroot)
 		assert(owl->pri_base <= OTP_MAX);
 
 		/* List should be bottom->top, so effective pri better ascend */
-		assert(PRI(owl) >= priority);
-		priority = PRI(owl);
+		{
+			const int nextpri = PRI(owl);
+			if(nextpri < priority) {
+				fprintf(stderr, "%s(): Priority went backward "
+				        "(%d:'%s' -> %d:'%s')\n",
+				        __func__,
+				        priority, owl->below->twm_win->name,
+				        nextpri, owl->twm_win->name);
+				OwlPrettyPrint(Scr->bottomOwl);
+				abort();
+			}
+			priority = nextpri;
+		}
 
 #if DEBUG_OTP
 
@@ -281,6 +293,7 @@ static bool OtpCheckConsistencyVS(VirtualScreen *currentvs, Window vroot)
 			nwins++;
 		}
 
+#ifdef WINBOX
 		if(twm_win->winbox) {
 			/*
 			 * We can't check windows in a WindowBox, since they are
@@ -289,6 +302,7 @@ static bool OtpCheckConsistencyVS(VirtualScreen *currentvs, Window vroot)
 			DPRINTF((stderr, "Can't check this window, it is in a WinBox\n"));
 			continue;
 		}
+#endif
 
 		/*
 		 * Check only windows from the current vitual screen; the others
@@ -370,6 +384,7 @@ static OtpWinList *GetOwlAtOrBelowInVS(OtpWinList *owl, VirtualScreen *vs)
 	return owl;
 }
 
+#ifdef WINBOX
 /*
  * Windows in a box don't really occur in the stacking order of the
  * root window.
@@ -394,6 +409,7 @@ static OtpWinList *GetOwlAtOrBelowInWinbox(OtpWinList **owlp, WindowBox *wb)
 	}
 	return owl;
 }
+#endif
 
 
 static void InsertOwlAbove(OtpWinList *owl, OtpWinList *other_owl)
@@ -432,12 +448,19 @@ static void InsertOwlAbove(OtpWinList *owl, OtpWinList *other_owl)
 		Scr->bottomOwl = owl;
 	}
 	else {
+#ifdef WINBOX
 		WindowBox *winbox = owl->twm_win->winbox;
+#endif
 		OtpWinList *vs_owl;
 
-		if(winbox != NULL) {
+		if(false) {
+			// dummy
+		}
+#ifdef WINBOX
+		else if(winbox != NULL) {
 			vs_owl = GetOwlAtOrBelowInWinbox(&other_owl, winbox);
 		}
+#endif
 		else {
 
 			vs_owl = GetOwlAtOrBelowInVS(other_owl, owl->twm_win->parent_vs);
@@ -876,9 +899,11 @@ void OtpSetPriority(TwmWindow *twm_win, WinType wintype, int new_pri, int where)
 	DPRINTF((stderr, "OtpSetPriority: new_pri=%d\n", new_pri));
 	assert(owl != NULL);
 
+#ifdef WINBOX
 	if(twm_win->winbox != NULL || twm_win->iswinbox) {
 		return;
 	}
+#endif
 
 	if(ABS(new_pri) > OTP_ZERO) {
 		DPRINTF((stderr, "invalid OnTopPriority value: %d\n", new_pri));
@@ -898,9 +923,11 @@ void OtpChangePriority(TwmWindow *twm_win, WinType wintype, int relpriority)
 	int priority = owl->pri_base + relpriority;
 	int where;
 
+#ifdef WINBOX
 	if(twm_win->winbox != NULL || twm_win->iswinbox) {
 		return;
 	}
+#endif
 
 	where = relpriority < 0 ? Below : Above;
 
@@ -919,9 +946,11 @@ void OtpSwitchPriority(TwmWindow *twm_win, WinType wintype)
 
 	assert(owl != NULL);
 
+#ifdef WINBOX
 	if(twm_win->winbox != NULL || twm_win->iswinbox) {
 		return;
 	}
+#endif
 
 	where = priority < OTP_ZERO ? Below : Above;
 	TryToMoveTransientsOfTo(owl, priority, where);
@@ -936,9 +965,11 @@ void OtpToggleSwitching(TwmWindow *twm_win, WinType wintype)
 	OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp;
 	assert(owl != NULL);
 
+#ifdef WINBOX
 	if(twm_win->winbox != NULL || twm_win->iswinbox) {
 		return;
 	}
+#endif
 
 	owl->switching = !owl->switching;
 
@@ -1197,11 +1228,16 @@ void OtpAdd(TwmWindow *twm_win, WinType wintype)
 
 	assert(*owlp == NULL);
 
-	/* windows in boxes *must* inherit priority from the box */
-	if(twm_win->winbox) {
+	if(false) {
+		// dummy
+	}
+#ifdef WINBOX
+	else if(twm_win->winbox) {
+		/* windows in boxes *must* inherit priority from the box */
 		parent = twm_win->winbox->twmwin->otp;
 		parent->switching = false;
 	}
+#endif
 	/* in case it's a transient, find the parent */
 	else if(wintype == WinWin && (twm_win->istransient
 	                              || !isGroupLeader(twm_win))) {
@@ -1398,7 +1434,7 @@ ReparentWindowAndIcon(Display *display, TwmWindow *twm_win,
 }
 
 /* Iterators.  */
-TwmWindow *OtpBottomWin()
+TwmWindow *OtpBottomWin(void)
 {
 	OtpWinList *owl = Scr->bottomOwl;
 	while(owl && owl->type != WinWin) {
@@ -1407,7 +1443,7 @@ TwmWindow *OtpBottomWin()
 	return owl ? owl->twm_win : NULL;
 }
 
-TwmWindow *OtpTopWin()
+TwmWindow *OtpTopWin(void)
 {
 	OtpWinList *owl = Scr->bottomOwl, *top = NULL;
 	while(owl) {
@@ -1438,6 +1474,46 @@ TwmWindow *OtpNextWinDown(TwmWindow *twm_win)
 }
 
 
+
+/*
+ * Outputting info to understand the state of OTP stuff.
+ */
+
+/// Pretty-print a whole OWL stack.  Works upward from the arg;
+/// generally, you'd call this with Scr->bottomOwl.
+static void
+OwlPrettyPrint(const OtpWinList *start)
+{
+	fprintf(stderr, "%s():\n", __func__);
+
+	for(const OtpWinList *owl = start ; owl != NULL ; owl = owl->above) {
+		fprintf(stderr, "  pri=%2d (%+d) %s 0x%lx:'%1.50s'\n",
+		        OtpEffectivePriority(owl->twm_win),
+		        OtpEffectiveDisplayPriority(owl->twm_win),
+		        (owl->type == WinWin ? "win" : "ico"),
+		        owl->twm_win->w, owl->twm_win->name);
+		fprintf(stderr, "         basepri=%d %s%s%s\n",
+		        owl->pri_base,
+#ifdef EWMH
+		        (owl->pri_aflags & OTP_AFLAG_ABOVE ? " _ABOVE" : ""),
+		        (owl->pri_aflags & OTP_AFLAG_BELOW ? " _BELOW" : ""),
+		        (owl->pri_aflags & OTP_AFLAG_FULLSCREEN ? " _FULLSCREEN" : "")
+#else
+		        "", "", ""
+#endif
+		       );
+		if(owl->twm_win->istransient) {
+			const TwmWindow *parent = GetTwmWindow(owl->twm_win->transientfor);
+			fprintf(stderr, "         transient for 0x%lx:%1.50s\n",
+			        parent->w, parent->name);
+		}
+	}
+
+	fprintf(stderr, "  Done.\n");
+}
+
+
+
 /*
  * Stuff for messing with pri_aflags
  */
@@ -1620,7 +1696,7 @@ OtpFocusWindowBE(TwmWindow *twm_win, int oldprio)
 	//
 	// XXX It should not be this freakin' hard to find a window's
 	// transients.  We should fix that more globally.
-	
+
 	// XXX Let's just get a friggin' vector implementation already...
 	size_t tlsz = 32;  // Should hardly ever be too small
 	size_t tlused = 0;
@@ -1637,7 +1713,7 @@ OtpFocusWindowBE(TwmWindow *twm_win, int oldprio)
 		OtpWinList *next = trans->above;
 
 		if((trans->type == WinWin)
-				&& isTransientOf(trans->twm_win, twm_win)) {
+		                && isTransientOf(trans->twm_win, twm_win)) {
 			// Got one, stash it
 			tlst[tlused++] = trans;
 
diff --git a/parse.c b/parse.c
index 37d501a..d77a49c 100644
--- a/parse.c
+++ b/parse.c
@@ -266,7 +266,6 @@ ParseTwmrc(const char *filename)
 		twmrc = start_m4(raw);
 	}
 	status = doparse(m4twmFileInput, "file", filename);
-	wait(0);
 	fclose(twmrc);
 	if(raw) {
 		fclose(raw);
diff --git a/parse_be.c b/parse_be.c
index bfd57da..e84e865 100644
--- a/parse_be.c
+++ b/parse_be.c
@@ -28,6 +28,9 @@
 #include "parse.h"
 #include "parse_be.h"
 #include "parse_yacc.h"
+#include "r_area.h"
+#include "r_area_list.h"
+#include "r_layout.h"
 #ifdef SOUNDS
 #  include "sound.h"
 #endif
@@ -342,6 +345,7 @@ static const TwmKeyword keytable[] = {
 	{ "menutitleforeground",    CKEYWORD, kwc_MenuTitleForeground },
 	{ "meta",                   META, 0 },
 	{ "mod",                    META, 0 },  /* fake it */
+	{ "monitorlayout",          MONITOR_LAYOUT, 0 },
 	{ "monochrome",             MONOCHROME, 0 },
 	{ "move",                   MOVE, 0 },
 	{ "movedelta",              NKEYWORD, kwn_MoveDelta },
@@ -451,7 +455,9 @@ static const TwmKeyword keytable[] = {
 	{ "usethreedmenus",         KEYWORD, kw0_Use3DMenus },
 	{ "usethreedtitles",        KEYWORD, kw0_Use3DTitles },
 	{ "usethreedwmap",          KEYWORD, kw0_Use3DWMap },
+#ifdef VSCREEN
 	{ "virtualscreens",         VIRTUAL_SCREENS, 0 },
+#endif
 	{ "w",                      WINDOW, 0 },
 	{ "wait",                   WAITC, 0 },
 	{ "warpcursor",             WARP_CURSOR, 0 },
@@ -461,7 +467,9 @@ static const TwmKeyword keytable[] = {
 	{ "warpunmapped",           KEYWORD, kw0_WarpUnmapped },
 	{ "west",                   GRAVITY, GRAV_WEST },
 	{ "window",                 WINDOW, 0 },
+#ifdef WINBOX
 	{ "windowbox",              WINDOW_BOX, 0 },
+#endif
 	{ "windowfunction",         WINDOW_FUNCTION, 0 },
 	{ "windowgeometries",       WINDOW_GEOMETRIES, 0 },
 	{ "windowregion",           WINDOW_REGION, 0 },
@@ -1548,12 +1556,13 @@ do_color_keyword(int keyword, int colormode, char *s)
 static void
 put_pixel_on_root(Pixel pixel)
 {
-	int           i, addPixel = 1;
+	bool addone = true;
 	Atom          retAtom;
 	int           retFormat;
 	unsigned long nPixels, retAfter;
 	Pixel        *retProp;
 
+	// Get current list
 	if(XGetWindowProperty(dpy, Scr->Root, XA__MIT_PRIORITY_COLORS, 0, 8192,
 	                      False, XA_CARDINAL, &retAtom,
 	                      &retFormat, &nPixels, &retAfter,
@@ -1561,60 +1570,74 @@ put_pixel_on_root(Pixel pixel)
 		return;
 	}
 
-	for(i = 0; i < nPixels; i++)
+	// See if we already have this one
+	for(int i = 0; i < nPixels; i++) {
 		if(pixel == retProp[i]) {
-			addPixel = 0;
+			addone = false;
 		}
-
+	}
 	XFree(retProp);
 
-	if(addPixel)
+	// If not, append it
+	if(addone) {
 		XChangeProperty(dpy, Scr->Root, XA__MIT_PRIORITY_COLORS,
 		                XA_CARDINAL, 32, PropModeAppend,
 		                (unsigned char *)&pixel, 1);
+	}
 }
 
+/*
+ * Stash for SaveColor{} values during config parsing.
+ */
+typedef struct _cnode {
+	int i;
+	int cmode;
+	char *sname;
+	struct _cnode *next;
+} Cnode;
+static Cnode *chead = NULL;
+
+/**
+ * Add a SaveColor{} entry to our stash.
+ */
+static void
+add_cnode(int kwcl, int cmode, char *colname)
+{
+	Cnode *cnew;
+
+	cnew = calloc(1, sizeof(Cnode));
+	cnew->i     = kwcl;
+	cnew->cmode = cmode;
+	cnew->sname = colname;
+
+	if(!chead) {
+		chead = cnew;
+	}
+	else {
+		cnew->next = chead;
+		chead = cnew;
+	}
+
+	return;
+}
+
+
 /*
  * do_string_savecolor() save a color from a string in the twmrc file.
  */
 void
 do_string_savecolor(int colormode, char *s)
 {
-	Pixel p;
-	GetColor(colormode, &p, s);
-	put_pixel_on_root(p);
-	return;
+	add_cnode(0, colormode, s);
 }
 
 /*
  * do_var_savecolor() save a color from a var in the twmrc file.
  */
-typedef struct _cnode {
-	int i;
-	struct _cnode *next;
-} Cnode, *Cptr;
-static Cptr chead = NULL;
-
 void
 do_var_savecolor(int key)
 {
-	Cptr cptrav, cpnew;
-	if(!chead) {
-		chead = malloc(sizeof(Cnode));
-		chead->i = key;
-		chead->next = NULL;
-	}
-	else {
-		cptrav = chead;
-		while(cptrav->next != NULL) {
-			cptrav = cptrav->next;
-		}
-		cpnew = malloc(sizeof(Cnode));
-		cpnew->i = key;
-		cpnew->next = NULL;
-		cptrav->next = cpnew;
-	}
-	return;
+	add_cnode(key, 0, NULL);
 }
 
 /*
@@ -1624,8 +1647,16 @@ do_var_savecolor(int key)
 void
 assign_var_savecolor(void)
 {
-	Cptr cp = chead;
+	Cnode *cp = chead;
+
+	// Start with an empty property
+	XChangeProperty(dpy, Scr->Root, XA__MIT_PRIORITY_COLORS,
+	                XA_CARDINAL, 32, PropModeReplace, NULL, 0);
+
+	// Loop over, stash 'em, and clean up
 	while(cp != NULL) {
+		Cnode *tmp_cp = cp;
+
 		switch(cp->i) {
 			case kwcl_BorderColor:
 				put_pixel_on_root(Scr->BorderColorC.back);
@@ -1666,11 +1697,18 @@ assign_var_savecolor(void)
 			case kwcl_MapWindowBackground:
 				put_pixel_on_root(Scr->workSpaceMgr.windowcp.back);
 				break;
+			case 0: {
+				// This means it's a string, not one of our keywords
+				Pixel p;
+				GetColor(cp->cmode, &p, cp->sname);
+				put_pixel_on_root(p);
+			}
 		}
+
 		cp = cp->next;
+		free(tmp_cp);
 	}
 	if(chead) {
-		free(chead);
 		chead = NULL;
 	}
 }
@@ -1969,3 +2007,163 @@ add_mwm_ignore(char *s)
 	ParseError = true;
 	return;
 }
+
+
+/*
+ * Parsing for Layout { } lists, to override the monitor layout we
+ * assumed or got from RANDR.
+ */
+static RAreaList *override_monitors;
+static struct {
+	char **names;
+	int len;
+	int cap;
+} override_monitors_names;
+
+
+/**
+ * Allocate space for our monitor override list.
+ */
+void
+init_layout_override(void)
+{
+	// 4 seems like a good guess.  If we're doing this, we're probably
+	// making at least 2 monitors, and >4 is gonna be pretty rare, so...
+	const int initsz = 4;
+
+	override_monitors = RAreaListNew(initsz, NULL);
+	if(override_monitors == NULL) {
+		twmrc_error_prefix();
+		fprintf(stderr, "Failed allocating RAreaList for monitors.\n");
+		ParseError = true;
+		return;
+		// Maybe we should just abort(); if malloc failed allocating a
+		// few dozen bytes this early, we're _screwed_.
+	}
+
+	override_monitors_names.names = calloc(initsz, sizeof(char *));
+	override_monitors_names.len = 0;
+	override_monitors_names.cap = initsz;
+
+	return;
+}
+
+/**
+ * Add an entry to our monitor list
+ *
+ * Expecting: [Name:]WxH[+X[+Y]]
+ */
+void
+add_layout_override_entry(const char *s)
+{
+	const char *tmp;
+	int xpgret;
+	int x, y;
+	unsigned int width, height;
+
+	if(override_monitors == NULL) {
+		// alloc failed, so just give up; we'll fail in the end anyway...
+		return;
+	}
+
+	// Got a name?
+	tmp = strchr(s, ':');
+	if(tmp != NULL && tmp != s) {
+		// Stash the name
+		override_monitors_names.names[override_monitors_names.len]
+		        = strndup(s, tmp - s);
+		// len advances below
+
+		// Advance to geom
+		s = tmp + 1;
+	}
+	// Advance whether we got a name or not, to keep in sync.
+	override_monitors_names.len++;
+
+
+	// Either way, s points at the geom now
+	xpgret = XParseGeometry(s, &x, &y, &width, &height);
+
+	// Width and height are non-optional.  If x/y aren't given, we assume
+	// +0+0.  If we're given -0's, well, we don't _support_ that, but
+	// XPG() turns them into positives for us, so just accept it...
+	const int has_hw = (WidthValue | HeightValue);
+	if((xpgret & has_hw) != has_hw) {
+		twmrc_error_prefix();
+		fprintf(stderr, "Need both height and width in '%s'\n", s);
+		ParseError = true;
+		// Don't bother free()'ing stuff, we're going to exit after
+		// parse completes
+		return;
+	}
+	if(!(xpgret & XValue)) {
+		x = 0;
+	}
+	if(!(xpgret & YValue)) {
+		y = 0;
+	}
+
+
+	// And stash it
+	RAreaListAdd(override_monitors, RAreaNewStatic(x, y, width, height));
+
+	// Whether we had a name for this 'monitor' or not, we need to
+	// possibly grow the names list, since it has to stay in lockstep
+	// with the areas as we add 'em.
+	{
+		char ***names = &override_monitors_names.names;
+		int len = override_monitors_names.len;
+
+		if(len == override_monitors_names.cap) {
+			char **tnames = realloc(*names, (len + 1) * sizeof(char *));
+			if(tnames == NULL) {
+				abort();
+			}
+			*names = tnames;
+			override_monitors_names.cap++;
+		}
+	}
+
+	return;
+}
+
+/**
+ * Finalize the override layout and store it up globally.
+ */
+void
+proc_layout_override(void)
+{
+	RLayout *new_layout;
+
+	// Guard
+	if(RAreaListLen(override_monitors) < 1) {
+		// Make this non-fatal, so an empty spec not-quite-quietly does
+		// nothing.
+		twmrc_error_prefix();
+		fprintf(stderr, "no monitors specified, ignoring MonitorLayout\n");
+
+		// Since it's non-fatal, we _do_ need to cleanup more
+		// carefully...
+		RAreaListFree(override_monitors);
+		for(int i = 0; i < override_monitors_names.len ; i++) {
+			free(override_monitors_names.names[i]);
+		}
+		free(override_monitors_names.names);
+		return;
+	}
+
+	new_layout = RLayoutNew(override_monitors);
+	RLayoutSetMonitorsNames(new_layout, override_monitors_names.names);
+	// Silently stop paying attention to o_m_n.  Don't free() anything,
+	// since new_layout now owns it.  If we get another MonitorLayout{}
+	// block, it'll start over again with init(), and allocate new space.
+
+#ifdef DEBUG
+	fprintf(stderr, "Overridden layout: ");
+	RLayoutPrint(new_layout);
+#endif
+
+	RLayoutFree(Scr->Layout);
+	Scr->Layout = new_layout;
+	return;
+}
diff --git a/parse_be.h b/parse_be.h
index 63fa73a..02ccb5f 100644
--- a/parse_be.h
+++ b/parse_be.h
@@ -26,4 +26,8 @@ void add_ewmh_ignore(char *s);
 void proc_mwm_ignore(void);
 void add_mwm_ignore(char *s);
 
+void init_layout_override(void);
+void add_layout_override_entry(const char *s);
+void proc_layout_override(void);
+
 #endif /* _CTWM_PARSE_BE_H */
diff --git a/parse_m4.c b/parse_m4.c
index c53d925..0ebe80b 100644
--- a/parse_m4.c
+++ b/parse_m4.c
@@ -90,8 +90,6 @@ start_m4(FILE *fraw)
 static char *
 m4_defs(Display *display, const char *host)
 {
-	Screen *screen;
-	Visual *visual;
 	char client[MAXHOSTNAME];
 	char *vc, *color;
 	char *tmp_name;
@@ -214,26 +212,33 @@ m4_defs(Display *display, const char *host)
 	/*
 	 * X server meta
 	 */
-	WR_NUM("VERSION", ProtocolVersion(display));
-	WR_NUM("REVISION", ProtocolRevision(display));
-	WR_DEF("VENDOR", ServerVendor(display));
-	WR_NUM("RELEASE", VendorRelease(display));
+	if(display) {
+		WR_NUM("VERSION", ProtocolVersion(display));
+		WR_NUM("REVISION", ProtocolRevision(display));
+		WR_DEF("VENDOR", ServerVendor(display));
+		WR_NUM("RELEASE", VendorRelease(display));
+	}
+	else {
+		// Standin numbers
+		WR_NUM("VERSION", 11);
+		WR_NUM("REVISION", 0);
+		WR_DEF("VENDOR", "Your Friendly Local Ctwm");
+		WR_NUM("RELEASE", 123456789);
+	}
 
 	/*
 	 * Information about the display
 	 */
-	screen = ScreenOfDisplay(display, Scr->screen);
-	visual = DefaultVisualOfScreen(screen);
-	WR_NUM("WIDTH", screen->width);
-	WR_NUM("HEIGHT", screen->height);
+	WR_NUM("WIDTH", Scr->rootw);
+	WR_NUM("HEIGHT", Scr->rooth);
 #define Resolution(pixels, mm)  ((((pixels) * 100000 / (mm)) + 50) / 100)
-	WR_NUM("X_RESOLUTION", Resolution(screen->width, screen->mwidth));
-	WR_NUM("Y_RESOLUTION", Resolution(screen->height, screen->mheight));
+	WR_NUM("X_RESOLUTION", Resolution(Scr->rootw, Scr->mm_w));
+	WR_NUM("Y_RESOLUTION", Resolution(Scr->rooth, Scr->mm_h));
 #undef Resolution
-	WR_NUM("PLANES", DisplayPlanes(display, Scr->screen));
-	WR_NUM("BITS_PER_RGB", visual->bits_per_rgb);
+	WR_NUM("PLANES", Scr->d_depth);
+	WR_NUM("BITS_PER_RGB", Scr->d_visual->bits_per_rgb);
 	color = "Yes";
-	switch(visual->class) {
+	switch(Scr->d_visual->class) {
 		case(StaticGray):
 			vc = "StaticGray";
 			color = "No";
@@ -264,10 +269,15 @@ m4_defs(Display *display, const char *host)
 	/*
 	 * Bits of "how this ctwm invocation is being run" data
 	 */
-	if(CLarg.is_captive && Scr->captivename) {
+	if(0) {
+		// Dummy
+	}
+#ifdef CAPTIVE
+	else if(CLarg.is_captive && Scr->captivename) {
 		WR_DEF("TWM_CAPTIVE", "Yes");
 		WR_DEF("TWM_CAPTIVE_NAME", Scr->captivename);
 	}
+#endif
 	else {
 		WR_DEF("TWM_CAPTIVE", "No");
 	}
@@ -289,6 +299,9 @@ m4_defs(Display *display, const char *host)
 #endif
 #ifdef EWMH
 	WR_DEF("EWMH", "Yes");
+#endif
+#ifdef XRANDR
+	WR_DEF("XRANDR", "Yes");
 #endif
 	/* Since this is no longer an option, it should be removed in the future */
 	WR_DEF("I18N", "Yes");
diff --git a/r_area.c b/r_area.c
new file mode 100644
index 0000000..b6e7544
--- /dev/null
+++ b/r_area.c
@@ -0,0 +1,415 @@
+/*
+ * Copyright notice...
+ */
+
+#include "ctwm.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "r_area.h"
+#include "r_area_list.h"
+#include "util.h"
+
+
+/**
+ * Construct an RArea from given components.
+ */
+RArea
+RAreaNew(int x, int y, int width, int height)
+{
+	RArea area = { x, y, width, height };
+	return area;
+}
+
+
+/**
+ * Return a pointer to a static newly constructed RArea.
+ *
+ * This is a thin wrapper around RAreaNew() that returns a static
+ * pointer.  This is used in places that need to take RArea pointers, but
+ * we don't want to futz with making local intermediate vars.  Currently
+ * exclusively used inline in RAreaListNew() calls.
+ */
+RArea *
+RAreaNewStatic(int x, int y, int width, int height)
+{
+	static RArea area;
+	area = RAreaNew(x, y, width, height);
+	return &area;
+}
+
+
+/**
+ * Return a facially-invalid RArea.
+ *
+ * This is used in places that need a sentinel value.
+ */
+RArea
+RAreaInvalid(void)
+{
+	RArea area = { -1, -1, -1, -1 };
+	return area;
+}
+
+
+/**
+ * Is an RArea facially valid?
+ *
+ * Mostly used to check against sentinel values in places that may or may
+ * not have a real value to work with.
+ */
+bool
+RAreaIsValid(const RArea *self)
+{
+	return self->width >= 0 && self->height >= 0;
+}
+
+
+/**
+ * Return the right edge of an RArea.
+ */
+int
+RAreaX2(const RArea *self)
+{
+	return self->x + self->width - 1;
+}
+
+
+/**
+ * Return the bottom edge of an RArea.
+ */
+int
+RAreaY2(const RArea *self)
+{
+	return self->y + self->height - 1;
+}
+
+
+/**
+ * Return the area of an RArea.
+ */
+int
+RAreaArea(const RArea *self)
+{
+	return self->width * self->height;
+}
+
+
+/**
+ * Return an RArea describing the intersection of two RArea's.
+ */
+RArea
+RAreaIntersect(const RArea *self, const RArea *other)
+{
+	// Do they even intersect?
+	if(RAreaIsIntersect(self, other)) {
+		int x1, x2, y1, y2;
+
+		x1 = max(other->x, self->x);
+		x2 = min(RAreaX2(other), RAreaX2(self));
+
+		y1 = max(other->y, self->y);
+		y2 = min(RAreaY2(other), RAreaY2(self));
+
+		return RAreaNew(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
+	}
+
+	// Nope, so nothing
+	return RAreaInvalid();
+}
+
+
+/**
+ * Do two areas intersect?
+ */
+bool
+RAreaIsIntersect(const RArea *self, const RArea *other)
+{
+	// [other][self]
+	if(RAreaX2(other) < self->x) {
+		return false;
+	}
+
+	// [self][other]
+	if(other->x > RAreaX2(self)) {
+		return false;
+	}
+
+	// [other]
+	// [self]
+	if(RAreaY2(other) < self->y) {
+		return false;
+	}
+
+	// [self]
+	// [other]
+	if(other->y > RAreaY2(self)) {
+		return false;
+	}
+
+	return true;
+}
+
+
+/**
+ * Is a given coordinate inside a RArea?
+ */
+bool
+RAreaContainsXY(const RArea *self, int x, int y)
+{
+	return x >= self->x && x <= RAreaX2(self)
+	       && y >= self->y && y <= RAreaY2(self);
+}
+
+
+/**
+ * Create a list of maximal horizontal stripes of two RArea's.
+ *
+ * This yields a set of RArea's that completely cover (without overlap)
+ * the pair of input RArea's (or NULL if the inputs are disjoint).  That
+ * could be just a single RArea if e.g. they're the same height and
+ * touch/overlap horizontally, or they're the same width and
+ * touch/overlap vertically.  Otherwise it will wind up being multiple
+ * rows (2 or 3).
+ *
+ * Only used in startup to populate the RLayout.horiz list.
+ */
+RAreaList *
+RAreaHorizontalUnion(const RArea *self, const RArea *other)
+{
+	// If there's horizontal space between them, they can't possibly
+	// combine.
+	// [other]  [self]    or     [self]  [other]
+	if(RAreaX2(other) < self->x - 1) {
+		return NULL;
+	}
+	if(other->x > RAreaX2(self) + 1) {
+		return NULL;
+	}
+
+	// No vertical overlap (though maybe they touch?)
+	// [other] or [self]
+	// [self]     [other]
+	if(RAreaY2(other) < self->y || other->y > RAreaY2(self)) {
+		// Special case: if they're the same width, and start at the same
+		// X coordinate, _and_ are touching each other vertically, we can
+		// combine them into a single block.
+		if(self->width == other->width && self->x == other->x) {
+			// [other]
+			// [self ]
+			if(RAreaY2(other) + 1 == self->y) {
+				return RAreaListNew(
+				               1,
+				               RAreaNewStatic(self->x, other->y,
+				                              self->width, self->height + other->height),
+				               NULL);
+			}
+
+			// [self ]
+			// [other]
+			if(RAreaY2(self) + 1 == other->y) {
+				return RAreaListNew(
+				               1,
+				               RAreaNewStatic(self->x, self->y,
+				                              self->width, self->height + other->height),
+				               NULL);
+			}
+
+			// Nope, there must be vertical space between us
+		}
+
+		// Can't combine
+		return NULL;
+	}
+
+	// No horizontal space between the two (may be touching, or
+	// overlapping), and there's some vertical overlap.
+	// So there must be some horizontal stripes we can create.
+	{
+		// left- and right-most x coords, which define the maximum width
+		// a union could be.
+		const int min_x = min(self->x, other->x);
+		const int max_x = max(RAreaX2(self), RAreaX2(other));
+		const int max_width = max_x - min_x + 1;
+
+		RAreaList *res = RAreaListNew(3, NULL);
+
+		// Figure which starts higher.
+		const RArea *top, *bot;
+		if(self->y < other->y) {
+			top = self;
+			bot = other;
+		}
+		else {
+			top = other;
+			bot = self;
+		}
+		// bot now starts even with or below top
+
+		//      [   ]    [   ]
+		// [bot][top] or [top][bot]
+		//      [   ]         [   ]
+
+		// Room in top before bot starts?  That's one stripe.
+		if(bot->y != top->y) {
+			RAreaListAdd(res, RAreaNewStatic(top->x, top->y,
+			                                 top->width, bot->y - top->y));
+		}
+
+		// Next there's a stripe across both of them.
+		RAreaListAdd(res,
+		             RAreaNewStatic(min_x, bot->y,
+		                            max_width,
+		                            min(RAreaY2(top), RAreaY2(bot)) - max(top->y, bot->y) + 1));
+
+		// If their bottoms aren't coincident, there's another stripe
+		// below them of whichever one is taller.
+		if(RAreaY2(top) != RAreaY2(bot)) {
+			if(RAreaY2(bot) < RAreaY2(top)) {
+				//      [   ]    [   ]
+				// [bot][top] or [top][bot]
+				//      [   ]    [   ]
+				RAreaListAdd(res,
+				             RAreaNewStatic(top->x, RAreaY2(bot) + 1,
+				                            top->width, RAreaY2(top) - RAreaY2(bot)));
+			}
+			else {
+				//      [   ]    [   ]
+				// [bot][top] or [top][bot]
+				// [  ]               [   ]
+				RAreaListAdd(res,
+				             RAreaNewStatic(bot->x, RAreaY2(top) + 1,
+				                            bot->width, RAreaY2(bot) - RAreaY2(top)));
+			}
+		}
+
+		// And there they are.
+		return res;
+	}
+}
+
+
+/**
+ * Create a list of maximal vertical stripes of two RArea's.
+ *
+ * This yields a set of RArea's that completely cover (without overlap)
+ * the pair of input RArea's (or NULL if the inputs are disjoint).  This
+ * is the equivalent of RAreaHorizontalUnion(), except with vertical
+ * stripes.
+ *
+ * Only used in startup to populate the RLayout.vert list.
+ */
+RAreaList *
+RAreaVerticalUnion(const RArea *self, const RArea *other)
+{
+	// Vertical space between them; can't possibly combine.
+	if(RAreaY2(other) < self->y - 1) {
+		return NULL;
+	}
+	if(other->y > RAreaY2(self) + 1) {
+		return NULL;
+	}
+
+	// No horizontal overlap (though perhaps touching)
+	// [other][self] or [self][other]
+	if(RAreaX2(other) < self->x || other->x > RAreaX2(self)) {
+		// Special case: if they're the same height, and start at the same
+		// Y coordinate, _and_ are touching each other horizontally, we can
+		// combine them into a single block.
+		if(self->height == other->height && self->y == other->y) {
+			// [other][self]
+			if(RAreaX2(other) + 1 == self->x) {
+				return RAreaListNew(
+				               1,
+				               RAreaNewStatic(other->x, self->y,
+				                              self->width + other->width, self->height),
+				               NULL);
+			}
+
+			// [self][other]
+			if(RAreaX2(self) + 1 == other->x) {
+				return RAreaListNew(
+				               1,
+				               RAreaNewStatic(self->x, self->y,
+				                              self->width + other->width, self->height),
+				               NULL);
+			}
+
+			// Nope, not touching; horizontal space means no combining.
+		}
+		return NULL;
+	}
+
+	// No vertical space (touching or overlap), and some horizontal
+	// overlap.  So there are vertical stripes we can make.
+	{
+		// top- and bottom-most y coords, giving a maximum height
+		const int min_y = min(self->y, other->y);
+		const int max_y = max(RAreaY2(self), RAreaY2(other));
+		const int max_height = max_y - min_y + 1;
+
+		RAreaList *res = RAreaListNew(3, NULL);
+
+		// Which starts left-most
+		const RArea *left, *right;
+		if(self->x < other->x) {
+			left = self;
+			right = other;
+		}
+		else {
+			left = other;
+			right = self;
+		}
+
+		// [--left--] or  [right]  or    [right] or [left]
+		//  [right]     [--left--]    [left]          [right]
+
+		// Room to the left before right starts?  That's one stripe.
+		if(right->x != left->x) {
+			RAreaListAdd(res,
+			             RAreaNewStatic(left->x, left->y,
+			                            right->x - left->x, left->height));
+		}
+
+		// There's a stripe of their overlap.
+		RAreaListAdd(res,
+		             RAreaNewStatic(right->x, min_y,
+		                            min(RAreaX2(left), RAreaX2(right)) - max(left->x, right->x) + 1,
+		                            max_height));
+
+		// If they don't end at the same x coord, there's a third stripe
+		// of a piece to the right of one or the other.
+		if(RAreaX2(left) != RAreaX2(right)) {
+			if(RAreaX2(right) < RAreaX2(left)) {
+				// [--left--] or  [right]
+				//  [right]     [--left--]
+				RAreaListAdd(res,
+				             RAreaNewStatic(RAreaX2(right) + 1, left->y,
+				                            RAreaX2(left) - RAreaX2(right), left->height));
+			}
+			else {
+				//     [right] or [left]
+				//  [left]          [right]
+				RAreaListAdd(res,
+				             RAreaNewStatic(RAreaX2(left) + 1, right->y,
+				                            RAreaX2(right) - RAreaX2(left), right->height));
+			}
+		}
+
+		return res;
+	}
+}
+
+
+/**
+ * Pretty-print an RArea.
+ *
+ * Used for dev/debug.
+ */
+void
+RAreaPrint(const RArea *self)
+{
+	fprintf(stderr, "[x=%d y=%d w=%d h=%d]", self->x, self->y, self->width,
+	        self->height);
+}
diff --git a/r_area.h b/r_area.h
new file mode 100644
index 0000000..0866dd0
--- /dev/null
+++ b/r_area.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright notice...
+ */
+
+#ifndef _CTWM_R_AREA_H
+#define _CTWM_R_AREA_H
+
+#include "r_structs.h"
+
+
+RArea *RAreaNewStatic(int x, int y, int width, int height);
+RArea RAreaNew(int x, int y, int width, int height);
+
+RArea RAreaInvalid(void);
+bool RAreaIsValid(const RArea *self);
+
+int RAreaX2(const RArea *self);
+int RAreaY2(const RArea *self);
+int RAreaArea(const RArea *self);
+RArea RAreaIntersect(const RArea *self, const RArea *other);
+bool RAreaIsIntersect(const RArea *self, const RArea *other);
+bool RAreaContainsXY(const RArea *self, int x, int y);
+RAreaList *RAreaHorizontalUnion(const RArea *self, const RArea *other);
+RAreaList *RAreaVerticalUnion(const RArea *self, const RArea *other);
+
+void RAreaPrint(const RArea *self);
+
+#endif  /* _CTWM_R_AREA_H */
diff --git a/r_area_list.c b/r_area_list.c
new file mode 100644
index 0000000..73c5635
--- /dev/null
+++ b/r_area_list.c
@@ -0,0 +1,575 @@
+/*
+ * Copyright notice...
+ */
+
+#include "ctwm.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "r_area_list.h"
+#include "r_area.h"
+
+
+/*
+ * Prototype internal funcs
+ */
+static RAreaList *RAreaListCopy(const RAreaList *self);
+static void RAreaListDelete(RAreaList *self, int index);
+static void RAreaListAddList(RAreaList *self, const RAreaList *other);
+static RAreaList *RAreaListIntersectCrop(const RAreaList *self,
+                const RArea *area);
+
+/* Sorts and their internal comparison routines */
+static int _cmpX(const void *av, const void *bv);
+static void RAreaListSortX(const RAreaList *self);
+static int _cmpY(const void *av, const void *bv);
+static void RAreaListSortY(const RAreaList *self);
+
+
+
+/**
+ * Create an RAreaList from a set of RArea's.
+ * \param cap Hint as to the number of RArea's being passed
+ * \param ... Sequence of RArea * to put in it.  Don't forget a trailing
+ *            NULL.
+ */
+RAreaList *
+RAreaListNew(int cap, ...)
+{
+	va_list ap;
+	RAreaList *list;
+	RArea *area;
+
+	list = malloc(sizeof(RAreaList));
+	if(list == NULL) {
+		abort();
+	}
+	list->len = 0;
+
+	if(cap <= 0) {
+		cap = 1;
+	}
+	list->cap = cap;
+	list->areas = malloc(cap * sizeof(RArea));
+	if(list->areas == NULL) {
+		abort();
+	}
+
+	va_start(ap, cap);
+
+	while((area = va_arg(ap, RArea *)) != NULL) {
+		RAreaListAdd(list, area);
+	}
+
+	va_end(ap);
+
+	return list;
+}
+
+
+/**
+ * Create a copy of a given RAreaList.
+ */
+static RAreaList *
+RAreaListCopy(const RAreaList *self)
+{
+	RAreaList *new = RAreaListNew(self->cap, NULL);
+
+	RAreaListAddList(new, self);
+
+	return new;
+}
+
+
+/**
+ * Create a copy of an RAreaList with given amounts cropped off the
+ * sides.  This is used principally during startup, to handle the
+ * BorderBottom/Top/Left/Right config params.
+ */
+RAreaList *
+RAreaListCopyCropped(const RAreaList *self, int left_margin,
+                     int right_margin,
+                     int top_margin, int bottom_margin)
+{
+	if(left_margin > 0 || right_margin > 0
+	                || top_margin > 0 || bottom_margin > 0) {
+		// Start with a big spanning square
+		RArea big_area = RAreaListBigArea(self);
+
+		// Guard against negative margins
+		if(left_margin < 0) {
+			left_margin = 0;
+		}
+		if(right_margin < 0) {
+			right_margin = 0;
+		}
+		if(top_margin < 0) {
+			top_margin = 0;
+		}
+		if(bottom_margin < 0) {
+			bottom_margin = 0;
+		}
+
+		// Squeeze down the big square by the asked for amounts
+		big_area.x += left_margin;
+		big_area.width -= left_margin + right_margin;
+		big_area.y += top_margin;
+		big_area.height -= top_margin + bottom_margin;
+
+		// If we cropped down to nothing, that's a RAreaList with nothing
+		// in it, so give back that.
+		if(big_area.width <= 0 || big_area.height <= 0) {
+			return RAreaListNew(0, NULL); // empty list
+		}
+
+		// Make a new RAreaList cropped down to that size.
+		return RAreaListIntersectCrop(self, &big_area);
+	}
+
+	// Nothing to do; our callers expect getting nothing back.
+	return NULL;
+}
+
+
+/**
+ * Clean up and free an RAreaList.
+ */
+void
+RAreaListFree(RAreaList *self)
+{
+	if(self == NULL) {
+		return;
+	}
+	free(self->areas);
+	free(self);
+}
+
+
+/**
+ * Delete an RArea from inside an RAreaList.
+ */
+static void
+RAreaListDelete(RAreaList *self, int index)
+{
+	if(index >= self->len) {
+		return;
+	}
+
+	self->len--;
+
+	if(index == self->len) {
+		return;
+	}
+
+	memcpy(&self->areas[index], &self->areas[index + 1],
+	       (self->len - index) * sizeof(RArea));
+}
+
+
+/**
+ * Add an RArea onto an RAreaList.
+ */
+void
+RAreaListAdd(RAreaList *self, const RArea *area)
+{
+	if(self->cap == self->len) {
+		RArea *new_list = realloc(self->areas, (self->cap + 1) * sizeof(RArea));
+		if(new_list == NULL) {
+			abort();
+		}
+
+		self->cap++;
+		self->areas = new_list;
+	}
+
+	self->areas[self->len++] = *area;
+}
+
+
+/**
+ * Add the RArea's from one RAreaList onto another.
+ */
+static void
+RAreaListAddList(RAreaList *self, const RAreaList *other)
+{
+	if(self->cap - self->len < other->len) {
+		RArea *new_list = realloc(self->areas,
+		                          (self->len + other->len) * sizeof(RArea));
+		if(new_list == NULL) {
+			abort();
+		}
+
+		self->cap = self->len + other->len;
+		self->areas = new_list;
+	}
+
+	memcpy(&self->areas[self->len], other->areas, other->len * sizeof(RArea));
+
+	self->len += other->len;
+}
+
+
+/**
+ * qsort comparison function to sort by RArea.x
+ */
+static int
+_cmpX(const void *av, const void *bv)
+{
+	const RArea *a = (const RArea *)av, *b = (const RArea *)bv;
+
+	if(a->x < b->x) {
+		return -1;
+	}
+
+	if(a->x > b->x) {
+		return 1;
+	}
+
+	return (a->y > b->y) - (a->y < b->y);
+}
+
+
+/**
+ * Sort the RArea's in an RAreaList by their x coordinate.
+ */
+static void
+RAreaListSortX(const RAreaList *self)
+{
+	if(self->len <= 1) {
+		return;
+	}
+
+	qsort(self->areas, self->len, sizeof(RArea), _cmpX);
+}
+
+
+/**
+ * qsort comparison function to sort by RArea.t
+ */
+static int
+_cmpY(const void *av, const void *bv)
+{
+	const RArea *a = (const RArea *)av, *b = (const RArea *)bv;
+
+	if(a->y < b->y) {
+		return -1;
+	}
+
+	if(a->y > b->y) {
+		return 1;
+	}
+
+	return (a->x > b->x) - (a->x < b->x);
+}
+
+
+/**
+ * Sort the RArea's in an RAreaList by their y coordinate.
+ */
+static void
+RAreaListSortY(const RAreaList *self)
+{
+	if(self->len <= 1) {
+		return;
+	}
+
+	qsort(self->areas, self->len, sizeof(RArea), _cmpY);
+}
+
+
+/**
+ * Create an RAreaList whose RArea's are the horizontal union of our
+ * RArea's.
+ */
+RAreaList *
+RAreaListHorizontalUnion(const RAreaList *self)
+{
+	RAreaList *copy = RAreaListCopy(self);
+
+refine:
+	// Two areas can't form a horizontal stripe if there's any space
+	// between them.  So start by putting them all in x-coord order to be
+	// sure any gaps there are necessary.
+	RAreaListSortX(copy);
+
+	// Try HorizontalUnion'ing each area with the next one.  If we can
+	// create a union, replace them with it, and hop back to the top of
+	// the process to start over.
+	for(int i = 0; i < copy->len - 1; i++) {
+		for(int j = i + 1; j < copy->len; j++) {
+			RAreaList *repl = RAreaHorizontalUnion(&copy->areas[i], &copy->areas[j]);
+			if(repl != NULL) {
+				if(repl->len) {
+					RAreaListDelete(copy, j);
+					RAreaListDelete(copy, i);
+					RAreaListAddList(copy, repl);
+					RAreaListFree(repl);
+					goto refine;
+				}
+				RAreaListFree(repl);
+			}
+		}
+	}
+
+	return copy;
+}
+
+
+/**
+ * Create an RAreaList whose RArea's are the vertical union of our
+ * RArea's.
+ */
+RAreaList *
+RAreaListVerticalUnion(const RAreaList *self)
+{
+	RAreaList *copy = RAreaListCopy(self);
+
+refine:
+	// X-ref logic above in RAreaListHorizontalUnion()
+	RAreaListSortY(copy);
+
+	for(int i = 0; i < copy->len - 1; i++) {
+		for(int j = i + 1; j < copy->len; j++) {
+			RAreaList *repl = RAreaVerticalUnion(&copy->areas[i], &copy->areas[j]);
+			if(repl != NULL) {
+				if(repl->len) {
+					RAreaListDelete(copy, j);
+					RAreaListDelete(copy, i);
+					RAreaListAddList(copy, repl);
+					RAreaListFree(repl);
+					goto refine;
+				}
+				RAreaListFree(repl);
+			}
+		}
+	}
+
+	return copy;
+}
+
+
+/**
+ * Create an RAreaList of all the areas in an RAreaList that a given
+ * RArea intersects with.
+ */
+RAreaList *
+RAreaListIntersect(const RAreaList *self, const RArea *area)
+{
+	RAreaList *new = RAreaListNew(self->len, NULL);
+
+	for(int i = 0; i < self->len; i++) {
+		if(RAreaIsIntersect(&self->areas[i], area)) {
+			RAreaListAdd(new, &self->areas[i]);
+		}
+	}
+
+	return new;
+}
+
+
+/**
+ * Run a function over each RArea in an RAreaList until one returns true,
+ * allowing them a place to stash other internal data.
+ */
+void
+RAreaListForeach(const RAreaList *self,
+                 bool (*func)(const RArea *cur_area, void *data),
+                 void *data)
+{
+	for(int i = 0 ; i < self->len ; i++) {
+		if(func(&(self->areas[i]), data) == true) {
+			break;
+		}
+	}
+}
+
+
+
+/**
+ * Create an RAreaList from another, cropped to a certain area defined by
+ * an RArea.
+ */
+static RAreaList *
+RAreaListIntersectCrop(const RAreaList *self, const RArea *area)
+{
+	RAreaList *new = RAreaListNew(self->len, NULL);
+
+	for(int i = 0; i < self->len; i++) {
+		RArea it = RAreaIntersect(&self->areas[i], area);
+		if(RAreaIsValid(&it)) {
+			RAreaListAdd(new, &it);
+		}
+	}
+
+	return new;
+}
+
+
+/**
+ * Create a maximal RArea describing the union of an RAreaList.
+ *
+ * This is used to construct a giant square that contains all our
+ * monitors (and the dead area necessary to cover them).  It winds up
+ * being the equivalent of a spanning pseudo-Root window, and is used
+ * when we need to figure some sort of "overall" positioning, like when
+ * figuring "real" x/y coordinates.
+ */
+RArea
+RAreaListBigArea(const RAreaList *self)
+{
+	int x, y, x2, y2;
+
+	// Guard; probably impossible
+	if(self->len < 1) {
+		return RAreaInvalid();
+	}
+
+	for(int i = 0 ; i < self->len ; i++) {
+		const RArea *area = &(self->areas[i]);
+		if(i == 0 || area->x < x) {
+			x = area->x;
+		}
+
+		if(i == 0 || area->y < y) {
+			y = area->y;
+		}
+
+		if(i == 0 || RAreaX2(area) > x2) {
+			x2 = RAreaX2(area);
+		}
+
+		if(i == 0 || RAreaY2(area) > y2) {
+			y2 = RAreaY2(area);
+		}
+	}
+
+	return RAreaNew(x, y, x2 - x + 1, y2 - y + 1);
+}
+
+
+/**
+ * Find the RArea in an RAreaList that has the largest intersection with
+ * a given RArea.  Colloquially, which area in an RAreaList does our
+ * RArea mostly fit into?  This is used to resize a window to fill one
+ * monitor, by finding which monitor it's on.
+ */
+RArea
+RAreaListBestTarget(const RAreaList *self, const RArea *area)
+{
+	RArea full_area = RAreaInvalid();
+	int max_area = -1;
+
+	for(int i = 0; i < self->len; i++) {
+		RArea it = RAreaIntersect(area, &self->areas[i]);
+		if(RAreaIsValid(&it) && (max_area < 0 || RAreaArea(&it) > max_area)) {
+			max_area = RAreaArea(&it);
+			full_area = self->areas[i];
+		}
+	}
+
+	return full_area;
+}
+
+
+/**
+ * Find the x coordinate of the right-most RArea in an RAreaList.
+ */
+int
+RAreaListMaxX(const RAreaList *self)
+{
+	RArea *cur_area = &self->areas[0], *area_end = &self->areas[self->len];
+	int max_x = self->len ? cur_area->x : 0;
+
+	// While a for(i=0 ; i<self->len ; i++) loop is generally nicer for
+	// these iterations, it winds up being a little trickier here, so we
+	// leave it as a pointer-stepper.
+	while(++cur_area < area_end) {
+		if(cur_area->x > max_x) {
+			max_x = cur_area->x;
+		}
+	}
+
+	return max_x;
+}
+
+
+/**
+ * Find the y coordinate of the bottom-most RArea in an RAreaList.
+ */
+int
+RAreaListMaxY(const RAreaList *self)
+{
+	RArea *cur_area = &self->areas[0], *area_end = &self->areas[self->len];
+	int max_y = self->len ? cur_area->y : 0;
+
+	while(++cur_area < area_end) {
+		if(cur_area->y > max_y) {
+			max_y = cur_area->y;
+		}
+	}
+
+	return max_y;
+}
+
+
+/**
+ * Find the x coordinate of the right edge of the left-most RArea in an
+ * RAreaList.
+ */
+int
+RAreaListMinX2(const RAreaList *self)
+{
+	RArea *cur_area = &self->areas[0], *area_end = &self->areas[self->len];
+	int min_x = self->len ? RAreaX2(cur_area) : 0;
+
+	while(++cur_area < area_end) {
+		if(RAreaX2(cur_area) < min_x) {
+			min_x = RAreaX2(cur_area);
+		}
+	}
+
+	return min_x;
+}
+
+
+/**
+ * Find the y coordinate of the bottom edge of the top-most RArea in an
+ * RAreaList.
+ */
+int
+RAreaListMinY2(const RAreaList *self)
+{
+	RArea *cur_area = &self->areas[0], *area_end = &self->areas[self->len];
+	int min_y = self->len ? RAreaY2(cur_area) : 0;
+
+	while(++cur_area < area_end) {
+		if(RAreaY2(cur_area) < min_y) {
+			min_y = RAreaY2(cur_area);
+		}
+	}
+
+	return min_y;
+}
+
+
+/**
+ * Pretty-print an RAreaList.
+ *
+ * Used for dev/debug.
+ */
+void
+RAreaListPrint(const RAreaList *self)
+{
+	fprintf(stderr, "[len=%d cap=%d", self->len, self->cap);
+
+	for(int i = 0 ; i < self->len ; i++) {
+		RArea *area = &self->areas[i];
+		fprintf(stderr, " ");
+		RAreaPrint(area);
+	}
+
+	fprintf(stderr, "]");
+}
diff --git a/r_area_list.h b/r_area_list.h
new file mode 100644
index 0000000..61a7949
--- /dev/null
+++ b/r_area_list.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright notice...
+ */
+
+#ifndef _CTWM_R_AREA_LIST_H
+#define _CTWM_R_AREA_LIST_H
+
+#include "r_structs.h"
+
+
+RAreaList *RAreaListNew(int cap, ...);
+
+void RAreaListFree(RAreaList *self);
+
+RAreaList *RAreaListCopyCropped(const RAreaList *self, int left_margin,
+                                int right_margin,
+                                int top_margin, int bottom_margin);
+
+void RAreaListAdd(RAreaList *self, const RArea *area);
+
+RAreaList *RAreaListHorizontalUnion(const RAreaList *self);
+RAreaList *RAreaListVerticalUnion(const RAreaList *self);
+
+RAreaList *RAreaListIntersect(const RAreaList *self, const RArea *area);
+void RAreaListForeach(const RAreaList *self,
+                      bool (*func)(const RArea *area, void *data),
+                      void *data);
+
+RArea RAreaListBigArea(const RAreaList *self);
+RArea RAreaListBestTarget(const RAreaList *self, const RArea *area);
+
+int RAreaListMaxX(const RAreaList *self);
+int RAreaListMaxY(const RAreaList *self);
+int RAreaListMinX2(const RAreaList *self);
+int RAreaListMinY2(const RAreaList *self);
+
+void RAreaListPrint(const RAreaList *self);
+
+
+/*
+ * Simple accessors to avoid unnecessary layering violations.
+ */
+/// How many RArea's are in the list?
+static inline int RAreaListLen(const RAreaList *self)
+{
+	return self->len;
+}
+
+
+#endif  /* _CTWM_R_AREA_LIST_H */
diff --git a/r_layout.c b/r_layout.c
new file mode 100644
index 0000000..4e72bda
--- /dev/null
+++ b/r_layout.c
@@ -0,0 +1,1041 @@
+/*
+ * Copyright notice...
+ */
+
+#include "ctwm.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "r_layout.h"
+#include "r_area_list.h"
+#include "r_area.h"
+#include "util.h"
+
+
+/*
+ * Prototype internal funcs
+ */
+static void _RLayoutFreeNames(RLayout *self);
+static RAreaList *_RLayoutRecenterVertically(const RLayout *self,
+                const RArea *far_area);
+static RAreaList *_RLayoutRecenterHorizontally(const RLayout *self,
+                const RArea *far_area);
+static RAreaList *_RLayoutVerticalIntersect(const RLayout *self,
+                const RArea *area);
+static RAreaList *_RLayoutHorizontalIntersect(const RLayout *self,
+                const RArea *area);
+
+/* Foreach() callbacks used in various lookups */
+static bool _findMonitorByXY(const RArea *cur, void *vdata);
+static bool _findMonitorBottomEdge(const RArea *cur, void *vdata);
+static bool _findMonitorTopEdge(const RArea *cur, void *vdata);
+static bool _findMonitorLeftEdge(const RArea *cur, void *vdata);
+static bool _findMonitorRightEdge(const RArea *cur, void *vdata);
+
+
+
+
+/************************
+ *
+ * First, some funcs for creating and destroying RLayout's in various
+ * ways.
+ *
+ ************************/
+
+
+/**
+ * Create an RLayout for a given set of monitors.
+ *
+ * This stashes up the list of monitors, and precalculates the
+ * horizontal/vertical stripes that compose it.
+ */
+RLayout *
+RLayoutNew(RAreaList *monitors)
+{
+	RLayout *layout = malloc(sizeof(RLayout));
+	if(layout == NULL) {
+		abort();
+	}
+
+	layout->monitors = monitors;
+	layout->horiz = RAreaListHorizontalUnion(monitors);
+	layout->vert = RAreaListVerticalUnion(monitors);
+	layout->names = NULL;
+
+	return layout;
+}
+
+
+/**
+ * Create a copy of an RLayout with given amounts cropped off the sides.
+ * This is used anywhere we need to pretend our display area is smaller
+ * than it actually is (e.g., via the BorderBottom/Top/Left/Right config
+ * params)
+ */
+RLayout *
+RLayoutCopyCropped(const RLayout *self, int left_margin, int right_margin,
+                   int top_margin, int bottom_margin)
+{
+	RAreaList *cropped_monitors = RAreaListCopyCropped(self->monitors,
+	                              left_margin, right_margin,
+	                              top_margin, bottom_margin);
+	if(cropped_monitors == NULL) {
+		return NULL;        // nothing to crop, same layout as passed
+	}
+
+	return RLayoutNew(cropped_monitors);
+}
+
+
+/**
+ * Clean up and free any RLayout.names there might be in an RLayout.
+ */
+static void
+_RLayoutFreeNames(RLayout *self)
+{
+	if(self == NULL) {
+		return;
+	}
+	if(self->names != NULL) {
+		free(self->names);
+		self->names = NULL;
+	}
+}
+
+
+/**
+ * Clean up and free an RLayout.
+ */
+void
+RLayoutFree(RLayout *self)
+{
+	if(self == NULL) {
+		return;
+	}
+
+	RAreaListFree(self->monitors);
+	RAreaListFree(self->horiz);
+	RAreaListFree(self->vert);
+	_RLayoutFreeNames(self);
+	free(self);
+}
+
+
+/**
+ * Set the names for our monitors in an RLayout.  This is only used for
+ * the RLayout that describes our complete monitor layout, which fills in
+ * the RANDR names for each output.
+ */
+RLayout *
+RLayoutSetMonitorsNames(RLayout *self, char **names)
+{
+	_RLayoutFreeNames(self);
+	self->names = names;
+	return self;
+}
+
+
+
+/************************
+ *
+ * Next, a few util funcs for dealing with RArea's that are outside our
+ * RLayout, but we want to find the nearest way to move them inside, then
+ * return a list of which RArea's they'd be intersecting with.
+ *
+ ************************/
+
+
+/**
+ * Given an RArea that doesn't reside in any of the areas in our RLayout,
+ * create a list of maximally-tall RArea slices out of our layout where
+ * it would wind up if we brought it onto the nearest screen edge.  This
+ * yields a RAreaList as tall as the slice[es] the window would touch if
+ * we moved it in.
+ *
+ * If we had the move the window horizontally (it was off-screen to the
+ * right or left), it results in a 1-pixel-wide slice of the right- or
+ * left-most self->vert.
+ *
+ * If we had to move it vertically (it was off to the top or bottom), it
+ * winds up being whatever horizontal intersection with self->vert would
+ * result from the window's x and width, with the full height of the
+ * involved slices.
+ *
+ * This is the vertical-stripe-returning counterpart of
+ * _RLayoutRecenterHorizontally().
+ *
+ * This is called only by \_RLayoutVerticalIntersect() when given an RArea
+ * that doesn't already intersect the RLayout.  Will probably not tell
+ * you something useful if given a far_area that already _does_ intersect
+ * self.
+ *
+ * \param self     Our current monitor layout
+ * \param far_area The area to act on
+ */
+static RAreaList *
+_RLayoutRecenterVertically(const RLayout *self, const RArea *far_area)
+{
+	RArea big = RAreaListBigArea(self->monitors), tmp;
+
+	// We assume far_area is outside of self.  So it's in one of the
+	// three labelled areas:
+	//
+	//  |_V_|   ___ tmp.top
+	//  |   |     |
+	// L|   |R    |
+	//  |___|   ___ tmp.bottom
+	//  | V |
+	//
+	// So we'll create an RArea that's the y and height of big (a giant
+	// rectangle covering all the monitors), so that its intersection
+	// with self->vert will always cover a full vertical stripe.  Then
+	// we'll set its x/width so that it's shifted to be at least
+	// minimally inside big somehow.
+
+	// Where did it wind up?
+	if((far_area->x >= big.x && far_area->x <= RAreaX2(&big))
+	                || (RAreaX2(far_area) >= big.x && RAreaX2(far_area) <= RAreaX2(&big))) {
+		// In one of the V areas.  It's already in a horizontal position
+		// that would fit, so we just keep x/width.
+		tmp = RAreaNew(far_area->x, big.y,
+		               far_area->width, big.height);
+	}
+	else if(RAreaX2(far_area) < big.x) {
+		// Off to the left side in L, so move it over just far enough
+		// that 1 pixel of it protrudes into the left side.
+		tmp = RAreaNew(big.x - far_area->width + 1, big.y,
+		               far_area->width, big.height);
+	}
+	else {
+		// Off to the right side in R, so move it over just far enough
+		// that 1 pixel of it protrudes into the right side.
+		tmp = RAreaNew(RAreaX2(&big), big.y,
+		               far_area->width, big.height);
+	}
+
+	// Then intersect that (full height, at least 1 pixel horizontally
+	// somewhere) with our collection of vertical stripes, to yield an
+	// answer.  If the window was off to the left or right, this will
+	// yield a 1-pixel-wide slice of either the left- or right-most
+	// ->vert of our layout.  If it were off the top of bottom, though,
+	// it'll yield some slice of 1 (or more) of our ->vert's, as wide as
+	// the window itself was.
+	return RAreaListIntersect(self->vert, &tmp);
+
+	// n.b.; _RLayoutRecenterHorizontally() is the counterpart to this
+	// with horizontal slices.  The comments in the two have been written
+	// independently with somewhat different explanatory styles, so if
+	// the description here was confusing, try reading the other one and
+	// transposing.
+}
+
+
+/**
+ * Given an RArea that doesn't reside in any of the areas in our RLayout,
+ * create a list of maximally-wide RArea slices out of our layout where
+ * it would wind up if we brought it onto the nearest screen edge.  This
+ * yields a RAreaList as wide as the slice[es] the window would touch if
+ * we moved it in.
+ *
+ * If we had the move the window vertically (it was off-screen to the top
+ * or bottom), it results in a 1-pixel-wide slice of the top- or
+ * bottom-most self->horiz.
+ *
+ * If we had to move it horizontally (it was off to the left or right),
+ * it winds up being whatever vertical intersection with self->horiz
+ * would result from the window's y and height, with the full width of
+ * the involved slices.
+ *
+ * This is the horizontal-stripe-returning counterpart of
+ * _RLayoutRecenterVertically().
+ *
+ * This is called only by \_RLayoutVerticalIntersect() when given an RArea
+ * that doesn't already intersect the RLayout.  Will probably not tell
+ * you something useful if given a far_area that already _does_ intersect
+ * self.
+ *
+ * \param self     Our current monitor layout
+ * \param far_area The area to act on
+ */
+static RAreaList *
+_RLayoutRecenterHorizontally(const RLayout *self, const RArea *far_area)
+{
+	RArea big = RAreaListBigArea(self->monitors), tmp;
+
+	// far_area is outside self, so it's in one of the 3 labelled areas:
+	//
+	// ___T___
+	//  |   |
+	// H|   |H
+	// _|___|_
+	//    B
+	//
+	// We create an RArea that's the x and width of big, so it always
+	// covers the entire width of any member of ->horiz.  Then we move
+	// the far_area in to the nearest edge to figure the y/height to set.
+
+	if((far_area->y >= big.y && far_area->y <= RAreaY2(&big))
+	                || (RAreaY2(far_area) >= big.y && RAreaY2(far_area) <= RAreaY2(&big))) {
+		// In one of the H areas.  Already in a valid place vertically,
+		// so make a horizontal strip that position/tall.
+		tmp = RAreaNew(big.x, far_area->y,
+		               big.width, far_area->height);
+	}
+	else if(RAreaY2(far_area) < big.y) {
+		// Off the top (T); move it down just far enough that it's bottom
+		// protrudes 1 pixel into the top.
+		tmp = RAreaNew(big.x, big.y - far_area->height + 1,
+		               big.width, far_area->height);
+	}
+	else {
+		// Off the bottom (B); move it up just enough that it's top
+		// protrudes 1 pixel into the bottom.
+		tmp = RAreaNew(big.x, RAreaY2(&big),
+		               big.width, far_area->height);
+	}
+
+	// And intersect that RArea with self->horiz.  This results in a
+	// full-width overlap with 1 pixel at the bottom of the bottom-most,
+	// 1 pixel at the top of the top-most, or 1..(far_area->height)
+	// overlap somewhere.  In that last case (far_area was in H), the
+	// intersection may yield multiple areas.
+	return RAreaListIntersect(self->horiz, &tmp);
+
+	// n.b.; _RLayoutRecenterVertically() is the counterpart to this with
+	// vertical slices.  The comments in the two have been written
+	// independently with somewhat different explanatory styles, so if
+	// the description here was confusing, try reading the other one and
+	// transposing.
+}
+
+
+
+/************************
+ *
+ * Some wrappers called when we need to Insersect an RArea with our
+ * RLayout, but also handle the case (using the above funcs) when the
+ * RArea doesn't Intersect our layout by finding the nearest border we
+ * could shuffle it over.
+ *
+ ************************/
+
+
+/**
+ * Find which vertical regions of our monitor layout a given RArea (often
+ * a window) is in.  If it's completely off the screen, we move it until
+ * it's just over the nearest edge, and return the vertical stripe(s) it
+ * would be in then.
+ *
+ * This function is used only by RLayoutFindTopBottomEdges()
+ */
+static RAreaList *
+_RLayoutVerticalIntersect(const RLayout *self, const RArea *area)
+{
+	RAreaList *mit = RAreaListIntersect(self->vert, area);
+
+	if(mit->len == 0) {
+		// Not on screen.  Move it to just over the nearest edge so it
+		// is, and give the slices it's in then.
+		RAreaListFree(mit);
+		mit = _RLayoutRecenterVertically(self, area);
+	}
+	return mit;
+}
+
+
+/**
+ * Find which horizontal regions of our monitor layout a given RArea
+ * (often a window) is in.  If it's completely off the screen, we move it
+ * until it's just over the nearest edge, and return the horizontal
+ * stripe(s) it would be in then.
+ *
+ * This function is used only by RLayoutFindLeftRightEdges()
+ */
+static RAreaList *
+_RLayoutHorizontalIntersect(const RLayout *self, const RArea *area)
+{
+	RAreaList *mit = RAreaListIntersect(self->horiz, area);
+
+	if(mit->len == 0) {
+		// Not on screen.  Move it to just over the nearest edge so it
+		// is, and give the slices it's in then.
+		RAreaListFree(mit);
+		mit = _RLayoutRecenterHorizontally(self, area);
+	}
+
+	return mit;
+}
+
+
+
+/************************
+ *
+ * Some funcs using the above (layers of) utils to find info about which
+ * stripes of the RLayout an Area appears in.   These are used mostly as
+ * backend utils for figuring various f.*zoom's.
+ *
+ ************************/
+
+
+/**
+ * Figure the position (or nearest practical position) of an area in our
+ * screen layout, and return info about the bottom/top stripes it fits
+ * into.
+ *
+ * Note that the return values (params) are slightly counterintuitive;
+ * top tells you where the top of the lowest stripe that area intersects
+ * with is, and bottom tells you the bottom of the highest.
+ *
+ * This is used as a backend piece of various calculations trying to be
+ * sure something winds up on-screen and when figuring out how to zoom
+ * it.
+ *
+ * \param[in]  self   The monitor layout to work from
+ * \param[in]  area   The area to be fit into the monitors
+ * \param[out] top    The top of the lowest stripe area fits into.
+ * \param[out] bottom The bottom of the highest stripe area fits into.
+ */
+void
+RLayoutFindTopBottomEdges(const RLayout *self, const RArea *area, int *top,
+                          int *bottom)
+{
+	RAreaList *mit = _RLayoutVerticalIntersect(self, area);
+
+	if(top != NULL) {
+		*top = RAreaListMaxY(mit);
+	}
+
+	if(bottom != NULL) {
+		*bottom = RAreaListMinY2(mit);
+	}
+
+	RAreaListFree(mit);
+}
+
+
+/**
+ * Find the bottom of the top stripe of self that area fits into.  A
+ * shortcut to get only the second return value of
+ * RLayoutFindTopBottomEdges().
+ */
+int
+RLayoutFindBottomEdge(const RLayout *self, const RArea *area)
+{
+	int min_y2;
+	RLayoutFindTopBottomEdges(self, area, NULL, &min_y2);
+	return min_y2;
+}
+
+
+/**
+ * Find the top of the bottom stripe of self that area fits into.  A
+ * shortcut to get only the first return value of
+ * RLayoutFindTopBottomEdges().
+ */
+int
+RLayoutFindTopEdge(const RLayout *self, const RArea *area)
+{
+	int max_y;
+	RLayoutFindTopBottomEdges(self, area, &max_y, NULL);
+	return max_y;
+}
+
+
+/**
+ * Figure the position (or nearest practical position) of an area in our
+ * screen layout, and return info about the left/rightmost stripes it fits
+ * into.
+ *
+ * As with RLayoutFindTopBottomEdges(), the return values (params) are
+ * slightly counterintuitive.  left tells you where the left-side of the
+ * right-most stripe that area intersects with is, and right tells you
+ * the right side of the left-most.
+ *
+ * This is used as a backend piece of various calculations trying to be
+ * sure something winds up on-screen and when figuring out how to zoom
+ * it.
+ *
+ * \param[in]  self   The monitor layout to work from
+ * \param[in]  area   The area to be fit into the monitors
+ * \param[out] left   The left edge of the right-most stripe area fits into.
+ * \param[out] right  The right edge of the left-most stripe area fits into.
+ */
+void
+RLayoutFindLeftRightEdges(const RLayout *self, const RArea *area, int *left,
+                          int *right)
+{
+	RAreaList *mit = _RLayoutHorizontalIntersect(self, area);
+
+	if(left != NULL) {
+		*left = RAreaListMaxX(mit);
+	}
+
+	if(right != NULL) {
+		*right = RAreaListMinX2(mit);
+	}
+
+	RAreaListFree(mit);
+}
+
+
+/**
+ * Find the left edge of the right-most stripe of self that area fits
+ * into.  A shortcut to get only the first return value of
+ * RLayoutFindLeftRightEdges().
+ */
+int
+RLayoutFindLeftEdge(const RLayout *self, const RArea *area)
+{
+	int max_x;
+	RLayoutFindLeftRightEdges(self, area, &max_x, NULL);
+	return max_x;
+}
+
+
+/**
+ * Find the right edge of the left-most stripe of self that area fits
+ * into.  A shortcut to get only the second return value of
+ * RLayoutFindLeftRightEdges().
+ */
+int
+RLayoutFindRightEdge(const RLayout *self, const RArea *area)
+{
+	int min_x2;
+	RLayoutFindLeftRightEdges(self, area, NULL, &min_x2);
+	return min_x2;
+}
+
+
+
+/************************
+ *
+ * Lookups to find areas in an RLayout by various means.
+ *
+ ************************/
+
+
+/// Internal structure for callback in RLayoutGetAreaAtXY().
+struct monitor_finder_xy {
+	const RArea *area;
+	int x, y;
+};
+
+/// Callback util for RLayoutGetAreaAtXY().
+static bool
+_findMonitorByXY(const RArea *cur, void *vdata)
+{
+	struct monitor_finder_xy *data = (struct monitor_finder_xy *)vdata;
+
+	if(RAreaContainsXY(cur, data->x, data->y)) {
+		data->area = cur;
+		return true;
+	}
+	return false;
+}
+
+/**
+ * Find the RArea in a RLayout that a given coordinate falls into.  In
+ * practice, the RArea's in self are the monitors of the desktop, so this
+ * answers "Which monitor is this position on?"
+ */
+RArea
+RLayoutGetAreaAtXY(const RLayout *self, int x, int y)
+{
+	struct monitor_finder_xy data = { .area = NULL, .x = x, .y = y };
+
+	RAreaListForeach(self->monitors, _findMonitorByXY, &data);
+
+	return data.area == NULL ? self->monitors->areas[0] : *data.area;
+}
+
+
+/**
+ * Return the index'th RArea in an RLayout, or RAreaInvalid() with an out
+ * of range index.
+ */
+RArea
+RLayoutGetAreaIndex(const RLayout *self, int index)
+{
+	if(index >= self->monitors->len || index < 0) {
+		return RAreaInvalid();
+	}
+
+	return self->monitors->areas[index];
+}
+
+
+/**
+ * Return the RArea in self with the name given by the string of length
+ * len at name.  This is only used in RLayoutXParseGeometry() to parse a
+ * fragment of a larger string, hence the need for len.  It's used to
+ * find the monitor with a given name (RANDR output name).
+ */
+RArea
+RLayoutGetAreaByName(const RLayout *self, const char *name, int len)
+{
+	if(self->names != NULL) {
+		if(len < 0) {
+			len = strlen(name);
+		}
+
+		for(int i = 0; i < self->monitors->len
+		                && self->names[i] != NULL; i++) {
+			if(strncmp(self->names[i], name, len) == 0) {
+				return self->monitors->areas[i];
+			}
+		}
+	}
+
+	return RAreaInvalid();
+}
+
+
+
+/************************
+ *
+ * Now some utils for finding various edges of the monitors a given RArea
+ * intersects with.
+ *
+ ************************/
+
+
+/// Internal struct for use in FindMonitor*Edge() callbacks.
+struct monitor_edge_finder {
+	const RArea *area;
+	union {
+		int max_x;
+		int max_y;
+		int min_x2;
+		int min_y2;
+	} u;
+	bool found;
+};
+
+/// Callback util for RLayoutFindMonitorBottomEdge()
+static bool
+_findMonitorBottomEdge(const RArea *cur, void *vdata)
+{
+	struct monitor_edge_finder *data = (struct monitor_edge_finder *)vdata;
+
+	// Does the area we're looking for intersect this piece of the
+	// RLayout, is the bottom of the area shown on it, and is the bottom
+	// of this piece the highest we've yet found that satisfies those
+	// conditions?
+	if(RAreaIsIntersect(cur, data->area)
+	                && RAreaY2(cur) > RAreaY2(data->area)
+	                && (!data->found || RAreaY2(cur) < data->u.min_y2)) {
+		data->u.min_y2 = RAreaY2(cur);
+		data->found = true;
+	}
+	return false;
+}
+
+/**
+ * Find the bottom edge of the top-most monitor that contains the most of
+ * a given RArea.  Generally, the area would be a window.
+ *
+ * That is, we find the monitor whose bottom is the highest up, but that
+ * still shows the bottom edge of the window, and return that monitor's
+ * bottom.  If the bottom of the window is off all the monitors, that's
+ * just the highest-ending monitor that contains the window.
+ */
+int
+RLayoutFindMonitorBottomEdge(const RLayout *self, const RArea *area)
+{
+	struct monitor_edge_finder data = { .area = area };
+
+	RAreaListForeach(self->monitors, _findMonitorBottomEdge, &data);
+
+	return data.found ? data.u.min_y2 : RLayoutFindBottomEdge(self, area);
+}
+
+
+/// Callback util for RLayoutFindMonitorTopEdge()
+static bool
+_findMonitorTopEdge(const RArea *cur, void *vdata)
+{
+	struct monitor_edge_finder *data = (struct monitor_edge_finder *)vdata;
+
+	// Does the area we're looking for intersect this piece of the
+	// RLayout, is the top of the area shown on it, and is the top
+	// of this piece the lowest we've yet found that satisfies those
+	// conditions?
+	if(RAreaIsIntersect(cur, data->area)
+	                && cur->y < data->area->y
+	                && (!data->found || cur->y > data->u.max_y)) {
+		data->u.max_y = cur->y;
+		data->found = true;
+	}
+	return false;
+}
+
+/**
+ * Find the top edge of the bottom-most monitor that contains the most of
+ * a given RArea.  Generally, the area would be a window.
+ *
+ * That is, we find the monitor whose top is the lowest down, but that
+ * still shows the top edge of the window, and return that monitor's top.
+ * If the top of the window is off all the monitors, that's just the
+ * lowest-ending monitor that contains part of the window.
+ */
+int
+RLayoutFindMonitorTopEdge(const RLayout *self, const RArea *area)
+{
+	struct monitor_edge_finder data = { .area = area };
+
+	RAreaListForeach(self->monitors, _findMonitorTopEdge, &data);
+
+	return data.found ? data.u.max_y : RLayoutFindTopEdge(self, area);
+}
+
+
+/// Callback util for RLayoutFindMonitorLeftEdge()
+static bool
+_findMonitorLeftEdge(const RArea *cur, void *vdata)
+{
+	struct monitor_edge_finder *data = (struct monitor_edge_finder *)vdata;
+
+	// Does the area we're looking for intersect this piece of the
+	// RLayout, is the left of the area shown on it, and is the left of
+	// this piece the right-most we've yet found that satisfies those
+	// conditions?
+	if(RAreaIsIntersect(cur, data->area)
+	                && cur->x < data->area->x
+	                && (!data->found || cur->x > data->u.max_x)) {
+		data->u.max_x = cur->x;
+		data->found = true;
+	}
+	return false;
+}
+
+/**
+ * Find the left edge of the right-most monitor that contains the most of
+ * a given RArea.  Generally, the area would be a window.
+ *
+ * That is, we find the monitor whose left is the furthest right, but
+ * that still shows the left edge of the window, and return that
+ * monitor's left.  If the left edge of the window is off all the
+ * monitors, that's just the right-most-ending monitor that contains the
+ * window.
+ */
+int
+RLayoutFindMonitorLeftEdge(const RLayout *self, const RArea *area)
+{
+	struct monitor_edge_finder data = { .area = area };
+
+	RAreaListForeach(self->monitors, _findMonitorLeftEdge, &data);
+
+	return data.found ? data.u.max_x : RLayoutFindLeftEdge(self, area);
+}
+
+
+/// Callback util for RLayoutFindMonitorRightEdge()
+static bool
+_findMonitorRightEdge(const RArea *cur, void *vdata)
+{
+	struct monitor_edge_finder *data = (struct monitor_edge_finder *)vdata;
+
+	// Does the area we're looking for intersect this piece of the
+	// RLayout, is the right of the area shown on it, and is the right of
+	// this piece the left-most we've yet found that satisfies those
+	// conditions?
+	if(RAreaIsIntersect(cur, data->area)
+	                && RAreaX2(cur) > RAreaX2(data->area)
+	                && (!data->found || RAreaX2(cur) < data->u.min_x2)) {
+		data->u.min_x2 = RAreaX2(cur);
+		data->found = true;
+	}
+	return false;
+}
+
+/**
+ * Find the right edge of the left-most monitor that contains the most of
+ * a given RArea.  Generally, the area would be a window.
+ *
+ * That is, we find the monitor whose right is the furthest left, but
+ * that still shows the right edge of the window, and return that
+ * monitor's right.  If the right edge of the window is off all the
+ * monitors, that's just the left-most-ending monitor that contains the
+ * window.
+ */
+int
+RLayoutFindMonitorRightEdge(const RLayout *self, const RArea *area)
+{
+	struct monitor_edge_finder data = { .area = area };
+
+	RAreaListForeach(self->monitors, _findMonitorRightEdge, &data);
+
+	return data.found ? data.u.min_x2 : RLayoutFindRightEdge(self, area);
+}
+
+
+
+/************************
+ *
+ * Backend funcs called by the f.*zoom handlers to figure the area we
+ * should zoom into.
+ *
+ ************************/
+
+
+/**
+ * Figure the best way to stretch an area across the full horizontal
+ * width of an RLayout.  This is the backend for the f.xhorizoom ctwm
+ * function, zooming a window to the full width of all monitors.
+ */
+RArea
+RLayoutFullHoriz(const RLayout *self, const RArea *area)
+{
+	int max_x, min_x2;
+
+	RLayoutFindLeftRightEdges(self, area, &max_x, &min_x2);
+
+	return RAreaNew(max_x, area->y, min_x2 - max_x + 1, area->height);
+
+	/**
+	 * This yields an area:
+	 * ~~~
+	 * TL   W
+	 *   *-----*
+	 *   |     |
+	 *  H|     |
+	 *   |     |
+	 *   *-----*
+	 * ~~~
+	 *
+	 * The precise construction of the area can be tricky.
+	 *
+	 * In the simplest case, the area is entirely in one horizontal
+	 * stripe to start with.  In that case, max_x is the left side of
+	 * that box, min_x2 is the right side, so the resulting area starts
+	 * at (left margin, area y), with the height of y and the width of
+	 * the whole stripe.  Easy.
+	 *
+	 * When it spans multiple, it's more convoluted.  Let's consider an
+	 * example layout (of horizontal stripes, so that top stripe may be
+	 * across 2 monitors) to make it a little clearer:
+	 *
+	 * ~~~
+	 * *--------------------------*
+	 * |             |......2.....|
+	 * |                          |  <-----.
+	 * |             1 =========  |         .
+	 * *-------------*-=========--*-*        >-- 2 horiz stripes
+	 *               | =========    |       '
+	 *               |  /           |  <---'
+	 *       area  --+-'            |
+	 *               *--------------*
+	 * ~~~
+	 *
+	 * So in this case, we're trying to stretch area out as far
+	 * horizontal as it can go, crossing monitors if possible.
+	 *
+	 * So, the top-left corner of our box (TL) has the X coordinate of
+	 * the right-most strip we started with (the lower), and the Y
+	 * coordinate of the top of the area, yielding point (1) above (not
+	 * the asterisk; specifically where (1) sits).
+	 *
+	 * The width W is the difference between the right of the
+	 * left-most-ending (in this case, the top) stripe, and the left of
+	 * the right-most-starting (the bottom) (plus 1 because math).
+	 * That's the width of the intersecting horizontal area (2) above.
+	 *
+	 * And the height H is just the height of the original area.  And so,
+	 * our resulting area is the height of that original area (in ='s),
+	 * and stretched to the left and right until it runs into one or the
+	 * other monitor edge (1 space to the left, 2 to the right, in our
+	 * diagram).
+	 */
+}
+
+
+/**
+ * Figure the best way to stretch an area across the full vertical height
+ * of an RLayout.  This is the backend for the f.xzoom ctwm function,
+ * zooming a window to the full height of all monitors.
+ */
+RArea
+RLayoutFullVert(const RLayout *self, const RArea *area)
+{
+	int max_y, min_y2;
+
+	RLayoutFindTopBottomEdges(self, area, &max_y, &min_y2);
+
+	return RAreaNew(area->x, max_y, area->width, min_y2 - max_y + 1);
+
+	// X-ref long comment above in RLayoutFullHoriz() for worked example.
+	// This is just rotated 90 degrees, but the logic works out about the
+	// same.
+}
+
+
+/**
+ * Figure the best way to stretch an area across the largest horizontal
+ * and vertical space it can from its current position.  Essentially,
+ * stretch it in all directions until it hits the edge of our available
+ * space.
+ *
+ * This is the backend for the f.xfullzoom function.
+ */
+RArea
+RLayoutFull(const RLayout *self, const RArea *area)
+{
+	RArea full_horiz, full_vert, full1, full2;
+
+	// Get the boxes for full horizontal and vertical zooms, using the
+	// above functions.
+	full_horiz = RLayoutFullHoriz(self, area);
+	full_vert = RLayoutFullVert(self, area);
+
+	// Now stretch each of those in the other direction...
+	full1 = RLayoutFullVert(self, &full_horiz);
+	full2 = RLayoutFullHoriz(self, &full_vert);
+
+	// And return whichever was bigger.
+	return RAreaArea(&full1) > RAreaArea(&full2) ? full1 : full2;
+}
+
+
+
+/**
+ * Figure the best way to stretch an area horizontally without crossing
+ * monitors.
+ *
+ * This is the backend for the f.horizoom ctwm function.
+ */
+RArea
+RLayoutFullHoriz1(const RLayout *self, const RArea *area)
+{
+	// Cheat by using RLayoutFull1() to find the RArea for the monitor
+	// it's most on.
+	RArea target = RLayoutFull1(self, area);
+	int max_y, min_y2;
+
+	// We're stretching horizontally, so the x and width of target (that
+	// monitor) are already right.  But we have to figure the y and
+	// height...
+
+	// Generally, the y is the window's original y, unless we had to move
+	// it down to get onto the target monitor.  XXX Wait, what if we
+	// moved it _up_?
+	max_y = max(area->y, target.y);
+	target.y = max_y;
+
+	// The bottom would be the bottom of the area, clipped to the bottom
+	// of the monitor.  So the height is the diff.
+	min_y2 = min(RAreaY2(area), RAreaY2(&target));
+	target.height = min_y2 - max_y + 1;
+
+	return target;
+}
+
+
+/**
+ * Figure the best way to stretch an area vertically without crossing
+ * monitors.
+ *
+ * This is the backend for the f.zoom ctwm function.
+ */
+RArea
+RLayoutFullVert1(const RLayout *self, const RArea *area)
+{
+	// Let RLayoutFull1() find the right monitor.
+	RArea target = RLayoutFull1(self, area);
+	int max_x, min_x2;
+
+	// Stretching vertically, so the y/height of the monitor are already
+	// right.
+
+	// x is where the window was, unless we had to move it right to get
+	// onto the monitor.  XXX What if we moved it left?
+	max_x = max(area->x, target.x);
+	target.x = max_x;
+
+	// Right side is where it was, unless we have to clip to the monitor.
+	min_x2 = min(RAreaX2(area), RAreaX2(&target));
+	target.width = min_x2 - max_x + 1;
+
+	return target;
+}
+
+
+/**
+ * Figure the best way to resize an area to fill one monitor.
+ *
+ * This is the backend for the f.fullzoom ctwm function.
+ *
+ * \param self  Monitor layout
+ * \param area  Area (window) to zoom out
+ */
+RArea
+RLayoutFull1(const RLayout *self, const RArea *area)
+{
+	RArea target;
+	RAreaList *mit = RAreaListIntersect(self->monitors, area);
+	// Start with a list of all the monitors the window is on now.
+
+	if(mit->len == 0) {
+		// Not on any screens.  Find the "nearest" place it would wind
+		// up.
+		RAreaListFree(mit);
+		mit = _RLayoutRecenterHorizontally(self, area);
+	}
+
+	// Of the monitors it's on, find the one that it's "most" on, and
+	// return the RArea of it.
+	target = RAreaListBestTarget(mit, area);
+	RAreaListFree(mit);
+	return target;
+}
+
+
+
+/************************
+ *
+ * Finally, some small misc utils.
+ *
+ ************************/
+
+
+/**
+ * Generate maximal spanning RArea.
+ *
+ * This is a trivial wrapper of RAreaListBigArea() to hide knowledge of
+ * RLayout internals.  Currently only used once; maybe should just be
+ * deref'd there...
+ */
+RArea
+RLayoutBigArea(const RLayout *self)
+{
+	return RAreaListBigArea(self->monitors);
+}
+
+
+/**
+ * How many monitors does a given RLayout contain?
+ */
+int
+RLayoutNumMonitors(const RLayout *self)
+{
+	return self->monitors->len;
+}
+
+
+/**
+ * Pretty-print an RLayout.
+ *
+ * Used for dev/debug.
+ */
+void
+RLayoutPrint(const RLayout *self)
+{
+	fprintf(stderr, "[monitors=");
+	RAreaListPrint(self->monitors);
+	fprintf(stderr, "\n horiz=");
+	RAreaListPrint(self->horiz);
+	fprintf(stderr, "\n vert=");
+	RAreaListPrint(self->vert);
+	fprintf(stderr, "]\n");
+}
diff --git a/r_layout.h b/r_layout.h
new file mode 100644
index 0000000..32ee944
--- /dev/null
+++ b/r_layout.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright notice...
+ */
+
+#ifndef _CTWM_R_LAYOUT_H
+#define _CTWM_R_LAYOUT_H
+
+#include "r_structs.h"
+
+
+RLayout *RLayoutNew(RAreaList *monitors);
+void RLayoutFree(RLayout *self);
+
+RLayout *RLayoutCopyCropped(const RLayout *self, int left_margin,
+                            int right_margin,
+                            int top_margin, int bottom_margin);
+
+RLayout *RLayoutSetMonitorsNames(RLayout *self, char **names);
+
+RArea RLayoutGetAreaAtXY(const RLayout *self, int x, int y);
+RArea RLayoutGetAreaIndex(const RLayout *self, int index);
+RArea RLayoutGetAreaByName(const RLayout *self, const char *name, int len);
+
+void RLayoutFindTopBottomEdges(const RLayout *self, const RArea *area, int *top,
+                               int *bottom);
+int RLayoutFindBottomEdge(const RLayout *self, const RArea *area);
+int RLayoutFindTopEdge(const RLayout *self, const RArea *area);
+void RLayoutFindLeftRightEdges(const RLayout *self, const RArea *area,
+                               int *left,
+                               int *right);
+int RLayoutFindLeftEdge(const RLayout *self, const RArea *area);
+int RLayoutFindRightEdge(const RLayout *self, const RArea *area);
+
+int RLayoutFindMonitorBottomEdge(const RLayout *self, const RArea *area);
+int RLayoutFindMonitorTopEdge(const RLayout *self, const RArea *area);
+int RLayoutFindMonitorLeftEdge(const RLayout *self, const RArea *area);
+int RLayoutFindMonitorRightEdge(const RLayout *self, const RArea *area);
+
+RArea RLayoutFullHoriz(const RLayout *self, const RArea *area);
+RArea RLayoutFullVert(const RLayout *self, const RArea *area);
+RArea RLayoutFull(const RLayout *self, const RArea *area);
+RArea RLayoutFullHoriz1(const RLayout *self, const RArea *area);
+RArea RLayoutFullVert1(const RLayout *self, const RArea *area);
+RArea RLayoutFull1(const RLayout *self, const RArea *area);
+
+RArea RLayoutBigArea(const RLayout *self);
+int RLayoutNumMonitors(const RLayout *self);
+void RLayoutPrint(const RLayout *self);
+
+#endif  /* _CTWM_R_LAYOUT_H */
diff --git a/r_structs.h b/r_structs.h
new file mode 100644
index 0000000..fbd1c55
--- /dev/null
+++ b/r_structs.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright notice...
+ */
+
+#ifndef _CTWM_R_STRUCTS_H
+#define _CTWM_R_STRUCTS_H
+
+
+/**
+ * A particular extent of space.
+ *
+ * This defines an area on the abstract display.  It commonly represents
+ * a monitor when defining our screen layout, and is also used to
+ * represent a window when we're manipulating it on our screen space.
+ */
+struct RArea {
+	int x; ///< X position
+	int y; ///< Y position
+	int width;  ///< X dimension
+	int height; ///< Y dimension
+};
+
+
+/**
+ * A set of RArea's.
+ *
+ * This is generally used to define a contiguous region formed of various
+ * stitched-together subareas.
+ */
+struct RAreaList {
+	int len; ///< How many we're using
+	int cap; ///< How many we have space for
+	RArea *areas; ///< Array of RArea members of this list
+};
+
+
+/**
+ * The layout of our display.
+ *
+ * This may encompass multiple monitors, of differing sizes.  It's
+ * generally only used by a few vars at startup describing the layout,
+ * which gets referred to when we need to find various borders of our
+ * output.
+ */
+struct RLayout {
+	RAreaList *monitors; ///< List of all output monitors
+	RAreaList *horiz; ///< List of all unique horizontal regions
+	RAreaList *vert;  ///< List of all unique vertical regions
+
+	/// List of names of the monitors.  `names[i]` corresponds with
+	/// `monitors->areas[i]`.  This is used for looking up geometries
+	/// with output names via RLayoutXParseGeometry(); e.g,
+	/// "HDMI1:800x600+20+50".
+	char **names;
+};
+
+#endif  /* _CTWM_R_STRUCTS_H */
diff --git a/screen.h b/screen.h
index 6b91c67..de281e4 100644
--- a/screen.h
+++ b/screen.h
@@ -96,10 +96,6 @@ struct TitlebarPixmaps {
 struct ScreenInfo {
 	int screen;       ///< Which screen (i.e., the x after the dot in ":0.x")
 
-	/// Whether we're taking over this screen.  Usually true, unless
-	/// running captive or \--cfgchk
-	bool takeover;
-
 	int d_depth;      ///< Copy of DefaultDepth(dpy, screen)
 	Visual *d_visual; ///< Copy of DefaultVisual(dpy, screen)
 	int Monochrome;   ///< Is the display monochrome?
@@ -118,6 +114,10 @@ struct ScreenInfo {
 	int rootw; ///< Copy of DisplayWidth(dpy, screen)
 	int rooth; ///< Copy of DisplayHeight(dpy, screen)
 
+	int mm_w;  ///< Physical mm width of the root
+	int mm_h;  ///< Physical mm height of the root
+
+#ifdef CAPTIVE
 	/**
 	 * \defgroup scr_captive_bits Captive ctwm bits
 	 * These are various fields related to running a captive ctwm (i.e.,
@@ -140,6 +140,7 @@ struct ScreenInfo {
 	/// \copydetails crootw
 	int crooth;
 	/// @}
+#endif
 
 	int MaxWindowWidth;   ///< Largest window width to allow
 	int MaxWindowHeight;  ///< Largest window height to allow
@@ -197,13 +198,20 @@ struct ScreenInfo {
 	 * as ScreenInfo.Root, and isn't changed afterward.
 	 */
 	Window XineramaRoot;
+#ifdef CAPTIVE
 	/// The captive root window, if any, or None
 	Window CaptiveRoot;
+#endif
 	/// The actual X root window of the display.  This is always X's
 	/// RootWindow().
 	Window RealRoot;
 	/// @}
 
+	/// Layout of our roow window and monitor(s).
+	RLayout *Layout;
+	/// Layout taking into account Border{Top,Left,Right,Bottom} config
+	/// params.
+	RLayout *BorderedLayout;
 
 	/**
 	 * Dimensions/coordinates window.  This is the small window (usually
@@ -504,8 +512,10 @@ struct ScreenInfo {
 	/// @{
 	VirtualScreen *vScreenList;    ///< Linked list of per-VS info
 	VirtualScreen *currentvs;      ///< Currently active VS
+#ifdef VSCREEN
 	name_list     *VirtualScreens; ///< List of defined VS's
 	int           numVscreens;     ///< Number of defined VS's
+#endif
 	/// @}
 
 	name_list   *OccupyAll;       ///< OccupyAll config var
@@ -730,9 +740,11 @@ struct ScreenInfo {
 	/// %WindowRegion config var.
 	struct WindowRegion *FirstWindowRegion;
 
+#ifdef WINBOX
 	/// Pointer to head of list of windowboxes on screen.  Built from
 	/// %WindowBox config var.
 	WindowBox *FirstWindowBox;
+#endif
 
 	char *IconDirectory;    ///< IconDirectory config var
 	char *PixmapDirectory;  ///< PixmapDirectory config var
diff --git a/session.c b/session.c
index b83af82..d64fe32 100644
--- a/session.c
+++ b/session.c
@@ -67,11 +67,13 @@
 #include <X11/Xatom.h>
 
 #include "ctwm_atoms.h"
+#include "ctwm_shutdown.h"
 #include "icons.h"
 #include "list.h"
 #include "screen.h"
 #include "session.h"
 
+
 SmcConn smcConn = NULL;
 static XtInputId iceInputId;
 static char *twm_clientId;
@@ -81,13 +83,24 @@ static bool sent_save_done = false;
 static void SaveYourselfCB(SmcConn smcCon, SmPointer clientData,
                            int saveType, Bool shutdown, int interactStyle,
                            Bool fast);
+static char *GetClientID(Window window);
+static char *GetWindowRole(Window window);
+static int WriteWinConfigEntry(FILE *configFile, TwmWindow *theWindow,
+                        char *clientId, char *windowRole);
+static int ReadWinConfigEntry(FILE *configFile, unsigned short version,
+                       TWMWinConfigEntry **pentry);
+static void SaveYourselfPhase2CB(SmcConn smcCon, SmPointer clientData);
+static void DieCB(SmcConn smcCon, SmPointer clientData);
+static void SaveCompleteCB(SmcConn smcCon, SmPointer clientData);
+static void ShutdownCancelledCB(SmcConn smcCon, SmPointer clientData);
+static void ProcessIceMsgProc(XtPointer client_data, int *source, XtInputId *id);
 
 #define SAVEFILE_VERSION 2
 
 
 /*===[ Get Client SM_CLIENT_ID ]=============================================*/
 
-char *GetClientID(Window window)
+static char *GetClientID(Window window)
 /* This function returns the value of the session manager client ID property
  * given a valid window handle. If no such property exists on a window then
  * null is returned
@@ -127,7 +140,7 @@ char *GetClientID(Window window)
 
 /*===[ Get Window Role ]=====================================================*/
 
-char *GetWindowRole(Window window)
+static char *GetWindowRole(Window window)
 /* this function returns the WM_WINDOW_ROLE property of a window
  */
 {
@@ -354,7 +367,7 @@ static int read_counted_string(FILE *file, char **stringp)
 
 /*===[ Write Window Config Entry to file ]===================================*/
 
-int WriteWinConfigEntry(FILE *configFile, TwmWindow *theWindow,
+static int WriteWinConfigEntry(FILE *configFile, TwmWindow *theWindow,
                         char *clientId, char *windowRole)
 /* this function writes a window configuration entry of a given window to
  * the given configuration file
@@ -496,7 +509,7 @@ int WriteWinConfigEntry(FILE *configFile, TwmWindow *theWindow,
 
 /*===[ Read Window Configuration Entry ]=====================================*/
 
-int ReadWinConfigEntry(FILE *configFile, unsigned short version,
+static int ReadWinConfigEntry(FILE *configFile, unsigned short version,
                        TWMWinConfigEntry **pentry)
 /* this function reads the next window configuration entry from the given file
  * else it returns 0 if none exists or there is a problem
@@ -863,7 +876,7 @@ static char *unique_filename(char *path, char *prefix, int *fd)
 #  define PATH_MAX 1023
 #endif
 
-void SaveYourselfPhase2CB(SmcConn smcCon, SmPointer clientData)
+static void SaveYourselfPhase2CB(SmcConn smcCon, SmPointer clientData)
 /* this is where all the work is done in saving the state of the windows.
  * it is not done in Phase One because phase one is used for the other clients
  * to make sure that all the property information on their windows is correct
@@ -1059,19 +1072,19 @@ static void SaveYourselfCB(SmcConn smcCon, SmPointer clientData,
 
 /*===[ Die SM Call Back ]====================================================*/
 
-void DieCB(SmcConn smcCon, SmPointer clientData)
+static void DieCB(SmcConn smcCon, SmPointer clientData)
 /* this procedure is called by the session manager when requesting that the
  * application shut istelf down
  */
 {
 	SmcCloseConnection(smcCon, 0, NULL);
 	XtRemoveInput(iceInputId);
-	Done(0);
+	DoShutdown();
 }
 
 /*===[ Save Complete SM Call Back ]==========================================*/
 
-void SaveCompleteCB(SmcConn smcCon, SmPointer clientData)
+static void SaveCompleteCB(SmcConn smcCon, SmPointer clientData)
 /* This function is called to say that the save has been completed and that
  * the program can continue its operation
  */
@@ -1081,7 +1094,7 @@ void SaveCompleteCB(SmcConn smcCon, SmPointer clientData)
 
 /*===[ Shutdown Cancelled SM Call Back ]=====================================*/
 
-void ShutdownCancelledCB(SmcConn smcCon, SmPointer clientData)
+static void ShutdownCancelledCB(SmcConn smcCon, SmPointer clientData)
 
 {
 	if(!sent_save_done) {
@@ -1092,7 +1105,7 @@ void ShutdownCancelledCB(SmcConn smcCon, SmPointer clientData)
 
 /*===[ Process ICE Message ]=================================================*/
 
-void ProcessIceMsgProc(XtPointer client_data, int *source, XtInputId *id)
+static void ProcessIceMsgProc(XtPointer client_data, int *source, XtInputId *id)
 
 {
 	IceConn     ice_conn = (IceConn) client_data;
@@ -1156,3 +1169,11 @@ void ConnectToSessionManager(char *previous_id)
 	                     ProcessIceMsgProc,
 	                     (XtPointer) iceConn);
 }
+
+
+void shutdown_session(void)
+{
+	if(smcConn) {
+		SmcCloseConnection(smcConn, 0, NULL);
+	}
+}
diff --git a/session.h b/session.h
index e07e1fd..edcd330 100644
--- a/session.h
+++ b/session.h
@@ -9,6 +9,11 @@
 
 #include <X11/SM/SMlib.h>
 
+// Guard
+#ifndef SESSION
+#error "You're unconditionally including session.h!"
+#endif
+
 
 /* Used in stashing session info */
 struct TWMWinConfigEntry {
@@ -35,17 +40,9 @@ struct TWMWinConfigEntry {
 	/* ====================================================================== */
 
 };
+typedef struct TWMWinConfigEntry TWMWinConfigEntry;
 
 
-/* XXX Only used in one place, should convert to a func? */
-extern SmcConn smcConn;
-
-char *GetClientID(Window window);
-char *GetWindowRole(Window window);
-int WriteWinConfigEntry(FILE *configFile, TwmWindow *theWindow,
-                        char *clientId, char *windowRole);
-int ReadWinConfigEntry(FILE *configFile, unsigned short version,
-                       TWMWinConfigEntry **pentry);
 void ReadWinConfigFile(char *filename);
 int GetWindowConfig(TwmWindow *theWindow,
                     short *x, short *y,
@@ -57,11 +54,7 @@ int GetWindowConfig(TwmWindow *theWindow,
                     bool *height_ever_changed_by_user,
                     int *occupation /* <== [ Matthew McNeill Feb 1997 ] == */
                    );
-void SaveYourselfPhase2CB(SmcConn smcCon, SmPointer clientData);
-void DieCB(SmcConn smcCon, SmPointer clientData);
-void SaveCompleteCB(SmcConn smcCon, SmPointer clientData);
-void ShutdownCancelledCB(SmcConn smcCon, SmPointer clientData);
-void ProcessIceMsgProc(XtPointer client_data, int *source, XtInputId *id);
 void ConnectToSessionManager(char *previous_id);
+void shutdown_session(void);
 
 #endif /* _CTWM_SESSION_H */
diff --git a/signals.c b/signals.c
new file mode 100644
index 0000000..b0a3e8a
--- /dev/null
+++ b/signals.c
@@ -0,0 +1,125 @@
+/*
+ * Signal handlers
+ */
+
+#include "ctwm.h"
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "ctwm_shutdown.h"
+#include "signals.h"
+
+
+/* Our backends */
+static void sh_restart(int signum);
+static void sh_shutdown(int signum);
+
+
+// Internal flags for which signals have called us
+static bool sig_restart = false;
+static bool sig_shutdown = false;
+
+// External flag for whether some signal handler has set a flag that
+// needs to trigger an action.
+bool SignalFlag = false;
+
+
+/**
+ * Setup signal handlers (run during startup)
+ */
+void
+setup_signal_handlers(void)
+{
+	// INT/QUIT/TERM: shutdown
+	// XXX Wildly unsafe handler; to be reworked
+	signal(SIGINT,  sh_shutdown);
+	signal(SIGQUIT, sh_shutdown);
+	signal(SIGTERM, sh_shutdown);
+
+	// SIGHUP: restart
+	signal(SIGHUP, sh_restart);
+
+	// We don't use alarm(), but if we get the stray signal we shouldn't
+	// die...
+	signal(SIGALRM, SIG_IGN);
+
+	// This should be set by default, but just in case; explicitly don't
+	// leave zombies.
+	signal(SIGCHLD, SIG_IGN);
+
+	return;
+}
+
+
+/**
+ * Handle stuff set by a signal flag.  Could be a Restart, could be a
+ * Shutdown...
+ */
+void
+handle_signal_flag(Time t)
+{
+	// Restarting?
+	if(sig_restart) {
+		// In case it fails, don't loop
+		sig_restart = false;
+
+		// Handle
+		DoRestart(t);
+
+		// Shouldn't return, but exec() might fail...
+		return;
+	}
+
+	// Shutting down?
+	if(sig_shutdown) {
+		// Doit
+		DoShutdown();
+
+		// Can't return!
+		fprintf(stderr, "%s: DoShutdown() shouldn't return!\n", ProgramName);
+		exit(1);
+	}
+
+	// ???
+	fprintf(stderr, "%s: Internal error: unexpected signal flag.\n",
+	        ProgramName);
+	return;
+}
+
+
+
+/*
+ * Internal backend bits
+ */
+
+/**
+ * Set flag to restart.  Backend for SIGHUP.
+ */
+static void
+sh_restart(int signum)
+{
+	// Signal handler; stdio isn't async-signal-safe, write(2) is
+	const char srf[] = ":  signal received, setting restart flag\n";
+	write(2, ProgramName, ProgramNameLen);
+	write(2, srf, sizeof(srf));
+
+	SignalFlag = sig_restart = true;
+}
+
+/**
+ * Set flag to shutdown.  Backend for SIGTERM etc.
+ */
+static void
+sh_shutdown(int signum)
+{
+	// Signal handler; stdio isn't async-signal-safe, write(2) is
+	const char srf[] = ":  signal received, setting shutdown flag\n";
+	write(2, ProgramName, ProgramNameLen);
+	write(2, srf, sizeof(srf));
+
+	SignalFlag = sig_shutdown = true;
+}
+
diff --git a/signals.h b/signals.h
new file mode 100644
index 0000000..c491128
--- /dev/null
+++ b/signals.h
@@ -0,0 +1,10 @@
+/*
+ * Signal handling plumbing
+ */
+#ifndef _CTWM_SIGNALS_H
+#define _CTWM_SIGNALS_H
+
+void setup_signal_handlers(void);
+void handle_signal_flag(Time t);
+
+#endif /* _CTWM_SIGNALS_H */
diff --git a/system.ctwmrc b/system.ctwmrc
index 975e171..f23b68d 100644
--- a/system.ctwmrc
+++ b/system.ctwmrc
@@ -138,7 +138,6 @@ menu "windowops" {
     ""			f.separator
     "Resize"		f.resize
     "Move"		f.move
-    "Adopt Window"	f.adoptwindow
     "Animate"		f.startanimation
     "Don't animate"	f.stopanimation
     ""			f.separator
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index d5338a1..6489a60 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -2,39 +2,104 @@
 # Automated tests
 #
 
-set(CTWMBIN "${CMAKE_BINARY_DIR}/ctwm")
+set(CTWMBIN "ctwm")
+set(CTWMPATH "${CMAKE_BINARY_DIR}/ctwm")
 add_custom_target(test_bins)
 add_dependencies(test_bins ctwm)
 
 
 # Add some infrastructure for building executables for unit tests
-macro(ctwm_simple_unit_test BIN)
+#
+# BIN - name of the binary to run.
+# BINPATH - path of the binary to run.
+# ARGS - Add'l arguments to the binary for the test
+# WORKING_DIRECTORY - self-explanatory
+#
+# Currently, ${BINPATH} means a thing is already built, so we don't need
+# to worry about building, that's just the path to run.  Else, ${BIN} is
+# built from ${BIN}.c (though multiple calls with the same ${BIN} won't
+# try to re-do the builds).  e.g., commonly, $BINPATH is used when we're
+# running the main ctwm binary, instead of s test-specific bin.
+#
+# This is not un-janky, but it works, and we'll worry about cleaning it
+# up if it gets worse.
+function(ctwm_simple_unit_test TNAME)
+	set(_opts ALLOW_DISPLAY)
+	set(_arg_one BIN BINPATH WORKING_DIRECTORY)
+	set(_arg_multi ARGS)
+	cmake_parse_arguments(_ARGS "${_opts}" "${_arg_one}" "${_arg_multi}"
+		${ARGN})
+
+	# Maybe we're passed a specific path to run
+	if(_ARGS_BINPATH)
+		set(BIN_RUN "${_ARGS_BINPATH}")
+	endif()
+
+	# What's the binary name?
+	if(_ARGS_BIN)
+		set(BIN "${_ARGS_BIN}")
+	else()
+		set(BIN "${TNAME}")
+	endif()
+
+	# Derive path if we only got a name
+	if(NOT BIN_RUN)
+		set(BIN_RUN $<TARGET_FILE:${BIN}>)
+	endif()
+
 	# Building and linking it
-	add_executable(${BIN} EXCLUDE_FROM_ALL "${BIN}.c")
-	target_link_libraries(${BIN} ctwmlib)
+	if(NOT TARGET ${BIN})
+		add_executable(${BIN} EXCLUDE_FROM_ALL "${BIN}.c")
+		target_sources(${BIN} PUBLIC $<TARGET_OBJECTS:ctwmlib>)
 
-	# Few of our tests really need any of the X or other libs we pull in.
-	# However, most of the .o's for ctwm itself are going to have some
-	# ref out to them somewhere, so any tests that wind up pulling
-	# functions from those are going to make the linker want to resolve
-	# them when we link ${BIN}.  So just unconditionally add them and
-	# don't worry about which tests may not actually need 'em.
-	target_link_libraries(${BIN} ${CTWMLIBS})
+		# Few of our tests really need any of the X or other libs we pull in.
+		# However, most of the .o's for ctwm itself are going to have some
+		# ref out to them somewhere, so any tests that wind up pulling
+		# functions from those are going to make the linker want to resolve
+		# them when we link ${TNAME}.  So just unconditionally add them and
+		# don't worry about which tests may not actually need 'em.
+		target_link_libraries(${BIN} ${CTWMLIBS})
 
-	# Add to pre-test target
-	add_dependencies(test_bins ${BIN})
+		# Add to pre-test target
+		add_dependencies(test_bins ${BIN})
+	endif()
 
 	# And add the test itself
-	add_test(NAME ${BIN}
-		COMMAND $<TARGET_FILE:${BIN}>
+	add_test(NAME ${TNAME}
+		COMMAND ${BIN_RUN} ${_ARGS_ARGS}
+		WORKING_DIRECTORY ${_ARGS_WORKING_DIRECTORY}
 		)
-endmacro()
+
+	# Don't allow $DISPLAY unless specifically requested.
+	if(NOT _ARGS_ALLOW_DISPLAY)
+		set_tests_properties(${TNAME} PROPERTIES
+			ENVIRONMENT DISPLAY=
+			)
+	endif()
+
+endfunction()
+
+
+# Simple wrapper for when we want to explicitly skip a test (as opposed
+# to just silently not doing it; better UX to say we're skipping one and
+# why...)
+#
+# XXX This seems like the simplest way of actually "skip"'ing a test
+# based on configure options.  That's nuts.  REQUIRED_FILES sounds like
+# it would, but actually causes the test to "not run" and be considered
+# failed   :(
+function(ctwm_skip_unit_test TNAME REASON)
+	add_test(NAME ${TNAME} COMMAND sh -c "echo Skipped: ${REASON} ; exit 99")
+	set_tests_properties(${TNAME} PROPERTIES SKIP_RETURN_CODE 99)
+endfunction()
 
 
 
 # First a simple smoke test of the built binary
-add_test(NAME run-info
-	COMMAND ${CTWMBIN} --info
+ctwm_simple_unit_test(run-info
+	BIN ${CTWMBIN}
+	BINPATH ${CTWMPATH}
+	ARGS --info
 	)
 set_tests_properties(run-info PROPERTIES
 	PASS_REGULAR_EXPRESSION "Twm version: "
@@ -42,39 +107,26 @@ set_tests_properties(run-info PROPERTIES
 
 
 # Try parsing the system.ctwmrc
-# Have to support skipping if no $DISPLAY is set, since we require
-# talking to the X server to get far enough to cfgchk.  Should really
-# find a way around that, but it's a lot more involved than you'd think,
-# so 'till then...
-add_test(NAME cfgchk
-	COMMAND sh -c "[ -z \"\$DISPLAY\" ] && exit 99 ; ${CTWMBIN} --cfgchk -f ${CMAKE_SOURCE_DIR}/system.ctwmrc"
-	)
-set_tests_properties(cfgchk PROPERTIES
-	# XXX Requires cmake 3.0...
-	SKIP_RETURN_CODE 99
+ctwm_simple_unit_test(cfgchk
+	BIN ${CTWMBIN}
+	BINPATH ${CTWMPATH}
+	ARGS --cfgchk -f ${CMAKE_SOURCE_DIR}/system.ctwmrc
 	)
 
 
 # Simple test of m4 preprocessing, but we skip if built without m4.
-# XXX Gotta skip if no $DISPLAY here too.  Fix that   :(
-# XXX This seems like the simplest way of actually "skip"'ing a test
-# based on configure options.  That's nuts.  REQUIRED_FILES sounds like
-# it would, but actually causes the test to "not run" and be considered
-# failed   :(
 if(USE_M4)
-	set(_test_m4_cmd
-		"[ -z \"\$DISPLAY\" ] && exit 99 \\; ${CTWMBIN} --cfgchk -f ${CMAKE_CURRENT_SOURCE_DIR}/test_m4/ctwmrc"
-		)
+	add_subdirectory(test_m4)
 else()
-	set(_test_m4_cmd "echo Built without m4, skipping \\; exit 99")
+	ctwm_skip_unit_test(test_m4 "Built without USE_M4")
 endif()
-add_test(NAME test_m4
-	COMMAND sh -c ${_test_m4_cmd}
-	)
-set_tests_properties(test_m4 PROPERTIES
-	SKIP_RETURN_CODE 99
-	)
 
 
 # A first run at a unit test
 add_subdirectory(util_expand)
+
+# RLayout stuff
+add_subdirectory(layout)
+
+# TwmKeys menu bits
+add_subdirectory(menu_twmkeys)
diff --git a/tests/layout/CMakeLists.txt b/tests/layout/CMakeLists.txt
new file mode 100644
index 0000000..8fff49f
--- /dev/null
+++ b/tests/layout/CMakeLists.txt
@@ -0,0 +1,20 @@
+# Test various rlayout-related bits
+
+# Bits directly testing the funcs
+ctwm_simple_unit_test(rlayout_overlap
+	BIN test_layout
+	ARGS overlap.tst
+	WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+	)
+
+ctwm_simple_unit_test(rlayout_test
+	BIN test_layout
+	ARGS test.tst
+	WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+	)
+
+
+# Test config parsing of MonitorLayout{} block
+ctwm_simple_unit_test(rlayout_monitor_layout
+	BIN test_monitor_layout
+	ARGS -f ${CMAKE_CURRENT_SOURCE_DIR}/monitor_layout.ctwmrc)
diff --git a/tests/layout/monitor_layout.ctwmrc b/tests/layout/monitor_layout.ctwmrc
new file mode 100644
index 0000000..408fe9e
--- /dev/null
+++ b/tests/layout/monitor_layout.ctwmrc
@@ -0,0 +1,7 @@
+# Allow testing MonitorLayout config
+MonitorLayout
+{
+	"One:1024x768+0+0"
+	"Two:768x1024+1024+0"
+	"800x600+1792+0" # Unnamed
+}
diff --git a/tests/layout/overlap.tst b/tests/layout/overlap.tst
new file mode 100644
index 0000000..2c8624e
--- /dev/null
+++ b/tests/layout/overlap.tst
@@ -0,0 +1,160 @@
+layout left:10x12+0+0 \
+       right:10x12+10+0 \
+       overlap:10x12+5+6
+=comment
+            1111111111
+  01234567890123456789
+ 0+left----++right---+
+ 1|        ||        |
+ 2|        ||        |
+ 3|        ||        |
+ 4|        ||        |
+ 5|        ||        |
+ 6|    +overlap-+    |
+ 7|    |   ||   |    |
+ 8|    |   ||   |    |
+ 9|    |   ||   |    |
+10|    |   ||   |    |
+11+----|---++---|----+
+12     |        |
+13     |        |
+14     |        |
+15     |        |
+16     |        |
+17     +--------+
+=end
+
+check_horizontal_layout 20x12+0+0 \
+			10x6+5+12
+=comment
+            1111111111
+  01234567890123456789
+ 0+------------------+
+ 1|                  |
+ 2|                  |
+ 3|                  |
+ 4|                  |
+ 5|                  |
+ 6|                  |
+ 7|                  |
+ 8|                  |
+ 9|                  |
+10|                  |
+11+------------------+
+12     +--------+
+13     |        |
+14     |        |
+15     |        |
+16     |        |
+17     +--------+
+=end
+
+check_vertical_layout 5x12+0+0 \
+		      10x18+5+0 \
+		      5x12+15+0
+=comment
+            1111111111
+  01234567890123456789
+ 0+---++--------++---+
+ 1|   ||        ||   |
+ 2|   ||        ||   |
+ 3|   ||        ||   |
+ 4|   ||        ||   |
+ 5|   ||        ||   |
+ 6|   ||        ||   |
+ 7|   ||        ||   |
+ 8|   ||        ||   |
+ 9|   ||        ||   |
+10|   ||        ||   |
+11+---+|        |+---+
+12     |        |
+13     |        |
+14     |        |
+15     |        |
+16     |        |
+17     +--------+
+=end
+
+########################################################################
+
+window 6x7+2+3
+=comment
+            1111111111
+  01234567890123456789
+ 0+left----++right---+
+ 1|        ||        |
+ 2|        ||        |
+ 3| +win.+ ||        |
+ 4| :    : ||        |
+ 5| :    : ||        |
+ 6| :  +overlap-+    |
+ 7| :  | : ||   |    |
+ 8| :  | : ||   |    |
+ 9| +..|.+ ||   |    |
+10|    |   ||   |    |
+11+----|---++---|----+
+12     |        |
+13     |        |
+14     |        |
+15     |        |
+16     |        |
+17     +--------+
+=end
+
+RLayoutFindTopBottomEdges 0 11
+RLayoutFindLeftRightEdges 0 19
+
+RLayoutFindMonitorTopEdge    0
+RLayoutFindMonitorBottomEdge 11
+RLayoutFindMonitorLeftEdge   0
+RLayoutFindMonitorRightEdge  9
+
+RLayoutFull       20x12+0+0
+RLayoutFullHoriz  20x7+0+3
+RLayoutFullVert   6x12+2+0
+# greater window area is in "left" monitor
+RLayoutFull1      10x12+0+0
+RLayoutFullHoriz1 10x7+0+3
+RLayoutFullVert1  6x12+2+0
+
+########################################################################
+
+window 6x7+7+3
+=comment
+            1111111111
+  01234567890123456789
+ 0+left----++right---+
+ 1|        ||        |
+ 2|        ||        |
+ 3|      +win.+      |
+ 4|      : || :      |
+ 5|      : || :      |
+ 6|    +overlap-+    |
+ 7|    | : || : |    |
+ 8|    | : || : |    |
+ 9|    | +.||.+ |    |
+10|    |   ||   |    |
+11+----|---++---|----+
+12     |        |
+13     |        |
+14     |        |
+15     |        |
+16     |        |
+17     +--------+
+=end
+
+RLayoutFindTopBottomEdges 0 17
+RLayoutFindLeftRightEdges 0 19
+
+RLayoutFindMonitorTopEdge    0
+RLayoutFindMonitorBottomEdge 11
+RLayoutFindMonitorLeftEdge   5
+RLayoutFindMonitorRightEdge  14
+
+RLayoutFull       20x12+0+0
+RLayoutFullHoriz  20x7+0+3
+RLayoutFullVert   6x18+7+0
+# greater window area is in "overlap" monitor
+RLayoutFull1      10x12+5+6
+RLayoutFullHoriz1 10x4+5+6
+RLayoutFullVert1  6x12+7+6
diff --git a/tests/layout/test.tst b/tests/layout/test.tst
new file mode 100644
index 0000000..86fb593
--- /dev/null
+++ b/tests/layout/test.tst
@@ -0,0 +1,72 @@
+layout 0:30x20+0+5 \
+       1:30x20+30+5 \
+       3:15x25+60+0
+
+=comment
+                                                            +3------------+
+                                                            |             |
+                                                            |             |
+                                                            |             |
+                                                            |             |
++0---------------------------++1---------------------------+|             |
+|                            ||                            ||             |
+|                            ||                            ||             |
+|                            ||                            ||             |
+|                            ||                            ||             |
+|                            ||                            ||             |
+|                            ||                            ||             |
+|                            ||                            ||             |
+|                            ||                            ||             |
+|                            ||                            ||             |
+|                            ||                            ||             |
+|                            ||                            ||             |
+|                            ||                            ||             |
+|                            ||                            ||             |
+|                            ||                            ||             |
+|                            ||                            ||             |
+|                            ||                            ||             |
+|                            ||                            ||             |
+|                            ||                            ||             |
++----------------------------++----------------------------++-------------+
+=end
+
+check_horizontal_layout 75x20+0+5 15x5+60+0
+check_vertical_layout  15x25+60+0 60x20+0+5
+
+#
+# window in monitor 0
+window 20x10+5+10
+
+RLayoutFindTopBottomEdges 5 24
+RLayoutFindLeftRightEdges 0 74
+
+RLayoutFindMonitorBottomEdge 24
+RLayoutFindMonitorTopEdge    5
+RLayoutFindMonitorLeftEdge   0
+RLayoutFindMonitorRightEdge  29
+
+RLayoutFull       75x20+0+5
+RLayoutFullHoriz  75x10+0+10
+RLayoutFullVert   20x20+5+5
+RLayoutFull1      30x20+0+5
+RLayoutFullHoriz1 30x10+0+10
+RLayoutFullVert1  20x20+5+5
+
+#
+# window in monitor 1
+window 20x10+35+10
+
+RLayoutFindTopBottomEdges 5 24
+RLayoutFindLeftRightEdges 0 74
+
+RLayoutFindMonitorBottomEdge 24
+RLayoutFindMonitorTopEdge    5
+RLayoutFindMonitorLeftEdge   30
+RLayoutFindMonitorRightEdge  59
+
+RLayoutFull       75x20+0+5
+RLayoutFullHoriz  75x10+0+10
+RLayoutFullVert   20x20+35+5
+RLayoutFull1      30x20+30+5
+RLayoutFullHoriz1 30x10+30+10
+RLayoutFullVert1  20x20+35+5
diff --git a/tests/layout/test_layout.c b/tests/layout/test_layout.c
new file mode 100644
index 0000000..85716f9
--- /dev/null
+++ b/tests/layout/test_layout.c
@@ -0,0 +1,522 @@
+#include "ctwm.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "r_layout.h"
+#include "r_area_list.h"
+#include "r_area.h"
+
+static char *
+trim_prefix(char *str, char *prefix)
+{
+	int prefix_len = strlen(prefix);
+
+	if(strncmp(str, prefix, prefix_len) == 0) {
+		str += prefix_len;
+	}
+
+	return str;
+}
+
+static char *
+trim_spaces(char *str)
+{
+	char *end;
+
+	while(*str == ' ' || *str == '\t') {
+		str++;
+	}
+
+	end = str + strlen(str) - 1;
+
+	while(end >= str && (*end == '\n' || *end == ' ' || *end == '\t')) {
+		end--;
+	}
+	end[1] = '\0';
+
+	// translate \t to space
+	for(end = str; *end; end++)
+		if(*end == '\t') {
+			*end = ' ';
+		}
+
+	return str;
+}
+
+static void
+print_rarea_list(RAreaList *list)
+{
+	printf("[len=%d", list->len);
+
+	for(int i = 0 ; i < list->len ; i++) {
+		RArea area = list->areas[i];
+		printf(" %dx%d+%d+%d", area.width, area.height, area.x, area.y);
+	}
+
+	printf("]");
+}
+
+static int
+cmp_rarea_list(char *filename, int linenum, char *function,
+               RAreaList *got, RAreaList *expected)
+{
+	if(got->len != expected->len
+	                || memcmp(got->areas, expected->areas, got->len * sizeof(got->areas[0]))) {
+		printf("%s:%d: %s failed\n"
+		       "\t     got: ", filename, linenum, function);
+		print_rarea_list(got);
+		printf("\n"
+		       "\texpected: ");
+		print_rarea_list(expected);
+		printf("\n");
+	}
+
+	return 1;
+}
+
+static int
+extract_geometry(char *buf, char ***names, int *num_names,
+                 unsigned int *x, unsigned int *y,
+                 unsigned int *width, unsigned int *height)
+{
+	char name[32];
+
+	// {monitor_name}:{width}x{height}+{x}+{y}
+	if(sscanf(buf, "%31[^:]:%ux%u+%u+%u", name, width, height, x, y) == 5) {
+		if(names == NULL) {
+			*names = malloc(2 * sizeof(char *));
+		}
+		else {
+			(*num_names)++;
+			*names = realloc(*names, (*num_names + 1) * sizeof(char *));
+		}
+
+		if(*names == NULL) {
+			perror("{m,re}alloc failed");
+			exit(1);
+		}
+
+		(*names)[*num_names] = strdup(name);
+		if((*names)[*num_names] == NULL) {
+			perror("strdup failed");
+			exit(1);
+		}
+	}
+	// {width}x{height}+{x}+{y}
+	else if(sscanf(buf, "%ux%u+%u+%u", width, height, x, y) != 4) {
+		return 0;
+	}
+
+	return 1;
+}
+
+static RLayout *
+extract_layout(char *filename, int linenum, char *line)
+{
+	char **names = NULL, *geom;
+	RAreaList *list;
+	int num_names = 0;
+	unsigned int width, height, x, y;
+
+	list = RAreaListNew(10, NULL);
+	names = NULL;
+
+	while((geom = strsep(&line, " ")) != NULL) {
+		if(geom[0] == '\0') {
+			continue;
+		}
+
+		switch(extract_geometry(geom, &names, &num_names,
+		                        &x, &y, &width, &height)) {
+			case 1:
+				break;
+
+			case 0:
+				fprintf(stderr, "%s:%d: layout unrecognized geometry (%s)\n",
+				        filename, linenum, geom);
+			// fallthrough
+			default:
+				free(names);
+				return NULL;
+		}
+
+		RAreaListAdd(list, RAreaNewStatic((int)x, (int)y, (int)width, (int)height));
+	}
+
+	return RLayoutSetMonitorsNames(RLayoutNew(list), names);
+}
+
+static RLayout *
+read_layout_file(FILE *file, char *filename)
+{
+	char buf[128], *line, **names;
+	RAreaList *list;
+	RLayout *layout;
+	int num, num_names;
+	bool comment = false;
+	unsigned int width, height, x, y;
+
+	list = RAreaListNew(10, NULL);
+	names = NULL;
+	num_names = 0;
+	for(num = 1; fgets(buf, sizeof(buf), file) != NULL; num++) {
+		line = trim_spaces(buf);
+
+		// Multiline comments: =comment -> =end
+		if(comment) {
+			if(strcmp(line, "=end") == 0) {
+				comment = false;
+			}
+			continue;
+		}
+
+		if(strcmp(line, "=comment") == 0) {
+			comment = true;
+			continue;
+		}
+
+		if(line[0] == '#' || line[0] == '\0') {
+			continue;
+		}
+
+		if(strcmp(line, "__END__") == 0) {
+			break;
+		}
+
+		switch(extract_geometry(line, &names, &num_names,
+		                        &x, &y, &width, &height)) {
+			case 1:
+				break;
+
+			case 0:
+				fprintf(stderr, "%s:%d: layout unrecognized line (%s)\n", filename, num, line);
+			// fallthrough
+			default:
+				free(names);
+				return NULL;
+		}
+
+		RAreaListAdd(list, RAreaNewStatic((int)x, (int)y, (int)width, (int)height));
+	}
+
+	layout = RLayoutSetMonitorsNames(RLayoutNew(list), names);
+
+	//RLayoutPrint(layout);
+
+	return layout;
+}
+
+static int
+read_test_from_file(char *filename)
+{
+	char buf[1024], *cur_buf, sub_buf[1024], func_buf[32], *line;
+	FILE *file;
+	RLayout *layout = NULL;
+	RArea win = { 0 };
+	int linenum, buf_size, expected1, expected2, errors;
+	bool comment = false;
+	unsigned int width, height, x, y;
+
+	file = fopen(filename, "r");
+	if(file == NULL) {
+		fprintf(stderr, "Cannot open %s: %s\n", filename, strerror(errno));
+		return 1;
+	}
+
+	errors = 0;
+
+	cur_buf = buf;
+	buf_size = sizeof(buf);
+
+	for(linenum = 1; fgets(cur_buf, buf_size, file) != NULL; linenum++) {
+		int last_chr_idx = strlen(cur_buf) - 1;
+		if(last_chr_idx >= 0) {
+			if(cur_buf[last_chr_idx] == '\n') {
+				last_chr_idx--;
+			}
+
+			if(last_chr_idx >= 0 && cur_buf[last_chr_idx] == '\\') {
+				cur_buf += last_chr_idx;
+				buf_size -= last_chr_idx;
+
+				if(buf_size > 0) {
+					continue;
+				}
+
+				fprintf(stderr, "%s:%d: line too long\n", filename, linenum);
+				errors++;
+				break;
+			}
+		}
+
+		cur_buf = buf;
+		buf_size = sizeof(buf);
+
+		line = trim_spaces(buf);
+
+		// Multiline comments: =comment -> =end
+		if(comment) {
+			if(strcmp(line, "=end") == 0) {
+				comment = false;
+			}
+			continue;
+		}
+
+		if(strcmp(line, "=comment") == 0) {
+			comment = true;
+			continue;
+		}
+
+		if(line[0] == '#' || line[0] == '\0') {
+			continue;
+		}
+
+		// layout FILENAME|area ...
+		if(sscanf(line, "layout %1023s", sub_buf) == 1) {
+			FILE *file_layout;
+
+			if((file_layout = fopen(sub_buf, "r")) != NULL) {
+				layout = read_layout_file(file_layout, sub_buf);
+				fclose(file_layout);
+			}
+			else if(errno == ENOENT) {
+				line = trim_spaces(&line[7]);
+
+				layout = extract_layout(filename, linenum, line);
+			}
+
+			if(layout != NULL) {
+				continue;
+			}
+
+			fprintf(stderr, "%s:%d: layout error\n", filename, linenum);
+			errors++;
+			break;
+		}
+
+		// Gotta have a layout by now, right?
+		assert(layout != NULL);
+
+		// check_horizontal_layout area ...
+		if(strncmp(line, "check_horizontal_layout ", 24) == 0) {
+			RLayout *check_layout;
+
+			line += 24;
+			line = trim_spaces(line);
+
+			check_layout = extract_layout(filename, linenum, line);
+			if(check_layout == NULL) {
+				break;
+			}
+
+			if(cmp_rarea_list(filename, linenum,
+			                  "check_horizontal_layout",
+			                  layout->horiz, check_layout->monitors)) {
+				continue;
+			}
+			break;
+		}
+
+		// check_vertical_layout area ...
+		if(strncmp(line, "check_vertical_layout ", 22) == 0) {
+			RLayout *check_layout;
+
+			line += 22;
+			line = trim_spaces(line);
+
+			check_layout = extract_layout(filename, linenum, line);
+			if(check_layout == NULL) {
+				break;
+			}
+
+			if(cmp_rarea_list(filename, linenum,
+			                  "check_vertical_layout",
+			                  layout->vert, check_layout->monitors)) {
+				continue;
+			}
+			break;
+		}
+
+		// window area
+		if(sscanf(line, "window %dx%d+%d+%d", &width, &height, &x, &y) == 4) {
+			win = RAreaNew((int)x, (int)y, (int)width, (int)height);
+			if(RAreaIsValid(&win)) {
+				continue;
+			}
+
+			fprintf(stderr, "%s:%d: bad window geometry\n", filename, linenum);
+			errors++;
+			break;
+		}
+
+		if(layout == NULL) {
+			fprintf(stderr,
+			        "%s:%d: cannot continue as `layout ...' line not found\n",
+			        filename, linenum);
+			errors++;
+			break;
+		}
+
+		if(!RAreaIsValid(&win)) {
+			fprintf(stderr,
+			        "%s:%d: cannot continue as `window ...' line not found\n",
+			        filename, linenum);
+			errors++;
+			break;
+		}
+
+		// Function area
+		//   RLayoutFull       area
+		//   RLayoutFullHoriz  area
+		//   RLayoutFullVert   area
+		//   RLayoutFull1      area
+		//   RLayoutFullHoriz1 area
+		//   RLayoutFullVert1  area
+		if(sscanf(line, "%31s %dx%d+%d+%d",
+		                func_buf, &width, &height, &x, &y) == 5) {
+			RArea got_area, expected_area;
+			char *function = trim_prefix(func_buf, "RLayoutFull");
+
+			expected_area = RAreaNew((int)x, (int)y, (int)width, (int)height);
+
+			if(function[0] == '\0') {
+				got_area = RLayoutFull(layout, &win);
+			}
+			else if(strcmp(function, "Horiz") == 0) {
+				got_area = RLayoutFullHoriz(layout, &win);
+			}
+			else if(strcmp(function, "Vert") == 0) {
+				got_area = RLayoutFullVert(layout, &win);
+			}
+			else if(strcmp(function, "1") == 0) {
+				got_area = RLayoutFull1(layout, &win);
+			}
+			else if(strcmp(function, "Horiz1") == 0) {
+				got_area = RLayoutFullHoriz1(layout, &win);
+			}
+			else if(strcmp(function, "Vert1") == 0) {
+				got_area = RLayoutFullVert1(layout, &win);
+			}
+			else {
+				fprintf(stderr, "%s:%d: bad function name bound to area\n",
+				        filename, linenum);
+				errors++;
+				break;
+			}
+
+			if(memcmp(&got_area, &expected_area, sizeof(got_area)) != 0) {
+				printf("%s:%d: %s failed\n"
+				       "\t     got: %dx%d+%d+%d\n"
+				       "\texpected: %dx%d+%d+%d\n",
+				       filename, linenum, func_buf,
+				       got_area.width, got_area.height, got_area.x, got_area.y,
+				       expected_area.width, expected_area.height,
+				       expected_area.x, expected_area.y);
+				errors++;
+			}
+		}
+		// Function num1 num2
+		//   RLayoutFindTopBottomEdges top  bottom
+		//   RLayoutFindLeftRightEdges left right
+		else if(sscanf(line, "%31s %d %d",
+		                func_buf, &expected1, &expected2) == 3) {
+			int got1, got2;
+			char *function = trim_prefix(func_buf, "RLayoutFind");
+
+			if(strcmp(function, "TopBottomEdges") == 0) {
+				RLayoutFindTopBottomEdges(layout, &win, &got1, &got2);
+			}
+			else if(strcmp(function, "LeftRightEdges") == 0) {
+				RLayoutFindLeftRightEdges(layout, &win, &got1, &got2);
+			}
+			else {
+				fprintf(stderr,
+				        "%s:%d: bad function name bound to 2 expected results\n",
+				        filename, linenum);
+				errors++;
+				break;
+			}
+
+			if(got1 != expected1 || got2 != expected2) {
+				printf("%s:%d: %s failed\n"
+				       "\t     got: (%d, %d)\n"
+				       "\texpected: (%d, %d)\n",
+				       filename, linenum, func_buf,
+				       got1, got2,
+				       expected1, expected2);
+				errors++;
+			}
+		}
+		// Function num
+		//   RLayoutFindMonitorBottomEdge bottom
+		//   RLayoutFindMonitorTopEdge    top
+		//   RLayoutFindMonitorLeftEdge   left
+		//   RLayoutFindMonitorRightEdge  right
+		else if(sscanf(line, "%31s %d", func_buf, &expected1) == 2) {
+			int got;
+			char *function = trim_prefix(func_buf, "RLayoutFindMonitor");
+
+			if(strcmp(function, "BottomEdge") == 0) {
+				got = RLayoutFindMonitorBottomEdge(layout, &win);
+			}
+			else if(strcmp(function, "TopEdge") == 0) {
+				got = RLayoutFindMonitorTopEdge(layout, &win);
+			}
+			else if(strcmp(function, "LeftEdge") == 0) {
+				got = RLayoutFindMonitorLeftEdge(layout, &win);
+			}
+			else if(strcmp(function, "RightEdge") == 0) {
+				got = RLayoutFindMonitorRightEdge(layout, &win);
+			}
+			else {
+				fprintf(stderr,
+				        "%s:%d: bad function name bound to one expected result\n",
+				        filename, linenum);
+				errors++;
+				break;
+			}
+
+			if(got != expected1) {
+				printf("%s:%d: %s failed\n"
+				       "\t     got: %d\n"
+				       "\texpected: %d\n",
+				       filename, linenum, func_buf,
+				       got,
+				       expected1);
+				errors++;
+			}
+		}
+		else {
+			fprintf(stderr, "%s:%d: test unrecognized line\n", filename, linenum);
+			errors++;
+			break;
+		}
+	}
+
+	fclose(file);
+
+	return errors;
+}
+
+int
+main(int argc, char **argv)
+{
+	int i, error;
+
+	if(argc < 2) {
+		fprintf(stderr, "usage: %s TEST_FILE ...\n", argv[0]);
+		return 1;
+	}
+
+	error = 0;
+	for(i = 1; i < argc; i++) {
+		printf("Testing %s\n", argv[i]);
+		error = read_test_from_file(argv[i]) || error;
+	}
+
+	return error;
+}
diff --git a/tests/layout/test_monitor_layout.c b/tests/layout/test_monitor_layout.c
new file mode 100644
index 0000000..669fb99
--- /dev/null
+++ b/tests/layout/test_monitor_layout.c
@@ -0,0 +1,112 @@
+/**
+ * Test that MonitorLayout config works
+ */
+
+#include "ctwm.h"
+
+#include <stdio.h>
+
+#include "ctwm_main.h"
+#include "ctwm_test.h"
+#include "r_layout.h"
+#include "screen.h"
+
+
+/**
+ * Callback: after the config file gets parsed, make sure we got the info
+ * we expected out of it.
+ */
+static int
+check_monitor_layout(void)
+{
+	// Guard against things that shouldn't happen
+	if(Scr == NULL) {
+		fprintf(stderr, "BUG: Scr == NULL\n");
+		return 1;
+	}
+	if(Scr->Layout == NULL) {
+		fprintf(stderr, "BUG: Scr->Layout == NULL\n");
+		return 1;
+	}
+
+	// Debug/dev
+	if(0) {
+		RLayoutPrint(Scr->Layout);
+		return 1;
+	}
+
+
+	// We should have 2 monitors...
+	const int nmons = RLayoutNumMonitors(Scr->Layout);
+	if(nmons != 3) {
+		fprintf(stderr, "Expected 3 monitors, got %d\n", nmons);
+		return 1;
+	}
+
+
+	// Check the names and sizes.
+	// XXX We maybe should have better accessors for this, than grubbing
+	// in the structs, but...
+	char **names = Scr->Layout->names;
+
+	if(strcmp(names[0], "One") != 0) {
+		fprintf(stderr, "First monitor should be 'One', not '%s'\n", names[0]);
+		return 1;
+	}
+	if(strcmp(names[1], "Two") != 0) {
+		fprintf(stderr, "Second monitor should be 'Two', not '%s'\n", names[1]);
+		return 1;
+	}
+	if(names[2] != NULL) {
+		fprintf(stderr, "Third monitor should be unnamed, not '%s'\n", names[2]);
+		return 1;
+	}
+
+
+	RAreaList *mons = Scr->Layout->monitors;
+
+#define CHK_MON_VAL(mon, fld, val) do { \
+                if(mons[0].areas[mon].fld != val) { \
+                        fprintf(stderr, "Monitor %d %s should be %d, not %d\n", mon, \
+                                #fld, val, mons[0].areas[mon].fld); \
+                        return 1; \
+                } \
+        } while(0)
+
+	CHK_MON_VAL(0, x, 0);
+	CHK_MON_VAL(0, y, 0);
+	CHK_MON_VAL(0, width,  1024);
+	CHK_MON_VAL(0, height, 768);
+
+	CHK_MON_VAL(1, x, 1024);
+	CHK_MON_VAL(1, y, 0);
+	CHK_MON_VAL(1, width,  768);
+	CHK_MON_VAL(1, height, 1024);
+
+	CHK_MON_VAL(2, x, 1792);
+	CHK_MON_VAL(2, y, 0);
+	CHK_MON_VAL(2, width,  800);
+	CHK_MON_VAL(2, height, 600);
+
+
+
+	// OK, everything was good.
+	fprintf(stdout, "OK\n");
+	return 0;
+}
+
+
+
+/*
+ * Connect up our callback and kick off ctwm.
+ *
+ * XXX Args should be more locally controlled; x-ref test_m4.
+ */
+int
+main(int argc, char *argv[])
+{
+	// Connect up
+	TEST_POSTPARSE(check_monitor_layout);
+
+	return ctwm_main(argc, argv);
+}
diff --git a/tests/menu_twmkeys/CMakeLists.txt b/tests/menu_twmkeys/CMakeLists.txt
new file mode 100644
index 0000000..832e9a9
--- /dev/null
+++ b/tests/menu_twmkeys/CMakeLists.txt
@@ -0,0 +1,2 @@
+# Test the strings for TwmKeys menu
+ctwm_simple_unit_test(mk_twmkeys_entry BIN mk_twmkeys_entry)
diff --git a/tests/menu_twmkeys/mk_twmkeys_entry.c b/tests/menu_twmkeys/mk_twmkeys_entry.c
new file mode 100644
index 0000000..65dc2ad
--- /dev/null
+++ b/tests/menu_twmkeys/mk_twmkeys_entry.c
@@ -0,0 +1,80 @@
+/*
+ * Test mk_twmkeys_entry()
+ */
+
+#include "ctwm.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "menus.h"
+
+int
+main(int argc, char *argv[])
+{
+	FuncKey key;
+
+	// Init
+	memset(&key, 0, sizeof(key));
+	key.name   = "KEY";
+	key.action = "ACT";
+
+#define TST(expect) do { \
+                const char *ret = mk_twmkeys_entry(&key); \
+                if(strcmp(ret, expect) != 0) { \
+                        fprintf(stderr, "Expected '%s', got '%s'\n", \
+                                        expect, ret); \
+                        exit(1); \
+                } \
+        } while(0)
+
+	// Simple
+	key.mods = ShiftMask;
+	TST("[S+KEY] ACT");
+
+	key.mods = ControlMask;
+	TST("[C+KEY] ACT");
+
+	// Combo
+	key.mods = Mod1Mask | Alt1Mask;
+	TST("[M+A1+KEY] ACT");
+
+	key.mods = Alt1Mask | Alt2Mask | Alt3Mask | Alt4Mask | Alt5Mask;
+	TST("[A1+A2+A3+A4+A5+KEY] ACT");
+
+	// All the mods!
+	key.mods = ShiftMask | ControlMask
+	           | Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask
+	           | Alt1Mask | Alt2Mask | Alt3Mask | Alt4Mask | Alt5Mask;
+	TST("[M+S+C+M2+M3+M4+M5+A1+A2+A3+A4+A5+KEY] ACT");
+
+
+	// Magic value used to test overflow
+	{
+		// Overflow by 1: inherit mods from above, and add a bit
+		key.mods |= 1 << 30;
+
+		const char *ret = mk_twmkeys_entry(&key);
+		if(ret != NULL) {
+			fprintf(stderr, "Should have blown up for Over1, instead "
+			        "got '%s'.\n", ret);
+			exit(1);
+		}
+	}
+	{
+		// Overflow by itself
+		key.mods = 1 << 31;
+
+		const char *ret = mk_twmkeys_entry(&key);
+		if(ret != NULL) {
+			fprintf(stderr, "Should have blown up for OverAll, instead "
+			        "got '%s'.\n", ret);
+			exit(1);
+		}
+	}
+
+
+	// OK then
+	exit(0);
+}
diff --git a/tests/test_m4/CMakeLists.txt b/tests/test_m4/CMakeLists.txt
new file mode 100644
index 0000000..63d7dd8
--- /dev/null
+++ b/tests/test_m4/CMakeLists.txt
@@ -0,0 +1,3 @@
+# Test that m4 D'sTRT
+ctwm_simple_unit_test(test_m4
+	ARGS -f ${CMAKE_CURRENT_SOURCE_DIR}/ctwmrc)
diff --git a/tests/test_m4/test_m4.c b/tests/test_m4/test_m4.c
new file mode 100644
index 0000000..b9ed2e4
--- /dev/null
+++ b/tests/test_m4/test_m4.c
@@ -0,0 +1,70 @@
+/**
+ * Test that m4 config rewriting works.
+ */
+
+#include "ctwm.h"
+
+#include <stdio.h>
+
+#include "ctwm_main.h"
+#include "ctwm_test.h"
+#include "screen.h"
+
+
+/**
+ * Callback: after the config file gets parsed, make sure we got the info
+ * we expected out of it.
+ */
+static int
+check_wsm_geom(void)
+{
+	const char *expected_geom = "300x100+10+10";
+	const int expected_columns = 2;
+
+	// Guard against things that shouldn't happen
+	if(Scr == NULL) {
+		fprintf(stderr, "BUG: Scr == NULL\n");
+		return 1;
+	}
+	if(Scr->workSpaceMgr.geometry == NULL) {
+		fprintf(stderr, "BUG: Scr->workSpaceMgr.geometry == NULL\n");
+		return 1;
+	}
+
+
+	// Now check that we got the expected result
+	if(strcmp(Scr->workSpaceMgr.geometry, expected_geom) != 0) {
+		fprintf(stderr, "Got '%s' instead of expected '%s' geometry.\n",
+		        Scr->workSpaceMgr.geometry, expected_geom);
+		return 1;
+	}
+
+	if(Scr->workSpaceMgr.columns != expected_columns) {
+		fprintf(stderr, "Got '%d' instead of expected '%d' columns.\n",
+		        Scr->workSpaceMgr.columns, expected_columns);
+		return 1;
+	}
+
+
+	// OK, everything was good.
+	fprintf(stdout, "OK\n");
+	return 0;
+}
+
+
+
+/*
+ * Connect up our callback and kick off ctwm.
+ *
+ * XXX We should probably have the various necessary args hardcoded into
+ * here instead of relying on our caller, but this is a workable first
+ * step.
+ */
+int
+main(int argc, char *argv[])
+{
+	// Connect up
+	TEST_POSTPARSE(check_wsm_geom);
+
+	return ctwm_main(argc, argv);
+}
diff --git a/tests/util_expand/CMakeLists.txt b/tests/util_expand/CMakeLists.txt
index 69029f0..1e9a64b 100644
--- a/tests/util_expand/CMakeLists.txt
+++ b/tests/util_expand/CMakeLists.txt
@@ -1,2 +1,2 @@
 # Test ExpandFilePath()
-ctwm_simple_unit_test(t_efp)
+ctwm_simple_unit_test(path_expansion BIN t_efp)
diff --git a/tests/util_expand/t_efp.c b/tests/util_expand/t_efp.c
index 026561f..02258b1 100644
--- a/tests/util_expand/t_efp.c
+++ b/tests/util_expand/t_efp.c
@@ -18,8 +18,9 @@ main(int argc, char *argv[])
 #define EXPECT "foo:FOO/bar"
 
 	char *ret = ExpandFilePath(in);
-	if(strcmp(ret, EXPECT) == 0)
+	if(strcmp(ret, EXPECT) == 0) {
 		exit(0);
+	}
 	fprintf(stderr, "'%s' != expected '%s'\n", ret, EXPECT);
 	exit(1);
 }
diff --git a/tools/mk_function_bits.sh b/tools/mk_function_bits.sh
index fc0e13d..ab707ba 100755
--- a/tools/mk_function_bits.sh
+++ b/tools/mk_function_bits.sh
@@ -156,7 +156,7 @@ typedef enum {
 static const _fdef_table_cursor fdef_table[] = {
 EOF
 
-	while read func curs
+	while read func curs ifdef
 	do
 		if [ "X${func}" = "X" ]; then
 			echo "Got no function!"
@@ -173,10 +173,16 @@ EOF
 			exit 1
 		fi
 
+		if [ "X${ifdef}" != "X-" ]; then
+			echo "#ifdef ${ifdef}"
+		fi
 		printf "\t%-23s = %s,\n" "[F_${func}]" "${scurs}"
+		if [ "X${ifdef}" != "X-" ]; then
+			echo "#endif"
+		fi
 	done << EOF
 	$(getsect main \
-		| ${AWK} '{ if ($3 != "-") {printf "%s %s\n", toupper($1), $3;} }')
+		| ${AWK} '{ if ($3 != "-") {printf "%s %s %s\n", toupper($1), $3, $4;} }')
 EOF
 
 	cat << EOF
diff --git a/tools/mk_tar.sh b/tools/mk_tar.sh
index 3603a79..c30963b 100755
--- a/tools/mk_tar.sh
+++ b/tools/mk_tar.sh
@@ -3,7 +3,7 @@
 # Setup and generate a release tarball
 
 # Make sure we're in the expected root of the tree
-rtdir=`bzr root $0`
+rtdir=`brz root $0`
 cd $rtdir
 
 # Figure out version
@@ -13,7 +13,7 @@ if [ ! -z "$1" ]; then
 	version=$1
 elif echo -n $version | grep -q '[^0-9\.]'; then
 	# If it's a non-release, append date
-    version="$version.`date '+%Y%m%d'`"
+	version="$version.`date '+%Y%m%d'`"
 fi
 
 # Setup the dir
@@ -31,7 +31,7 @@ fi
 mkdir -pm755 $dir
 
 # Create a totally fresh branch in it
-bzr branch --use-existing-dir $rtdir $dir
+brz branch --use-existing-dir $rtdir $dir
 
 # Do various setup in the branch to prepare
 (
diff --git a/tools/rewrite_version_bzr.sh b/tools/rewrite_version_bzr.sh
index 7c90b63..d0c611d 100755
--- a/tools/rewrite_version_bzr.sh
+++ b/tools/rewrite_version_bzr.sh
@@ -5,7 +5,7 @@
 
 
 # "revno revid" of the working tree
-REVID=`bzr revision-info --tree | cut -d ' ' -f2-`
+REVID=`${BZR_CMD} revision-info --tree | cut -d ' ' -f2-`
 if [ $? -ne 0 ]; then
 	# Failed somehow
 	REVID="???"
diff --git a/tools/try_all_opts.pl b/tools/try_all_opts.pl
index c2e7a3e..e524f74 100755
--- a/tools/try_all_opts.pl
+++ b/tools/try_all_opts.pl
@@ -38,6 +38,10 @@ my %OPTS = (
 	USE_EWMH =>  {
 		desc => 'EWMH support',
 	},
+	USE_XRANDR =>  {
+		desc => 'XRANDR support',
+		req_i => 'X11/extensions/Xrandr.h',
+	},
 );
 
 # Default include paths to check
@@ -119,6 +123,10 @@ my $tmpdir = File::Temp->newdir("ctwm-opts-XXXXXXXX",
 print "Testing in $tmpdir...\n";
 
 
+# Clear $DISPLAY
+delete $ENV{DISPLAY};
+
+
 # Now, actually running them.
 my ($suc, $fail) = (0,0);
 my @fails;
diff --git a/twm_window_struct.h b/twm_window_struct.h
index 9ea3540..9b579b5 100644
--- a/twm_window_struct.h
+++ b/twm_window_struct.h
@@ -243,8 +243,10 @@ struct TwmWindow {
 
 	/// WindowRegion containing this window.  \todo Write-only?  Reap?
 	WindowRegion *wr;
+#ifdef WINBOX
 	WindowBox *winbox; ///< WindowBox containing this window.
 	bool iswinbox;     ///< This is a WindowBox window.
+#endif
 
 	/// Saved window geometry.  Used in f.savegeometry and
 	/// f.restoregeometry.
diff --git a/types.h b/types.h
index d9a4c7a..f9ef2ad 100644
--- a/types.h
+++ b/types.h
@@ -43,7 +43,6 @@ typedef struct WindowRegion WindowRegion;
 typedef struct WindowEntry WindowEntry;
 typedef struct WindowBox WindowBox;
 typedef struct TwmWindow TwmWindow;
-typedef struct TWMWinConfigEntry TWMWinConfigEntry;
 
 /* From image.h */
 typedef struct Image Image;
@@ -66,4 +65,9 @@ typedef struct OccupyWindow OccupyWindow;
 typedef struct OtpWinList OtpWinList;
 typedef struct OtpPreferences OtpPreferences;
 
+/* From r_structs.h */
+typedef struct RArea RArea;
+typedef struct RAreaList RAreaList;
+typedef struct RLayout RLayout;
+
 #endif /* _CTWM_TYPES_H */
diff --git a/util.c b/util.c
index 800fb25..3cc9150 100644
--- a/util.c
+++ b/util.c
@@ -143,12 +143,26 @@ ExpandFilename(const char *name)
 }
 
 
+
+/*
+ * Some color utils
+ */
+/**
+ * Get info from the server about a given color.
+ */
 void
 GetColor(int kind, Pixel *what, const char *name)
 {
 	XColor color;
 	Colormap cmap = Scr->RootColormaps.cwins[0]->colormap->c;
 
+	// If we have no valid X connection (generally means a --cfgchk or
+	// similar run; wont' happen in normal operations), just stub out.
+	if(dpy == NULL) {
+		*what = 0;
+		return;
+	}
+
 #ifndef TOM
 	if(!Scr->FirstTime) {
 		return;
@@ -223,7 +237,12 @@ gotit:
 	return;
 }
 
-void GetShadeColors(ColorPair *cp)
+
+/**
+ * Try and create a 'shaded' version of a color for prettier UI.
+ */
+void
+GetShadeColors(ColorPair *cp)
 {
 	XColor      xcol;
 	Colormap    cmap = Scr->RootColormaps.cwins[0]->colormap->c;
@@ -232,6 +251,14 @@ void GetShadeColors(ColorPair *cp)
 	float       darkfactor;
 	char        clearcol [32], darkcol [32];
 
+	// If we have no valid X connection (generally means a --cfgchk or
+	// similar run; wont' happen in normal operations), just stub out.
+	if(dpy == NULL) {
+		cp->shadc = 0;
+		cp->shadd = 0;
+		return;
+	}
+
 	clearfactor = (float) Scr->ClearShadowContrast / 100.0;
 	darkfactor  = (100.0 - (float) Scr->DarkShadowContrast)  / 100.0;
 	xcol.pixel = cp->back;
@@ -253,6 +280,14 @@ void GetShadeColors(ColorPair *cp)
 	Scr->FirstTime = save;
 }
 
+
+
+/*
+ * Various font utils
+ */
+/**
+ * Try adjusting a font's height.  Used in drawing the icon manager.
+ */
 bool
 UpdateFont(MyFont *font, int height)
 {
@@ -271,7 +306,12 @@ UpdateFont(MyFont *font, int height)
 	return (prev != font->avg_height);
 }
 
-void GetFont(MyFont *font)
+
+/**
+ * Load up fontsets from the X server.  Only used by CreateFonts() below.
+ */
+static void
+GetFont(MyFont *font)
 {
 	char *deffontname = "fixed,*";
 	char **missing_charset_list_return;
@@ -286,6 +326,14 @@ void GetFont(MyFont *font)
 	int fnum;
 	char *basename2;
 
+	// In special cases where we have no dpy, I don't think we're going
+	// to need details here, so just leave things untouched.  We may need
+	// to stub in some magic values; deal with that when we run into the
+	// case.
+	if(dpy == NULL) {
+		return;
+	}
+
 	if(font->font_set != NULL) {
 		XFreeFontSet(dpy, font->font_set);
 	}
@@ -328,6 +376,27 @@ void GetFont(MyFont *font)
 }
 
 
+/**
+ * Load up our various defined fonts
+ */
+void
+CreateFonts(ScreenInfo *scr)
+{
+#define LOADFONT(fld) (GetFont(&scr->fld##Font))
+	LOADFONT(TitleBar);
+	LOADFONT(Menu);
+	LOADFONT(Icon);
+	LOADFONT(Size);
+	LOADFONT(IconManager);
+	LOADFONT(Default);
+	LOADFONT(workSpaceMgr.window);
+#undef LOADFONT
+
+	scr->HaveFonts = true;
+}
+
+
+
 #if 0
 static void move_to_head(TwmWindow *t)
 {
@@ -395,6 +464,10 @@ void move_to_after(TwmWindow *t, TwmWindow *after)
 #endif
 
 
+
+/**
+ * Backend for f.rescuewindows
+ */
 void RescueWindows(void)
 {
 	TwmWindow *twm_win = Scr->FirstWindow;
@@ -471,7 +544,13 @@ void RescueWindows(void)
 	}
 }
 
-void DebugTrace(char *file)
+
+
+/**
+ * Backend for f.trace
+ */
+void
+DebugTrace(char *file)
 {
 	if(!file) {
 		return;
@@ -495,6 +574,7 @@ void DebugTrace(char *file)
 }
 
 
+
 /*
  * A safe strncpy(), which always ensures NUL-termination.
  *
diff --git a/util.h b/util.h
index a8ac6d0..055f058 100644
--- a/util.h
+++ b/util.h
@@ -20,11 +20,23 @@
 #include <ctype.h>
 #include <stdio.h>
 
+
+/* Simple int utils */
+static inline int max(int a, int b)
+{
+	return a > b ? a : b;
+}
+
+static inline int min(int a, int b)
+{
+	return a < b ? a : b;
+}
+
 #ifndef MAX
-#define MAX(x,y) ((x)>(y)?(x):(y))
+#define MAX(x,y) (max(x,y))
 #endif
 #ifndef MIN
-#define MIN(x,y) ((x)<(y)?(x):(y))
+#define MIN(x,y) (min(x,y))
 #endif
 #ifndef ABS
 #define ABS(x) ((x)<0?-(x):(x))
@@ -48,8 +60,8 @@ char     *ExpandFilePath(char *path);
 
 void GetColor(int kind, Pixel *what, const char *name);
 void GetShadeColors(ColorPair *cp);
-void GetFont(MyFont *font);
 bool UpdateFont(MyFont *font, int height);
+void CreateFonts(ScreenInfo *scr);
 #if 0
 void move_to_after(TwmWindow *t, TwmWindow *after);
 #endif
diff --git a/vscreen.c b/vscreen.c
index 1fa2cfe..d80186b 100644
--- a/vscreen.c
+++ b/vscreen.c
@@ -4,6 +4,7 @@
 
 #include "ctwm.h"
 
+#include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
 
@@ -22,39 +23,46 @@
 static void DisplayWinUnchecked(VirtualScreen *vs, TwmWindow *tmp_win);
 
 
+static void init_def_vscreen(ScreenInfo *scr)
+{
+	VirtualScreen *vs = malloc(sizeof(VirtualScreen));
+
+	vs->x      = 0;
+	vs->y      = 0;
+	vs->w      = scr->rootw;
+	vs->h      = scr->rooth;
+	vs->window = scr->Root;
+	vs->next   = NULL;
+	vs->wsw    = 0;
+	scr->vScreenList = vs;
+	scr->currentvs   = vs;
+#ifdef VSCREEN
+	scr->numVscreens = 1;
+#endif
+	return;
+}
+
+
 void InitVirtualScreens(ScreenInfo *scr)
 {
+#ifndef VSCREEN
+	// Just do the faking if vscreens are all off anyway.
+	init_def_vscreen(scr);
+	return;
+#else
+
+	// Real implementation
 	Cursor cursor;
 	unsigned long valuemask, attrmask;
 	XSetWindowAttributes attributes;
 	name_list *nptr;
-	bool userealroot = true;
 	VirtualScreen *vs00 = NULL;
 
 	NewFontCursor(&cursor, "X_cursor");
 
 	if(scr->VirtualScreens == NULL) {
-		if(userealroot) {
-			VirtualScreen *vs = malloc(sizeof(VirtualScreen));
-
-			vs->x      = 0;
-			vs->y      = 0;
-			vs->w      = scr->rootw;
-			vs->h      = scr->rooth;
-			vs->window = scr->Root;
-			vs->next   = NULL;
-			vs->wsw    = 0;
-			scr->vScreenList = vs;
-			scr->currentvs   = vs;
-			scr->numVscreens = 1;
-			return;
-		}
-		else {
-			scr->VirtualScreens = malloc(sizeof(name_list));
-			scr->VirtualScreens->next = NULL;
-			asprintf(&scr->VirtualScreens->name, "%dx%d+0+0",
-			         scr->rootw, scr->rooth);
-		}
+		init_def_vscreen(scr);
+		return;
 	}
 	scr->numVscreens = 0;
 
@@ -121,13 +129,20 @@ void InitVirtualScreens(ScreenInfo *scr)
 	}
 
 	Scr->Root  = vs00->window;
+#ifdef CAPTIVE
 	Scr->rootx = Scr->crootx + vs00->x;
 	Scr->rooty = Scr->crooty + vs00->y;
+#else
+	Scr->rootx = vs00->x;
+	Scr->rooty = vs00->y;
+#endif
 	Scr->rootw = vs00->w;
 	Scr->rooth = vs00->h;
 	Scr->currentvs = vs00;
+#endif  // VSCREEN
 }
 
+#ifdef VSCREEN
 VirtualScreen *findIfVScreenOf(int x, int y)
 {
 	VirtualScreen *vs;
@@ -140,15 +155,7 @@ VirtualScreen *findIfVScreenOf(int x, int y)
 	}
 	return NULL;
 }
-
-VirtualScreen *getVScreenOf(int x, int y)
-{
-	VirtualScreen *vs;
-	if((vs = findIfVScreenOf(x, y))) {
-		return vs;
-	}
-	return Scr->vScreenList;
-}
+#endif
 
 /*
  * Returns the order that virtual screens are displayed for the vscreen
@@ -311,6 +318,9 @@ ReparentFrameAndIcon(TwmWindow *tmp_win)
 	if(vs != tmp_win->parent_vs) {
 		struct Icon *icon = tmp_win->icon;
 
+		// This must always be something...
+		assert(vs != NULL);
+
 		tmp_win->parent_vs = vs;
 
 		if(icon && icon->w) {
diff --git a/vscreen.h b/vscreen.h
index e01bf5c..5582886 100644
--- a/vscreen.h
+++ b/vscreen.h
@@ -13,8 +13,9 @@ struct VirtualScreen {
 };
 
 void InitVirtualScreens(ScreenInfo *scr);
+#ifdef VSCREEN
 VirtualScreen *findIfVScreenOf(int x, int y);
-VirtualScreen *getVScreenOf(int x, int y);
+#endif
 char *CtwmGetVScreenMap(Display *display, Window rootw);
 bool CtwmSetVScreenMap(Display *display, Window rootw,
                        struct VirtualScreen *firstvs);
diff --git a/win_decorations.c b/win_decorations.c
index 0c6a89c..1ee72d3 100644
--- a/win_decorations.c
+++ b/win_decorations.c
@@ -17,6 +17,8 @@
 #include "screen.h"
 #include "drawing.h"
 #include "occupation.h"
+#include "r_area.h"
+#include "r_layout.h"
 #include "win_utils.h"
 #include "workspace_manager.h"
 
@@ -92,34 +94,36 @@ SetupFrame(TwmWindow *tmp_win, int x, int y, int w, int h, int bw,
 	 * Set some bounds on the window location, to be sure part of it is
 	 * visible.
 	 */
-#define MARGIN 16  /* one "average" cursor width */
-
-	/*
-	 * (x,y) is the top left of the window.  Make sure it's not off the
-	 * right or bottom of the screen
-	 */
-	if(x >= Scr->rootw) {
-		x = Scr->rootw - MARGIN;
-	}
-	if(y >= Scr->rooth) {
-		y = Scr->rooth - MARGIN;
-	}
-
-	/*
-	 * Make sure the bottom right isn't off the left or top of the
-	 * screen.
-	 *
-	 * XXX Should this be 2*bw?
-	 */
-	if((x + w + bw <= 0)) {
-		x = -w + MARGIN;
-	}
-	if((y + h + bw <= 0)) {
-		y = -h + MARGIN;
-	}
+	{
+#define MARGIN (16 - 1)  /* one "average" cursor width - 1 */
+		RArea area = RAreaNew(x, y, w, h);
+		int limit;
+
+		/* Make sure the window is not vertically off the screen */
+		limit = RLayoutFindBottomEdge(Scr->Layout, &area);
+		if(y > limit) {
+			y = limit - MARGIN;
+		}
+		else {
+			limit = RLayoutFindTopEdge(Scr->Layout, &area);
+			if(y + h + bw < limit) {
+				y = limit - h + MARGIN;
+			}
+		}
 
+		/* Make sure the window is not horizontally off the screen */
+		limit = RLayoutFindRightEdge(Scr->Layout, &area);
+		if(x > limit) {
+			x = limit - MARGIN;
+		}
+		else {
+			limit = RLayoutFindLeftEdge(Scr->Layout, &area);
+			if(x + w + bw < limit) {
+				x = limit - w + MARGIN;
+			}
+		}
 #undef MARGIN
-
+	}
 
 	/*
 	 * Do some magic if the window being Setup'd is an icon manager.  The
@@ -827,7 +831,15 @@ ComputeWindowTitleOffsets(TwmWindow *tmp_win, unsigned int width, bool squeeze)
 	 * Space available for the window title for calculating name_x.
 	 * (window width) - (space reserved l and r for buttons)
 	 */
-	int titlew = width - Scr->TBInfo.titlex - Scr->TBInfo.rightoff;
+	const int titlew = width - Scr->TBInfo.titlex - Scr->TBInfo.rightoff;
+
+	/*
+	 * If our title is long enough, it'll overflow the available space.
+	 * At that point, any "justification" is pretty moot, so just pretend
+	 * anything long enough is left-justified.
+	 */
+	const TitleJust eff_just = (tmp_win->name_width >= titlew)
+	                           ? TJ_LEFT : Scr->TitleJustification;
 
 	/*
 	 * First figure where the window name goes, depending on
@@ -841,7 +853,7 @@ ComputeWindowTitleOffsets(TwmWindow *tmp_win, unsigned int width, bool squeeze)
 	 * The fixing below at least theoretically fixes that, though other
 	 * parts of the drawing will still cause Bad Side Effects.
 	 */
-	switch(Scr->TitleJustification) {
+	switch(eff_just) {
 		case TJ_UNDEF:
 			/* Can't happen; fallthru to TJ_LEFT */
 			fprintf(stderr, "%s(): Unexpected Scr->TitleJustification %d, "
diff --git a/win_iconify.c b/win_iconify.c
index 409b9e0..1fbf494 100644
--- a/win_iconify.c
+++ b/win_iconify.c
@@ -179,9 +179,12 @@ DeIconify(TwmWindow *tmp_win)
 			Zoom(tmp_win->icon->w, tmp_win->frame);
 		}
 		else if(tmp_win->group != (Window) 0) {
-			t = GetTwmWindow(tmp_win->group);
-			if(t && t->icon_on && t->icon && t->icon->w) {
-				Zoom(t->icon->w, tmp_win->frame);
+			TwmWindow *tmpt = GetTwmWindow(tmp_win->group);
+			if(tmpt) {
+				t = tmpt;
+				if(t->icon_on && t->icon && t->icon->w) {
+					Zoom(t->icon->w, tmp_win->frame);
+				}
 			}
 		}
 	}
@@ -378,6 +381,7 @@ SweepWindow(TwmWindow *tmp_win, Window blanket)
 	if(dist1 < dist) {
 		dir = 3;
 		dist = dist1;
+		ALLOW_DEAD_STORE(dist);
 	}
 
 	switch(dir) {
@@ -495,9 +499,11 @@ ReMapOne(TwmWindow *t, TwmWindow *leader)
 		XMapWindow(dpy, t->w);
 	}
 	t->mapped = true;
+#ifdef CAPTIVE
 	if(false && Scr->Root != Scr->CaptiveRoot) {        /* XXX dubious test */
 		ReparentWindow(dpy, t, WinWin, Scr->Root, t->frame_x, t->frame_y);
 	}
+#endif
 	if(!Scr->NoRaiseDeicon) {
 		OtpRaise(t, WinWin);
 	}
diff --git a/win_ops.c b/win_ops.c
index 9af346d..4bb5842 100644
--- a/win_ops.c
+++ b/win_ops.c
@@ -229,7 +229,7 @@ Squeeze(TwmWindow *tmp_win)
 	             ? tmp_win->hints.win_gravity : NorthWestGravity);
 	long eventMask;
 	if(tmp_win->squeezed) {
-		tmp_win->squeezed = False;
+		tmp_win->squeezed = false;
 #ifdef EWMH
 		EwmhSet_NET_WM_STATE(tmp_win, EWMH_STATE_SHADED);
 #endif /* EWMH */
@@ -271,8 +271,8 @@ Squeeze(TwmWindow *tmp_win)
 		fy += tmp_win->frame_height - newh;
 	}
 	if(tmp_win->squeeze_info) {
-		fx  += tmp_win->title_x - tmp_win->frame_bw3D;
-		neww = tmp_win->title_width + 2 * (tmp_win->frame_bw + tmp_win->frame_bw3D);
+		fx  += tmp_win->title_x + tmp_win->frame_bw - tmp_win->frame_bw3D;
+		neww = tmp_win->title_width + 2 * tmp_win->frame_bw3D;
 	}
 
 	eventMask = mask_out_event(tmp_win->w, StructureNotifyMask);
diff --git a/win_regions.c b/win_regions.c
index f08d713..955bc58 100644
--- a/win_regions.c
+++ b/win_regions.c
@@ -9,6 +9,7 @@
 #include "list.h"
 #include "screen.h"
 #include "win_regions.h"
+#include "xparsegeometry.h"
 
 
 static void splitWindowRegionEntry(WindowEntry *we,
@@ -43,8 +44,9 @@ AddWindowRegion(char *geom, RegGravity grav1, RegGravity grav2)
 	wr->grav2      = grav2;
 	wr->x = wr->y = wr->w = wr->h = 0;
 
-	mask = XParseGeometry(geom, &wr->x, &wr->y, (unsigned int *) &wr->w,
-	                      (unsigned int *) &wr->h);
+	mask = RLayoutXParseGeometry(Scr->Layout, geom, &wr->x, &wr->y,
+	                             (unsigned int *) &wr->w,
+	                             (unsigned int *) &wr->h);
 
 	if(mask & XNegative) {
 		wr->x += Scr->rootw - wr->w;
diff --git a/win_resize.c b/win_resize.c
index 04e3998..39ef9ac 100644
--- a/win_resize.c
+++ b/win_resize.c
@@ -36,6 +36,9 @@
 #include "colormaps.h"
 #include "screen.h"
 #include "drawing.h"
+#include "r_area.h"
+#include "r_area_list.h"
+#include "r_layout.h"
 #include "win_decorations.h"
 #include "win_ops.h"
 #include "win_resize.h"
@@ -194,9 +197,11 @@ void StartResize(XEvent *evp, TwmWindow *tmp_win,
 	                 ButtonMotionMask | PointerMotionHintMask;
 
 	grabwin = Scr->Root;
+#ifdef WINBOX
 	if(tmp_win->winbox) {
 		grabwin = tmp_win->winbox->window;
 	}
+#endif
 	XGrabPointer(dpy, grabwin, True, resizeGrabMask,
 	             GrabModeAsync, GrabModeAsync, grabwin, cursor, CurrentTime);
 
@@ -216,9 +221,9 @@ void StartResize(XEvent *evp, TwmWindow *tmp_win,
 	}
 
 	Scr->SizeStringOffset = SIZE_HINDENT;
-	XResizeWindow(dpy, Scr->SizeWindow,
-	              Scr->SizeStringWidth + SIZE_HINDENT * 2,
-	              Scr->SizeFont.height + SIZE_VINDENT * 2);
+	MoveResizeSizeWindow(evp->xbutton.x_root, evp->xbutton.y_root,
+	                     Scr->SizeStringWidth + SIZE_HINDENT * 2,
+	                     Scr->SizeFont.height + SIZE_VINDENT * 2);
 	XMapRaised(dpy, Scr->SizeWindow);
 	InstallRootColormap();
 	last_width = 0;
@@ -252,9 +257,9 @@ void MenuStartResize(TwmWindow *tmp_win, int x, int y, int w, int h)
 	last_width = 0;
 	last_height = 0;
 	Scr->SizeStringOffset = SIZE_HINDENT;
-	XResizeWindow(dpy, Scr->SizeWindow,
-	              Scr->SizeStringWidth + SIZE_HINDENT * 2,
-	              Scr->SizeFont.height + SIZE_VINDENT * 2);
+	MoveResizeSizeWindow(dragx, dragy,
+	                     Scr->SizeStringWidth + SIZE_HINDENT * 2,
+	                     Scr->SizeFont.height + SIZE_VINDENT * 2);
 	XMapRaised(dpy, Scr->SizeWindow);
 	DisplaySize(tmp_win, origWidth, origHeight);
 	if(! Scr->OpaqueResize)
@@ -723,7 +728,6 @@ void ConstrainSize(TwmWindow *tmp_win,
                    unsigned int *widthp, unsigned int *heightp)
 {
 #define makemult(a,b) ((b==1) ? (a) : (((int)((a)/(b))) * (b)) )
-#define _min(a,b) (((a) < (b)) ? (a) : (b))
 
 	int minWidth, minHeight, maxWidth, maxHeight, xinc, yinc, delta;
 	int baseWidth, baseHeight;
@@ -759,8 +763,8 @@ void ConstrainSize(TwmWindow *tmp_win,
 
 
 	if(tmp_win->hints.flags & PMaxSize) {
-		maxWidth = _min(Scr->MaxWindowWidth, tmp_win->hints.max_width);
-		maxHeight = _min(Scr->MaxWindowHeight, tmp_win->hints.max_height);
+		maxWidth = min(Scr->MaxWindowWidth, tmp_win->hints.max_width);
+		maxHeight = min(Scr->MaxWindowHeight, tmp_win->hints.max_height);
 	}
 	else {
 		maxWidth = Scr->MaxWindowWidth;
@@ -893,14 +897,8 @@ void fullzoom(TwmWindow *tmp_win, int func)
 {
 	Window      junkRoot;
 	unsigned int junkbw, junkDepth;
-	int basex, basey;
-	int border_x, border_y;
-	int frame_bw_times_2;
-	int zwidth  = Scr->rootw;
-	int zheight = Scr->rooth;
 	int tmpX, tmpY, tmpW, tmpH;
 
-
 	/*
 	 * All our callers [need to] do this, so moving it here saves a few
 	 * lines in some places around the calling, and when redundant it
@@ -917,12 +915,6 @@ void fullzoom(TwmWindow *tmp_win, int func)
 	             &junkbw,
 	             &junkDepth);
 
-	basex = Scr->BorderLeft;
-	basey = Scr->BorderTop;
-
-	border_x = Scr->BorderLeft + Scr->BorderRight;
-	border_y = Scr->BorderTop + Scr->BorderBottom;
-
 	/*
 	 * Guard; if it was already not zoomed, and we're asking to unzoom
 	 * it, just finish right away.  This saves us work, but also avoids
@@ -941,17 +933,6 @@ void fullzoom(TwmWindow *tmp_win, int func)
 		return;
 	}
 
-	if(tmp_win->winbox) {
-		XWindowAttributes winattrs;
-		if(XGetWindowAttributes(dpy, tmp_win->winbox->window, &winattrs)) {
-			zwidth   = winattrs.width;
-			zheight  = winattrs.height;
-		}
-		basex    = 0;
-		basey    = 0;
-		border_x = 0;
-		border_y = 0;
-	}
 	if(tmp_win->zoomed == func) {
 		/* It was already zoomed this way, unzoom it */
 		dragHeight = tmp_win->save_frame_height;
@@ -964,6 +945,28 @@ void fullzoom(TwmWindow *tmp_win, int func)
 		/* XXX _should_ it be falling through here? */
 	}
 	else {
+		RLayout *borderedLayout = NULL;
+		RArea area, finalArea = RAreaInvalid();
+		int frame_bw_times_2;
+
+#ifdef WINBOX
+		if(tmp_win->winbox) {
+			XWindowAttributes winattrs;
+			if(XGetWindowAttributes(dpy, tmp_win->winbox->window, &winattrs)) {
+				borderedLayout = RLayoutNew(
+				                         RAreaListNew(1,
+				                                      RAreaNew(winattrs.x,
+				                                                      winattrs.y,
+				                                                      winattrs.width,
+				                                                      winattrs.height),
+				                                      NULL));
+			}
+		}
+#endif
+		if(borderedLayout == NULL) {
+			borderedLayout = Scr->BorderedLayout;
+		}
+
 		if(tmp_win->zoomed == ZOOM_NONE) {
 			tmp_win->save_frame_x = dragx;
 			tmp_win->save_frame_y = dragy;
@@ -974,56 +977,100 @@ void fullzoom(TwmWindow *tmp_win, int func)
 
 		frame_bw_times_2 = 2 * tmp_win->frame_bw;
 
+		area = RAreaNew(dragx, dragy, dragWidth, dragHeight);
+
 		switch(func) {
 			case ZOOM_NONE:
 				break;
+			case F_XZOOM:
+				finalArea = RLayoutFullVert(borderedLayout, &area);
+			/* fall through */
 			case F_ZOOM:
-				dragHeight = zheight - border_y - frame_bw_times_2;
-				dragy = basey;
+				if(!RAreaIsValid(&finalArea)) {
+					finalArea = RLayoutFullVert1(borderedLayout, &area);
+				}
+				dragy = finalArea.y;
+				dragHeight = finalArea.height - frame_bw_times_2;
 				break;
+			case F_XHORIZOOM:
+				finalArea = RLayoutFullHoriz(borderedLayout, &area);
+			/* fall through */
 			case F_HORIZOOM:
-				dragx = basex;
-				dragWidth = zwidth - border_x - frame_bw_times_2;
+				if(!RAreaIsValid(&finalArea)) {
+					finalArea = RLayoutFullHoriz1(borderedLayout, &area);
+				}
+				dragx = finalArea.x;
+				dragWidth = finalArea.width - frame_bw_times_2;
 				break;
+			case F_XFULLZOOM:
+				finalArea = RLayoutFull(borderedLayout, &area);
+			/* fall through */
 			case F_FULLZOOM:
-				dragx = basex;
-				dragy = basey;
-				dragHeight = zheight - border_y - frame_bw_times_2;
-				dragWidth = zwidth - border_x - frame_bw_times_2;
+				if(!RAreaIsValid(&finalArea)) {
+					finalArea = RLayoutFull1(borderedLayout, &area);
+				}
+				dragx = finalArea.x;
+				dragy = finalArea.y;
+				dragWidth = finalArea.width - frame_bw_times_2;
+				dragHeight = finalArea.height - frame_bw_times_2;
+				break;
+			case F_XLEFTZOOM:
+				dragx = RLayoutFindLeftEdge(borderedLayout, &area);
+				dragWidth += area.x - dragx;
+				// TODO make it visible if hidden
 				break;
 			case F_LEFTZOOM:
-				dragx = basex;
-				dragy = basey;
-				dragHeight = zheight - border_y - frame_bw_times_2;
-				dragWidth = (zwidth - border_x) / 2 - frame_bw_times_2;
+				dragx = RLayoutFindMonitorLeftEdge(borderedLayout, &area);
+				dragWidth += area.x - dragx;
+				// TODO make it visible if hidden
 				break;
-			case F_RIGHTZOOM:
-				dragx = basex + (zwidth - border_x) / 2;
-				dragy = basey;
-				dragHeight = zheight - border_y - frame_bw_times_2;
-				dragWidth = (zwidth - border_x) / 2 - frame_bw_times_2;
+			case F_XRIGHTZOOM: {
+				int limit = RLayoutFindRightEdge(borderedLayout, &area);
+				dragWidth = limit - area.x + 1 - frame_bw_times_2;
+				// TODO make it visible if hidden
+			}
+			break;
+			case F_RIGHTZOOM: {
+				int limit = RLayoutFindMonitorRightEdge(borderedLayout, &area);
+				dragWidth = limit - area.x + 1 - frame_bw_times_2;
+				// TODO make it visible if hidden
+			}
+			break;
+			case F_XTOPZOOM:
+				dragy = RLayoutFindTopEdge(borderedLayout, &area);
+				dragHeight += area.y - dragy;
+				// TODO make it visible if hidden
 				break;
 			case F_TOPZOOM:
-				dragx = basex;
-				dragy = basey;
-				dragHeight = (zheight - border_y) / 2 - frame_bw_times_2;
-				dragWidth = zwidth - border_x - frame_bw_times_2;
+				dragy = RLayoutFindMonitorTopEdge(borderedLayout, &area);
+				dragHeight += area.y - dragy;
+				// TODO make it visible if hidden
 				break;
-			case F_BOTTOMZOOM:
-				dragx = basex;
-				dragy = basey + (zheight - border_y) / 2;
-				dragHeight = (zheight - border_y) / 2 - frame_bw_times_2;
-				dragWidth = zwidth - border_x - frame_bw_times_2;
-				break;
-			case F_FULLSCREENZOOM: {
+			case F_XBOTTOMZOOM: {
+				int limit = RLayoutFindBottomEdge(borderedLayout, &area);
+				dragHeight = limit - area.y + 1 - frame_bw_times_2;
+				// TODO make it visible if hidden
+			}
+			break;
+			case F_BOTTOMZOOM: {
+				int limit = RLayoutFindMonitorBottomEdge(borderedLayout, &area);
+				dragHeight = limit - area.y + 1 - frame_bw_times_2;
+				// TODO make it visible if hidden
+			}
+			break;
+			case F_FULLSCREENZOOM:
+			case F_XFULLSCREENZOOM: {
 				int bw3D = tmp_win->frame_bw3D;
 				int bw3D_times_2 = 2 * bw3D;
 				int bw = tmp_win->frame_bw + bw3D;
 
-				dragx = -bw;
-				dragy = -tmp_win->title_height - bw;
-				dragHeight = zheight + tmp_win->title_height + bw3D_times_2;
-				dragWidth = zwidth + bw3D_times_2;
+				finalArea = func == F_XFULLSCREENZOOM
+				            ? RLayoutFull(borderedLayout, &area)
+				            : RLayoutFull1(borderedLayout, &area);
+				dragx = finalArea.x - bw;
+				dragy = finalArea.y - tmp_win->title_height - bw;
+				dragWidth = finalArea.width + bw3D_times_2;
+				dragHeight = finalArea.height + tmp_win->title_height + bw3D_times_2;
 
 				/* and should ignore aspect ratio and size increments... */
 #ifdef EWMH
@@ -1034,6 +1081,11 @@ void fullzoom(TwmWindow *tmp_win, int func)
 #endif
 			}
 		}
+
+		/* Temporary built layout? */
+		if(borderedLayout != Scr->BorderedLayout) {
+			RLayoutFree(borderedLayout);
+		}
 	}
 
 	if(!Scr->NoRaiseResize && func != F_FULLSCREENZOOM) {
diff --git a/win_ring.c b/win_ring.c
new file mode 100644
index 0000000..721033c
--- /dev/null
+++ b/win_ring.c
@@ -0,0 +1,88 @@
+/*
+ * Functions related to the window ring.
+ *
+ * Invariants:
+ * - If a window is not on the ring, its TwmWindow::ring.next and .prev
+ *   are both NULL.
+ * - If a window is on the ring, they are both not NULL and point to a
+ *   window which is also on the ring.
+ * - Corollary: if a window is the only one on the ring, .next and .prev
+ *   point to itself.
+ * - Functions which act on the "current" ring window, i.e. the window
+ *   that has most recently been entered and is on the ring, use
+ *   Scr->RingLeader.
+ * - If RingLeader is NULL, fall back to Scr->Ring.
+ * - If Ring is NULL, the ring is empty (and RingLeader is also NULL).
+ */
+
+#include "ctwm.h"
+
+#include <assert.h>
+
+#include "screen.h"
+#include "win_ring.h"
+
+void
+UnlinkWindowFromRing(TwmWindow *win)
+{
+	TwmWindow *prev = win->ring.prev;
+	TwmWindow *next = win->ring.next;
+
+	// We call this unconditionally at various window deletion times, and
+	// if it's not on the ring, there's nothing to do.  e.g., if we don't
+	// have any WindowRing config enabled...
+	if(!WindowIsOnRing(win)) {
+		return;
+	}
+
+	// But if it is, prev/next should always exist.
+	assert(prev != NULL);
+	assert(next != NULL);
+
+	/*
+	* 1. Unlink window
+	* 2. If window was only thing in ring, null out ring
+	* 3. If window was ring leader, set to next (or null)
+	*
+	* If the window is the only one in the ring, prev == next == win,
+	* so the unlinking effectively is a NOP, but that doesn't matter.
+	*/
+	prev->ring.next = next;
+	next->ring.prev = prev;
+
+	win->ring.next = win->ring.prev = NULL;
+
+	if(Scr->Ring == win) {
+		Scr->Ring = (next != win ? next : NULL);
+	}
+
+	if(!Scr->Ring || Scr->RingLeader == win) {
+		Scr->RingLeader = Scr->Ring;
+	}
+}
+
+static void
+AddWindowToRingUnchecked(TwmWindow *win, TwmWindow *after)
+{
+	TwmWindow *before = after->ring.next;
+
+	win->ring.next = before;
+	win->ring.prev = after;
+
+	after->ring.next = win;
+	before->ring.prev = win;
+}
+
+void
+AddWindowToRing(TwmWindow *win)
+{
+	assert(win->ring.next == NULL);
+	assert(win->ring.prev == NULL);
+
+	if(Scr->Ring) {
+		AddWindowToRingUnchecked(win, Scr->Ring);
+	}
+	else {
+		win->ring.next = win->ring.prev = Scr->Ring = win;
+	}
+}
diff --git a/win_ring.h b/win_ring.h
new file mode 100644
index 0000000..05f3038
--- /dev/null
+++ b/win_ring.h
@@ -0,0 +1,13 @@
+/*
+ * Functions related to the window ring.
+ */
+#ifndef _CTWM_WIN_RING_H
+#define _CTWM_WIN_RING_H
+
+void UnlinkWindowFromRing(TwmWindow *win);
+void AddWindowToRing(TwmWindow *win);
+
+#define WindowIsOnRing(win) ((win) && (win)->ring.next)
+#define InitWindowNotOnRing(win) ((win)->ring.next = (win)->ring.prev = NULL)
+
+#endif
diff --git a/win_utils.c b/win_utils.c
index 04c06ff..a6c262a 100644
--- a/win_utils.c
+++ b/win_utils.c
@@ -21,6 +21,9 @@
 #include "list.h"
 #include "occupation.h"
 #include "otp.h"
+#include "r_area.h"
+#include "r_area_list.h"
+#include "r_layout.h"
 #include "screen.h"
 #include "util.h"
 #include "win_decorations.h"
@@ -488,6 +491,20 @@ DisplayPosition(const TwmWindow *_unused_tmp_win, int x, int y)
 	                   Scr->SizeFont.ascent + SIZE_VINDENT, str, 13);
 }
 
+void
+MoveResizeSizeWindow(int x, int y, unsigned int width, unsigned int height)
+{
+	XResizeWindow(dpy, Scr->SizeWindow, width, height);
+
+	if(Scr->CenterFeedbackWindow) {
+		RArea monitor = RLayoutGetAreaAtXY(Scr->BorderedLayout, x, y);
+
+		XMoveWindow(dpy, Scr->SizeWindow,
+		            monitor.x + monitor.width / 2 - width / 2,
+		            monitor.y + monitor.height / 2 - height / 2);
+	}
+}
+
 
 /*
  * Various funcs for adjusting coordinates for windows based on
@@ -495,24 +512,75 @@ DisplayPosition(const TwmWindow *_unused_tmp_win, int x, int y)
  *
  * XXX In desperate need of better commenting.
  */
+static void
+_tryToPack(RArea *final, const RArea *cur_win)
+{
+	if(final->x >= cur_win->x + cur_win->width) {
+		return;
+	}
+	if(final->y >= cur_win->y + cur_win->height) {
+		return;
+	}
+	if(final->x + final->width <= cur_win->x) {
+		return;
+	}
+	if(final->y + final->height <= cur_win->y) {
+		return;
+	}
+
+	if(final->x + Scr->MovePackResistance > cur_win->x +
+	                cur_win->width) {  /* left */
+		final->x = MAX(final->x, cur_win->x + cur_win->width);
+		return;
+	}
+	if(final->x + final->width < cur_win->x +
+	                Scr->MovePackResistance) {  /* right */
+		final->x = MIN(final->x, cur_win->x - final->width);
+		return;
+	}
+	if(final->y + Scr->MovePackResistance > cur_win->y +
+	                cur_win->height) {  /* top */
+		final->y = MAX(final->y, cur_win->y + cur_win->height);
+		return;
+	}
+	if(final->y + final->height < cur_win->y +
+	                Scr->MovePackResistance) {  /* bottom */
+		final->y = MIN(final->y, cur_win->y - final->height);
+	}
+}
+
+static bool
+_tryToPackVsEachMonitor(const RArea *monitor_area, void *vfinal)
+{
+	_tryToPack((RArea *)vfinal, monitor_area);
+	return false;
+}
+
 void
 TryToPack(TwmWindow *tmp_win, int *x, int *y)
 {
 	TwmWindow   *t;
-	int         newx, newy;
-	int         w, h;
-	int         winw = tmp_win->frame_width  + 2 * tmp_win->frame_bw;
-	int         winh = tmp_win->frame_height + 2 * tmp_win->frame_bw;
+	RArea cur_win;
+	RArea final = RAreaNew(*x, *y,
+	                       tmp_win->frame_width  + 2 * tmp_win->frame_bw,
+	                       tmp_win->frame_height + 2 * tmp_win->frame_bw);
+
+	/* Global layout is not a single rectangle, check against the
+	 * monitor borders */
+	if(Scr->BorderedLayout->horiz->len > 1) {
+		RAreaListForeach(
+		        Scr->BorderedLayout->monitors, _tryToPackVsEachMonitor, &final);
+	}
 
-	newx = *x;
-	newy = *y;
 	for(t = Scr->FirstWindow; t != NULL; t = t->next) {
 		if(t == tmp_win) {
 			continue;
 		}
+#ifdef WINBOX
 		if(t->winbox != tmp_win->winbox) {
 			continue;
 		}
+#endif
 		if(t->vs != tmp_win->vs) {
 			continue;
 		}
@@ -520,40 +588,15 @@ TryToPack(TwmWindow *tmp_win, int *x, int *y)
 			continue;
 		}
 
-		w = t->frame_width  + 2 * t->frame_bw;
-		h = t->frame_height + 2 * t->frame_bw;
-		if(newx >= t->frame_x + w) {
-			continue;
-		}
-		if(newy >= t->frame_y + h) {
-			continue;
-		}
-		if(newx + winw <= t->frame_x) {
-			continue;
-		}
-		if(newy + winh <= t->frame_y) {
-			continue;
-		}
+		cur_win = RAreaNew(t->frame_x, t->frame_y,
+		                   t->frame_width  + 2 * t->frame_bw,
+		                   t->frame_height + 2 * t->frame_bw);
 
-		if(newx + Scr->MovePackResistance > t->frame_x + w) {  /* left */
-			newx = MAX(newx, t->frame_x + w);
-			continue;
-		}
-		if(newx + winw < t->frame_x + Scr->MovePackResistance) {  /* right */
-			newx = MIN(newx, t->frame_x - winw);
-			continue;
-		}
-		if(newy + Scr->MovePackResistance > t->frame_y + h) {  /* top */
-			newy = MAX(newy, t->frame_y + h);
-			continue;
-		}
-		if(newy + winh < t->frame_y + Scr->MovePackResistance) {  /* bottom */
-			newy = MIN(newy, t->frame_y - winh);
-			continue;
-		}
+		_tryToPack(&final, &cur_win);
 	}
-	*x = newx;
-	*y = newy;
+
+	*x = final.x;
+	*y = final.y;
 }
 
 
@@ -590,9 +633,11 @@ TryToPush_be(TwmWindow *tmp_win, int x, int y, PushDirection dir)
 		if(t == tmp_win) {
 			continue;
 		}
+#ifdef WINBOX
 		if(t->winbox != tmp_win->winbox) {
 			continue;
 		}
+#endif
 		if(t->vs != tmp_win->vs) {
 			continue;
 		}
@@ -701,6 +746,7 @@ TryToGrid(TwmWindow *tmp_win, int *x, int *y)
 
 
 
+#ifdef WINBOX
 /*
  * Functions related to keeping windows from being placed off-screen (or
  * off-screen too far).  Involved in handling of params like DontMoveOff
@@ -708,21 +754,82 @@ TryToGrid(TwmWindow *tmp_win, int *x, int *y)
  */
 static void ConstrainLeftTop(int *value, int border);
 static void ConstrainRightBottom(int *value, int size1, int border, int size2);
+#endif
+
+bool
+ConstrainByLayout(RLayout *layout, int move_off_res, int *left, int width,
+                  int *top, int height)
+{
+	RArea area = RAreaNew(*left, *top, width, height);
+	int limit;
+	bool clipped = false;
+
+	limit = RLayoutFindBottomEdge(layout, &area) - height + 1;
+	if(area.y > limit) {
+		if(move_off_res >= 0 && area.y >= limit + move_off_res) {
+			area.y -= move_off_res;
+		}
+		else {
+			area.y = limit;
+			clipped = true;
+		}
+	}
+
+	limit = RLayoutFindRightEdge(layout, &area) - width + 1;
+	if(area.x > limit) {
+		if(move_off_res >= 0 && area.x >= limit + move_off_res) {
+			area.x -= move_off_res;
+		}
+		else {
+			area.x = limit;
+			clipped = true;
+		}
+	}
+
+	limit = RLayoutFindLeftEdge(layout, &area);
+	if(area.x < limit) {
+		if(move_off_res >= 0 && area.x <= limit - move_off_res) {
+			area.x += move_off_res;
+		}
+		else {
+			area.x = limit;
+			clipped = true;
+		}
+	}
+
+	limit = RLayoutFindTopEdge(layout, &area);
+	if(area.y < limit) {
+		if(move_off_res >= 0 && area.y <= limit - move_off_res) {
+			area.y += move_off_res;
+		}
+		else {
+			area.y = limit;
+			clipped = true;
+		}
+	}
+
+	*left = area.x;
+	*top = area.y;
+
+	return clipped;
+}
 
 void
 ConstrainByBorders1(int *left, int width, int *top, int height)
 {
-	ConstrainRightBottom(left, width, Scr->BorderRight, Scr->rootw);
-	ConstrainLeftTop(left, Scr->BorderLeft);
-	ConstrainRightBottom(top, height, Scr->BorderBottom, Scr->rooth);
-	ConstrainLeftTop(top, Scr->BorderTop);
+	ConstrainByLayout(Scr->BorderedLayout, Scr->MoveOffResistance,
+	                  left, width, top, height);
 }
 
 void
 ConstrainByBorders(TwmWindow *twmwin, int *left, int width,
                    int *top, int height)
 {
-	if(twmwin->winbox) {
+	if(false) {
+		// Dummy
+	}
+#ifdef WINBOX
+	else if(twmwin->winbox) {
 		XWindowAttributes attr;
 		XGetWindowAttributes(dpy, twmwin->winbox->window, &attr);
 		ConstrainRightBottom(left, width, 0, attr.width);
@@ -730,11 +837,13 @@ ConstrainByBorders(TwmWindow *twmwin, int *left, int width,
 		ConstrainRightBottom(top, height, 0, attr.height);
 		ConstrainLeftTop(top, 0);
 	}
+#endif
 	else {
 		ConstrainByBorders1(left, width, top, height);
 	}
 }
 
+#ifdef WINBOX
 static void
 ConstrainLeftTop(int *value, int border)
 {
@@ -764,6 +873,7 @@ ConstrainRightBottom(int *value, int size1, int border, int size2)
 		}
 	}
 }
+#endif
 
 
 /*
diff --git a/win_utils.h b/win_utils.h
index f29485a..cbb60f3 100644
--- a/win_utils.h
+++ b/win_utils.h
@@ -18,9 +18,13 @@ int restore_mask(Window w, long restore);
 void SetMapStateProp(TwmWindow *tmp_win, int state);
 bool GetWMState(Window w, int *statep, Window *iwp);
 void DisplayPosition(const TwmWindow *_unused_tmp_win, int x, int y);
+void MoveResizeSizeWindow(int x, int y, unsigned int width,
+                          unsigned int height);
 void TryToPack(TwmWindow *tmp_win, int *x, int *y);
 void TryToPush(TwmWindow *tmp_win, int x, int y);
 void TryToGrid(TwmWindow *tmp_win, int *x, int *y);
+bool ConstrainByLayout(RLayout *layout, int move_off_res, int *left, int width,
+                       int *top, int height);
 void ConstrainByBorders1(int *left, int width, int *top, int height);
 void ConstrainByBorders(TwmWindow *twmwin, int *left, int width,
                         int *top, int height);
diff --git a/windowbox.c b/windowbox.c
index debe79b..cb58533 100644
--- a/windowbox.c
+++ b/windowbox.c
@@ -17,6 +17,7 @@
 #include "win_decorations.h"
 #include "win_resize.h"
 #include "win_utils.h"
+#include "xparsegeometry.h"
 
 name_list **addWindowBox(char *boxname, char *geometry)
 {
@@ -48,7 +49,8 @@ void createWindowBoxes(void)
 		unsigned int w, h;
 		Window win;
 
-		mask = XParseGeometry(winbox->geometry, &x, &y, &w, &h);
+		mask = RLayoutXParseGeometry(Scr->Layout, winbox->geometry,
+		                             &x, &y, &w, &h);
 		if(mask & XNegative) {
 			x += Scr->rootw  - w;
 			gravity = (mask & YNegative) ? SouthEastGravity : NorthEastGravity;
diff --git a/workspace_manager.c b/workspace_manager.c
index 9ae3507..f3a984a 100644
--- a/workspace_manager.c
+++ b/workspace_manager.c
@@ -29,6 +29,8 @@
 #include "win_utils.h"
 #include "workspace_manager.h"
 #include "workspace_utils.h"
+#include "xparsegeometry.h"
+
 
 #include "gram.tab.h"
 
@@ -75,17 +77,41 @@ InitWorkSpaceManagerContext(void)
 }
 
 
-/*
+/**
  * Prep up structures for WSM windows in each VS.  Called (for each
- * screen) in startup after InitVirtualScreens() has setup the VS stuff
- * (and after config file processing).
+ * Screen) in startup after InitVirtualScreens() has setup the VS stuff
+ * (and after config file processing).  This also retrieves info for each
+ * vs about which workspace is active, if available (from restarting
+ * ourself, etc).
+ *
+ * XXX Passed param isn't quite complete, as we call some funcs that use
+ * global Scr...
  */
 void
-ConfigureWorkSpaceManager(void)
+ConfigureWorkSpaceManager(ScreenInfo *scr)
 {
-	VirtualScreen *vs;
+	WorkSpace *ws = Scr->workSpaceMgr.workSpaceList;
+	char *vsmapbuf, *vsmap;
+
+	// Get the workspace name that's up on this vscreen.  This is
+	// where the startup process actually sets what workspace we're
+	// [re]starting in.
+	vsmapbuf = CtwmGetVScreenMap(dpy, Scr->Root);
+	if(vsmapbuf != NULL) {
+		// Property is a comma-separate list of the workspaces for
+		// each vscreen, in magic order.  So we start by chopping off
+		// the first and then re-chop in the loop below.
+		vsmap = strtok(vsmapbuf, ",");
+	}
+	else {
+		vsmap = NULL;
+	}
+
+
+	// Setup each vs
+	for(VirtualScreen *vs = scr->vScreenList; vs != NULL; vs = vs->next) {
+		WorkSpace *fws;
 
-	for(vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
 		/*
 		 * Make sure this is all properly initialized to nothing.  Otherwise
 		 * bad and undefined behavior can show up in certain cases (e.g.,
@@ -94,12 +120,32 @@ ConfigureWorkSpaceManager(void)
 		 * e.g.  f.menu "TwmWindows".)
 		 */
 		WorkSpaceWindow *wsw = calloc(1, sizeof(WorkSpaceWindow));
-		wsw->state = Scr->workSpaceMgr.initialstate;
+		wsw->state = scr->workSpaceMgr.initialstate;
+
+		// If we have a current ws for this vs, assign it in, and
+		// loop onward to the ws for the next vs.  For any we don't
+		// have a default for, the default ws is the first one we haven't
+		// used yet.
+		if((fws = GetWorkspace(vsmap)) != NULL) {
+			wsw->currentwspc = fws;
+			vsmap = strtok(NULL, ",");
+		}
+		else {
+			wsw->currentwspc = ws;
+			if(ws) {
+				ws = ws->next;
+			}
+		}
+
 		vs->wsw = wsw;
 	}
+
+	free(vsmapbuf);
 }
 
 
+
+
 /*
  * Create workspace manager windows for each vscreen.  Called (for each
  * screen) late in startup, after the preceeding funcs have run their
@@ -130,45 +176,8 @@ CreateWorkSpaceManager(void)
 	 * workspace (we just reuse the same one), but we do need one for
 	 * each vscreen (since they have to be displayed simultaneously).
 	 */
-	{
-		WorkSpace *ws, *fws;
-		char *vsmapbuf, *vsmap;
-
-		vsmapbuf = CtwmGetVScreenMap(dpy, Scr->Root);
-		if(vsmapbuf != NULL) {
-			vsmap = strtok(vsmapbuf, ",");
-		}
-		else {
-			vsmap = NULL;
-		}
-
-		/*
-		 * weird things can happen if the config file is changed or the
-		 * atom returned above is messed with.  Sometimes windows may
-		 * disappear in that case depending on what's changed.
-		 * (depending on where they were on the actual screen.
-		 */
-		ws = Scr->workSpaceMgr.workSpaceList;
-		for(VirtualScreen *vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
-			WorkSpaceWindow *wsw = vs->wsw;
-			if(vsmap) {
-				fws = GetWorkspace(vsmap);
-			}
-			else {
-				fws = NULL;
-			}
-			if(fws) {
-				wsw->currentwspc = fws;
-				vsmap = strtok(NULL, ",");
-			}
-			else {
-				wsw->currentwspc = ws;
-				ws = ws->next;
-			}
-			CreateWorkSpaceManagerWindow(vs);
-		}
-
-		free(vsmapbuf);
+	for(VirtualScreen *vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
+		CreateWorkSpaceManagerWindow(vs);
 	}
 
 
@@ -313,7 +322,7 @@ CreateWorkSpaceManagerWindow(VirtualScreen *vs)
 			bheight = 22;
 
 			/* Adjust to WSMGeometry if specified */
-			mask = XParseGeometry(geometry, &x, &y, &width, &height);
+			mask = RLayoutXParseGeometry(Scr->Layout, geometry, &x, &y, &width, &height);
 			if(mask & WidthValue) {
 				bwidth = (width - (columns * hspace)) / columns;
 			}
diff --git a/workspace_manager.h b/workspace_manager.h
index b5d6a21..7ca7716 100644
--- a/workspace_manager.h
+++ b/workspace_manager.h
@@ -7,7 +7,7 @@
 
 /* General creation/WSM drawing */
 void InitWorkSpaceManagerContext(void);
-void ConfigureWorkSpaceManager(void);
+void ConfigureWorkSpaceManager(ScreenInfo *scr);
 void CreateWorkSpaceManager(void);
 void PaintWorkSpaceManager(VirtualScreen *vs);
 void WMgrHandleExposeEvent(VirtualScreen *vs, XEvent *event);
diff --git a/workspace_utils.c b/workspace_utils.c
index 41c70fe..f6df125 100644
--- a/workspace_utils.c
+++ b/workspace_utils.c
@@ -119,9 +119,9 @@ GotoWorkSpace(VirtualScreen *vs, WorkSpace *ws)
 	                twmWin = OtpNextWinUp(twmWin)) {
 		if(twmWin->vs == vs) {
 			if(!OCCUPY(twmWin, newws)) {
-				VirtualScreen *tvs;
 
 				Vanish(vs, twmWin);
+#ifdef VSCREEN
 				/*
 				 * Now that the window has Vanished from one virtual screen,
 				 * check to see if it is wanted on another one.
@@ -129,6 +129,7 @@ GotoWorkSpace(VirtualScreen *vs, WorkSpace *ws)
 				 * top-to-bottom order here.
 				 */
 				if(Scr->numVscreens > 1) {
+					VirtualScreen *tvs;
 					for(tvs = Scr->vScreenList; tvs != NULL; tvs = tvs->next) {
 						if(tvs == vs) { /* no, not back on the old one */
 							continue;
@@ -139,6 +140,7 @@ GotoWorkSpace(VirtualScreen *vs, WorkSpace *ws)
 						}
 					}
 				}
+#endif
 			}
 			else if(twmWin->hasfocusvisible) {
 				focuswindow = twmWin;
diff --git a/xparsegeometry.c b/xparsegeometry.c
new file mode 100644
index 0000000..7257dd9
--- /dev/null
+++ b/xparsegeometry.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright notice...
+ */
+
+#include "ctwm.h"
+
+#include <string.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#include "r_layout.h"
+#include "r_area.h"
+#include "xparsegeometry.h"
+
+
+/**
+ * Parse an X Geometry out to get the positions and sizes.
+ *
+ * This generally wraps and replaces our uses of XParseGeometry in order
+ * to allow positioning relative to a XRANDR output name.  This allows
+ * specifying a geometry relative to a particular monitor, rather than on
+ * the whole composite multi-screen output meta-display.
+ */
+int
+RLayoutXParseGeometry(RLayout *layout, const char *geometry, int *x, int *y,
+                      unsigned int *width, unsigned int *height)
+{
+	char *sep;
+
+	// Got something that looks like a display?
+	sep = strchr(geometry, ':');
+	if(sep != NULL) {
+		RArea mon = RLayoutGetAreaByName(layout, geometry, sep - geometry);
+		if(RAreaIsValid(&mon)) {
+			// Yep, one of our monitors; figure the placement on our
+			// whole root where that part of this monitor lies.
+			int mask = XParseGeometry(sep + 1, x, y, width, height);
+			RArea big = RLayoutBigArea(layout);
+
+			if(mask & XValue) {
+				if(mask & XNegative) {
+					*x -= big.width - mon.width - (mon.x - big.x);
+				}
+				else {
+					*x += mon.x - big.x;
+				}
+			}
+
+			if(mask & YValue) {
+				if(mask & YNegative) {
+					*y -= big.height - mon.height - (mon.y - big.y);
+				}
+				else {
+					*y += mon.y - big.y;
+				}
+			}
+
+			return mask;
+		}
+
+		// Name not found, keep the geometry part as-is
+		geometry = sep + 1;
+	}
+
+	return XParseGeometry(geometry, x, y, width, height);
+}
diff --git a/xparsegeometry.h b/xparsegeometry.h
new file mode 100644
index 0000000..8a4fc2f
--- /dev/null
+++ b/xparsegeometry.h
@@ -0,0 +1,13 @@
+/*
+ * Copyright notice...
+ */
+
+#ifndef _CTWM_XPARSEGEOMETRY_H
+#define _CTWM_XPARSEGEOMETRY_H
+
+#include "r_structs.h"
+
+int RLayoutXParseGeometry(RLayout *layout, const char *geometry, int *x, int *y,
+                          unsigned int *width, unsigned int *height);
+
+#endif  /* _CTWM_XPARSEGEOMETRY_H */
diff --git a/xrandr.c b/xrandr.c
new file mode 100644
index 0000000..158d027
--- /dev/null
+++ b/xrandr.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright notice...
+ */
+
+#include "ctwm.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <X11/extensions/Xrandr.h>
+
+#include "r_area_list.h"
+#include "r_area.h"
+#include "r_layout.h"
+#include "xrandr.h"
+
+
+/**
+ * Use XRANDR to figure out how our monitors are laid out.
+ */
+RLayout *
+XrandrNewLayout(Display *disp, Window rootw)
+{
+	int i_nmonitors = 0;
+	XRRMonitorInfo *ps_monitors;
+	char **monitor_names;
+	RAreaList *areas;
+	int evt_base, err_base, ver_maj, ver_min;
+	// XXX *_base and ver_* should move more globally if we start doing
+	// randr stuff anywhere else.
+
+	// If the server doesn't talk RANDR, we have nothing to do.
+	if(XRRQueryExtension(disp, &evt_base, &err_base) != True) {
+		// No RANDR
+#ifdef DEBUG
+		fprintf(stderr, "No RANDR on the server.\n");
+#endif
+		return NULL;
+	}
+
+	// XRRGetMonitors() wraps the RRGetMonitors request, which requires
+	// 1.5.
+	if(XRRQueryVersion(disp, &ver_maj, &ver_min) == 0) {
+		// Couldn't get the version
+#ifdef DEBUG
+		fprintf(stderr, "Couldn't get server RANDR version.\n");
+#endif
+		return NULL;
+	}
+	if(ver_maj < 1 || (ver_maj == 1 && ver_min < 5)) {
+#ifdef DEBUG
+		fprintf(stderr, "Server has RANDR %d.%d, we need 1.5+.\n",
+		        ver_maj, ver_min);
+#endif
+		return NULL;
+	}
+
+	// RANDR 1.5 function to get info about 'em.
+	ps_monitors = XRRGetMonitors(disp, rootw, 1, &i_nmonitors);
+	if(ps_monitors == NULL || i_nmonitors == 0) {
+		fprintf(stderr, "XRRGetMonitors failed\n");
+		return NULL;
+	}
+
+	// Useful note: vtwm also apparently has RANDR support.  It uses
+	// XRRGetScreenResources() and looping XRRGetCrtcInfo() to load info
+	// about the screen, and its conditionals suggest that's RANDR 1.2.
+	// Look into that if we decide to worry about earlier versions.
+
+	// Add space for all their names (plus trailing NULL)
+	monitor_names = calloc((i_nmonitors + 1), sizeof(char *));
+	if(monitor_names == NULL) {
+		abort();
+	}
+
+	// Add each and its name into an RAreaList
+	areas = RAreaListNew(i_nmonitors, NULL);
+	for(int i = 0; i < i_nmonitors; i++) {
+		RArea cur_area = RAreaNew(ps_monitors[i].x,
+		                          ps_monitors[i].y,
+		                          ps_monitors[i].width,
+		                          ps_monitors[i].height);
+
+		char *name = XGetAtomName(disp, ps_monitors[i].name);
+#ifdef DEBUG
+		fprintf(stderr, "NEW area: %s%s",
+		        name != NULL ? name : "",
+		        name != NULL ? ":" : "");
+		RAreaPrint(&cur_area);
+		fprintf(stderr, "\n");
+#endif
+		if(name != NULL) {
+			monitor_names[i] = strdup(name);
+			XFree(name);
+		}
+		else {
+			monitor_names[i] = strdup("");
+		}
+
+		RAreaListAdd(areas, &cur_area);
+	}
+
+	XRRFreeMonitors(ps_monitors);
+
+	// Build up an RLayout of those areas and their names, and hand it
+	// back.
+	return RLayoutSetMonitorsNames(RLayoutNew(areas), monitor_names);
+}
diff --git a/xrandr.h b/xrandr.h
new file mode 100644
index 0000000..4111fc7
--- /dev/null
+++ b/xrandr.h
@@ -0,0 +1,10 @@
+/*
+ * Copyright notice...
+ */
+
+#ifndef _CTWM_XRANDR_H
+#define _CTWM_XRANDR_H
+
+RLayout *XrandrNewLayout(Display *disp, Window rootw);
+
+#endif /* _CTWM_XRANDR_H */

More details

Full run details

Historical runs