diff --git a/Gameleap/code/CoreTech/Libraries/GameOS/VideoCard.cpp b/Gameleap/code/CoreTech/Libraries/GameOS/VideoCard.cpp index f7fb8fee..144b23a3 100644 --- a/Gameleap/code/CoreTech/Libraries/GameOS/VideoCard.cpp +++ b/Gameleap/code/CoreTech/Libraries/GameOS/VideoCard.cpp @@ -38,6 +38,10 @@ typedef struct GUID GUID; char* lpDriverDescription; char* lpDriverName; + // [displaylog] Which physical monitor this device drives. Only the Ex form of + // the enumeration reports it, and it is the ONLY link between a DirectDraw + // device index and a monitor the operator can actually identify. + HMONITOR hMonitor; } videoDevices; @@ -271,12 +275,291 @@ BOOL CALLBACK DirectDrawEnumerateExCallback( GUID* lpGUID, LPSTR lpDriverDescrip BOOL CALLBACK DirectDrawEnumerateCallback( GUID* lpGUID, LPSTR lpDriverDescription, LPSTR lpDriverName, LPVOID ); HRESULT CALLBACK EnumModesCallback2( LPDDSURFACEDESC2 lpDDSurfaceDesc, LPVOID lpContext ); +//===========================================================================// +// [displaylog] Progressive display-enumeration report -> gos-displays.txt +// +// Every stage of display discovery is written out as it happens: the Windows +// desktop topology, each DirectDraw enumeration callback, the device table +// before and after the NULL-device merge, every role-selection decision, and +// the -tmon overrides. SPEW is compiled out of shipping builds, so without +// this there is no way to tell why a panel opened on the wrong monitor - or on +// the SAME monitor as another panel, which surfaces only as a DirectDraw error +// that names neither the role nor the display. +// +// Each line is opened/appended/closed so the report survives a hard crash. +//===========================================================================// +// Monitor driving each DeviceArray slot, kept in step through the NULL-device merge. +static HMONITOR g_ahDevMonitor[8] = { 0,0,0,0,0,0,0,0 }; +// Index into BufferedDevices of the device DoEnum is currently checking. +static int g_nEnumBufferedIndex = -1; +// Declared here so the report does not depend on the 1998 SDK's multimon headers; +// layouts match MONITORINFOEXA and DISPLAY_DEVICEA exactly. +typedef struct +{ + DWORD cbSize; + RECT rcMonitor; + RECT rcWork; + DWORD dwFlags; + char szDevice[32]; +} DispLogMonitorInfo; +typedef struct +{ + DWORD cb; + char DeviceName[32]; + char DeviceString[128]; + DWORD StateFlags; + char DeviceID[128]; + char DeviceKey[128]; +} DispLogDisplayDevice; +typedef BOOL (WINAPI* PFNDISPLOGGETMONITORINFOA)( HMONITOR, DispLogMonitorInfo* ); +typedef BOOL (WINAPI* PFNDISPLOGENUMDISPLAYDEVICESA)( LPCSTR, DWORD, DispLogDisplayDevice*, DWORD ); - +// The build toolchain predates these; define defensively rather than assume. +#ifndef ENUM_CURRENT_SETTINGS +#define ENUM_CURRENT_SETTINGS ((DWORD)-1) +#endif + +static PFNDISPLOGGETMONITORINFOA pfnDispLogGetMonitorInfoA = 0; +static PFNDISPLOGENUMDISPLAYDEVICESA pfnDispLogEnumDisplayDevicesA = 0; +static bool bDispLogApisResolved = false; + +// +// Resolved at run time: these APIs postdate the build toolchain's headers. +// +static void DispLogResolveApis() +{ + if( bDispLogApisResolved ) + return; + bDispLogApisResolved=true; + HMODULE hUser=GetModuleHandleA( "user32.dll" ); + if( hUser ) + { + pfnDispLogGetMonitorInfoA =(PFNDISPLOGGETMONITORINFOA)GetProcAddress( hUser, "GetMonitorInfoA" ); + pfnDispLogEnumDisplayDevicesA=(PFNDISPLOGENUMDISPLAYDEVICESA)GetProcAddress( hUser, "EnumDisplayDevicesA" ); + } +} + +static void DispLogPath( char* szPath ) +{ + char szDir[MAX_PATH]; + szDir[0]='\0'; + GetModuleFileNameA( NULL, szDir, sizeof(szDir) ); + char* pSlash=strrchr( szDir, '\\' ); + if( pSlash ) + *(pSlash+1)='\0'; + else + szDir[0]='\0'; + sprintf( szPath, "%sgos-displays.txt", szDir ); +} + +// +// Append one line. Opened and closed per call so a crash cannot lose the tail. +// +static void DispLog( const char* fmt, ... ) +{ + char szPath[MAX_PATH]; + char szLine[1024]; + DWORD dwWritten=0; + va_list ap; + + DispLogPath( szPath ); + HANDLE hFile=CreateFileA( szPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, + OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); + if( hFile==INVALID_HANDLE_VALUE ) + return; + SetFilePointer( hFile, 0, NULL, FILE_END ); + + va_start( ap, fmt ); + vsprintf( szLine, fmt, ap ); + va_end( ap ); + + WriteFile( hFile, szLine, (DWORD)strlen(szLine), &dwWritten, NULL ); + CloseHandle( hFile ); +} + +// +// Truncate the report at the start of a run. +// +static void DispLogReset() +{ + char szPath[MAX_PATH]; + DispLogPath( szPath ); + HANDLE hFile=CreateFileA( szPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); + if( hFile!=INVALID_HANDLE_VALUE ) + CloseHandle( hFile ); +} + +// +// A zeroed GUID is not "no GUID" - it is how GameOS stores the NULL device that +// DirectDraw reports first, which aliases whichever monitor Windows calls primary. +// Saying so explicitly here has settled more than one wrong-monitor report. +// +static const char* DispLogGuid( const GUID* pG, char* szBuf ) +{ + GUID zero; + if( !pG ) + { + strcpy( szBuf, "NULL pointer = primary display driver (no dedicated GUID)" ); + return szBuf; + } + memset( &zero, 0, sizeof(zero) ); + if( 0==memcmp( pG, &zero, sizeof(GUID) ) ) + { + strcpy( szBuf, "{00000000-0000-0000-0000-000000000000} ZERO = primary display driver alias" ); + return szBuf; + } + sprintf( szBuf, "{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", + (unsigned long)pG->Data1, (unsigned int)pG->Data2, (unsigned int)pG->Data3, + pG->Data4[0], pG->Data4[1], pG->Data4[2], pG->Data4[3], + pG->Data4[4], pG->Data4[5], pG->Data4[6], pG->Data4[7] ); + return szBuf; +} + +// +// Resolve an HMONITOR to the name Windows uses for it, plus its desktop rectangle. +// This is the bridge between a DirectDraw device index and a monitor the operator +// can point at. +// +static const char* DispLogMonitorName( HMONITOR hm, char* szBuf ) +{ + DispLogMonitorInfo mi; + DispLogResolveApis(); + if( !hm ) + { + strcpy( szBuf, "(none reported - primary display driver alias)" ); + return szBuf; + } + if( !pfnDispLogGetMonitorInfoA ) + { + sprintf( szBuf, "HMONITOR %p (GetMonitorInfoA unavailable)", hm ); + return szBuf; + } + memset( &mi, 0, sizeof(mi) ); + mi.cbSize=sizeof(mi); + if( pfnDispLogGetMonitorInfoA( hm, &mi ) ) + sprintf( szBuf, "%-12s %ldx%ld at %ld,%ld%s", + mi.szDevice, + (long)(mi.rcMonitor.right-mi.rcMonitor.left), + (long)(mi.rcMonitor.bottom-mi.rcMonitor.top), + (long)mi.rcMonitor.left, (long)mi.rcMonitor.top, + (mi.dwFlags & 1) ? " [WINDOWS PRIMARY]" : "" ); + else + sprintf( szBuf, "HMONITOR %p (GetMonitorInfoA failed)", hm ); + return szBuf; +} + +// +// Dump what Windows itself thinks is attached. Operators read monitor numbers off +// Display Settings and assume -tmon takes them; it does not, it takes DirectDraw +// device indices, and on multi-adapter machines the two orders differ. +// +static void DispLogWindowsTopology() +{ + DispLogDisplayDevice dd; + DWORD iAdapter; + + DispLogResolveApis(); + DispLog( "\r\n--- Windows desktop topology -------------------------------------------\r\n" ); + DispLog( "Display Settings numbers monitors independently of the DirectDraw device\r\n" + "order below. -tmon takes DIRECTDRAW indices, not Windows monitor numbers.\r\n" + "Cross-reference using the \\\\.\\DISPLAYn name shown against each device.\r\n\r\n" ); + + if( !pfnDispLogEnumDisplayDevicesA ) + { + DispLog( " EnumDisplayDevicesA unavailable - cannot report Windows topology.\r\n" ); + return; + } + + for( iAdapter=0; iAdapter<16; iAdapter++ ) + { + DEVMODEA dm; + memset( &dd, 0, sizeof(dd) ); + dd.cb=sizeof(dd); + if( !pfnDispLogEnumDisplayDevicesA( NULL, iAdapter, &dd, 0 ) ) + break; + + DispLog( " %-14s %s\r\n", dd.DeviceName, dd.DeviceString ); + DispLog( " state : %s%s\r\n", + (dd.StateFlags & 0x00000001) ? "attached-to-desktop " : "NOT attached ", + (dd.StateFlags & 0x00000004) ? "PRIMARY" : "" ); + + memset( &dm, 0, sizeof(dm) ); + dm.dmSize=sizeof(dm); + if( EnumDisplaySettingsA( dd.DeviceName, ENUM_CURRENT_SETTINGS, &dm ) ) + DispLog( " mode : %lux%lu %lubpp @ %luHz\r\n", + (unsigned long)dm.dmPelsWidth, (unsigned long)dm.dmPelsHeight, + (unsigned long)dm.dmBitsPerPel, (unsigned long)dm.dmDisplayFrequency ); + } +} + +// +// Full device table. Printed before and after the NULL-device merge so an index +// shift is visible rather than inferred. +// +static void DispLogDeviceTable( const char* szWhen ) +{ + char szG[192]; + char szDI[192]; + char szMon[192]; + DWORD iDev; + + DispLog( "\r\n--- DirectDraw device table: %s (NumDevices=%d) ---\r\n", + szWhen, (int)NumDevices ); + + for( iDev=0; iDev= 0) && (dev < (int)NumDevices)) + if( dev < 0 ) + { + DispLog( " %-5s : auto (kept device %d)\r\n", apszOverrideRole[slot], *pTarget ); + return; + } + if( dev < (int)NumDevices ) + { + DispLog( " %-5s : device %d APPLIED (auto-detect had chosen %d)\r\n", + apszOverrideRole[slot], dev, *pTarget ); *pTarget = dev; + } + else + { + DispLog( " %-5s : device %d *** REJECTED - only %d device(s) exist, keeping %d ***\r\n", + apszOverrideRole[slot], dev, (int)NumDevices, *pTarget ); + } } // -// [displaylog] Append one printf-style line to the display report. -// -static void EmitDisplayLine( HANDLE hFile, const char* fmt, ... ) -{ - char szLine[512]; - DWORD dwWritten=0; - va_list ap; - va_start( ap, fmt ); - vsprintf( szLine, fmt, ap ); - va_end( ap ); - WriteFile( hFile, szLine, (DWORD)strlen(szLine), &dwWritten, NULL ); -} - -// -// [displaylog] Write gos-displays.txt next to the executable, describing every -// DirectDraw device GameOS found and which display role each one was assigned. -// -// This exists because there is no other way to see it: the SPEW macros are compiled -// out of shipping builds, and a monitor that simply never lights up gives no clue -// whether the device was missing, lacked hardware caps, or was assigned elsewhere. +// [displaylog] Final summary: the role assignment, how each role maps back to a +// physical monitor, and whether the assignment is self-consistent. // static void LogDisplayDevices() { - char szDir[MAX_PATH]; - char szPath[MAX_PATH]; + static const char* apszRoleName[6] = + { "main ", "radar", "span ", "span2", "mfd1 ", "mfd2 " }; + int anRoleDev[6]; + char szMon[192]; + int iRole; - szDir[0]='\0'; - GetModuleFileNameA( NULL, szDir, sizeof(szDir) ); - char* pSlash=strrchr( szDir, '\\' ); - if( pSlash ) - *(pSlash+1)='\0'; - else - szDir[0]='\0'; - sprintf( szPath, "%sgos-displays.txt", szDir ); + DispLogDeviceTable( "FINAL" ); - HANDLE hFile=CreateFileA( szPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, - CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); - if( hFile==INVALID_HANDLE_VALUE ) - return; + anRoleDev[0]=Environment.FullScreenDevice; + anRoleDev[1]=g_nNonDualHead; + anRoleDev[2]=g_nDualHead; + anRoleDev[3]=g_nDualHead2; + anRoleDev[4]=g_nMFD1; + anRoleDev[5]=g_nMFD2; - EmitDisplayLine( hFile, "GameOS display device enumeration\r\n" ); - EmitDisplayLine( hFile, "=================================\r\n\r\n" ); - EmitDisplayLine( hFile, "NumDevices = %d (DeviceArray holds at most 8)\r\n", (int)NumDevices ); - EmitDisplayLine( hFile, "NumHWDevices = %d\r\n", (int)NumHWDevices ); - EmitDisplayLine( hFile, "NumMonitors = %d\r\n\r\n", (int)NumMonitors ); + DispLog( "\r\n--- Final role assignment ----------------------------------------------\r\n" ); + DispLog( " main (FullScreenDevice) = %d\r\n", Environment.FullScreenDevice ); + DispLog( " radar (g_nNonDualHead) = %d\r\n", g_nNonDualHead ); + DispLog( " span (g_nDualHead) = %d\r\n", g_nDualHead ); + DispLog( " span2 (g_nDualHead2) = %d\r\n", g_nDualHead2 ); + DispLog( " mfd1 (g_nMFD1) = %d\r\n", g_nMFD1 ); + DispLog( " mfd2 (g_nMFD2) = %d\r\n", g_nMFD2 ); + DispLog( " (-1 means the role was never assigned a device)\r\n" ); - for( DWORD t0=0; t0 device -> physical monitor:\r\n" ); + for( iRole=0; iRole<6; iRole++ ) { - EmitDisplayLine( hFile, " [%d] %-40s hw_rasterization=%s\r\n", - (int)t0, - DeviceArray[t0].DDid.szDescription, - (DeviceArray[t0].D3DCaps.dwDevCaps & D3DDEVCAPS_HWRASTERIZATION) ? "yes" : "NO" ); + if( anRoleDev[iRole] < 0 || anRoleDev[iRole] >= (int)NumDevices ) + continue; + DispLog( " %s -> device %d -> %s\r\n", + apszRoleName[iRole], anRoleDev[iRole], + DispLogMonitorName( g_ahDevMonitor[anRoleDev[iRole]], szMon ) ); } - EmitDisplayLine( hFile, "\r\nAssigned roles (-1 means not found):\r\n" ); - EmitDisplayLine( hFile, " main (FullScreenDevice) = %d\r\n", Environment.FullScreenDevice ); - EmitDisplayLine( hFile, " radar (g_nNonDualHead) = %d\r\n", g_nNonDualHead ); - EmitDisplayLine( hFile, " span (g_nDualHead) = %d\r\n", g_nDualHead ); - EmitDisplayLine( hFile, " span2 (g_nDualHead2) = %d\r\n", g_nDualHead2 ); - EmitDisplayLine( hFile, " mfd1 (g_nMFD1) = %d\r\n", g_nMFD1 ); - EmitDisplayLine( hFile, " mfd2 (g_nMFD2) = %d\r\n", g_nMFD2 ); - - // Report what -tmon asked for and whether each slot could be honoured. + // + // Two roles on one device cannot work: the first takes exclusive fullscreen and + // the second fails with DDERR_EXCLUSIVEMODEALREADYSET, an error that names + // neither role. -tmon applies each slot independently and has no cross-check, + // so a hand-written override is the usual way to get here. + // + DispLog( "\r\n--- Consistency check --------------------------------------------------\r\n" ); { - static const char* apszRole[4] = { "main", "radar", "mfd1", "mfd2" }; - bool bAny=false; - for( int s=0; s<4; s++ ) - if( g_naMonitorOverride[s] >= 0 ) - bAny=true; - if( bAny ) + bool bClash=false; + int iA; + for( iA=0; iA<6; iA++ ) { - EmitDisplayLine( hFile, "\r\n-tmon override:\r\n" ); - for( int s2=0; s2<4; s2++ ) + int iB; + if( anRoleDev[iA] < 0 ) + continue; + for( iB=iA+1; iB<6; iB++ ) { - int dev=g_naMonitorOverride[s2]; - if( dev < 0 ) - EmitDisplayLine( hFile, " %-5s : auto\r\n", apszRole[s2] ); - else if( dev < (int)NumDevices ) - EmitDisplayLine( hFile, " %-5s : device %d APPLIED\r\n", apszRole[s2], dev ); - else - EmitDisplayLine( hFile, " %-5s : device %d *** REJECTED - only %d device(s) exist ***\r\n", - apszRole[s2], dev, (int)NumDevices ); + if( anRoleDev[iB] < 0 ) + continue; + if( anRoleDev[iA]==anRoleDev[iB] ) + { + DispLog( " *** CLASH: %s and %s are BOTH on device %d ***\r\n", + apszRoleName[iA], apszRoleName[iB], anRoleDev[iA] ); + bClash=true; + } } } + if( bClash ) + DispLog( " Two roles cannot share one display. Whichever initialises second will\r\n" + " fail with DDERR_EXCLUSIVEMODEALREADYSET because the first already holds\r\n" + " exclusive fullscreen on that monitor. Fix the -tmon assignment, or drop\r\n" + " -tmon entirely - auto-detection cannot produce a duplicate.\r\n" ); else - { - EmitDisplayLine( hFile, "\r\n-tmon override: not supplied (all roles auto-detected)\r\n" ); - } + DispLog( " No duplicate device assignments.\r\n" ); + + // Device 0 is the primary-display-driver slot. If the merge below did not + // collapse it onto a real device it still aliases the Windows primary, so any + // panel pointed at it lands on the main display's monitor. + if( !g_ahDevMonitor[0] && NumDevices ) + DispLog( " *** WARNING: device 0 has no monitor of its own - it is still the primary\r\n" + " display driver alias. Any role assigned to device 0 will open on the\r\n" + " Windows primary monitor, whichever device that really is. ***\r\n" ); } // The decisive line: whether the MFD subsystem will initialise at all. - EmitDisplayLine( hFile, "\r\nMFD mode (-tmfds) = %d\r\n", g_nTypeOfMFDs ); + DispLog( "\r\nMFD mode (-tmfds) = %d\r\n", g_nTypeOfMFDs ); if( g_nTypeOfMFDs == 4 ) { bool bOK = (g_nMFD1 != -1) && (g_nMFD2 != -1); - EmitDisplayLine( hFile, "mode 4 requires BOTH mfd1 and mfd2: %s\r\n", bOK ? "OK" : "*** FAILED ***" ); + DispLog( "mode 4 requires BOTH mfd1 and mfd2: %s\r\n", bOK ? "OK" : "*** FAILED ***" ); if( !bOK ) - EmitDisplayLine( hFile, + DispLog( " -> IsMultimonitorAvaliable() returns false, so HSH_EnterFullScreen2()\r\n" " falls back to the single-secondary-monitor path and the two MFD\r\n" " displays are never opened. Four hardware-rasterizing DirectDraw\r\n" " devices are needed: main, radar, mfd1, mfd2.\r\n" ); } - CloseHandle( hFile ); + DispLog( "\r\n--- Panel initialisation (DirectDraw results per display) --------------\r\n" ); } // @@ -417,49 +708,112 @@ void FindVideoCards() NumHWDevices=0; NumBuffered=0; pDeviceArray=DeviceArray; + + // [displaylog] Start a fresh report for this run. + memset( g_ahDevMonitor, 0, sizeof(g_ahDevMonitor) ); + DispLogReset(); + DispLog( "GameOS display enumeration report\r\n" ); + DispLog( "=================================\r\n" ); + DispLog( "Written by FindVideoCards() at startup, then appended to by each panel as\r\n" ); + DispLog( "it initialises. Read top to bottom: it follows the order the game does.\r\n" ); + { + char szExe[MAX_PATH]; + szExe[0]='\0'; + GetModuleFileNameA( NULL, szExe, sizeof(szExe) ); + DispLog( "\r\nexecutable : %s\r\n", szExe ); + DispLog( "command line : %s\r\n", GetCommandLineA() ); + } + DispLog( "requested : -tmfds %d -tmon %d,%d,%d,%d (0 = auto; values are 1-based\r\n" + " on the command line and appear here already converted to\r\n" + " 0-based DirectDraw device indices, -1 meaning auto)\r\n", + g_nTypeOfMFDs, + g_naMonitorOverride[0], g_naMonitorOverride[1], + g_naMonitorOverride[2], g_naMonitorOverride[3] ); + + DispLogWindowsTopology(); // // Disable all hardware devices? // if( gUseBlade ) + { + DispLog( "\r\n*** gUseBlade set - hardware device enumeration skipped entirely. ***\r\n" ); return; + } // // Enumerate all D3D cards // + DispLog( "\r\n--- Stage 1: DirectDraw enumeration callbacks ---------------------------\r\n" ); gos_MathExceptions( 0,0 ); wDirectDrawEnumerate(DirectDrawEnumerateCallback,DirectDrawEnumerateExCallback,0); gos_MathExceptions(1,0); + DispLog( "\r\n %d device(s) buffered, NumMonitors=%d\r\n", (int)NumBuffered, (int)NumMonitors ); // // Now test them all // + DispLog( "\r\n--- Stage 2: capability check of each enumerated device ----------------\r\n" ); CheckDevices(); + DispLog( "\r\n NumDevices=%d NumHWDevices=%d (DeviceArray holds at most 8)\r\n", + (int)NumDevices, (int)NumHWDevices ); + + DispLogDeviceTable( "after enumeration, BEFORE the NULL-device merge" ); // // On a multimonitor system remove the NULL device // + DispLog( "\r\n--- Stage 3: NULL-device merge -----------------------------------------\r\n" ); + DispLog( "DirectDraw reports the primary display driver first, with no GUID. It is an\r\n" + "ALIAS of whichever monitor Windows calls primary, not an extra output. It is\r\n" + "collapsed onto the real device sharing its guidDeviceIdentifier (= same card),\r\n" + "which shifts every later device index down by one.\r\n" ); if( NumMonitors>=2 && NumDevices ) { // // Find the real device GUID that matches the primary // + DispLog( " guard NumMonitors(%d)>=2 && NumDevices(%d) : PASSED - merge will run\r\n", + (int)NumMonitors, (int)NumDevices ); + bool bMerged=false; for( int t0=1; t0 copying it into slot 0 and shifting devices %d..%d down one\r\n", + t0+1, (int)NumDevices-1 ); DeviceArray[0]=DeviceArray[t0]; + g_ahDevMonitor[0]=g_ahDevMonitor[t0]; while( t0+1=2 && NumDevices(%d) : NOT met - merge SKIPPED.\r\n", + (int)NumMonitors, (int)NumDevices ); + DispLog( " *** Device 0 is still the primary display driver alias. ***\r\n" ); + } + + DispLogDeviceTable( "AFTER the NULL-device merge - these are the indices -tmon uses" ); // // Make sure initial full screen device is valid. -1 means select 1st // + DispLog( "\r\n--- Stage 4: automatic role selection ----------------------------------\r\n" ); // jcem - dual heads... { + DispLog( " span search (a device offering a 1280x480 mode):\r\n" ); for(int t0 = 0; t0 < NumDevices; t0++) { for( int t1=0; t1 g_nDualHead\r\n", t0 ); } else if (g_nDualHead2 == -1) { g_nDualHead2 = t0; + DispLog( " device %d has 1280x480 -> g_nDualHead2\r\n", t0 ); } break; } } } + if( g_nDualHead==-1 ) + DispLog( " no device offers 1280x480 (normal unless using spanned MFD mode 1/3)\r\n" ); } // jcem //if( Environment.FullScreenDevice==-1 ) { + DispLog( " main search (first hardware rasterizer that is not a span device):\r\n" ); for( int t0=0; t0=NumDevices || Environment.FullScreenDevice<0 ) + { + DispLog( " main device %d is out of range - forced to 0\r\n", Environment.FullScreenDevice ); Environment.FullScreenDevice=0; + } // jcem if (g_nDualHead != -1) { if (g_nDualHead2 != -1) { g_nNonDualHead = g_nDualHead2; + DispLog( " radar : taken from g_nDualHead2 -> device %d\r\n", g_nNonDualHead ); } } if (g_nNonDualHead == -1) { + DispLog( " radar search (first hardware rasterizer that is not main or span):\r\n" ); for( int t0=0; t0DeviceGUID, 0, sizeof( GUID )); } + // [displaylog] Capture the monitor before the slot index advances - the + // enumeration is the only place the association is available. + if( NumDevices<8 ) + g_ahDevMonitor[NumDevices]= + ( g_nEnumBufferedIndex>=0 && g_nEnumBufferedIndex<8 ) + ? BufferedDevices[g_nEnumBufferedIndex].hMonitor : 0; + DispLog( " result : ACCEPTED as DirectDraw device [%d]%s\r\n", + (int)NumDevices, FoundHal ? " (HAL present)" : " (no HAL)" ); + pDeviceArray++; NumDevices++; if( FoundHal ) NumHWDevices++; } + else + { + DispLog( " result : REJECTED - failed capability checks, no device index assigned\r\n" ); + } return (NumDevices<8) ? DDENUMRET_OK : DDENUMRET_CANCEL; } @@ -1347,17 +1741,26 @@ void CheckDevices() { for( int t0=0; t0