BT_POD_RGB: the authentic RGB-split output -- one window per VGA PORT
Implements what the pod actually does (pod-hardware.md THE RGB SPLIT): a VGA port's R/G/B lines each drive a separate mono MFD monitor, so a window is not one MFD -- it is one PORT carrying up to three. BT_POD_RGB=1 collapses the five MFD windows into the two ports the cab drives (Port A: Comm=red, Mfd2=green, Heat=blue; Port B: Mfd1=red, Mfd3=green) and composites each group's planes into the colour channels, leaving the radar on its own full-colour port. Channel comes from the live port (GetEnableID), never a hardcoded table, so an Eng-page swap follows automatically; BlankColor planes contribute nothing, exactly as on the pod. Implies BT_POD_SURFACES (bare 640x480 pictures -- the cab's buttons are physical). Verified locally (bare windows, "RGB COMPOSITE of 2 plane(s): Comm Mfd2") and LIVE ON NICK'S CRASH CART over the tailnet: Port A -> DISPLAY4, Port B -> DISPLAY2, radar -> DISPLAY1, all exact-fit. Pod scratch kit included (ssh helper, layout cfgs for both modes, launcher; the mission-egg launcher fix -- a bare MP.EGG exits the mission loop with no relay, and BT_FE_SOLO parks at the menu, so neither lights the panels). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
e0e9dcc292
commit
e179c7033f
@@ -115,6 +115,14 @@ struct GWin
|
||||
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
|
||||
|
||||
// RGB CHANNEL GROUP (BT_POD_RGB): on splitter-wired pod glass this window
|
||||
// is one VGA PORT feeding up to three mono monitors through R/G/B. These
|
||||
// are the OTHER surfaces sharing the port; each is composited into its own
|
||||
// channel by BlitSurface. groupCount 0 = ordinary single-surface window.
|
||||
const char *groupPort[3];
|
||||
const char *groupAlt[3]; // the Eng<n> twin, or NULL
|
||||
int groupCount;
|
||||
};
|
||||
|
||||
static GWin gWins[8];
|
||||
@@ -124,7 +132,8 @@ static int pressedAddress = -1;
|
||||
static unsigned char latched[128];
|
||||
static int gRadarRot = 3; // BT_GAUGE_SEC_ROT (default CW, upright)
|
||||
static bool sRepositionedToMain = false;
|
||||
static unsigned long *gStage = NULL; // 640*480 BGRA staging (shared, main-thread)
|
||||
static unsigned long *gStage = NULL; // 640*480 BGRA staging (shared, main-thread)
|
||||
static unsigned long *gStage2 = NULL; // RGB composite scratch (BT_POD_RGB)
|
||||
|
||||
static HFONT titleFont = NULL;
|
||||
static HFONT buttonFont = NULL;
|
||||
@@ -417,6 +426,21 @@ static const char *layoutFileName = "glass_layout.cfg";
|
||||
// The Flight Controls window (no surface at all) is not created.
|
||||
// BT_POD_SURFACES=1 -> every display window goes bare
|
||||
// glass_layout.cfg "<title>=x,y,bare" -> just that one (mixed rigs)
|
||||
// BT_POD_RGB=1 -- the pod's REAL video wiring (see pod-hardware.md §THE RGB
|
||||
// SPLIT): one VGA port per window, its R/G/B analog lines split to three
|
||||
// monochrome MFD monitors. Implies pod surface mode.
|
||||
static int
|
||||
PodRgbSplitMode()
|
||||
{
|
||||
static int m = -1;
|
||||
if (m < 0)
|
||||
{
|
||||
const char *e = getenv("BT_POD_RGB");
|
||||
m = (e != NULL && e[0] != 0 && e[0] != '0') ? 1 : 0;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
static int
|
||||
PodSurfaceMode()
|
||||
{
|
||||
@@ -425,6 +449,8 @@ static int
|
||||
{
|
||||
const char *e = getenv("BT_POD_SURFACES");
|
||||
m = (e != NULL && *e != '\0' && *e != '0') ? 1 : 0;
|
||||
if (!m && PodRgbSplitMode())
|
||||
m = 1; // the RGB split is pod glass -- always bare
|
||||
if (m)
|
||||
DEBUG_STREAM << "[glasswin] POD SURFACE MODE: bare surfaces, no "
|
||||
"button banks (physical pod buttons assumed)\n" << std::flush;
|
||||
@@ -817,6 +843,71 @@ static void
|
||||
}
|
||||
int ow = 0, oh = 0;
|
||||
svga->ExpandPlaneToBGRA(mask, palId, tint, w->rotate, gStage, &ow, &oh);
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// RGB CHANNEL COMPOSITE (BT_POD_RGB, 2026-08-06) -- the AUTHENTIC pod
|
||||
// output. The cab does not give each mono MFD its own video port: one
|
||||
// VGA port's R/G/B analog lines are SPLIT to three monochrome monitors,
|
||||
// and the game puts a different surface in each colour channel of one
|
||||
// palettized framebuffer (L4GAUGE.CFG: Comm=red, Mfd2=green, Heat=blue
|
||||
// on clut2; Mfd1=red, Mfd3=green on clut1 -- and
|
||||
// L4GraphicsPort::BuildSecondaryColor writes exactly ONE component per
|
||||
// port, which IS the split). So on splitter-wired glass this window is
|
||||
// not one MFD: it is one VGA PORT, carrying up to three. Composite the
|
||||
// group's planes into this frame, each in its own channel, and let the
|
||||
// hardware separate them again.
|
||||
//
|
||||
// Channel comes from the port itself (GetEnableID), so the authored
|
||||
// config -- including a page swap that hands the channel to an Eng
|
||||
// plane -- decides, never a hardcoded table here.
|
||||
// ---------------------------------------------------------------------
|
||||
if (w->groupCount > 0 && ow > 0 && oh > 0)
|
||||
{
|
||||
const int n = ow * oh;
|
||||
for (int gi = 0; gi < w->groupCount; ++gi)
|
||||
{
|
||||
L4GraphicsPort *gp =
|
||||
static_cast<L4GraphicsPort*>(gr->GetGraphicsPort(w->groupPort[gi]));
|
||||
if (gp == NULL)
|
||||
continue;
|
||||
// honour the live Mfd<n>/Eng<n> page swap: take whichever twin
|
||||
// currently owns a channel
|
||||
if (gp->GetEnableID() == L4GraphicsPort::BlankColor
|
||||
&& w->groupAlt[gi] != NULL)
|
||||
{
|
||||
L4GraphicsPort *ga =
|
||||
static_cast<L4GraphicsPort*>(gr->GetGraphicsPort(w->groupAlt[gi]));
|
||||
if (ga != NULL && ga->GetEnableID() != L4GraphicsPort::BlankColor)
|
||||
gp = ga;
|
||||
}
|
||||
const int ch = gp->GetEnableID();
|
||||
unsigned long chMask;
|
||||
switch (ch & 0x7F)
|
||||
{
|
||||
case L4GraphicsPort::RedChannel: chMask = 0x00FF0000ul; break;
|
||||
case L4GraphicsPort::GreenChannel: chMask = 0x0000FF00ul; break;
|
||||
case L4GraphicsPort::BlueChannel: chMask = 0x000000FFul; break;
|
||||
default: continue; // blank / direct-colour -> contributes nothing
|
||||
}
|
||||
int gw = 0, gh = 0;
|
||||
// -1 tint would palette-expand; pass white so the plane comes back
|
||||
// as full-intensity mono, then keep only this port's channel.
|
||||
svga->ExpandPlaneToBGRA(gp->GetBitMask(), gp->paletteID,
|
||||
(int)0x00FFFFFF, w->rotate, gStage2, &gw, &gh);
|
||||
if (gw != ow || gh != oh)
|
||||
continue;
|
||||
for (int i = 0; i < n; ++i)
|
||||
gStage[i] |= (gStage2[i] & chMask);
|
||||
}
|
||||
if (log)
|
||||
{
|
||||
DEBUG_STREAM << "[glass] '" << w->title << "' RGB COMPOSITE of "
|
||||
<< w->groupCount << " plane(s):";
|
||||
for (int gi = 0; gi < w->groupCount; ++gi)
|
||||
DEBUG_STREAM << " " << w->groupPort[gi];
|
||||
DEBUG_STREAM << "\n" << std::flush;
|
||||
}
|
||||
}
|
||||
if (log)
|
||||
{
|
||||
int nz = 0, n = ow * oh;
|
||||
@@ -1126,6 +1217,8 @@ void
|
||||
sRepositionedToMain = false;
|
||||
if (gStage == NULL)
|
||||
gStage = new unsigned long[640 * 480];
|
||||
if (gStage2 == NULL)
|
||||
gStage2 = new unsigned long[640 * 480];
|
||||
|
||||
// Build the seven windows. Order fixes the ComputeLayout roles (0..6).
|
||||
BuildMfd (gWins[0], "Heat MFD", "Heat", NULL, 0x2F); // UL 0x28-0x2F
|
||||
@@ -1147,6 +1240,42 @@ void
|
||||
gWinCount = 6; // drop Flight Controls
|
||||
}
|
||||
|
||||
// RGB SPLIT MODE (BT_POD_RGB): the cab's real wiring -- ONE VGA port per
|
||||
// window, its R/G/B lines split to three mono monitors. Collapse the five
|
||||
// separate MFD windows into the TWO ports the pod actually drives
|
||||
// (L4GAUGE.CFG clut2 = Comm/Mfd2/Heat, clut1 = Mfd1/Mfd3), each window
|
||||
// compositing its group into the colour channels; the radar keeps its own
|
||||
// full-colour port. Channels are read live from each port, so a page swap
|
||||
// to an Eng plane follows automatically.
|
||||
if (PodRgbSplitMode())
|
||||
{
|
||||
GWin port2 = gWins[0]; // reuse the Heat window's shape
|
||||
port2.title = "VGA Port A"; // Comm=red, Mfd2=green, Heat=blue
|
||||
port2.portPrimary = "Heat"; // base plane (blue); group adds the rest
|
||||
port2.portAlt = NULL;
|
||||
port2.groupCount = 2;
|
||||
port2.groupPort[0] = "Comm"; port2.groupAlt[0] = NULL;
|
||||
port2.groupPort[1] = "Mfd2"; port2.groupAlt[1] = "Eng2";
|
||||
port2.monoTint = (int)0x000000FF; // Heat rides BLUE
|
||||
|
||||
GWin port1 = gWins[3]; // the Mfd1 window's shape
|
||||
port1.title = "VGA Port B"; // Mfd1=red, Mfd3=green (blue spare)
|
||||
port1.portPrimary = "Mfd1";
|
||||
port1.portAlt = "Eng1";
|
||||
port1.groupCount = 1;
|
||||
port1.groupPort[0] = "Mfd3"; port1.groupAlt[0] = "Eng3";
|
||||
port1.monoTint = (int)0x00FF0000; // Mfd1 rides RED
|
||||
|
||||
GWin radar = gWins[5]; // unchanged: its own colour port
|
||||
|
||||
gWins[0] = port2;
|
||||
gWins[1] = port1;
|
||||
gWins[2] = radar;
|
||||
gWinCount = 3;
|
||||
DEBUG_STREAM << "[glasswin] RGB SPLIT MODE: 2 VGA ports + radar "
|
||||
"(each port's R/G/B feeds three mono panels)\n" << std::flush;
|
||||
}
|
||||
|
||||
titleFont = CreateFontW(-11, 0, 0, 0, FW_BOLD, 0, 0, 0,
|
||||
DEFAULT_CHARSET, 0, 0, CLEARTYPE_QUALITY, 0, L"Segoe UI");
|
||||
buttonFont = CreateFontW(-10, 0, 0, 0, FW_NORMAL, 0, 0, 0,
|
||||
|
||||
Reference in New Issue
Block a user