Glass panels: flash at full rate when unfocused + red Panic/Eject button

Two glass-panel indicator fixes reported by playtesters.

1. FLASH SLOW UNLESS FOCUSED.  The per-display panel windows repaint off a
   62ms WM_TIMER, and Windows coalesces/throttles timer + paint messages for a
   window that is in the BACKGROUND (unfocused) -- so with the game window
   holding focus the panels' lamp flash (a repaint-driven animation) crawled,
   and giving a panel focus un-throttled it.  (My earlier surround fix drew in
   the main D3D frame and never touched these separate windows.)  New
   BTGlassPanels_Tick(), called once per frame from the main render loop
   (L4VIDEO), drives a synchronous InvalidateRect+UpdateWindow at the ~16Hz
   flash cadence -- the main loop runs every frame regardless of which window
   has focus, and the forced paint bypasses the throttled timer-message path.
   The WM_TIMER stays for its one-shot re-snap.

2. RED PANIC/EJECT.  0x3D is the Panic/Eject button; on the blue flight panel
   it was just another blue block.  The shared flight-grid geometry (L4RIOBANK)
   now tags 0x3D as colorClass 0 (red); L4GLASSWIN's AdoptBank maps colorClass
   0 -> ClrRed explicitly (the flight bank's ClrBlue default no longer swallows
   it), and the surround (L4VB16) already renders non-blue/yellow as red -- so
   the eject stands out red in both the surround and the exploded window.

Verified: Release links clean (40 tolerated /FORCE externals only); exploded
panels boot + run 13s no crash under the per-frame repaint pump; riobank dump
confirms 0x3d class=0 (red), neighbours class=2 (blue).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-08-04 19:28:11 -05:00
co-authored by Claude Opus 4.8
parent f99003cf50
commit 7d112ce309
4 changed files with 68 additions and 2 deletions
+43 -1
View File
@@ -190,7 +190,9 @@ static void
GButton &b = w.buttons[w.buttonCount++];
b.address = src.address;
b.color = (src.colorClass == 1) ? ClrYellow
: (src.colorClass == 2) ? ClrBlue : color;
: (src.colorClass == 2) ? ClrBlue
: (src.colorClass == 0) ? ClrRed // 0 = red (MFD, and the red Panic/Eject 0x3D)
: color;
b.rect.left = src.x + dx;
b.rect.top = src.y + dy;
b.rect.right = src.x + dx + src.w;
@@ -1050,3 +1052,43 @@ void
if (subFont) { DeleteObject(subFont); subFont = NULL; }
if (gStage) { delete[] gStage; gStage = NULL; }
}
//###########################################################################
// Per-frame repaint pump (2026-08-04, "indicators flash slowly unless focused").
//
// These windows repaint off a 62 ms WM_TIMER. Windows COALESCES/THROTTLES timer
// AND paint messages for a window that is in the BACKGROUND (not focused) -- so
// when the game window (or another app) holds focus, the panel's WM_TIMER slows
// to a crawl and the lamp FLASH (a repaint-driven animation: BTLampBrightnessOf
// alternates the shade off GetTickCount every paint) drags. Giving the panel
// focus un-throttles its timer, which is exactly what playtesters saw.
//
// Fix: drive the repaint from the MAIN render loop (BTGlassPanels_Tick, called
// once per frame from L4VIDEO), gated to the same ~16 Hz flash cadence. The main
// loop runs every frame while the game is up regardless of which window has focus,
// and InvalidateRect + UpdateWindow forces a SYNCHRONOUS WM_PAINT -- bypassing the
// throttled timer-message path entirely. The WM_TIMER stays (it still owns the
// one-shot re-snap); a focused window just repaints from whichever fires first.
//###########################################################################
void
BTGlassPanels_Tick()
{
if (gWinCount == 0)
return;
static unsigned long sLastPaint = 0;
unsigned long now = GetTickCount();
if (now - sLastPaint < (unsigned long)RepaintMilliseconds)
return;
sLastPaint = now;
for (int i = 0; i < gWinCount; ++i)
{
if (gWins[i].hwnd != NULL)
{
InvalidateRect(gWins[i].hwnd, NULL, FALSE);
UpdateWindow(gWins[i].hwnd); // synchronous paint, not the throttled queue
}
}
}
+4 -1
View File
@@ -258,7 +258,10 @@ void
int row = i / columns;
int x = originX + column * (cw + gap);
int y = originY + row * (ch + gap);
Push(out, baseAddress + i, x, y, cw, ch, 2,
// Flight blocks are blue (colorClass 2), EXCEPT the Panic/Eject at 0x3D:
// red (colorClass 0) so the eject stands out on the blue panel.
int cc = (baseAddress + i == 0x3D) ? 0 : 2;
Push(out, baseAddress + i, x, y, cw, ch, cc,
(inert != 0) ? inert[i] : 0,
(labels != 0) ? labels[i] : 0);
GrowBounds(out, x, y, cw, ch);
+11
View File
@@ -8952,6 +8952,17 @@ void DPLRenderer::ExecuteImplementation(RendererComplexity, RendererOrigin::Inte
extern void BTGaugeWindowRenderAndPresent(LPDIRECT3DDEVICE9 device);
BTGaugeWindowRenderAndPresent(mDevice);
#ifdef BT_GLASS
// Drive the per-display glass panels' repaint from the (always-running) main
// loop so their lamp flash keeps animating when they're in the background --
// Windows throttles an unfocused window's own WM_TIMER ("indicators flash
// slowly unless you focus that window"). No-op unless the panels are up.
{
extern void BTGlassPanels_Tick();
BTGlassPanels_Tick();
}
#endif
// DIAG (turn-hitch hunt): draw CPU is _rt0..here; Present blocks on the GPU.
LARGE_INTEGER _rt1; QueryPerformanceCounter(&_rt1);
// COCKPIT LETTERBOX: uniform-scale the canvas into the client (NULL = the
+10
View File
@@ -31,6 +31,16 @@ void
void
BTGlassPanels_Destroy();
//
// Per-frame repaint pump. Call once per frame from the main render loop so the
// per-display windows' lamp flash keeps animating even when they are in the
// background (Windows throttles a background window's own WM_TIMER; this drives a
// synchronous repaint at the flash cadence instead). No-op unless the windows
// are up. See the note at the definition (L4GLASSWIN.cpp).
//
void
BTGlassPanels_Tick();
//
// True when BT_GLASS_PANELS mode is selected (default ON under `-platform
// glass`, OFF otherwise). Read by the render loop / L4VB16 dev-composite to