Source code of Windows XP (NT5)
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.

390 lines
8.2 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. RestrictDisplayModes.cpp
  5. Abstract:
  6. Restrict the mode list enumerated by EnumDisplaySettings. This shim was
  7. built for an application that enumerated only 10 modes and was hoping to
  8. find 800x600 in that list. However, other applications that have fixed
  9. size buffers for mode tables may also find this shim useful.
  10. Notes:
  11. This is a general purpose shim.
  12. History:
  13. 05/05/2000 linstev Created
  14. --*/
  15. #include "precomp.h"
  16. IMPLEMENT_SHIM_BEGIN(HideDisplayModes)
  17. #include "ShimHookMacro.h"
  18. APIHOOK_ENUM_BEGIN
  19. APIHOOK_ENUM_ENTRY(EnumDisplaySettingsA)
  20. APIHOOK_ENUM_ENTRY(EnumDisplaySettingsW)
  21. APIHOOK_ENUM_END
  22. //
  23. // Data needed in mode table
  24. //
  25. typedef struct _MODE
  26. {
  27. DWORD dmBitsPerPel;
  28. DWORD dmPelsWidth;
  29. DWORD dmPelsHeight;
  30. DWORD dmDisplayFlags;
  31. DWORD dmDisplayFrequency;
  32. DWORD dwActualIndex;
  33. DWORD bIgnore;
  34. } MODE;
  35. // Permanent mode table
  36. MODE* g_pModeTable = NULL;
  37. // Number of entries in the mode table
  38. DWORD g_dwCount = 0;
  39. // Build the mode table on first call
  40. BOOL g_bInit = FALSE;
  41. void BuildModeList(void);
  42. /*++
  43. Lookup from the sanitized mode table.
  44. --*/
  45. BOOL
  46. APIHOOK(EnumDisplaySettingsA)(
  47. LPCSTR lpszDeviceName,
  48. DWORD iModeNum,
  49. LPDEVMODEA lpDevMode
  50. )
  51. {
  52. BuildModeList();
  53. BOOL bRet = FALSE;
  54. if (lpszDeviceName || ((LONG)iModeNum < 0) || !g_pModeTable) {
  55. bRet = ORIGINAL_API(EnumDisplaySettingsA)(
  56. lpszDeviceName,
  57. iModeNum,
  58. lpDevMode);
  59. } else if (iModeNum < g_dwCount) {
  60. MODE* pmode = g_pModeTable + iModeNum;
  61. bRet = ORIGINAL_API(EnumDisplaySettingsA)(
  62. lpszDeviceName,
  63. pmode->dwActualIndex,
  64. lpDevMode);
  65. if (bRet) {
  66. LOGN(
  67. eDbgLevelError,
  68. "[EnumDisplaySettingsA] Returning shorter list of display modes.");
  69. lpDevMode->dmBitsPerPel = pmode->dmBitsPerPel;
  70. lpDevMode->dmPelsWidth = pmode->dmPelsWidth;
  71. lpDevMode->dmPelsHeight = pmode->dmPelsHeight;
  72. lpDevMode->dmDisplayFlags = pmode->dmDisplayFlags;
  73. lpDevMode->dmDisplayFrequency = pmode->dmDisplayFrequency;
  74. }
  75. }
  76. return bRet;
  77. }
  78. /*++
  79. Lookup from the sanitized mode table.
  80. --*/
  81. BOOL
  82. APIHOOK(EnumDisplaySettingsW)(
  83. LPCWSTR lpszDeviceName,
  84. DWORD iModeNum,
  85. LPDEVMODEW lpDevMode
  86. )
  87. {
  88. BuildModeList();
  89. BOOL bRet = FALSE;
  90. if (lpszDeviceName || ((LONG)iModeNum < 0) || !g_pModeTable) {
  91. bRet = ORIGINAL_API(EnumDisplaySettingsW)(
  92. lpszDeviceName,
  93. iModeNum,
  94. lpDevMode);
  95. } else if (iModeNum < g_dwCount) {
  96. MODE* pmode = g_pModeTable + iModeNum;
  97. bRet = ORIGINAL_API(EnumDisplaySettingsW)(
  98. lpszDeviceName,
  99. pmode->dwActualIndex,
  100. lpDevMode);
  101. if (bRet) {
  102. LOGN(
  103. eDbgLevelError,
  104. "[EnumDisplaySettingsW] Returning shorter list of display modes.");
  105. lpDevMode->dmBitsPerPel = pmode->dmBitsPerPel;
  106. lpDevMode->dmPelsWidth = pmode->dmPelsWidth;
  107. lpDevMode->dmPelsHeight = pmode->dmPelsHeight;
  108. lpDevMode->dmDisplayFlags = pmode->dmDisplayFlags;
  109. lpDevMode->dmDisplayFrequency = pmode->dmDisplayFrequency;
  110. }
  111. }
  112. return bRet;
  113. }
  114. /*++
  115. Sort the table by Width+Height+BitsPerPel+Frequency in that order so that
  116. they can be easily filtered.
  117. --*/
  118. int
  119. _cdecl
  120. compare1(
  121. const void* a1,
  122. const void* a2
  123. )
  124. {
  125. MODE* arg1 = (MODE*)a1;
  126. MODE* arg2 = (MODE*)a2;
  127. int d;
  128. d = arg1->dmPelsWidth - arg2->dmPelsWidth;
  129. if (d == 0) {
  130. d = arg1->dmPelsHeight - arg2->dmPelsHeight;
  131. }
  132. if (d == 0) {
  133. d = arg1->dmBitsPerPel - arg2->dmBitsPerPel;
  134. }
  135. if (d == 0) {
  136. d = arg1->dmDisplayFrequency - arg2->dmDisplayFrequency;
  137. }
  138. return d;
  139. }
  140. /*++
  141. Sort the table so it looks like a Win9x mode table, i.e. BitsPerPel is the
  142. primary sort key.
  143. --*/
  144. int
  145. _cdecl
  146. compare2(
  147. const void* a1,
  148. const void* a2
  149. )
  150. {
  151. MODE* arg1 = (MODE*)a1;
  152. MODE* arg2 = (MODE*)a2;
  153. int d;
  154. d = arg1->dmBitsPerPel - arg2->dmBitsPerPel;
  155. if (d == 0) {
  156. d = arg1->dmPelsWidth - arg2->dmPelsWidth;
  157. }
  158. if (d == 0) {
  159. d = arg1->dmPelsHeight - arg2->dmPelsHeight;
  160. }
  161. if (d == 0) {
  162. d = arg1->dmDisplayFrequency - arg2->dmDisplayFrequency;
  163. }
  164. return d;
  165. }
  166. /*++
  167. Create a new mode table based upon the sanitized existing table. To do this,
  168. we do the following:
  169. 1. Get the entire table
  170. 2. Sort it - to allow efficient removal of duplicates
  171. 3. Remove duplicates and unwanted modes
  172. 4. Build a new table with only the modes that 'pass'
  173. --*/
  174. void
  175. BuildModeList(
  176. void
  177. )
  178. {
  179. if (g_bInit) {
  180. return;
  181. }
  182. DEVMODEA dm;
  183. ULONG i, j;
  184. dm.dmSize = sizeof(DEVMODEA);
  185. //
  186. // Figure out how many modes there are.
  187. //
  188. i = 0;
  189. while (EnumDisplaySettingsA(NULL, i, &dm)) {
  190. i++;
  191. }
  192. //
  193. // Allocate the full mode table.
  194. //
  195. MODE* pTempTable = (MODE*)malloc(sizeof(MODE) * i);
  196. if (!pTempTable) {
  197. LOGN(
  198. eDbgLevelError,
  199. "[BuildModeList] Failed to allocate %d bytes.",
  200. sizeof(MODE) * i);
  201. return;
  202. }
  203. MODE* pmode = pTempTable;
  204. //
  205. // Get all the modes.
  206. //
  207. i = 0;
  208. while (EnumDisplaySettingsA(NULL, i, &dm)) {
  209. pmode->dmBitsPerPel = dm.dmBitsPerPel;
  210. pmode->dmPelsWidth = dm.dmPelsWidth;
  211. pmode->dmPelsHeight = dm.dmPelsHeight;
  212. pmode->dmDisplayFlags = dm.dmDisplayFlags;
  213. pmode->dmDisplayFrequency = 0; // dm.dmDisplayFrequency;
  214. pmode->dwActualIndex = i;
  215. pmode->bIgnore = FALSE;
  216. pmode++;
  217. i++;
  218. }
  219. //
  220. // Sort the full table so we can remove duplicates easily.
  221. //
  222. qsort((void*)pTempTable, (size_t)i, sizeof(MODE), compare1);
  223. //
  224. // Strip away bad modes by setting them as ignored.
  225. //
  226. pmode = pTempTable;
  227. MODE* pprev = NULL;
  228. for (j = 0; j < i; j++) {
  229. if ((pmode->dmBitsPerPel < 8) ||
  230. (pmode->dmPelsWidth < 640) ||
  231. (pmode->dmPelsHeight < 480) ||
  232. (pmode->dmPelsWidth > 1280) ||
  233. (pprev &&
  234. (pprev->dmBitsPerPel == pmode->dmBitsPerPel) &&
  235. (pprev->dmPelsWidth == pmode->dmPelsWidth) &&
  236. (pprev->dmPelsHeight == pmode->dmPelsHeight))) {
  237. //
  238. // Special-case 640x480x4bit.
  239. //
  240. if ((pmode->dmBitsPerPel == 4) &&
  241. (pmode->dmPelsWidth == 640) &&
  242. (pmode->dmPelsHeight == 480)) {
  243. g_dwCount++;
  244. } else {
  245. pmode->bIgnore = TRUE;
  246. }
  247. } else {
  248. g_dwCount++;
  249. }
  250. pprev = pmode;
  251. pmode++;
  252. }
  253. //
  254. // Build the new table with only the modes that passed.
  255. //
  256. g_pModeTable = (MODE*)malloc(sizeof(MODE) * g_dwCount);
  257. if (!g_pModeTable) {
  258. LOGN(
  259. eDbgLevelError,
  260. "[BuildModeList] Failed to allocate %d bytes.",
  261. sizeof(MODE) * g_dwCount);
  262. free(pTempTable);
  263. return;
  264. }
  265. MODE* pmoden = g_pModeTable;
  266. pmode = pTempTable;
  267. for (j = 0; j < i; j++) {
  268. if (!pmode->bIgnore) {
  269. MoveMemory(pmoden, pmode, sizeof(MODE));
  270. pmoden++;
  271. }
  272. pmode++;
  273. }
  274. //
  275. // Sort the full table so we can remove duplicates easily.
  276. //
  277. qsort((void*)g_pModeTable, (size_t)g_dwCount, sizeof(MODE), compare2);
  278. free(pTempTable);
  279. g_bInit = TRUE;
  280. }
  281. /*++
  282. Register hooked functions
  283. --*/
  284. HOOK_BEGIN
  285. APIHOOK_ENTRY(USER32.DLL, EnumDisplaySettingsA)
  286. APIHOOK_ENTRY(USER32.DLL, EnumDisplaySettingsW)
  287. HOOK_END
  288. IMPLEMENT_SHIM_END