|
|
/*==========================================================================
* * Copyright (C) 1994-1999 Microsoft Corporation. All Rights Reserved. * * File: ddrefrsh.c * Content: DirectDraw Refresh Rate support * * On Win98, we don't have detailed information regarding what * refresh rates the montor supports. We can get some information * from the monitor (EDID data), but we cannot absolutely rely on * it, so we require that the user manually verify each refresh * rate before we allow it to be used. * * History: * Date By Reason * ==== == ====== * 02-apr-99 smac Created it * ***************************************************************************/ #include "ddrawpr.h"
#include "edid.h"
#undef DPF_MODNAME
#define DPF_MODNAME "Refresh"
#ifdef WIN95
static WORD SupportedRefreshRates[] = {60, 75, 85, 100, 120}; #define NUM_SUPPORTED_REFRESH_RATES ( sizeof( SupportedRefreshRates ) / sizeof( WORD ) )
/*
* GetEDIDData */ HRESULT GetEDIDData( LPDDRAWI_DIRECTDRAW_GBL pddd, VESA_EDID * pEDIDData ) { memset( pEDIDData, 0, sizeof( VESA_EDID ) ); if( DD16_GetMonitorEDIDData( pddd->cDriverName, (LPVOID)pEDIDData) ) { return DD_OK; }
if( !( pddd->dwFlags & DDRAWI_DISPLAYDRV ) ) { // HACK: Use primary display EDID data for passthrough devices
if( DD16_GetMonitorEDIDData( g_szPrimaryDisplay, (LPVOID)pEDIDData) ) { return DD_OK; } }
return DDERR_UNSUPPORTED; }
/*
* CheckEdidBandiwdth * * Takes a resoltion/refrsh rate and calculates the bandwidth required for * this, and then updates lpHighestRefresh and lpHighestBandwidth to keep * track of the highest refresh rate and badnwidth info that we've encountered. */ void CheckEdidBandwidth( DWORD dwWidth, DWORD dwHeight, DWORD dwRefreshRate, LPDWORD lpHighestRefresh, LPDWORD lpHighestBandwidth ) { DWORD dwBandwidth;
dwBandwidth = dwWidth * dwHeight * dwRefreshRate; if( dwBandwidth > *lpHighestBandwidth ) { *lpHighestBandwidth = dwBandwidth; } if( dwRefreshRate > *lpHighestRefresh ) { *lpHighestRefresh = dwRefreshRate; } }
/*
* StdTimeXRES */ int StdTimeXRES(WORD StdTime) { if (StdTime == 0 || StdTime == 0x0101) return 0; else return ((StdTime & veStdTime_HorzResMask) + 31) * 8; }
/*
* StdTimeYRES */ int StdTimeYRES(WORD StdTime) { if (StdTime == 0 || StdTime == 0x0101) return 0;
switch (StdTime & veStdTime_AspectRatioMask) { case veStdTime_AspectRatio1to1: return StdTimeXRES(StdTime); case veStdTime_AspectRatio4to3: return StdTimeXRES(StdTime) * 3 / 4; case veStdTime_AspectRatio5to4: return StdTimeXRES(StdTime) * 4 / 5; case veStdTime_AspectRatio16to9: return StdTimeXRES(StdTime) * 9 / 16; } return 0; }
/*
* StdTimeRATE */ int StdTimeRATE(WORD StdTime) { if (StdTime == 0 || StdTime == 0x0101) return 0; else return ((StdTime & veStdTime_RefreshRateMask) >> 8) + 60; }
__inline UINT DetTimeXRES(BYTE *DetTime) { return (UINT)DetTime[2] + (((UINT)DetTime[4] & 0xF0) << 4); }
__inline UINT DetTimeYRES(BYTE *DetTime) { return (UINT)DetTime[5] + (((UINT)DetTime[7] & 0xF0) << 4); }
__inline UINT DetTimeXBLANK(BYTE *DetTime) { return (UINT)DetTime[3] + (((UINT)DetTime[4] & 0x0F) << 4); }
__inline UINT DetTimeYBLANK(BYTE *DetTime) { return (UINT)DetTime[6] + (((UINT)DetTime[7] & 0x0F) << 0); }
int DetTimeRATE(BYTE *DetTime) { ULONG clk; ULONG x; ULONG y;
clk = *(WORD*)DetTime; x = DetTimeXRES(DetTime) + DetTimeXBLANK(DetTime); y = DetTimeYRES(DetTime) + DetTimeYBLANK(DetTime);
if (clk == 0 || x == 0 || y == 0) return 0;
return (int)((clk * 10000) / (x * y)); }
/*
* GetDetailedTime */ void GetDetailedTime(BYTE *DetTime, LPDWORD lpHighestRefresh, LPDWORD lpHighestBandwidth ) { char ach[14]; int i; DWORD dw;
dw = *(DWORD *)DetTime;
if( dw == 0xFD000000 ) // Monitor limits
{ if( (DWORD)(DetTime[6]) > *lpHighestRefresh ) { *lpHighestRefresh = (DWORD)(DetTime[6]); } } else if (dw == 0xFA000000) // more standard timings
{ WORD * StdTime = (WORD *)&DetTime[5];
CheckEdidBandwidth( StdTimeXRES( StdTime[0] ), StdTimeYRES( StdTime[0] ), StdTimeRATE( StdTime[0] ), lpHighestRefresh, lpHighestBandwidth ); CheckEdidBandwidth( StdTimeXRES( StdTime[1] ), StdTimeYRES( StdTime[1] ), StdTimeRATE( StdTime[1] ), lpHighestRefresh, lpHighestBandwidth ); CheckEdidBandwidth( StdTimeXRES( StdTime[2] ), StdTimeYRES( StdTime[2] ), StdTimeRATE( StdTime[2] ), lpHighestRefresh, lpHighestBandwidth ); CheckEdidBandwidth( StdTimeXRES( StdTime[3] ), StdTimeYRES( StdTime[3] ), StdTimeRATE( StdTime[3] ), lpHighestRefresh, lpHighestBandwidth ); CheckEdidBandwidth( StdTimeXRES( StdTime[4] ), StdTimeYRES( StdTime[4] ), StdTimeRATE( StdTime[4] ), lpHighestRefresh, lpHighestBandwidth ); CheckEdidBandwidth( StdTimeXRES( StdTime[5] ), StdTimeYRES( StdTime[5] ), StdTimeRATE( StdTime[5] ), lpHighestRefresh, lpHighestBandwidth ); } else if( ( dw != 0xFF000000 ) && // Serial number
( dw != 0xFE000000 ) && // Monitor String
( dw != 0xFC000000 ) && // Monitor Name
( dw != 0xFB000000 ) && // ColorPoint data
( DetTimeRATE( DetTime) ) ) { CheckEdidBandwidth( DetTimeXRES( DetTime ), DetTimeYRES( DetTime ), DetTimeRATE( DetTime ), lpHighestRefresh, lpHighestBandwidth ); } }
/*
* EvaluateMonitor * * Determines the amount of bandwidth that the monitor can handle. */ void EvaluateMonitor( VESA_EDID *lpEdidData, DWORD *lpHighestRefresh, DWORD *lpHighestBandwidth ) { BYTE chk; int i;
*lpHighestRefresh = 0; *lpHighestBandwidth = 0;
/*
* Do some sanity checking to make sure that the EDID data looks sane */
for( chk = i = 0; i < 128; i++) { chk += ((BYTE *)lpEdidData)[i]; } if (chk != 0) { // Bad checksum
return; }
/*
* First get the bandwidth from the established timings */ if( lpEdidData->veEstTime1 & veEstTime1_720x400x70Hz) { CheckEdidBandwidth( 720, 400, 70, lpHighestRefresh, lpHighestBandwidth ); } if( lpEdidData->veEstTime1 & veEstTime1_720x400x88Hz) { CheckEdidBandwidth( 720, 400, 88, lpHighestRefresh, lpHighestBandwidth ); } if( lpEdidData->veEstTime1 & veEstTime1_640x480x60Hz) { CheckEdidBandwidth( 640, 480, 60, lpHighestRefresh, lpHighestBandwidth ); } if( lpEdidData->veEstTime1 & veEstTime1_640x480x67Hz) { CheckEdidBandwidth( 640, 480, 67, lpHighestRefresh, lpHighestBandwidth ); } if( lpEdidData->veEstTime1 & veEstTime1_640x480x72Hz) { CheckEdidBandwidth( 640, 480, 72, lpHighestRefresh, lpHighestBandwidth ); } if( lpEdidData->veEstTime1 & veEstTime1_640x480x75Hz) { CheckEdidBandwidth( 640, 480, 75, lpHighestRefresh, lpHighestBandwidth ); } if( lpEdidData->veEstTime1 & veEstTime1_800x600x60Hz) { CheckEdidBandwidth( 800, 600, 60, lpHighestRefresh, lpHighestBandwidth ); } if( lpEdidData->veEstTime2 & veEstTime2_800x600x72Hz) { CheckEdidBandwidth( 800, 600, 72, lpHighestRefresh, lpHighestBandwidth ); } if( lpEdidData->veEstTime2 & veEstTime2_800x600x75Hz) { CheckEdidBandwidth( 800, 600, 75, lpHighestRefresh, lpHighestBandwidth ); } if( lpEdidData->veEstTime2 & veEstTime2_1024x768x60Hz) { CheckEdidBandwidth( 1024, 768, 60, lpHighestRefresh, lpHighestBandwidth ); } if( lpEdidData->veEstTime2 & veEstTime2_1024x768x70Hz) { CheckEdidBandwidth( 1024, 768, 70, lpHighestRefresh, lpHighestBandwidth ); } if( lpEdidData->veEstTime2 & veEstTime2_1024x768x75Hz) { CheckEdidBandwidth( 1024, 768, 75, lpHighestRefresh, lpHighestBandwidth ); } if( lpEdidData->veEstTime2 & veEstTime2_1280x1024x75Hz) { CheckEdidBandwidth( 1280, 1024, 75, lpHighestRefresh, lpHighestBandwidth ); } if( lpEdidData->veEstTime3 & veEstTime3_1152x870x75Hz) { CheckEdidBandwidth( 1152, 870, 75, lpHighestRefresh, lpHighestBandwidth ); }
/*
* Now get the bandwidth from the standard timings */ CheckEdidBandwidth( StdTimeXRES( lpEdidData->veStdTimeID1 ), StdTimeYRES( lpEdidData->veStdTimeID1 ), StdTimeRATE( lpEdidData->veStdTimeID1 ), lpHighestRefresh, lpHighestBandwidth ); CheckEdidBandwidth( StdTimeXRES( lpEdidData->veStdTimeID2 ), StdTimeYRES( lpEdidData->veStdTimeID2 ), StdTimeRATE( lpEdidData->veStdTimeID2 ), lpHighestRefresh, lpHighestBandwidth ); CheckEdidBandwidth( StdTimeXRES( lpEdidData->veStdTimeID3 ), StdTimeYRES( lpEdidData->veStdTimeID3 ), StdTimeRATE( lpEdidData->veStdTimeID3 ), lpHighestRefresh, lpHighestBandwidth ); CheckEdidBandwidth( StdTimeXRES( lpEdidData->veStdTimeID4 ), StdTimeYRES( lpEdidData->veStdTimeID4 ), StdTimeRATE( lpEdidData->veStdTimeID4 ), lpHighestRefresh, lpHighestBandwidth ); CheckEdidBandwidth( StdTimeXRES( lpEdidData->veStdTimeID5 ), StdTimeYRES( lpEdidData->veStdTimeID5 ), StdTimeRATE( lpEdidData->veStdTimeID5 ), lpHighestRefresh, lpHighestBandwidth ); CheckEdidBandwidth( StdTimeXRES( lpEdidData->veStdTimeID6 ), StdTimeYRES( lpEdidData->veStdTimeID6 ), StdTimeRATE( lpEdidData->veStdTimeID6 ), lpHighestRefresh, lpHighestBandwidth ); CheckEdidBandwidth( StdTimeXRES( lpEdidData->veStdTimeID7 ), StdTimeYRES( lpEdidData->veStdTimeID7 ), StdTimeRATE( lpEdidData->veStdTimeID7 ), lpHighestRefresh, lpHighestBandwidth ); CheckEdidBandwidth( StdTimeXRES( lpEdidData->veStdTimeID8 ), StdTimeYRES( lpEdidData->veStdTimeID8 ), StdTimeRATE( lpEdidData->veStdTimeID8 ), lpHighestRefresh, lpHighestBandwidth );
/*
* Now get the detailed timing information */ GetDetailedTime( lpEdidData->veDetailTime1, lpHighestRefresh, lpHighestBandwidth ); GetDetailedTime( lpEdidData->veDetailTime2, lpHighestRefresh, lpHighestBandwidth ); GetDetailedTime( lpEdidData->veDetailTime3, lpHighestRefresh, lpHighestBandwidth ); GetDetailedTime( lpEdidData->veDetailTime4, lpHighestRefresh, lpHighestBandwidth ); }
//=============================================================================
//
// Function Description:
//
// Finds an item in a registry-based most recently used (MRU) list, and
// either retrieves the contents of that item, or updates (add if it doesn't
// exist) the item.
//
// Arguments:
//
// [IN/OUT] item - Contains at least the unique portion of the item to
// search for [IN/OUT]. If writing the item, should contain
// the entire item [IN].
// [IN] writeItem - Set to TRUE if updating/adding an item to the MRU list.
//
// Return Value:
//
// TRUE - If writeItem is TRUE, then the item was written to the registry.
// Otherwise the item was found and its contents stored in findItem.
// FALSE - Failure; no more information available.
//
// Created:
//
// 04/08/1999 johnstep
//
//=============================================================================
//-----------------------------------------------------------------------------
// Define global MRU list values here, for simplicity:
//
// gMruRegKey - Registry key where MRU list is stored
// gMruRegOrderValue - Name of MRU list order value
// gMruBaseChar - Base index into MRU list
// gMruMaxChar - Maximum index into MRU list
// gMruItemSize - Size of findItem.
// gMruUniqueOffset - Offset of unique portion of item. This unique portion
// is what will be used to compare items.
// gMruUniqueSize - Size of unique portion of item.
//-----------------------------------------------------------------------------
static const CHAR *gMruRegKey = REGSTR_PATH_DDRAW "\\" REGSTR_KEY_RECENTMONITORS; static const CHAR *gMruRegOrderValue = REGSTR_VAL_DDRAW_MONITORSORDER; #define gMruBaseChar '0'
#define gMruMaxChar '9'
#define gMruItemSize sizeof (DDMONITORINFO)
#define gMruUniqueOffset 0
#define gMruUniqueSize offsetof(DDMONITORINFO, Mode640x480)
BOOL MruList( VOID *item, const BOOL writeItem ) { BOOL success = FALSE; HKEY hkey;
// Create or open the root key, with permission to query and set values;
// only continue if successful:
if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, gMruRegKey, 0, NULL, 0, KEY_QUERY_VALUE | KEY_SET_VALUE, NULL, &hkey, NULL) == ERROR_SUCCESS) { CHAR mruOrder[gMruMaxChar - gMruBaseChar + 2]; DWORD type; DWORD count = sizeof mruOrder; UINT i;
{ CHAR temp[sizeof mruOrder];
// If we read the order value successfully, copy the valid chars
// into mruOrder, removing duplicates:
if (RegQueryValueEx(hkey, gMruRegOrderValue, NULL, &type, (BYTE *) temp, &count) == ERROR_SUCCESS) { UINT j = 0;
for (--count, i = 0; i < count; i++) { if ((temp[i] >= gMruBaseChar) && (temp[i] <= gMruMaxChar)) { UINT k;
for (k = 0; k < j; k++) { if (mruOrder[k] == temp[i]) { break; } } if (k == j) { mruOrder[j++] = temp[i]; } } } count = j; } else { count = 0; } }
// Only continue if we found at least one valid value in the order
// list, or if we're writing the item:
if ((count > 0) || writeItem) { CHAR regValue[2]; BYTE regItem[gMruItemSize];
regValue[1] = '\0';
// Search for the item in the MRU list:
for (i = 0; i < count; i++) { DWORD size = sizeof regItem;
regValue[0] = mruOrder[i];
if ((RegQueryValueEx(hkey, regValue, NULL, &type, (BYTE *) ®Item, &size) == ERROR_SUCCESS) && (size == sizeof regItem)) { if (memcmp( (BYTE *) ®Item + gMruUniqueOffset, (BYTE *) item + gMruUniqueOffset, gMruUniqueSize) == 0) { break; } } }
// Keep going if we found the item or in any case if we need to
// write the item:
if ((i < count) || writeItem) { UINT j;
// If we didn't find the item, then we must be writing, so
// adjust the index appropriately. If the list is already
// full, we just use the last item (LRU item), otherwise, add
// a new item:
if (i == count) { if (count == (gMruMaxChar - gMruBaseChar + 1)) { i--; } else { // Adding a new item; search for the lowest unused
// valid char:
for (mruOrder[i] = gMruBaseChar; mruOrder[i] < gMruMaxChar; mruOrder[i]++) { for (j = 0; j < i; j++) { if (mruOrder[j] == mruOrder[i]) { break; } } if (j == i) { break; } } count++; } } // Update the MRU order list if necessary. We update if we
// found or are adding an item after the first, or if this is
// the first item in the list:
if (i > 0 || (count == 1)) { // Bubble found item to head of order list:
for (j = i; j > 0; j--) { CHAR temp = mruOrder[j]; mruOrder[j] = mruOrder[j - 1]; mruOrder[j - 1] = temp; }
// Write the order list:
mruOrder[count] = '\0'; RegSetValueEx(hkey, gMruRegOrderValue, 0, REG_SZ, (BYTE *) mruOrder, count + 1); }
// If we want to write the item, do it now. We will always
// write to the first item in the order list. If not writing,
// copy what was read from the registry into item:
if (writeItem) { regValue[0] = mruOrder[0]; if (RegSetValueEx(hkey, regValue, 0, REG_BINARY, (BYTE *) item, sizeof regItem) == ERROR_SUCCESS) { success = TRUE; } } else { memcpy( (BYTE *) item + gMruUniqueOffset, regItem + gMruUniqueOffset, sizeof regItem);
success = TRUE; } } }
// Always close the registry key when finished:
RegCloseKey(hkey); }
return success; }
//-----------------------------------------------------------------------------
/*
* DDSaveMonitorInfo * * Writes the monitor info to the registry. */ HRESULT DDSaveMonitorInfo( LPDDRAWI_DIRECTDRAW_INT lpDD_int ) { return MruList( (VOID *) lpDD_int->lpLcl->lpGbl->lpMonitorInfo, TRUE ) ? DD_OK : DDERR_GENERIC; }
__inline IsValidRefreshRate( DWORD dwWidth, DWORD dwHeight, int refreshRate, DWORD dwHighestBandwidth ) { return ( ( refreshRate >= 0 ) && ( ( dwWidth * dwHeight * (DWORD) refreshRate ) <= dwHighestBandwidth ) ); }
/*
* DDGetMonitorInfo * * Reads the monitor info from the registry and verifies that it still applies. */ HRESULT DDGetMonitorInfo( LPDDRAWI_DIRECTDRAW_INT lpDD_int ) { LPDDMONITORINFO pInfo; static DDDEVICEIDENTIFIER DeviceIdentifier; HRESULT hr;
if( ( lpDD_int->lpVtbl == &dd7Callbacks ) && ( lpDD_int->lpLcl->lpGbl->lpMonitorInfo == NULL ) ) { VESA_EDID EDIDData; DWORD dwHighestRefresh; DWORD dwHighestBandwidth; HKEY hKey; BOOL bGotLastMonitor = FALSE;
hr = GetEDIDData( lpDD_int->lpLcl->lpGbl, &EDIDData ); if( hr != DD_OK ) { // There is no EDID data
return DDERR_GENERIC; } EvaluateMonitor( &EDIDData, &dwHighestRefresh, &dwHighestBandwidth );
hr = DD_GetDeviceIdentifier( (LPDIRECTDRAW) lpDD_int, &DeviceIdentifier, 0 ); if( hr != DD_OK ) { // Failed to get device identifier for monitor info
return hr; }
pInfo = (LPDDMONITORINFO) MemAlloc( sizeof( DDMONITORINFO ) ); if( pInfo == NULL ) { // Out of memory allocating monitor info structure
return DDERR_OUTOFMEMORY; }
pInfo->Manufacturer = *(WORD *)&EDIDData.veManufactID[0]; pInfo->Product = *(WORD *)&EDIDData.veProductCode[0]; pInfo->SerialNumber = EDIDData.veSerialNbr; pInfo->DeviceIdentifier = DeviceIdentifier.guidDeviceIdentifier;
// Read monitor information from registry, if available. We need to
// compare this to the EDID data to see if the monitor or adapter
// changed and verify the selected refresh rates are sane:
if( MruList( (VOID *) pInfo, FALSE ) ) { // Validate modes here against EDID data:
if( !IsValidRefreshRate( 640, 480, pInfo->Mode640x480, dwHighestBandwidth ) ) { pInfo->Mode640x480 = -1; } if( !IsValidRefreshRate( 800, 600, pInfo->Mode800x600, dwHighestBandwidth ) ) { pInfo->Mode800x600 = -1; } if( !IsValidRefreshRate( 1024, 768, pInfo->Mode1024x768, dwHighestBandwidth ) ) { pInfo->Mode1024x768 = -1; } if( !IsValidRefreshRate( 1280, 1024, pInfo->Mode1280x1024, dwHighestBandwidth ) ) { pInfo->Mode1280x1024 = -1; } if( !IsValidRefreshRate( 1600, 1200, pInfo->Mode1600x1200, dwHighestBandwidth ) ) { pInfo->Mode1600x1200 = -1; } bGotLastMonitor = TRUE; } if( !bGotLastMonitor ) { pInfo->Mode640x480 = -1; pInfo->Mode800x600 = -1; pInfo->Mode1024x768 = -1; pInfo->Mode1280x1024 = -1; pInfo->Mode1600x1200 = -1; }
pInfo->ModeReserved1 = -1; pInfo->ModeReserved2 = -1; pInfo->ModeReserved3 = -1;
lpDD_int->lpLcl->lpGbl->lpMonitorInfo = pInfo; } return DD_OK; }
/*
* ExpandModeTable * * On Win9X, drivers can specify their maximum refresh rate for each mode, * allowing DDraw to add modes for each refresh rate that we care about. * This allows drivers to add refresh rate easily without having to * maintain huge tables. This also allows us to avoid regressions by allowing * us to only enumerate these refresh rates on newer interfaces. */ HRESULT ExpandModeTable( LPDDRAWI_DIRECTDRAW_GBL pddd ) { DWORD i; DWORD j; DWORD iNumModes = 0; LPDDHALMODEINFO pNewModeTable; DWORD iModeIndex; WORD wMaxRefresh;
/*
* Count the number of entries that we'll need */ if( pddd->lpModeInfo != NULL ) { for( i = 0; i < pddd->dwNumModes; i++ ) { iNumModes++; if( pddd->lpModeInfo[i].wFlags & DDMODEINFO_MAXREFRESH ) { for( j = 0; j < NUM_SUPPORTED_REFRESH_RATES; j++ ) { if( SupportedRefreshRates[j] <= pddd->lpModeInfo[i].wRefreshRate ) { iNumModes++; } } } }
if( iNumModes > pddd->dwNumModes ) { /*
* We do have to add modes and allocate a new table */ pNewModeTable = (LPDDHALMODEINFO) MemAlloc( sizeof( DDHALMODEINFO ) * iNumModes ); if( pNewModeTable == NULL ) { /*
* Instead of failing here, we'll just clear all of the MAXREFRESHRATE * flags and set the rate to 0. */ for( i = 0; i < pddd->dwNumModes; i++ ) { if( pddd->lpModeInfo[i].wFlags & DDMODEINFO_MAXREFRESH ) { pddd->lpModeInfo[i].wFlags &= ~DDMODEINFO_MAXREFRESH; pddd->lpModeInfo[i].wRefreshRate = 0; } } } else { memcpy( pNewModeTable, pddd->lpModeInfo, pddd->dwNumModes * sizeof( DDHALMODEINFO ) );
/*
* Now add the new refresh rates */ iModeIndex = pddd->dwNumModes; for( i = 0; i < pddd->dwNumModes; i++ ) { if( pddd->lpModeInfo[i].wFlags & DDMODEINFO_MAXREFRESH ) { pNewModeTable[i].wFlags &= ~DDMODEINFO_MAXREFRESH; wMaxRefresh = pNewModeTable[i].wRefreshRate; pNewModeTable[i].wRefreshRate = 0;
for( j = 0; j < NUM_SUPPORTED_REFRESH_RATES; j++ ) { if( SupportedRefreshRates[j] <= wMaxRefresh ) { memcpy( &(pNewModeTable[iModeIndex]), &(pNewModeTable[i]), sizeof( DDHALMODEINFO ) ); pNewModeTable[iModeIndex].wFlags |= DDMODEINFO_DX7ONLY; pNewModeTable[iModeIndex++].wRefreshRate = SupportedRefreshRates[j]; } } } }
MemFree( pddd->lpModeInfo ); pddd->lpModeInfo = pNewModeTable; pddd->dwNumModes = iModeIndex; } } }
return DD_OK; }
/*
* CanMonitorHandleRefreshRate * * Has the specified refresh rate been tested and verified that it works? */ BOOL CanMonitorHandleRefreshRate( LPDDRAWI_DIRECTDRAW_GBL pddd, DWORD dwWidth, DWORD dwHeight, int wRefresh ) { if( wRefresh == 0 ) { return TRUE; }
if( pddd->lpMonitorInfo == NULL ) { return FALSE; }
/*
* If we are setting this mode because we are testing it, then we should * allow it so the user can verify whether it worked or not. */ if( pddd->dwFlags & DDRAWI_TESTINGMODES ) { return TRUE; }
if( ( dwWidth <= 640 ) && ( dwHeight <= 480 ) ) { if( pddd->lpMonitorInfo->Mode640x480 >= wRefresh ) { return TRUE; } }
if( ( dwWidth <= 800 ) && ( dwHeight <= 600 ) ) { if( pddd->lpMonitorInfo->Mode800x600 >= wRefresh ) { return TRUE; } }
if( ( dwWidth <= 1024 ) && ( dwHeight <= 768 ) ) { if( pddd->lpMonitorInfo->Mode1024x768 >= wRefresh ) { return TRUE; } }
if( ( dwWidth <= 1280 ) && ( dwHeight <= 1024 ) ) { if( pddd->lpMonitorInfo->Mode1280x1024 >= wRefresh ) { return TRUE; } }
if( ( dwWidth <= 1600 ) && ( dwHeight <= 1200 ) ) { if( pddd->lpMonitorInfo->Mode1600x1200 >= wRefresh ) { return TRUE; } }
return FALSE; }
/*
* IsModeTested * * Determines if we already have data for the requested mode. */ BOOL IsModeTested( LPDDRAWI_DIRECTDRAW_GBL pddd, DWORD dwWidth, DWORD dwHeight ) { if( pddd->lpMonitorInfo == NULL ) { return FALSE; }
if( ( dwWidth <= 640 ) && ( dwHeight <= 480 ) ) { if( pddd->lpMonitorInfo->Mode640x480 != -1 ) { return TRUE; } }
else if( ( dwWidth <= 800 ) && ( dwHeight <= 600 ) ) { if( pddd->lpMonitorInfo->Mode800x600 != -1 ) { return TRUE; } }
else if( ( dwWidth <= 1024 ) && ( dwHeight <= 768 ) ) { if( pddd->lpMonitorInfo->Mode1024x768 != -1 ) { return TRUE; } }
else if( ( dwWidth <= 1280 ) && ( dwHeight <= 1024 ) ) { if( pddd->lpMonitorInfo->Mode1280x1024 != -1 ) { return TRUE; } }
else if( ( dwWidth <= 1600 ) && ( dwHeight <= 1200 ) ) { if( pddd->lpMonitorInfo->Mode1600x1200 != -1 ) { return TRUE; } }
return FALSE; }
/*
* UpdateMonitorInfo */ void UpdateMonitorInfo( LPDDRAWI_DIRECTDRAW_GBL pddd, DWORD dwWidth, DWORD dwHeight, int iRefreshRate ) { if( pddd->lpMonitorInfo == NULL ) { return; }
if( ( dwWidth <= 640 ) && ( dwHeight <= 480 ) ) { pddd->lpMonitorInfo->Mode640x480 = iRefreshRate; }
else if( ( dwWidth <= 800 ) && ( dwHeight <= 600 ) ) { pddd->lpMonitorInfo->Mode800x600 = iRefreshRate; }
else if( ( dwWidth <= 1024 ) && ( dwHeight <= 768 ) ) { pddd->lpMonitorInfo->Mode1024x768 = iRefreshRate; }
else if( ( dwWidth <= 1280 ) && ( dwHeight <= 1024 ) ) { pddd->lpMonitorInfo->Mode1280x1024 = iRefreshRate; }
else if( ( dwWidth <= 1600 ) && ( dwHeight <= 1200 ) ) { pddd->lpMonitorInfo->Mode1600x1200 = iRefreshRate; } }
/*
* GetModeToTest */ HRESULT GetModeToTest( DWORD dwInWidth, DWORD dwInHeight, LPDWORD lpdwOutWidth, LPDWORD lpdwOutHeight ) { if( ( dwInWidth <= 640 ) && ( dwInHeight <= 480 ) ) { *lpdwOutWidth = 640; *lpdwOutHeight = 480; }
else if( ( dwInWidth <= 800 ) && ( dwInHeight <= 600 ) ) { *lpdwOutWidth = 800; *lpdwOutHeight = 600; }
else if( ( dwInWidth <= 1024 ) && ( dwInHeight <= 768 ) ) { *lpdwOutWidth = 1024; *lpdwOutHeight = 768; }
else if( ( dwInWidth <= 1280 ) && ( dwInHeight <= 1024 ) ) { *lpdwOutWidth = 1280; *lpdwOutHeight = 1024; }
else if( ( dwInWidth <= 1600 ) && ( dwInHeight <= 1200 ) ) { *lpdwOutWidth = 1600; *lpdwOutHeight = 1200; } else { return DDERR_GENERIC; }
return DD_OK; }
/*
* GuestimateRefreshRate */ int GuestimateRefreshRate( LPDDRAWI_DIRECTDRAW_GBL pddd, DWORD dwWidth, DWORD dwHeight, DWORD dwHighestRefresh, DWORD dwHighestBandwidth ) { int i; DWORD dwBandwidth;
if( ( pddd->lpMonitorInfo == NULL ) || ( dwHighestRefresh == 0 ) ) { return 0; }
// Sanity check to see if the monitor can handle the resolution
if( !MonitorCanHandleMode( pddd, dwWidth, dwHeight, 0 ) ) { return 0; }
// If the monitor did not return any refresh rates higher than 60,
// something is up so we'd better stick to it.
if( dwHighestRefresh == 60 ) { return 60; }
// Likwise, we will only go after the 100+ refresh rates if the monitor
// enumerated a refresh rate of at least 85hz. This may be an unnecesary
// restiction, but it seems safe.
for( i = NUM_SUPPORTED_REFRESH_RATES - 1; i >= 0; i-- ) { if( ( SupportedRefreshRates[i] <= 85 ) || ( dwHighestRefresh >= 85 ) ) { dwBandwidth = dwWidth * dwHeight * SupportedRefreshRates[i]; if( dwBandwidth <= dwHighestBandwidth ) { return SupportedRefreshRates[i]; } } }
return 0; }
/*
* SetTheMode */ HRESULT SetTheMode( LPDIRECTDRAW7 lpDD, LPMODETESTCONTEXT pContext ) { HRESULT hr; DWORD dwBPP;
/*
* We set an internal flag indicating that we are running a mode test. * This lets CanMonitorHandleRefreshRate know that the requested mode * should be used, even though it hasb't successfully been tested yet. */ ((LPDDRAWI_DIRECTDRAW_INT)lpDD)->lpLcl->lpGbl->dwFlags |= DDRAWI_TESTINGMODES;
//
// There might be a case in which we decided that the lowest BPP the driver
// could do for this mode was, say, 8bpp. But that didn't take into consideration
// what rates the driver said it could do. E.g. if the driver (for whatever
// reason) doesn't advertise 8bpp at 60Hz, but DOES advertise 16bpp at 60Hz
// then we can go ahead and use the 16bpp mode. We are here to test monitor
// bandwidth, not the DAC. The monitor's bandwidth is entirely determined
// by the spatial resolution and refresh rate. If we bump the driver to
// a higher BPP (one that it says it can do), then we are still testing
// the correct monitor setup.
// We do this in a really dumb way: just keep cranking up from the lowest BPP
// (which is in the context mode list) in steps of 8 until we succeed the
// mode change, or exceed 32bpp.
//
dwBPP = pContext->lpModeList[pContext->dwCurrentMode].dwBPP; do { hr = DD_SetDisplayMode2( (LPDIRECTDRAW)lpDD, pContext->lpModeList[pContext->dwCurrentMode].dwWidth, pContext->lpModeList[pContext->dwCurrentMode].dwHeight, dwBPP, pContext->lpModeList[pContext->dwCurrentMode].dwRefreshRate, DDSDM_STANDARDVGAMODE );
dwBPP += 8; } while(FAILED(hr) && (dwBPP <= 32) );
((LPDDRAWI_DIRECTDRAW_INT)lpDD)->lpLcl->lpGbl->dwFlags &= ~DDRAWI_TESTINGMODES;
return hr; }
/*
* SetupNextTest */ void SetupNextTest( LPDIRECTDRAW7 lpDD, LPMODETESTCONTEXT pContext ) { int i; /*
* Drop back to the next refrsh rate if there is one, otherwise * move on to the next mode. */ for( i = NUM_SUPPORTED_REFRESH_RATES - 1; i >= 0; i-- ) { if( SupportedRefreshRates[i] < pContext->lpModeList[pContext->dwCurrentMode].dwRefreshRate ) { pContext->lpModeList[pContext->dwCurrentMode].dwRefreshRate = SupportedRefreshRates[i]; break; } } if( i < 0 ) { // We've tried everything in this mode, so move on
UpdateMonitorInfo( ((LPDDRAWI_DIRECTDRAW_INT)lpDD)->lpLcl->lpGbl, pContext->lpModeList[pContext->dwCurrentMode].dwWidth, pContext->lpModeList[pContext->dwCurrentMode].dwHeight, 0 );
pContext->dwCurrentMode++; } }
/*
* RunNextTest */ HRESULT RunNextTest( LPDIRECTDRAW7 lpDD, LPMODETESTCONTEXT pContext ) { HRESULT hr; LPDDRAWI_DIRECTDRAW_GBL pddd;
do { if( pContext->dwCurrentMode >= pContext->dwNumModes ) { // Restore the mode if we've changed it
pddd = ((LPDDRAWI_DIRECTDRAW_INT)lpDD)->lpLcl->lpGbl; if( pContext->dwOrigModeIndex != pddd->dwModeIndex ) { DD_SetDisplayMode2( (LPDIRECTDRAW)lpDD, pddd->lpModeInfo[pContext->dwOrigModeIndex].dwWidth, pddd->lpModeInfo[pContext->dwOrigModeIndex].dwHeight, pddd->lpModeInfo[pContext->dwOrigModeIndex].dwBPP, pddd->lpModeInfo[pContext->dwOrigModeIndex].wRefreshRate, 0 ); }
DDSaveMonitorInfo( (LPDDRAWI_DIRECTDRAW_INT)lpDD );
MemFree( pContext->lpModeList ); MemFree( pContext ); ((LPDDRAWI_DIRECTDRAW_INT)lpDD)->lpLcl->lpModeTestContext = NULL;
return DDERR_TESTFINISHED; } hr = SetTheMode( lpDD, pContext ); if( hr != DD_OK ) { SetupNextTest( lpDD, pContext ); } } while( ( hr != DD_OK ) && (hr != DDERR_TESTFINISHED ) );
if( hr != DDERR_TESTFINISHED ) { pContext->dwTimeStamp = GetTickCount(); }
return hr; }
#endif
/*
* DD_StartModeTest * * Indicates that the app wants to start testing a mode (or modes). */ HRESULT DDAPI DD_StartModeTest( LPDIRECTDRAW7 lpDD, LPSIZE lpModesToTest, DWORD dwNumEntries, DWORD dwFlags ) { #ifdef WIN95
LPDDRAWI_DIRECTDRAW_INT this_int; LPDDRAWI_DIRECTDRAW_LCL this_lcl; LPDDRAWI_DIRECTDRAW_GBL this; BOOL excl_exists; BOOL is_excl; LPMODETESTCONTEXT pContext; DWORD i; DWORD j; HRESULT hr; DWORD dwRefreshRate; DWORD dwModeWidth; DWORD dwModeHeight; VESA_EDID EDIDData; DWORD dwHighestRefresh; DWORD dwHighestBandwidth;
ENTER_DDRAW(); #endif
DPF(2,A,"ENTERAPI: DD_StartModeTest");
#ifdef WINNT
return DDERR_TESTFINISHED;
#else
TRY { this_int = (LPDDRAWI_DIRECTDRAW_INT) lpDD; if( !VALID_DIRECTDRAW_PTR( this_int ) ) { LEAVE_DDRAW(); return DDERR_INVALIDOBJECT; } this_lcl = this_int->lpLcl; this = this_lcl->lpGbl;
if( this->lpMonitorInfo == NULL ) { // There is no monitor info
LEAVE_DDRAW(); return DDERR_NOMONITORINFORMATION; }
if( this_lcl->lpModeTestContext != NULL ) { DPF_ERR( "Mode test already running" ); LEAVE_DDRAW(); return DDERR_INVALIDPARAMS; }
if( dwFlags & ~DDSMT_VALID ) { DPF_ERR( "Invalid Flags specified" ); LEAVE_DDRAW(); return DDERR_INVALIDPARAMS; }
if( ( dwFlags & DDSMT_ISTESTREQUIRED ) && ( lpModesToTest == NULL ) ) { DPF_ERR( "No modes specified to test" ); LEAVE_DDRAW(); return DDERR_INVALIDPARAMS; }
CheckExclusiveMode( this_lcl, &excl_exists, &is_excl, FALSE, NULL, FALSE); if( lpModesToTest != NULL ) { if ( ( dwNumEntries == 0 ) || ( !VALID_BYTE_ARRAY( lpModesToTest, sizeof( SIZE ) * dwNumEntries ) ) ) { DPF_ERR( "Invalid mode list specified" ); LEAVE_DDRAW(); return DDERR_INVALIDPARAMS; } /*
* The app must own exclusive mode to test the modes */ if ( !is_excl || !(this->dwFlags & DDRAWI_FULLSCREEN) ) { DPF_ERR( "Must be in full-screen exclusive mode to test the modes" ); LEAVE_DDRAW(); return DDERR_NOEXCLUSIVEMODE; } } else { /*
* There must not be another app running which owns exclusive mode */ if( !excl_exists || is_excl ) { this->lpMonitorInfo->Mode640x480 = -1; this->lpMonitorInfo->Mode800x600 = -1; this->lpMonitorInfo->Mode1024x768 = -1; this->lpMonitorInfo->Mode1280x1024 = -1; this->lpMonitorInfo->Mode1600x1200 = -1; this->lpMonitorInfo->ModeReserved1 = -1; this->lpMonitorInfo->ModeReserved2 = -1; this->lpMonitorInfo->ModeReserved3 = -1; hr = DDSaveMonitorInfo( this_int ); LEAVE_DDRAW(); return hr; } else { DPF_ERR( "Cannot reset monitor info; another app owns exclusive mode" ); LEAVE_DDRAW(); return DDERR_NOEXCLUSIVEMODE; }
} } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { DPF_ERR( "Exception encountered validating parameters" ); LEAVE_DDRAW(); return DDERR_INVALIDPARAMS; }
/*
* Get and evaluate the EDID data */ hr = GetEDIDData( this, &EDIDData ); if( hr != DD_OK ) { // There is no EDID data
LEAVE_DDRAW(); return DDERR_NOMONITORINFORMATION; } EvaluateMonitor( &EDIDData, &dwHighestRefresh, &dwHighestBandwidth );
for( i = 0; i < this->dwNumModes; i++ ) { if( this->lpModeInfo[i].wRefreshRate > 0 ) { break; } } if( i == this->dwNumModes ) { // The driver does not enumerate display mode refresh rates.
LEAVE_DDRAW(); return DDERR_NODRIVERSUPPORT; } /*
* Allocate a context for ourselves */ pContext = (LPMODETESTCONTEXT) MemAlloc( sizeof( MODETESTCONTEXT ) ); if( pContext == NULL ) { DPF_ERR( "Insufficient memory" ); LEAVE_DDRAW(); return DDERR_OUTOFMEMORY; } pContext->dwNumModes = 0; pContext->dwCurrentMode = 0; pContext->lpModeList = (LPMODETESTDATA) MemAlloc( sizeof( MODETESTDATA ) * dwNumEntries ); if( pContext->lpModeList == NULL ) { MemFree( pContext ); LEAVE_DDRAW(); return DDERR_OUTOFMEMORY; } this_lcl->lpModeTestContext = pContext; /*
* Guestimate which refresh rates we should try for each mode in the list * based on the EDID data */ for( i = 0; i < dwNumEntries; i++ ) { DWORD dwLowestBPP = 0xFFFFFFFF; /*
* Verify that the driver understands the resolution */ for( j = 0; j < this->dwNumModes; j++ ) { if( ( this->lpModeInfo[j].dwHeight == (DWORD) lpModesToTest[i].cy ) && ( this->lpModeInfo[j].dwWidth == (DWORD) lpModesToTest[i].cx ) ) { if( this->lpModeInfo[j].dwBPP < dwLowestBPP ) { dwLowestBPP = this->lpModeInfo[j].dwBPP; } } } if( dwLowestBPP == 0xFFFFFFFF ) { /*
* The driver doesn't undestand this mode, so the app is dumb * for not enumerating the modes first. */ MemFree( pContext->lpModeList ); MemFree( pContext ); this_lcl->lpModeTestContext = NULL; DPF_ERR( "Invalid mode specified in mode list" ); LEAVE_DDRAW(); return DDERR_INVALIDPARAMS; }
/*
* Get the actual mode to test. For example, the app may want * to test 320x240, 640x400, and 640x480, but we treat all of these * modes the same so we only have to do a single test. */ hr = GetModeToTest( lpModesToTest[i].cx, lpModesToTest[i].cy, &dwModeWidth, &dwModeHeight ); if( hr != DD_OK ) { // They are testing a mode higher the 1600x1200
continue; } for( j = 0; j < pContext->dwNumModes; j++ ) { if( ( pContext->lpModeList[j].dwWidth == dwModeWidth ) && ( pContext->lpModeList[j].dwHeight == dwModeHeight ) ) { break; } } if( j < pContext->dwNumModes ) { // Duplicate mode
continue; }
if( !IsModeTested( this, dwModeWidth, dwModeHeight ) ) { dwRefreshRate = GuestimateRefreshRate( this, dwModeWidth, dwModeHeight, dwHighestRefresh, dwHighestBandwidth ); pContext->lpModeList[pContext->dwNumModes].dwWidth = dwModeWidth; pContext->lpModeList[pContext->dwNumModes].dwHeight = dwModeHeight; pContext->lpModeList[pContext->dwNumModes].dwBPP = dwLowestBPP; pContext->lpModeList[pContext->dwNumModes].dwRefreshRate = dwRefreshRate; pContext->dwNumModes++; } }
/*
* After all of that, do we still have any modes that need testing? * If not, we can stop now */ if( dwFlags & DDSMT_ISTESTREQUIRED ) { hr = ( pContext->dwNumModes > 0 ) ? DDERR_NEWMODE : DDERR_TESTFINISHED; MemFree( pContext->lpModeList ); MemFree( pContext ); this_lcl->lpModeTestContext = NULL; } else { pContext->dwOrigModeIndex = this->dwModeIndex; hr = RunNextTest( lpDD, pContext ); }
LEAVE_DDRAW(); return hr; #endif
}
/*
* DD_EvaluateMode * * Called at high frequency while the mode test is being performed. If the user has indicated * that a mode succeeded or failed, we move on to the next moe in the test; otherwise, we will * simply check the 15 second timeout value and fail the mode when we hit it.. */ HRESULT DDAPI DD_EvaluateMode( LPDIRECTDRAW7 lpDD, DWORD dwFlags, DWORD *pSecondsUntilTimeout) { #ifdef WIN95
LPDDRAWI_DIRECTDRAW_INT this_int; LPDDRAWI_DIRECTDRAW_LCL this_lcl; LPDDRAWI_DIRECTDRAW_GBL this; BOOL excl_exists; BOOL is_excl; LPMODETESTCONTEXT pContext; DWORD i; DWORD j; HRESULT hr = DD_OK; DWORD dwTick;
ENTER_DDRAW(); #endif
DPF(2,A,"ENTERAPI: DD_EvaluateMode");
#ifdef WINNT
return DDERR_INVALIDPARAMS;
#else
TRY { this_int = (LPDDRAWI_DIRECTDRAW_INT) lpDD; if( !VALID_DIRECTDRAW_PTR( this_int ) ) { LEAVE_DDRAW(); return DDERR_INVALIDOBJECT; } this_lcl = this_int->lpLcl; this = this_lcl->lpGbl;
if( this->lpMonitorInfo == NULL ) { // There is no monitor info so we should not be here
LEAVE_DDRAW(); return DDERR_NOMONITORINFORMATION; } pContext = this_lcl->lpModeTestContext; if( NULL == pContext ) { DPF_ERR( "Must call StartModeTest before EvaulateMode" ); LEAVE_DDRAW(); return DDERR_INVALIDPARAMS; }
if( ( NULL != pSecondsUntilTimeout ) && !VALID_BYTE_ARRAY( pSecondsUntilTimeout, sizeof( DWORD ) ) ) { DPF_ERR( "Invalid pointer to timeout counter" ); LEAVE_DDRAW(); return DDERR_INVALIDPARAMS; }
if( dwFlags & ~DDEM_VALID ) { DPF_ERR( "Invalid Flags specified" ); LEAVE_DDRAW(); return DDERR_INVALIDPARAMS; } if( ( dwFlags & DDEM_MODEPASSED ) && ( dwFlags & DDEM_MODEFAILED ) ) { DPF_ERR( "Invalid Flags specified" ); LEAVE_DDRAW(); return DDERR_INVALIDPARAMS; }
/*
* If we lost exclusive mode, we should stop the test now */ CheckExclusiveMode( this_lcl, &excl_exists, &is_excl, FALSE, NULL, FALSE); if (!is_excl || !(this->dwFlags & DDRAWI_FULLSCREEN) ) { DPF_ERR( "Exclusive mode lost" ); MemFree( pContext->lpModeList ); MemFree( pContext ); this_lcl->lpModeTestContext = NULL; LEAVE_DDRAW(); return DDERR_TESTFINISHED; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { DPF_ERR( "Exception encountered validating parameters" ); LEAVE_DDRAW(); return DDERR_INVALIDPARAMS; }
if( dwFlags & DDEM_MODEPASSED ) { // The current data is good, so save it
UpdateMonitorInfo( this, pContext->lpModeList[pContext->dwCurrentMode].dwWidth, pContext->lpModeList[pContext->dwCurrentMode].dwHeight, pContext->lpModeList[pContext->dwCurrentMode].dwRefreshRate ); // Move on to the next test, if there is one
pContext->dwCurrentMode++; hr = RunNextTest( lpDD, pContext ); if( hr == DD_OK ) { hr = DDERR_NEWMODE; } } else { // Has our timeout expired?
dwTick = GetTickCount(); if( dwTick - pContext->dwTimeStamp > 15000 ) { dwFlags |= DDEM_MODEFAILED; }
if( dwFlags & DDEM_MODEFAILED ) { // Drop down to the next refresh rate or the next mode
SetupNextTest( lpDD, pContext ); hr = RunNextTest( lpDD, pContext ); if( hr == DD_OK ) { hr = DDERR_NEWMODE; }
dwTick = GetTickCount(); } }
if( pSecondsUntilTimeout != NULL ) { if( hr == DDERR_TESTFINISHED ) { *pSecondsUntilTimeout = 0; } else { *pSecondsUntilTimeout = 15 - ( ( dwTick - pContext->dwTimeStamp) / 1000 ); } }
LEAVE_DDRAW(); return hr; #endif
}
//
// This function is designed to allow DX7 apps to see some modes that would otherwise be
// incorrectly masked by the mode enumeration thing.
//
// If a driver exposes a list of modes with full-on refresh rates in EVERY entry in the mode table,
// then we will enum exactly NONE of them to the app, since any rate with a rate cannot be enumerated
// until the mode test has run. Apps that don't run the mode test will see NO modes at all.
//
// The s3 savage 4 is a case in point: it fills in the refresh rate for the current display mode,
// (and no other mode) and doesn't dupe the entry to one with a zero refresh rate.
//
// What we do is: every time we find an instance of a mode (size, bitdepth, masks) that has no
// zero-rate entry, we append a zero-rate entry to the end of the mode list.
//
//No need to massage on winNT
#ifdef WIN95
void MassageModeTable(LPDDRAWI_DIRECTDRAW_GBL pddd) { DWORD iMode, iCheckZero; if( pddd->lpModeInfo != NULL ) { RestartLoop: for( iMode = 0; iMode < pddd->dwNumModes; iMode++ ) { if (pddd->lpModeInfo[iMode].wRefreshRate != 0) { // Found a mode with non-zero rate. Check to see if the mode is also represented
// by a zero-rate entry. If it is not, then append such an entry
for( iCheckZero = 0; iCheckZero < pddd->dwNumModes; iCheckZero++ ) { if( (pddd->lpModeInfo[iCheckZero].dwWidth == pddd->lpModeInfo[iMode].dwWidth) && (pddd->lpModeInfo[iCheckZero].dwHeight == pddd->lpModeInfo[iMode].dwHeight) && (pddd->lpModeInfo[iCheckZero].dwBPP == pddd->lpModeInfo[iMode].dwBPP) && (pddd->lpModeInfo[iCheckZero].dwRBitMask == pddd->lpModeInfo[iMode].dwRBitMask) && (pddd->lpModeInfo[iCheckZero].dwGBitMask == pddd->lpModeInfo[iMode].dwGBitMask) && (pddd->lpModeInfo[iCheckZero].dwBBitMask == pddd->lpModeInfo[iMode].dwBBitMask)) { // found a matching mode, in terms of size and depth.
// If the refresh rate is zero, then we can break out and go on to the next iMode
if (pddd->lpModeInfo[iCheckZero].wRefreshRate == 0) { goto NextMode; } } } // If we got here, then there was no entry in the mode list for this size+depth
// that had a zero refresh rate. Append one now.
// Note how expanding the mode list like this means that if the driver (as it typically
// will) offers several rates for a given mode, we'll expand the table on the first
// hit of that mode, but then the expanded table will satisfy us for every subsequent
// rate of that mode (i.e. now there WILL be a zero-rated entry for that mode (since
// we just added it)).
{ LPDDHALMODEINFO pmi;
pmi = (LPDDHALMODEINFO) MemAlloc(sizeof(*pmi) * (pddd->dwNumModes+1)); if (pmi == NULL) { //oh just give up....
return; }
memcpy(pmi, pddd->lpModeInfo, sizeof(*pmi)*pddd->dwNumModes ); MemFree(pddd->lpModeInfo); pddd->lpModeInfo = pmi;
// Now put the zero-rated mode in there
memcpy( &pddd->lpModeInfo[pddd->dwNumModes], &pddd->lpModeInfo[iMode], sizeof(*pmi)); pddd->lpModeInfo[pddd->dwNumModes].wRefreshRate = 0; pddd->lpModeInfo[pddd->dwNumModes].wFlags |= DDMODEINFO_DX7ONLY;
pddd->dwNumModes++;
//Now we have to restart the whole loop because we changed the lpModeInfo pointer:
goto RestartLoop; } } NextMode:; } } } #endif //WIN95
|