Regression from d213c98 (the pod PadRIO/panel coupling fix).
BTGlassPanels_Destroy calls SaveLayout FIRST, unconditionally, before it
looks at whether any windows are left. d213c98 added a SECOND caller
(~LBE4ControlsManager) alongside the existing one in ~PadRIO, so on the
desktop path both run: ~LBE4ControlsManager does `delete rioPointer`, which
fires ~PadRIO -> destroy #1 saves the live windows and zeroes gWinCount ->
destroy #2 then rewrites the whole file from an empty list.
Why only the MFDs vanished, which is the detail that identifies it: external
windows (plasma) already cached a last-known rect (gExtern[].haveLast) and
were written from the cache; the per-display glass windows had no cache and
were simply skipped once their HWND was gone. Hence the reported signature,
"all the MFDs and secondary lines missing, but plasma was still there".
The pod was never affected -- no PadRIO there, so only one destroy, and it
runs BT_GLASS_LAYOUT=load off a frozen master regardless.
Two fixes, because the guard alone would leave the trap armed for the next
teardown-ordering change:
1. glass windows get the same remembered-geometry cache the extern windows
have, kept ACROSS teardown. The file is now monotonic -- a save can
update a line or add one, never drop one.
2. the teardown save is guarded on there being windows to report.
Also corrects the comment at the L4CTRL call site, which claimed the second
call was "a no-op on the PadRIO path". That claim is what made it look safe.
Verified (scratchpad/night13/layoutsave.sh -- the tester's round trip, not a
single launch, since the report was "saved fine, reset on relaunch"):
run 1 one "saved 8 window position(s)" line (was two, the second wiping)
cfg holds all 7 glass windows + plasma
run 2 "restored 7 window position(s)", cfg byte-identical after the trip
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NCJQkvq6G2JNrpVbA75tVZ