From 9ba2d594b16258c661f1c5656b6ee8bacbde63a4 Mon Sep 17 00:00:00 2001 From: RT Date: Fri, 24 Jul 2026 23:39:59 -0500 Subject: [PATCH] Add -tmon switch to hard-assign display devices Display roles are auto-detected in FindVideoCards(): the main view takes the first hardware-rasterizing device that is not a 1280x480-capable span, the radar takes the next, and mode 4's two MFD panels take the two after that. That works until DirectDraw enumerates the adapters in an unexpected order, at which point the wrong content appears on the wrong monitor with no way to correct it. Adds an override that keeps auto-detection as the default: -tmon
,,, Values are 1-based, so the normal case is "-tmon 1,2,3,4". A value of 0 leaves that slot auto-detected, so "-tmon 2,1,0,0" swaps only main and radar. Separators may be ',' '/' or ':'. mfd1/mfd2 are only meaningful with -tmfds 4. Omitting the switch entirely preserves existing behaviour exactly. Implementation: - Parsed in MW4Application's WinMain alongside -tmfds. That runs before GameOS calls FindVideoCards(), so the values are in place for device selection. - Stored in g_naMonitorOverride[4] (VideoCard.cpp), extern'd in MW4Application.cpp. - ApplyMonitorOverride() validates the index against NumDevices, so a stale -tmon on a machine with fewer monitors falls back to auto-detection rather than breaking startup. - Ordering matters and is deliberate: main and radar are applied BEFORE the mode 4 MFD search so that search still skips whichever devices the operator picked; mfd1/mfd2 are applied after it. Partial overrides therefore compose correctly. Role map: main = Environment.FullScreenDevice, radar = g_nNonDualHead, mfd1 = g_nMFD1, mfd2 = g_nMFD2. The mode 1 span (g_nDualHead) is intentionally not overridable -- it is detected by 1280x480 mode support, which only the span card advertises, so that detection is reliable. Also adds a SPEW line logging the final role -> device map and device count. Note this is only visible in Profile/LAB builds; SPEW compiles out in Release (gos2X/Gos.h), so in Release the order is determined by observation. Verified: compiles clean, console launches. Multi-monitor testing pending. Co-authored-by: Claude Opus 5 (Anthropic) Co-authored-by: GitHub Copilot --- .../CoreTech/Libraries/GameOS/VideoCard.cpp | 28 ++++++++++++++++ .../Code/MW4Application/MW4Application.cpp | 33 ++++++++++++++++++- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/Gameleap/code/CoreTech/Libraries/GameOS/VideoCard.cpp b/Gameleap/code/CoreTech/Libraries/GameOS/VideoCard.cpp index 170e144d..11d7f523 100644 --- a/Gameleap/code/CoreTech/Libraries/GameOS/VideoCard.cpp +++ b/Gameleap/code/CoreTech/Libraries/GameOS/VideoCard.cpp @@ -48,6 +48,10 @@ int g_nDualHead2 = -1; // jcem int g_nNonDualHead = -1; // jcem int g_nMFD1 = -1; // mode 4: left 640x480 MFD monitor int g_nMFD2 = -1; // mode 4: right 640x480 MFD monitor +// [tmon] Operator override for display-device selection, in Main/Radar/MFD1/MFD2 order. +// -1 = leave that slot to auto-detection. Populated by the -tmon command-line switch, +// which is parsed in MW4Application's WinMain - that runs before FindVideoCards(). +int g_naMonitorOverride[4] = { -1, -1, -1, -1 }; // MSL 5.03 Mechview int g_nMechViewType; // jcem : 0 - no mechview, 1 - on radar screen, 2 - on main screen @@ -277,6 +281,19 @@ HRESULT CALLBACK EnumModesCallback2( LPDDSURFACEDESC2 lpDDSurfaceDesc, LPVOID lp // // // Called once at startup, enumerate and get caps of all 3D video cards +// +// [tmon] Apply a hard-assigned display device, but only if the operator supplied one +// and it names a device that actually exists. Anything else is ignored, so a stale +// -tmon left on the command line of a machine with fewer monitors simply falls back +// to auto-detection instead of breaking startup. +// +static void ApplyMonitorOverride(int slot, int* pTarget) +{ + int dev = g_naMonitorOverride[slot]; + if ((dev >= 0) && (dev < (int)NumDevices)) + *pTarget = dev; +} + // // void FindVideoCards() @@ -382,6 +399,10 @@ void FindVideoCards() } } } + // [tmon] Hard-assigned Main/Radar overrides. Applied BEFORE the mode 4 MFD search + // below so that search still skips whichever devices the operator picked. + ApplyMonitorOverride(0, &Environment.FullScreenDevice); + ApplyMonitorOverride(1, &g_nNonDualHead); // Mode 4 (split dual 640x480): find two extra D3D devices beyond // FullScreenDevice and g_nNonDualHead. These need no special resolution. { @@ -396,6 +417,13 @@ void FindVideoCards() } } } + // [tmon] Hard-assigned MFD overrides (only meaningful with -tmfds 4). + ApplyMonitorOverride(2, &g_nMFD1); + ApplyMonitorOverride(3, &g_nMFD2); + // Log the final assignment so the operator can confirm which physical monitor each + // role landed on without needing a debugger. + SPEW(( GROUP_GAMEOS, "Display roles: devices=%d main=%d radar=%d mfd1=%d mfd2=%d span=%d", + (int)NumDevices, Environment.FullScreenDevice, g_nNonDualHead, g_nMFD1, g_nMFD2, g_nDualHead )); // jcem // // Check any known video cards have up to date drivers (Only on Windows9x) diff --git a/Gameleap/code/mw4/Code/MW4Application/MW4Application.cpp b/Gameleap/code/mw4/Code/MW4Application/MW4Application.cpp index 3c051e6d..01dc4d62 100644 --- a/Gameleap/code/mw4/Code/MW4Application/MW4Application.cpp +++ b/Gameleap/code/mw4/Code/MW4Application/MW4Application.cpp @@ -85,7 +85,10 @@ extern Time g_PlasmaClearTime; extern Time g_TOC_ADT; extern Time g_AUX_ADT; extern bool g_UseMSRSP; -extern int g_nTypeOfMFDs; // 0 - no MFDs, 1 - orginal(5+1=dual & single), 2 - B&W(3 + 1=1 dual), 3 - color(3 + 1=2 dual) +extern int g_nTypeOfMFDs; // 0 - no MFDs, 1 - orginal(5+1=dual & single), 2 - B&W(3 + 1=1 dual), 3 - color(3 + 1=2 dual), 4 - split dual 640x480 +// [tmon] Display-device override in Main/Radar/MFD1/MFD2 order; -1 = auto-detect. +// Defined in CoreTech GameOS VideoCard.cpp and consumed by FindVideoCards(). +extern int g_naMonitorOverride[4]; extern int g_nRIOType; extern DWORD g_dwRIOBaud; // [tbaud] extern LONG g_ThrottleDir; @@ -1443,6 +1446,34 @@ int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int n if ((0 <= n) && (n <= 4)) // 0 - no MFDs, 1 - orginal(5+1=dual & single), 2 - B&W(3 + 1=1 dual), 3 - color(3 + 1=2 dual), 4 - split dual 640x480 g_nTypeOfMFDs = n; } + // [tmon] Hard-assign the display devices when DirectDraw enumerates them in an + // unexpected order. Syntax: -tmon
,,, + // Values are 1-based (the normal case is "-tmon 1,2,3,4"); a value of 0 leaves that + // slot to auto-detection, so "-tmon 2,1,0,0" only swaps main and radar. Separators + // may be ',' '/' or ':'. mfd1/mfd2 are only used by -tmfds 4. + token = strstr(all_lower, "-tmon "); + if (token) + { + const char* p = &token[6]; + int slot = 0; + while (slot < 4) + { + while (*p == ' ') + p++; + if ((*p < '0') || ('9' < *p)) + break; + int v = atoi(p); + while (('0' <= *p) && (*p <= '9')) + p++; + // 0 = leave auto-detected, otherwise convert 1-based to a 0-based device index + g_naMonitorOverride[slot] = (0 < v) ? (v - 1) : -1; + slot++; + if ((*p == ',') || (*p == '/') || (*p == ':')) + p++; + else + break; + } + } token = strstr(all_lower, "-trio "); if (token && token[6]) {