win32_monitor.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. //========================================================================
  2. // GLFW 3.4 Win32 - www.glfw.org
  3. //------------------------------------------------------------------------
  4. // Copyright (c) 2002-2006 Marcus Geelnard
  5. // Copyright (c) 2006-2019 Camilla Löwy <[email protected]>
  6. //
  7. // This software is provided 'as-is', without any express or implied
  8. // warranty. In no event will the authors be held liable for any damages
  9. // arising from the use of this software.
  10. //
  11. // Permission is granted to anyone to use this software for any purpose,
  12. // including commercial applications, and to alter it and redistribute it
  13. // freely, subject to the following restrictions:
  14. //
  15. // 1. The origin of this software must not be misrepresented; you must not
  16. // claim that you wrote the original software. If you use this software
  17. // in a product, an acknowledgment in the product documentation would
  18. // be appreciated but is not required.
  19. //
  20. // 2. Altered source versions must be plainly marked as such, and must not
  21. // be misrepresented as being the original software.
  22. //
  23. // 3. This notice may not be removed or altered from any source
  24. // distribution.
  25. //
  26. //========================================================================
  27. // Please use C89 style variable declarations in this file because VS 2010
  28. //========================================================================
  29. #include "internal.h"
  30. #include <stdlib.h>
  31. #include <string.h>
  32. #include <limits.h>
  33. #include <malloc.h>
  34. #include <wchar.h>
  35. // Callback for EnumDisplayMonitors in createMonitor
  36. //
  37. static BOOL CALLBACK monitorCallback(HMONITOR handle,
  38. HDC dc,
  39. RECT* rect,
  40. LPARAM data)
  41. {
  42. MONITORINFOEXW mi;
  43. ZeroMemory(&mi, sizeof(mi));
  44. mi.cbSize = sizeof(mi);
  45. if (GetMonitorInfoW(handle, (MONITORINFO*) &mi))
  46. {
  47. _GLFWmonitor* monitor = (_GLFWmonitor*) data;
  48. if (wcscmp(mi.szDevice, monitor->win32.adapterName) == 0)
  49. monitor->win32.handle = handle;
  50. }
  51. return TRUE;
  52. }
  53. // Create monitor from an adapter and (optionally) a display
  54. //
  55. static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter,
  56. DISPLAY_DEVICEW* display)
  57. {
  58. _GLFWmonitor* monitor;
  59. int widthMM, heightMM;
  60. char* name;
  61. HDC dc;
  62. DEVMODEW dm;
  63. RECT rect;
  64. if (display)
  65. name = _glfwCreateUTF8FromWideStringWin32(display->DeviceString);
  66. else
  67. name = _glfwCreateUTF8FromWideStringWin32(adapter->DeviceString);
  68. if (!name)
  69. return NULL;
  70. ZeroMemory(&dm, sizeof(dm));
  71. dm.dmSize = sizeof(dm);
  72. EnumDisplaySettingsW(adapter->DeviceName, ENUM_CURRENT_SETTINGS, &dm);
  73. dc = CreateDCW(L"DISPLAY", adapter->DeviceName, NULL, NULL);
  74. if (IsWindows8Point1OrGreater())
  75. {
  76. widthMM = GetDeviceCaps(dc, HORZSIZE);
  77. heightMM = GetDeviceCaps(dc, VERTSIZE);
  78. }
  79. else
  80. {
  81. widthMM = (int) (dm.dmPelsWidth * 25.4f / GetDeviceCaps(dc, LOGPIXELSX));
  82. heightMM = (int) (dm.dmPelsHeight * 25.4f / GetDeviceCaps(dc, LOGPIXELSY));
  83. }
  84. DeleteDC(dc);
  85. monitor = _glfwAllocMonitor(name, widthMM, heightMM);
  86. free(name);
  87. if (adapter->StateFlags & DISPLAY_DEVICE_MODESPRUNED)
  88. monitor->win32.modesPruned = GLFW_TRUE;
  89. wcscpy(monitor->win32.adapterName, adapter->DeviceName);
  90. WideCharToMultiByte(CP_UTF8, 0,
  91. adapter->DeviceName, -1,
  92. monitor->win32.publicAdapterName,
  93. sizeof(monitor->win32.publicAdapterName),
  94. NULL, NULL);
  95. if (display)
  96. {
  97. wcscpy(monitor->win32.displayName, display->DeviceName);
  98. WideCharToMultiByte(CP_UTF8, 0,
  99. display->DeviceName, -1,
  100. monitor->win32.publicDisplayName,
  101. sizeof(monitor->win32.publicDisplayName),
  102. NULL, NULL);
  103. }
  104. rect.left = dm.dmPosition.x;
  105. rect.top = dm.dmPosition.y;
  106. rect.right = dm.dmPosition.x + dm.dmPelsWidth;
  107. rect.bottom = dm.dmPosition.y + dm.dmPelsHeight;
  108. EnumDisplayMonitors(NULL, &rect, monitorCallback, (LPARAM) monitor);
  109. return monitor;
  110. }
  111. //////////////////////////////////////////////////////////////////////////
  112. ////// GLFW internal API //////
  113. //////////////////////////////////////////////////////////////////////////
  114. // Poll for changes in the set of connected monitors
  115. //
  116. void _glfwPollMonitorsWin32(void)
  117. {
  118. int i, disconnectedCount;
  119. _GLFWmonitor** disconnected = NULL;
  120. DWORD adapterIndex, displayIndex;
  121. DISPLAY_DEVICEW adapter, display;
  122. _GLFWmonitor* monitor;
  123. disconnectedCount = _glfw.monitorCount;
  124. if (disconnectedCount)
  125. {
  126. disconnected = (_GLFWmonitor**)calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*));
  127. memcpy(disconnected,
  128. _glfw.monitors,
  129. _glfw.monitorCount * sizeof(_GLFWmonitor*));
  130. }
  131. for (adapterIndex = 0; ; adapterIndex++)
  132. {
  133. int type = _GLFW_INSERT_LAST;
  134. ZeroMemory(&adapter, sizeof(adapter));
  135. adapter.cb = sizeof(adapter);
  136. if (!EnumDisplayDevicesW(NULL, adapterIndex, &adapter, 0))
  137. break;
  138. if (!(adapter.StateFlags & DISPLAY_DEVICE_ACTIVE))
  139. continue;
  140. if (adapter.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
  141. type = _GLFW_INSERT_FIRST;
  142. for (displayIndex = 0; ; displayIndex++)
  143. {
  144. ZeroMemory(&display, sizeof(display));
  145. display.cb = sizeof(display);
  146. if (!EnumDisplayDevicesW(adapter.DeviceName, displayIndex, &display, 0))
  147. break;
  148. if (!(display.StateFlags & DISPLAY_DEVICE_ACTIVE))
  149. continue;
  150. for (i = 0; i < disconnectedCount; i++)
  151. {
  152. if (disconnected[i] &&
  153. wcscmp(disconnected[i]->win32.displayName,
  154. display.DeviceName) == 0)
  155. {
  156. disconnected[i] = NULL;
  157. break;
  158. }
  159. }
  160. if (i < disconnectedCount)
  161. continue;
  162. monitor = createMonitor(&adapter, &display);
  163. if (!monitor)
  164. {
  165. free(disconnected);
  166. return;
  167. }
  168. _glfwInputMonitor(monitor, GLFW_CONNECTED, type);
  169. type = _GLFW_INSERT_LAST;
  170. }
  171. // HACK: If an active adapter does not have any display devices
  172. // (as sometimes happens), add it directly as a monitor
  173. if (displayIndex == 0)
  174. {
  175. for (i = 0; i < disconnectedCount; i++)
  176. {
  177. if (disconnected[i] &&
  178. wcscmp(disconnected[i]->win32.adapterName,
  179. adapter.DeviceName) == 0)
  180. {
  181. disconnected[i] = NULL;
  182. break;
  183. }
  184. }
  185. if (i < disconnectedCount)
  186. continue;
  187. monitor = createMonitor(&adapter, NULL);
  188. if (!monitor)
  189. {
  190. free(disconnected);
  191. return;
  192. }
  193. _glfwInputMonitor(monitor, GLFW_CONNECTED, type);
  194. }
  195. }
  196. for (i = 0; i < disconnectedCount; i++)
  197. {
  198. if (disconnected[i])
  199. _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0);
  200. }
  201. free(disconnected);
  202. }
  203. // Change the current video mode
  204. //
  205. void _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired)
  206. {
  207. GLFWvidmode current;
  208. const GLFWvidmode* best;
  209. DEVMODEW dm;
  210. LONG result;
  211. best = _glfwChooseVideoMode(monitor, desired);
  212. _glfwPlatformGetVideoMode(monitor, &current);
  213. if (_glfwCompareVideoModes(&current, best) == 0)
  214. return;
  215. ZeroMemory(&dm, sizeof(dm));
  216. dm.dmSize = sizeof(dm);
  217. dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL |
  218. DM_DISPLAYFREQUENCY;
  219. dm.dmPelsWidth = best->width;
  220. dm.dmPelsHeight = best->height;
  221. dm.dmBitsPerPel = best->redBits + best->greenBits + best->blueBits;
  222. dm.dmDisplayFrequency = best->refreshRate;
  223. if (dm.dmBitsPerPel < 15 || dm.dmBitsPerPel >= 24)
  224. dm.dmBitsPerPel = 32;
  225. result = ChangeDisplaySettingsExW(monitor->win32.adapterName,
  226. &dm,
  227. NULL,
  228. CDS_FULLSCREEN,
  229. NULL);
  230. if (result == DISP_CHANGE_SUCCESSFUL)
  231. monitor->win32.modeChanged = GLFW_TRUE;
  232. else
  233. {
  234. const char* description = "Unknown error";
  235. if (result == DISP_CHANGE_BADDUALVIEW)
  236. description = "The system uses DualView";
  237. else if (result == DISP_CHANGE_BADFLAGS)
  238. description = "Invalid flags";
  239. else if (result == DISP_CHANGE_BADMODE)
  240. description = "Graphics mode not supported";
  241. else if (result == DISP_CHANGE_BADPARAM)
  242. description = "Invalid parameter";
  243. else if (result == DISP_CHANGE_FAILED)
  244. description = "Graphics mode failed";
  245. else if (result == DISP_CHANGE_NOTUPDATED)
  246. description = "Failed to write to registry";
  247. else if (result == DISP_CHANGE_RESTART)
  248. description = "Computer restart required";
  249. _glfwInputError(GLFW_PLATFORM_ERROR,
  250. "Win32: Failed to set video mode: %s",
  251. description);
  252. }
  253. }
  254. // Restore the previously saved (original) video mode
  255. //
  256. void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor)
  257. {
  258. if (monitor->win32.modeChanged)
  259. {
  260. ChangeDisplaySettingsExW(monitor->win32.adapterName,
  261. NULL, NULL, CDS_FULLSCREEN, NULL);
  262. monitor->win32.modeChanged = GLFW_FALSE;
  263. }
  264. }
  265. void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* yscale)
  266. {
  267. UINT xdpi, ydpi;
  268. if (IsWindows8Point1OrGreater())
  269. GetDpiForMonitor(handle, MDT_EFFECTIVE_DPI, &xdpi, &ydpi);
  270. else
  271. {
  272. const HDC dc = GetDC(NULL);
  273. xdpi = GetDeviceCaps(dc, LOGPIXELSX);
  274. ydpi = GetDeviceCaps(dc, LOGPIXELSY);
  275. ReleaseDC(NULL, dc);
  276. }
  277. if (xscale)
  278. *xscale = xdpi / (float) USER_DEFAULT_SCREEN_DPI;
  279. if (yscale)
  280. *yscale = ydpi / (float) USER_DEFAULT_SCREEN_DPI;
  281. }
  282. //////////////////////////////////////////////////////////////////////////
  283. ////// GLFW platform API //////
  284. //////////////////////////////////////////////////////////////////////////
  285. void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
  286. {
  287. }
  288. void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
  289. {
  290. DEVMODEW dm;
  291. ZeroMemory(&dm, sizeof(dm));
  292. dm.dmSize = sizeof(dm);
  293. EnumDisplaySettingsExW(monitor->win32.adapterName,
  294. ENUM_CURRENT_SETTINGS,
  295. &dm,
  296. EDS_ROTATEDMODE);
  297. if (xpos)
  298. *xpos = dm.dmPosition.x;
  299. if (ypos)
  300. *ypos = dm.dmPosition.y;
  301. }
  302. void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,
  303. float* xscale, float* yscale)
  304. {
  305. _glfwGetMonitorContentScaleWin32(monitor->win32.handle, xscale, yscale);
  306. }
  307. void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor,
  308. int* xpos, int* ypos,
  309. int* width, int* height)
  310. {
  311. MONITORINFO mi = { sizeof(mi) };
  312. GetMonitorInfo(monitor->win32.handle, &mi);
  313. if (xpos)
  314. *xpos = mi.rcWork.left;
  315. if (ypos)
  316. *ypos = mi.rcWork.top;
  317. if (width)
  318. *width = mi.rcWork.right - mi.rcWork.left;
  319. if (height)
  320. *height = mi.rcWork.bottom - mi.rcWork.top;
  321. }
  322. GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
  323. {
  324. int modeIndex = 0, size = 0;
  325. GLFWvidmode* result = NULL;
  326. *count = 0;
  327. for (;;)
  328. {
  329. int i;
  330. GLFWvidmode mode;
  331. DEVMODEW dm;
  332. ZeroMemory(&dm, sizeof(dm));
  333. dm.dmSize = sizeof(dm);
  334. if (!EnumDisplaySettingsW(monitor->win32.adapterName, modeIndex, &dm))
  335. break;
  336. modeIndex++;
  337. // Skip modes with less than 15 BPP
  338. if (dm.dmBitsPerPel < 15)
  339. continue;
  340. mode.width = dm.dmPelsWidth;
  341. mode.height = dm.dmPelsHeight;
  342. mode.refreshRate = dm.dmDisplayFrequency;
  343. _glfwSplitBPP(dm.dmBitsPerPel,
  344. &mode.redBits,
  345. &mode.greenBits,
  346. &mode.blueBits);
  347. for (i = 0; i < *count; i++)
  348. {
  349. if (_glfwCompareVideoModes(result + i, &mode) == 0)
  350. break;
  351. }
  352. // Skip duplicate modes
  353. if (i < *count)
  354. continue;
  355. if (monitor->win32.modesPruned)
  356. {
  357. // Skip modes not supported by the connected displays
  358. if (ChangeDisplaySettingsExW(monitor->win32.adapterName,
  359. &dm,
  360. NULL,
  361. CDS_TEST,
  362. NULL) != DISP_CHANGE_SUCCESSFUL)
  363. {
  364. continue;
  365. }
  366. }
  367. if (*count == size)
  368. {
  369. size += 128;
  370. result = (GLFWvidmode*) realloc(result, size * sizeof(GLFWvidmode));
  371. }
  372. (*count)++;
  373. result[*count - 1] = mode;
  374. }
  375. if (!*count)
  376. {
  377. // HACK: Report the current mode if no valid modes were found
  378. result = (GLFWvidmode*)calloc(1, sizeof(GLFWvidmode));
  379. _glfwPlatformGetVideoMode(monitor, result);
  380. *count = 1;
  381. }
  382. return result;
  383. }
  384. void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode)
  385. {
  386. DEVMODEW dm;
  387. ZeroMemory(&dm, sizeof(dm));
  388. dm.dmSize = sizeof(dm);
  389. EnumDisplaySettingsW(monitor->win32.adapterName, ENUM_CURRENT_SETTINGS, &dm);
  390. mode->width = dm.dmPelsWidth;
  391. mode->height = dm.dmPelsHeight;
  392. mode->refreshRate = dm.dmDisplayFrequency;
  393. _glfwSplitBPP(dm.dmBitsPerPel,
  394. &mode->redBits,
  395. &mode->greenBits,
  396. &mode->blueBits);
  397. }
  398. GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
  399. {
  400. HDC dc;
  401. WORD values[3][256];
  402. dc = CreateDCW(L"DISPLAY", monitor->win32.adapterName, NULL, NULL);
  403. GetDeviceGammaRamp(dc, values);
  404. DeleteDC(dc);
  405. _glfwAllocGammaArrays(ramp, 256);
  406. memcpy(ramp->red, values[0], sizeof(values[0]));
  407. memcpy(ramp->green, values[1], sizeof(values[1]));
  408. memcpy(ramp->blue, values[2], sizeof(values[2]));
  409. return GLFW_TRUE;
  410. }
  411. void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
  412. {
  413. HDC dc;
  414. WORD values[3][256];
  415. if (ramp->size != 256)
  416. {
  417. _glfwInputError(GLFW_PLATFORM_ERROR,
  418. "Win32: Gamma ramp size must be 256");
  419. return;
  420. }
  421. memcpy(values[0], ramp->red, sizeof(values[0]));
  422. memcpy(values[1], ramp->green, sizeof(values[1]));
  423. memcpy(values[2], ramp->blue, sizeof(values[2]));
  424. dc = CreateDCW(L"DISPLAY", monitor->win32.adapterName, NULL, NULL);
  425. SetDeviceGammaRamp(dc, values);
  426. DeleteDC(dc);
  427. }
  428. //////////////////////////////////////////////////////////////////////////
  429. ////// GLFW native API //////
  430. //////////////////////////////////////////////////////////////////////////
  431. GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* handle)
  432. {
  433. _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
  434. _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
  435. return monitor->win32.publicAdapterName;
  436. }
  437. GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* handle)
  438. {
  439. _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
  440. _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
  441. return monitor->win32.publicDisplayName;
  442. }