Merge remote-tracking branch 'origin/glass-cockpit-refit'

This commit is contained in:
Joe DiPrima
2026-07-29 19:56:31 -05:00
4 changed files with 151 additions and 13 deletions
+72 -13
View File
@@ -114,6 +114,7 @@ struct GWin
int frameW, frameH;
int wantX, wantY;
int restored; // wantX/wantY came from glass_layout.cfg -> don't re-snap
int noFrame; // ",noframe" in the cfg -> WS_POPUP, drag by the surface
};
static GWin gWins[8];
@@ -379,12 +380,41 @@ static void
// a later geometry change can't distort an old save. Windows carry WS_CAPTION
// in every mode, so they are always draggable -- the gate only controls whether
// a drag STICKS.
//
// PER-LINE OPTION ",noframe" (2026-07-29): append it to a window's line and
// that window loses its title bar and border (WS_POPUP) -- for a clean
// multi-monitor wall where the chrome is just noise.
//
// Heat MFD=1920,0,657,539,noframe
//
// THE WORKFLOW IS DELIBERATELY TWO-STAGE, and the option is the last step:
// 1. run with BT_GLASS_LAYOUT=save and DRAG the windows where you want them
// (they have title bars, so this is the normal sticky-layout flow),
// 2. quit,
// 3. hand-edit the cfg and append ,noframe to the ones you want bare.
// From then on those windows come up frameless AND PINNED -- with no caption
// there is nothing to drag them by, which is the point: the arrangement is
// finished. Re-frame a window by deleting the flag.
//
// The one thing it must not do is eat itself: SaveLayout rewrites the WHOLE
// file (any later drag of a FRAMED window triggers it), so it writes the flag
// back for every window that carries it.
//###########################################################################
enum { LayoutOff = 0, LayoutLoad = 1, LayoutSave = 2 };
static const char *layoutFileName = "glass_layout.cfg";
// A framed window is a normal tool window; ",noframe" makes it a bare
// WS_POPUP -- no caption, no border, nothing but the display and its buttons.
static DWORD
GlassWindowStyle(const GWin &w)
{
return w.noFrame
? (DWORD)WS_POPUP
: (DWORD)(WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX);
}
static int
GlassLayoutMode()
{
@@ -424,7 +454,7 @@ static GWin *
// computed spot. Restored windows are flagged so the WM_TIMER re-snap (which
// re-runs ComputeLayout once the main window appears) leaves them alone.
static void
LoadLayout()
LoadLayout(int quiet)
{
if (GlassLayoutMode() == LayoutOff)
return;
@@ -432,8 +462,9 @@ static void
FILE *f = fopen(layoutFileName, "rt");
if (f == NULL)
{
DEBUG_STREAM << "[glasswin] no " << layoutFileName
<< " yet (using computed placement)\n" << std::flush;
if (!quiet)
DEBUG_STREAM << "[glasswin] no " << layoutFileName
<< " yet (using computed placement)\n" << std::flush;
return;
}
@@ -459,17 +490,34 @@ static void
if (sscanf(eq + 1, "%d,%d,%d,%d", &x, &y, &w, &h) < 2)
continue;
// Trailing options after the numbers, comma-separated. Unknown ones
// are ignored (so an older build reading a newer file just loses the
// option, never the line) -- the bindings.txt grammar rule.
int noframe = 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)
{
noframe = 1;
break;
}
}
GWin *gw = FindGWinByTitle(s);
if (gw == NULL)
continue;
gw->wantX = x;
gw->wantY = y;
gw->noFrame = noframe;
gw->restored = 1;
++restored;
}
fclose(f);
DEBUG_STREAM << "[glasswin] restored " << restored << " window position(s) from "
<< layoutFileName << "\n" << std::flush;
if (!quiet)
DEBUG_STREAM << "[glasswin] restored " << restored << " window position(s) from "
<< layoutFileName << "\n" << std::flush;
}
// Write every window's current on-screen frame rect. Whole-file rewrite (it is
@@ -490,7 +538,14 @@ static void
}
fputs("# BT411 glass cockpit window layout (BT_GLASS_LAYOUT=save writes this\n"
"# on finished-drag/exit; =load restores it). <title>=<x>,<y>,<w>,<h>\n",
"# on finished-drag/exit; =load restores it). <title>=<x>,<y>,<w>,<h>\n"
"#\n"
"# Happy with where everything sits? Quit, then append ,noframe to any\n"
"# line to bring that window up with no title bar or border:\n"
"# 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",
f);
int wrote = 0;
for (int i = 0; i < gWinCount; ++i)
@@ -501,9 +556,10 @@ static void
RECT r;
if (!GetWindowRect(gw.hwnd, &r))
continue;
fprintf(f, "%s=%ld,%ld,%ld,%ld\n", gw.title,
fprintf(f, "%s=%ld,%ld,%ld,%ld%s\n", gw.title,
(long)r.left, (long)r.top,
(long)(r.right - r.left), (long)(r.bottom - r.top));
(long)(r.right - r.left), (long)(r.bottom - r.top),
gw.noFrame ? ",noframe" : ""); // keep the hand-added option
++wrote;
}
fclose(f);
@@ -793,6 +849,7 @@ static LRESULT CALLBACK
case WM_ERASEBKGND:
return 1; // PaintGlass fills the frame (double-buffered)
case WM_TIMER:
if (wparam == RepaintTimerId)
{
@@ -926,18 +983,20 @@ void
wc.lpszClassName = L"BTGlassWin";
RegisterClassW(&wc);
DWORD style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
// The ",noframe" flag is per WINDOW and arrives with the layout file, so
// read that FIRST -- the frame size (and therefore ComputeLayout's ring
// placement) depends on which windows carry chrome.
LoadLayout(1); // quiet: this pass only needs the ",noframe" flags
// Frame sizes first (ComputeLayout positions by frame dims).
for (int i = 0; i < gWinCount; ++i)
{
RECT fr = { 0, 0, gWins[i].clientW, gWins[i].clientH };
AdjustWindowRect(&fr, style, FALSE);
AdjustWindowRect(&fr, GlassWindowStyle(gWins[i]), FALSE);
gWins[i].frameW = fr.right - fr.left;
gWins[i].frameH = fr.bottom - fr.top;
}
ComputeLayout();
LoadLayout(); // BT_GLASS_LAYOUT=load/save: restore saved positions over the ring
LoadLayout(0); // re-apply saved positions over the computed ring
for (int i = 0; i < gWinCount; ++i)
{
@@ -951,7 +1010,7 @@ void
// of the game's D3D swap chain, ending GDI-vs-D3D presentation strobing.
w.hwnd = CreateWindowExW(
WS_EX_TOPMOST | WS_EX_LAYERED,
L"BTGlassWin", wtitle, style,
L"BTGlassWin", wtitle, GlassWindowStyle(w),
w.wantX, w.wantY, w.frameW, w.frameH,
NULL, NULL, GetModuleHandleW(NULL), NULL);
if (w.hwnd == NULL)