414 lines
9.1 KiB
C++
414 lines
9.1 KiB
C++
/*++
|
|
|
|
Copyright (c) 2000-2002 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
RestrictDisplayModes.cpp
|
|
|
|
Abstract:
|
|
|
|
Restrict the mode list enumerated by EnumDisplaySettings. This shim was
|
|
built for an application that enumerated only 10 modes and was hoping to
|
|
find 800x600 in that list. However, other applications that have fixed
|
|
size buffers for mode tables may also find this shim useful.
|
|
|
|
Notes:
|
|
|
|
This is a general purpose shim.
|
|
|
|
History:
|
|
|
|
05/05/2000 linstev Created
|
|
02/18/2002 robkenny Protected g_pModeTable with a critical section
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#include "CharVector.h"
|
|
|
|
IMPLEMENT_SHIM_BEGIN(HideDisplayModes)
|
|
#include "ShimHookMacro.h"
|
|
|
|
|
|
APIHOOK_ENUM_BEGIN
|
|
APIHOOK_ENUM_ENTRY(EnumDisplaySettingsA)
|
|
APIHOOK_ENUM_ENTRY(EnumDisplaySettingsW)
|
|
APIHOOK_ENUM_END
|
|
|
|
//
|
|
// Data needed in mode table
|
|
//
|
|
|
|
typedef struct _MODE
|
|
{
|
|
DWORD dmBitsPerPel;
|
|
DWORD dmPelsWidth;
|
|
DWORD dmPelsHeight;
|
|
DWORD dmDisplayFlags;
|
|
DWORD dmDisplayFrequency;
|
|
DWORD dwActualIndex;
|
|
DWORD bIgnore;
|
|
} MODE;
|
|
|
|
// Used by BuildModeList to prevent multiple simultaneous access to g_pModeTable
|
|
CRITICAL_SECTION g_CriticalSection;
|
|
|
|
// Permanent mode table
|
|
MODE* g_pModeTable = NULL;
|
|
|
|
// Number of entries in the mode table
|
|
DWORD g_dwCount = 0;
|
|
|
|
// Build the mode table on first call
|
|
BOOL g_bInit = FALSE;
|
|
void BuildModeList(void);
|
|
|
|
/*++
|
|
|
|
Lookup from the sanitized mode table.
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
APIHOOK(EnumDisplaySettingsA)(
|
|
LPCSTR lpszDeviceName,
|
|
DWORD iModeNum,
|
|
LPDEVMODEA lpDevMode
|
|
)
|
|
{
|
|
BuildModeList();
|
|
|
|
BOOL bRet = FALSE;
|
|
|
|
if (lpszDeviceName || ((LONG)iModeNum < 0) || !g_pModeTable) {
|
|
bRet = ORIGINAL_API(EnumDisplaySettingsA)(
|
|
lpszDeviceName,
|
|
iModeNum,
|
|
lpDevMode);
|
|
} else if (iModeNum < g_dwCount) {
|
|
MODE* pmode = g_pModeTable + iModeNum;
|
|
|
|
bRet = ORIGINAL_API(EnumDisplaySettingsA)(
|
|
lpszDeviceName,
|
|
pmode->dwActualIndex,
|
|
lpDevMode);
|
|
|
|
if (bRet) {
|
|
LOGN(
|
|
eDbgLevelError,
|
|
"[EnumDisplaySettingsA] Returning shorter list of display modes.");
|
|
|
|
lpDevMode->dmBitsPerPel = pmode->dmBitsPerPel;
|
|
lpDevMode->dmPelsWidth = pmode->dmPelsWidth;
|
|
lpDevMode->dmPelsHeight = pmode->dmPelsHeight;
|
|
lpDevMode->dmDisplayFlags = pmode->dmDisplayFlags;
|
|
lpDevMode->dmDisplayFrequency = pmode->dmDisplayFrequency;
|
|
}
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Lookup from the sanitized mode table.
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
APIHOOK(EnumDisplaySettingsW)(
|
|
LPCWSTR lpszDeviceName,
|
|
DWORD iModeNum,
|
|
LPDEVMODEW lpDevMode
|
|
)
|
|
{
|
|
BuildModeList();
|
|
|
|
BOOL bRet = FALSE;
|
|
|
|
if (lpszDeviceName || ((LONG)iModeNum < 0) || !g_pModeTable) {
|
|
bRet = ORIGINAL_API(EnumDisplaySettingsW)(
|
|
lpszDeviceName,
|
|
iModeNum,
|
|
lpDevMode);
|
|
} else if (iModeNum < g_dwCount) {
|
|
MODE* pmode = g_pModeTable + iModeNum;
|
|
|
|
bRet = ORIGINAL_API(EnumDisplaySettingsW)(
|
|
lpszDeviceName,
|
|
pmode->dwActualIndex,
|
|
lpDevMode);
|
|
|
|
if (bRet) {
|
|
LOGN(
|
|
eDbgLevelError,
|
|
"[EnumDisplaySettingsW] Returning shorter list of display modes.");
|
|
|
|
lpDevMode->dmBitsPerPel = pmode->dmBitsPerPel;
|
|
lpDevMode->dmPelsWidth = pmode->dmPelsWidth;
|
|
lpDevMode->dmPelsHeight = pmode->dmPelsHeight;
|
|
lpDevMode->dmDisplayFlags = pmode->dmDisplayFlags;
|
|
lpDevMode->dmDisplayFrequency = pmode->dmDisplayFrequency;
|
|
}
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Sort the table by Width+Height+BitsPerPel+Frequency in that order so that
|
|
they can be easily filtered.
|
|
|
|
--*/
|
|
|
|
int
|
|
_cdecl
|
|
compare1(
|
|
const void* a1,
|
|
const void* a2
|
|
)
|
|
{
|
|
MODE* arg1 = (MODE*)a1;
|
|
MODE* arg2 = (MODE*)a2;
|
|
|
|
int d;
|
|
|
|
d = arg1->dmPelsWidth - arg2->dmPelsWidth;
|
|
|
|
if (d == 0) {
|
|
d = arg1->dmPelsHeight - arg2->dmPelsHeight;
|
|
}
|
|
|
|
if (d == 0) {
|
|
d = arg1->dmBitsPerPel - arg2->dmBitsPerPel;
|
|
}
|
|
|
|
if (d == 0) {
|
|
d = arg1->dmDisplayFrequency - arg2->dmDisplayFrequency;
|
|
}
|
|
|
|
return d;
|
|
}
|
|
|
|
/*++
|
|
|
|
Sort the table so it looks like a Win9x mode table, i.e. BitsPerPel is the
|
|
primary sort key.
|
|
|
|
--*/
|
|
|
|
int
|
|
_cdecl
|
|
compare2(
|
|
const void* a1,
|
|
const void* a2
|
|
)
|
|
{
|
|
MODE* arg1 = (MODE*)a1;
|
|
MODE* arg2 = (MODE*)a2;
|
|
|
|
int d;
|
|
|
|
d = arg1->dmBitsPerPel - arg2->dmBitsPerPel;
|
|
|
|
if (d == 0) {
|
|
d = arg1->dmPelsWidth - arg2->dmPelsWidth;
|
|
}
|
|
|
|
if (d == 0) {
|
|
d = arg1->dmPelsHeight - arg2->dmPelsHeight;
|
|
}
|
|
|
|
if (d == 0) {
|
|
d = arg1->dmDisplayFrequency - arg2->dmDisplayFrequency;
|
|
}
|
|
|
|
return d;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Create a new mode table based upon the sanitized existing table. To do this,
|
|
we do the following:
|
|
|
|
1. Get the entire table
|
|
2. Sort it - to allow efficient removal of duplicates
|
|
3. Remove duplicates and unwanted modes
|
|
4. Build a new table with only the modes that 'pass'
|
|
|
|
--*/
|
|
|
|
void
|
|
BuildModeList(
|
|
void
|
|
)
|
|
{
|
|
CAutoCrit autoCrit(&g_CriticalSection);
|
|
|
|
if (g_bInit) {
|
|
return;
|
|
}
|
|
|
|
DEVMODEA dm;
|
|
ULONG i, j;
|
|
|
|
dm.dmSize = sizeof(DEVMODEA);
|
|
|
|
//
|
|
// Figure out how many modes there are.
|
|
//
|
|
|
|
i = 0;
|
|
|
|
while (EnumDisplaySettingsA(NULL, i, &dm)) {
|
|
i++;
|
|
}
|
|
|
|
//
|
|
// Allocate the full mode table.
|
|
//
|
|
MODE* pTempTable = (MODE*)malloc(sizeof(MODE) * i);
|
|
|
|
if (!pTempTable) {
|
|
LOGN(
|
|
eDbgLevelError,
|
|
"[BuildModeList] Failed to allocate %d bytes.",
|
|
sizeof(MODE) * i);
|
|
|
|
return;
|
|
}
|
|
|
|
MODE* pmode = pTempTable;
|
|
|
|
//
|
|
// Get all the modes.
|
|
//
|
|
i = 0;
|
|
|
|
while (EnumDisplaySettingsA(NULL, i, &dm)) {
|
|
pmode->dmBitsPerPel = dm.dmBitsPerPel;
|
|
pmode->dmPelsWidth = dm.dmPelsWidth;
|
|
pmode->dmPelsHeight = dm.dmPelsHeight;
|
|
pmode->dmDisplayFlags = dm.dmDisplayFlags;
|
|
pmode->dmDisplayFrequency = 0; // dm.dmDisplayFrequency;
|
|
pmode->dwActualIndex = i;
|
|
pmode->bIgnore = FALSE;
|
|
|
|
pmode++;
|
|
i++;
|
|
}
|
|
|
|
//
|
|
// Sort the full table so we can remove duplicates easily.
|
|
//
|
|
qsort((void*)pTempTable, (size_t)i, sizeof(MODE), compare1);
|
|
|
|
//
|
|
// Strip away bad modes by setting them as ignored.
|
|
//
|
|
pmode = pTempTable;
|
|
|
|
MODE* pprev = NULL;
|
|
|
|
for (j = 0; j < i; j++) {
|
|
if ((pmode->dmBitsPerPel < 8) ||
|
|
(pmode->dmPelsWidth < 640) ||
|
|
(pmode->dmPelsHeight < 480) ||
|
|
(pmode->dmPelsWidth > 1280) ||
|
|
(pprev &&
|
|
(pprev->dmBitsPerPel == pmode->dmBitsPerPel) &&
|
|
(pprev->dmPelsWidth == pmode->dmPelsWidth) &&
|
|
(pprev->dmPelsHeight == pmode->dmPelsHeight))) {
|
|
|
|
//
|
|
// Special-case 640x480x4bit.
|
|
//
|
|
if ((pmode->dmBitsPerPel == 4) &&
|
|
(pmode->dmPelsWidth == 640) &&
|
|
(pmode->dmPelsHeight == 480)) {
|
|
|
|
g_dwCount++;
|
|
} else {
|
|
pmode->bIgnore = TRUE;
|
|
}
|
|
} else {
|
|
g_dwCount++;
|
|
}
|
|
|
|
pprev = pmode;
|
|
pmode++;
|
|
|
|
}
|
|
|
|
//
|
|
// Build the new table with only the modes that passed.
|
|
//
|
|
g_pModeTable = (MODE*)malloc(sizeof(MODE) * g_dwCount);
|
|
|
|
if (!g_pModeTable) {
|
|
LOGN(
|
|
eDbgLevelError,
|
|
"[BuildModeList] Failed to allocate %d bytes.",
|
|
sizeof(MODE) * g_dwCount);
|
|
|
|
free(pTempTable);
|
|
return;
|
|
}
|
|
|
|
MODE* pmoden = g_pModeTable;
|
|
|
|
pmode = pTempTable;
|
|
|
|
for (j = 0; j < i; j++) {
|
|
if (!pmode->bIgnore) {
|
|
MoveMemory(pmoden, pmode, sizeof(MODE));
|
|
pmoden++;
|
|
}
|
|
pmode++;
|
|
}
|
|
|
|
//
|
|
// Sort the full table so we can remove duplicates easily.
|
|
//
|
|
qsort((void*)g_pModeTable, (size_t)g_dwCount, sizeof(MODE), compare2);
|
|
|
|
free(pTempTable);
|
|
|
|
g_bInit = TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
NOTIFY_FUNCTION(
|
|
DWORD fdwReason)
|
|
{
|
|
if (fdwReason == DLL_PROCESS_ATTACH)
|
|
{
|
|
return InitializeCriticalSectionAndSpinCount(&g_CriticalSection, 0x80000000);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*++
|
|
|
|
Register hooked functions
|
|
|
|
--*/
|
|
|
|
HOOK_BEGIN
|
|
|
|
|
|
CALL_NOTIFY_FUNCTION
|
|
|
|
APIHOOK_ENTRY(USER32.DLL, EnumDisplaySettingsA)
|
|
APIHOOK_ENTRY(USER32.DLL, EnumDisplaySettingsW)
|
|
|
|
HOOK_END
|
|
|
|
|
|
IMPLEMENT_SHIM_END
|
|
|