Windows2003-3790/windows/appcompat/shims/layer/hidedisplaymodes.cpp
2020-09-30 16:53:55 +02:00

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