You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1737 lines
54 KiB
1737 lines
54 KiB
/*==========================================================================
|
|
*
|
|
* 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
|