Plasma window joins the glass_layout.cfg no-frame list
The desktop plasma window (L4PLASMAWIN, "BattleTech - Plasma") is created by a
different TU than the per-display panels, so it couldn't be a gWins[] entry and
sat outside the sticky-layout / ,noframe system. Add a tiny extern-window
registry to L4GLASSWIN so it rides the same glass_layout.cfg:
- BTGlassLayout_QueryWindow(title, rect, noframe) -- read a title's saved rect
and ,noframe flag (used by the plasma on creation, BEFORE sizing, since
WS_POPUP's frame extent differs from the framed tool window).
- BTGlassLayout_RegisterExtern(hwnd, title) -- register an externally-created
window so SaveLayout writes its line (with a last-known-rect cache so a
teardown before the save still preserves the line).
- BTGlassLayout_Save() -- public save trigger the plasma WndProc calls on
WM_EXITSIZEMOVE / teardown.
L4PLASMAWIN now reads its saved rect+flag on create (WS_POPUP when ,noframe,
position restored), registers itself, and saves on finished-drag/teardown. All
BT_GLASS-gated; the pod is untouched. The plasma blits directly every frame, so
it has no WM_TIMER focus-throttle.
Verified: Release links clean; drag wrote "BattleTech - Plasma=321,222,528,167",
reload with ,noframe brought it up WS_POPUP (WS_CAPTION absent) at 321,222.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -169,6 +169,19 @@ Verified live: two flagged windows came up caption-less while the other five kep
|
||||
still dispatched on the frameless radar (`CLICK 'Secondary / Radar' addr=0x18`), and a
|
||||
finished-drag save round-tripped both flags back into the file.
|
||||
|
||||
**The plasma window is in the list too (2026-08-04) [T2 round-trip-verified].** The desktop plasma
|
||||
display (`L4PLASMAWIN`, title `BattleTech - Plasma`) is created by a DIFFERENT TU than the
|
||||
per-display panels, so it can't be a `gWins[]` entry. Instead it registers with a small
|
||||
extern-window registry in `L4GLASSWIN` (`BTGlassLayout_RegisterExtern`), and on creation it reads
|
||||
its saved rect + `,noframe` via `BTGlassLayout_QueryWindow` (BEFORE sizing — `WS_POPUP`'s frame
|
||||
extent differs). `SaveLayout` then writes the plasma's line alongside the panels (with a
|
||||
last-known-rect cache so a teardown before the save still preserves its line), and the plasma
|
||||
WndProc calls `BTGlassLayout_Save` on `WM_EXITSIZEMOVE`/teardown. So `BattleTech - Plasma=x,y,w,h`
|
||||
appears in the cfg like any panel and honours `,noframe`. Verified: a drag wrote
|
||||
`BattleTech - Plasma=321,222,…`; a reload with `,noframe` brought it up `WS_POPUP` (no `WS_CAPTION`)
|
||||
at 321,222. NB the plasma window blits directly every frame (`GetDC`+`StretchDIBits`), so unlike
|
||||
the panels it has no `WM_TIMER` focus-throttle to worry about.
|
||||
|
||||
**Verified 2026-07-26 [T2]:** `BT_RIOBANK_LOG=1` dumps every bank's rects;
|
||||
`scratchpad/checkbank.py` reports the per-bank census and proves no address is SHADOWED (has a
|
||||
point no earlier button covers); `scratchpad/clickbank.py` then posts a real click at every
|
||||
|
||||
@@ -451,6 +451,91 @@ static GWin *
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//###########################################################################
|
||||
// External windows that ride glass_layout.cfg (2026-08-04). A window created
|
||||
// by ANOTHER TU -- the plasma window (L4PLASMAWIN) -- can register here so it
|
||||
// shares the SAME position + ",noframe" persistence as the per-display windows:
|
||||
// it queries its saved rect/flag on creation, and Save() writes its line too.
|
||||
// Tiny (HWND + title + last-known rect); no dynamic state.
|
||||
//###########################################################################
|
||||
|
||||
struct ExternWin
|
||||
{
|
||||
HWND hwnd;
|
||||
char title[64];
|
||||
int noFrame;
|
||||
RECT last;
|
||||
int haveLast;
|
||||
};
|
||||
static ExternWin gExtern[4];
|
||||
static int gExternCount = 0;
|
||||
|
||||
// Read the saved rect + ",noframe" flag for a title (glass window OR extern).
|
||||
// Returns 1 if the title has a line in the cfg. rect / noframe may be NULL.
|
||||
int
|
||||
BTGlassLayout_QueryWindow(const char *title, RECT *rect, int *noframe)
|
||||
{
|
||||
if (title == NULL || GlassLayoutMode() == LayoutOff)
|
||||
return 0;
|
||||
FILE *f = fopen(layoutFileName, "rt");
|
||||
if (f == NULL)
|
||||
return 0;
|
||||
|
||||
int found = 0;
|
||||
char line[256];
|
||||
while (fgets(line, sizeof(line), f) != NULL)
|
||||
{
|
||||
char *s = line;
|
||||
while (*s == ' ' || *s == '\t') ++s;
|
||||
if (*s == '#' || *s == '\r' || *s == '\n' || *s == '\0')
|
||||
continue;
|
||||
char *eq = strchr(s, '=');
|
||||
if (eq == NULL)
|
||||
continue;
|
||||
*eq = '\0';
|
||||
char *end = eq;
|
||||
while (end > s && (end[-1] == ' ' || end[-1] == '\t')) --end;
|
||||
*end = '\0';
|
||||
if (strcmp(s, title) != 0)
|
||||
continue;
|
||||
|
||||
int x = 0, y = 0, w = 0, h = 0;
|
||||
if (sscanf(eq + 1, "%d,%d,%d,%d", &x, &y, &w, &h) < 2)
|
||||
continue;
|
||||
int nf = 0;
|
||||
for (const char *opt = strchr(eq + 1, ','); opt != NULL; opt = strchr(opt + 1, ','))
|
||||
{
|
||||
const char *t = opt + 1;
|
||||
while (*t == ' ' || *t == '\t') ++t;
|
||||
if (_strnicmp(t, "noframe", 7) == 0) { nf = 1; break; }
|
||||
}
|
||||
if (rect) { rect->left = x; rect->top = y; rect->right = x + w; rect->bottom = y + h; }
|
||||
if (noframe) *noframe = nf;
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
fclose(f);
|
||||
return found;
|
||||
}
|
||||
|
||||
// Register an externally-created window so Save() writes its line. Loads its
|
||||
// current ",noframe" flag from the cfg so a save round-trips the flag.
|
||||
void
|
||||
BTGlassLayout_RegisterExtern(HWND hwnd, const char *title)
|
||||
{
|
||||
if (hwnd == NULL || title == NULL)
|
||||
return;
|
||||
if (gExternCount >= (int)(sizeof(gExtern) / sizeof(gExtern[0])))
|
||||
return;
|
||||
ExternWin &e = gExtern[gExternCount++];
|
||||
e.hwnd = hwnd;
|
||||
strncpy(e.title, title, sizeof(e.title) - 1);
|
||||
e.title[sizeof(e.title) - 1] = '\0';
|
||||
e.noFrame = 0;
|
||||
e.haveLast = 0;
|
||||
BTGlassLayout_QueryWindow(title, NULL, &e.noFrame);
|
||||
}
|
||||
|
||||
// Restore saved positions over the just-computed pod-faithful defaults. Called
|
||||
// from Create AFTER ComputeLayout, so any window not named in the file keeps its
|
||||
// computed spot. Restored windows are flagged so the WM_TIMER re-snap (which
|
||||
@@ -547,7 +632,8 @@ static void
|
||||
"# Heat MFD=1920,0,657,539,noframe\n"
|
||||
"# A frameless window is also PINNED (no caption = nothing to drag it\n"
|
||||
"# by), so do the arranging first. Delete the flag to get the frame\n"
|
||||
"# back. Saves preserve it.\n",
|
||||
"# back. Saves preserve it. The 'BattleTech - Plasma' window is in\n"
|
||||
"# this list too -- it can be dragged, remembered and set ,noframe.\n",
|
||||
f);
|
||||
int wrote = 0;
|
||||
for (int i = 0; i < gWinCount; ++i)
|
||||
@@ -564,11 +650,41 @@ static void
|
||||
gw.noFrame ? ",noframe" : ""); // keep the hand-added option
|
||||
++wrote;
|
||||
}
|
||||
// External windows (the plasma window) ride the same file. Cache the last
|
||||
// good rect so a window torn down before this save still keeps its line.
|
||||
for (int i = 0; i < gExternCount; ++i)
|
||||
{
|
||||
ExternWin &e = gExtern[i];
|
||||
RECT r;
|
||||
if (e.hwnd != NULL && IsWindow(e.hwnd) && GetWindowRect(e.hwnd, &r))
|
||||
{
|
||||
e.last = r; e.haveLast = 1;
|
||||
}
|
||||
else if (e.haveLast)
|
||||
{
|
||||
r = e.last; // window gone -> preserve its last-known line
|
||||
}
|
||||
else
|
||||
continue;
|
||||
fprintf(f, "%s=%ld,%ld,%ld,%ld%s\n", e.title,
|
||||
(long)r.left, (long)r.top,
|
||||
(long)(r.right - r.left), (long)(r.bottom - r.top),
|
||||
e.noFrame ? ",noframe" : "");
|
||||
++wrote;
|
||||
}
|
||||
fclose(f);
|
||||
DEBUG_STREAM << "[glasswin] saved " << wrote << " window position(s) to "
|
||||
<< layoutFileName << "\n" << std::flush;
|
||||
}
|
||||
|
||||
// Public save trigger for registered external windows -- their WndProc calls
|
||||
// this on WM_EXITSIZEMOVE / teardown. No-op unless BT_GLASS_LAYOUT=save.
|
||||
void
|
||||
BTGlassLayout_Save()
|
||||
{
|
||||
SaveLayout();
|
||||
}
|
||||
|
||||
//###########################################################################
|
||||
// Painting
|
||||
//###########################################################################
|
||||
|
||||
@@ -18,6 +18,12 @@ static LRESULT CALLBACK
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case WM_EXITSIZEMOVE:
|
||||
// Finished dragging (framed mode): persist the new position so it sticks
|
||||
// -- same contract as the per-display panels. No-op unless save mode.
|
||||
{ extern void BTGlassLayout_Save(); BTGlassLayout_Save(); }
|
||||
return 0;
|
||||
|
||||
case WM_CLOSE:
|
||||
ShowWindow(window, SW_HIDE); // hide only; the renderer owns it
|
||||
return 0;
|
||||
@@ -52,6 +58,7 @@ PlasmaWindow::~PlasmaWindow()
|
||||
{
|
||||
if (window != NULL)
|
||||
{
|
||||
{ extern void BTGlassLayout_Save(); BTGlassLayout_Save(); } // backstop before the HWND goes
|
||||
DestroyWindow((HWND)window);
|
||||
window = NULL;
|
||||
}
|
||||
@@ -75,20 +82,43 @@ void
|
||||
window_class.lpszClassName = L"BTPlasmaWnd";
|
||||
RegisterClassW(&window_class);
|
||||
|
||||
RECT frame = { 0, 0, plasmaWidth * scale, plasmaHeight * scale };
|
||||
DWORD style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
|
||||
AdjustWindowRect(&frame, style, FALSE);
|
||||
// glass_layout.cfg integration (2026-08-04): the plasma window rides the same
|
||||
// file as the per-display panels, so it can be dragged-and-remembered and set
|
||||
// frameless with ",noframe" -- BT_GLASS_LAYOUT selects load/save. Read its
|
||||
// saved rect + flag BEFORE sizing (WS_POPUP has a different frame extent than
|
||||
// the framed tool window).
|
||||
extern int BTGlassLayout_QueryWindow(const char *title, RECT *rect, int *noframe);
|
||||
extern void BTGlassLayout_RegisterExtern(HWND hwnd, const char *title);
|
||||
RECT saved;
|
||||
int noframe = 0;
|
||||
int have_saved = BTGlassLayout_QueryWindow("BattleTech - Plasma", &saved, &noframe);
|
||||
|
||||
//
|
||||
// Bottom-right, TOPMOST -- under the (topmost, right-parked) button
|
||||
// panel, clear of the game window (which would otherwise bury it).
|
||||
//
|
||||
DWORD style = noframe
|
||||
? (DWORD)WS_POPUP // bare + pinned, same as a ",noframe" glass panel
|
||||
: (DWORD)(WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX);
|
||||
|
||||
RECT frame = { 0, 0, plasmaWidth * scale, plasmaHeight * scale };
|
||||
AdjustWindowRect(&frame, style, FALSE);
|
||||
int frame_width = frame.right - frame.left;
|
||||
int frame_height = frame.bottom - frame.top;
|
||||
int plasma_x = GetSystemMetrics(SM_CXSCREEN) - frame_width - 12;
|
||||
int plasma_y = GetSystemMetrics(SM_CYSCREEN) - frame_height - 60;
|
||||
if (plasma_x < 0) plasma_x = 0;
|
||||
if (plasma_y < 0) plasma_y = 0;
|
||||
|
||||
int plasma_x, plasma_y;
|
||||
if (have_saved)
|
||||
{
|
||||
plasma_x = saved.left; // position restored; size stays native
|
||||
plasma_y = saved.top;
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// Default: bottom-right, TOPMOST -- under the (topmost, right-parked)
|
||||
// button panel, clear of the game window (which would otherwise bury it).
|
||||
//
|
||||
plasma_x = GetSystemMetrics(SM_CXSCREEN) - frame_width - 12;
|
||||
plasma_y = GetSystemMetrics(SM_CYSCREEN) - frame_height - 60;
|
||||
if (plasma_x < 0) plasma_x = 0;
|
||||
if (plasma_y < 0) plasma_y = 0;
|
||||
}
|
||||
|
||||
window = CreateWindowExW(
|
||||
WS_EX_TOPMOST,
|
||||
@@ -102,6 +132,7 @@ void
|
||||
DEBUG_STREAM << "[plasmawin] CreateWindow failed\n" << std::flush;
|
||||
return;
|
||||
}
|
||||
BTGlassLayout_RegisterExtern((HWND)window, "BattleTech - Plasma");
|
||||
ShowWindow((HWND)window, SW_SHOWNOACTIVATE);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user