win32_monitor.c (16993B)
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 <elmindreda@glfw.org> 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 28 #include "internal.h" 29 30 #if defined(_GLFW_WIN32) 31 32 #include <stdlib.h> 33 #include <string.h> 34 #include <limits.h> 35 #include <wchar.h> 36 37 38 // Callback for EnumDisplayMonitors in createMonitor 39 // 40 static BOOL CALLBACK monitorCallback(HMONITOR handle, 41 HDC dc, 42 RECT* rect, 43 LPARAM data) 44 { 45 MONITORINFOEXW mi; 46 ZeroMemory(&mi, sizeof(mi)); 47 mi.cbSize = sizeof(mi); 48 49 if (GetMonitorInfoW(handle, (MONITORINFO*) &mi)) 50 { 51 _GLFWmonitor* monitor = (_GLFWmonitor*) data; 52 if (wcscmp(mi.szDevice, monitor->win32.adapterName) == 0) 53 monitor->win32.handle = handle; 54 } 55 56 return TRUE; 57 } 58 59 // Create monitor from an adapter and (optionally) a display 60 // 61 static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter, 62 DISPLAY_DEVICEW* display) 63 { 64 _GLFWmonitor* monitor; 65 int widthMM, heightMM; 66 char* name; 67 HDC dc; 68 DEVMODEW dm; 69 RECT rect; 70 71 if (display) 72 name = _glfwCreateUTF8FromWideStringWin32(display->DeviceString); 73 else 74 name = _glfwCreateUTF8FromWideStringWin32(adapter->DeviceString); 75 if (!name) 76 return NULL; 77 78 ZeroMemory(&dm, sizeof(dm)); 79 dm.dmSize = sizeof(dm); 80 EnumDisplaySettingsW(adapter->DeviceName, ENUM_CURRENT_SETTINGS, &dm); 81 82 dc = CreateDCW(L"DISPLAY", adapter->DeviceName, NULL, NULL); 83 84 if (IsWindows8Point1OrGreater()) 85 { 86 widthMM = GetDeviceCaps(dc, HORZSIZE); 87 heightMM = GetDeviceCaps(dc, VERTSIZE); 88 } 89 else 90 { 91 widthMM = (int) (dm.dmPelsWidth * 25.4f / GetDeviceCaps(dc, LOGPIXELSX)); 92 heightMM = (int) (dm.dmPelsHeight * 25.4f / GetDeviceCaps(dc, LOGPIXELSY)); 93 } 94 95 DeleteDC(dc); 96 97 monitor = _glfwAllocMonitor(name, widthMM, heightMM); 98 _glfw_free(name); 99 100 if (adapter->StateFlags & DISPLAY_DEVICE_MODESPRUNED) 101 monitor->win32.modesPruned = GLFW_TRUE; 102 103 wcscpy(monitor->win32.adapterName, adapter->DeviceName); 104 WideCharToMultiByte(CP_UTF8, 0, 105 adapter->DeviceName, -1, 106 monitor->win32.publicAdapterName, 107 sizeof(monitor->win32.publicAdapterName), 108 NULL, NULL); 109 110 if (display) 111 { 112 wcscpy(monitor->win32.displayName, display->DeviceName); 113 WideCharToMultiByte(CP_UTF8, 0, 114 display->DeviceName, -1, 115 monitor->win32.publicDisplayName, 116 sizeof(monitor->win32.publicDisplayName), 117 NULL, NULL); 118 } 119 120 rect.left = dm.dmPosition.x; 121 rect.top = dm.dmPosition.y; 122 rect.right = dm.dmPosition.x + dm.dmPelsWidth; 123 rect.bottom = dm.dmPosition.y + dm.dmPelsHeight; 124 125 EnumDisplayMonitors(NULL, &rect, monitorCallback, (LPARAM) monitor); 126 return monitor; 127 } 128 129 130 ////////////////////////////////////////////////////////////////////////// 131 ////// GLFW internal API ////// 132 ////////////////////////////////////////////////////////////////////////// 133 134 // Poll for changes in the set of connected monitors 135 // 136 void _glfwPollMonitorsWin32(void) 137 { 138 int i, disconnectedCount; 139 _GLFWmonitor** disconnected = NULL; 140 DWORD adapterIndex, displayIndex; 141 DISPLAY_DEVICEW adapter, display; 142 _GLFWmonitor* monitor; 143 144 disconnectedCount = _glfw.monitorCount; 145 if (disconnectedCount) 146 { 147 disconnected = _glfw_calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*)); 148 memcpy(disconnected, 149 _glfw.monitors, 150 _glfw.monitorCount * sizeof(_GLFWmonitor*)); 151 } 152 153 for (adapterIndex = 0; ; adapterIndex++) 154 { 155 int type = _GLFW_INSERT_LAST; 156 157 ZeroMemory(&adapter, sizeof(adapter)); 158 adapter.cb = sizeof(adapter); 159 160 if (!EnumDisplayDevicesW(NULL, adapterIndex, &adapter, 0)) 161 break; 162 163 if (!(adapter.StateFlags & DISPLAY_DEVICE_ACTIVE)) 164 continue; 165 166 if (adapter.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) 167 type = _GLFW_INSERT_FIRST; 168 169 for (displayIndex = 0; ; displayIndex++) 170 { 171 ZeroMemory(&display, sizeof(display)); 172 display.cb = sizeof(display); 173 174 if (!EnumDisplayDevicesW(adapter.DeviceName, displayIndex, &display, 0)) 175 break; 176 177 if (!(display.StateFlags & DISPLAY_DEVICE_ACTIVE)) 178 continue; 179 180 for (i = 0; i < disconnectedCount; i++) 181 { 182 if (disconnected[i] && 183 wcscmp(disconnected[i]->win32.displayName, 184 display.DeviceName) == 0) 185 { 186 disconnected[i] = NULL; 187 // handle may have changed, update 188 EnumDisplayMonitors(NULL, NULL, monitorCallback, (LPARAM) _glfw.monitors[i]); 189 break; 190 } 191 } 192 193 if (i < disconnectedCount) 194 continue; 195 196 monitor = createMonitor(&adapter, &display); 197 if (!monitor) 198 { 199 _glfw_free(disconnected); 200 return; 201 } 202 203 _glfwInputMonitor(monitor, GLFW_CONNECTED, type); 204 205 type = _GLFW_INSERT_LAST; 206 } 207 208 // HACK: If an active adapter does not have any display devices 209 // (as sometimes happens), add it directly as a monitor 210 if (displayIndex == 0) 211 { 212 for (i = 0; i < disconnectedCount; i++) 213 { 214 if (disconnected[i] && 215 wcscmp(disconnected[i]->win32.adapterName, 216 adapter.DeviceName) == 0) 217 { 218 disconnected[i] = NULL; 219 break; 220 } 221 } 222 223 if (i < disconnectedCount) 224 continue; 225 226 monitor = createMonitor(&adapter, NULL); 227 if (!monitor) 228 { 229 _glfw_free(disconnected); 230 return; 231 } 232 233 _glfwInputMonitor(monitor, GLFW_CONNECTED, type); 234 } 235 } 236 237 for (i = 0; i < disconnectedCount; i++) 238 { 239 if (disconnected[i]) 240 _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0); 241 } 242 243 _glfw_free(disconnected); 244 } 245 246 // Change the current video mode 247 // 248 void _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired) 249 { 250 GLFWvidmode current; 251 const GLFWvidmode* best; 252 DEVMODEW dm; 253 LONG result; 254 255 best = _glfwChooseVideoMode(monitor, desired); 256 _glfwGetVideoModeWin32(monitor, ¤t); 257 if (_glfwCompareVideoModes(¤t, best) == 0) 258 return; 259 260 ZeroMemory(&dm, sizeof(dm)); 261 dm.dmSize = sizeof(dm); 262 dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | 263 DM_DISPLAYFREQUENCY; 264 dm.dmPelsWidth = best->width; 265 dm.dmPelsHeight = best->height; 266 dm.dmBitsPerPel = best->redBits + best->greenBits + best->blueBits; 267 dm.dmDisplayFrequency = best->refreshRate; 268 269 if (dm.dmBitsPerPel < 15 || dm.dmBitsPerPel >= 24) 270 dm.dmBitsPerPel = 32; 271 272 result = ChangeDisplaySettingsExW(monitor->win32.adapterName, 273 &dm, 274 NULL, 275 CDS_FULLSCREEN, 276 NULL); 277 if (result == DISP_CHANGE_SUCCESSFUL) 278 monitor->win32.modeChanged = GLFW_TRUE; 279 else 280 { 281 const char* description = "Unknown error"; 282 283 if (result == DISP_CHANGE_BADDUALVIEW) 284 description = "The system uses DualView"; 285 else if (result == DISP_CHANGE_BADFLAGS) 286 description = "Invalid flags"; 287 else if (result == DISP_CHANGE_BADMODE) 288 description = "Graphics mode not supported"; 289 else if (result == DISP_CHANGE_BADPARAM) 290 description = "Invalid parameter"; 291 else if (result == DISP_CHANGE_FAILED) 292 description = "Graphics mode failed"; 293 else if (result == DISP_CHANGE_NOTUPDATED) 294 description = "Failed to write to registry"; 295 else if (result == DISP_CHANGE_RESTART) 296 description = "Computer restart required"; 297 298 _glfwInputError(GLFW_PLATFORM_ERROR, 299 "Win32: Failed to set video mode: %s", 300 description); 301 } 302 } 303 304 // Restore the previously saved (original) video mode 305 // 306 void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor) 307 { 308 if (monitor->win32.modeChanged) 309 { 310 ChangeDisplaySettingsExW(monitor->win32.adapterName, 311 NULL, NULL, CDS_FULLSCREEN, NULL); 312 monitor->win32.modeChanged = GLFW_FALSE; 313 } 314 } 315 316 void _glfwGetHMONITORContentScaleWin32(HMONITOR handle, float* xscale, float* yscale) 317 { 318 UINT xdpi, ydpi; 319 320 if (xscale) 321 *xscale = 0.f; 322 if (yscale) 323 *yscale = 0.f; 324 325 if (IsWindows8Point1OrGreater()) 326 { 327 if (GetDpiForMonitor(handle, MDT_EFFECTIVE_DPI, &xdpi, &ydpi) != S_OK) 328 { 329 _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to query monitor DPI"); 330 return; 331 } 332 } 333 else 334 { 335 const HDC dc = GetDC(NULL); 336 xdpi = GetDeviceCaps(dc, LOGPIXELSX); 337 ydpi = GetDeviceCaps(dc, LOGPIXELSY); 338 ReleaseDC(NULL, dc); 339 } 340 341 if (xscale) 342 *xscale = xdpi / (float) USER_DEFAULT_SCREEN_DPI; 343 if (yscale) 344 *yscale = ydpi / (float) USER_DEFAULT_SCREEN_DPI; 345 } 346 347 348 ////////////////////////////////////////////////////////////////////////// 349 ////// GLFW platform API ////// 350 ////////////////////////////////////////////////////////////////////////// 351 352 void _glfwFreeMonitorWin32(_GLFWmonitor* monitor) 353 { 354 } 355 356 void _glfwGetMonitorPosWin32(_GLFWmonitor* monitor, int* xpos, int* ypos) 357 { 358 DEVMODEW dm; 359 ZeroMemory(&dm, sizeof(dm)); 360 dm.dmSize = sizeof(dm); 361 362 EnumDisplaySettingsExW(monitor->win32.adapterName, 363 ENUM_CURRENT_SETTINGS, 364 &dm, 365 EDS_ROTATEDMODE); 366 367 if (xpos) 368 *xpos = dm.dmPosition.x; 369 if (ypos) 370 *ypos = dm.dmPosition.y; 371 } 372 373 void _glfwGetMonitorContentScaleWin32(_GLFWmonitor* monitor, 374 float* xscale, float* yscale) 375 { 376 _glfwGetHMONITORContentScaleWin32(monitor->win32.handle, xscale, yscale); 377 } 378 379 void _glfwGetMonitorWorkareaWin32(_GLFWmonitor* monitor, 380 int* xpos, int* ypos, 381 int* width, int* height) 382 { 383 MONITORINFO mi = { sizeof(mi) }; 384 GetMonitorInfoW(monitor->win32.handle, &mi); 385 386 if (xpos) 387 *xpos = mi.rcWork.left; 388 if (ypos) 389 *ypos = mi.rcWork.top; 390 if (width) 391 *width = mi.rcWork.right - mi.rcWork.left; 392 if (height) 393 *height = mi.rcWork.bottom - mi.rcWork.top; 394 } 395 396 GLFWvidmode* _glfwGetVideoModesWin32(_GLFWmonitor* monitor, int* count) 397 { 398 int modeIndex = 0, size = 0; 399 GLFWvidmode* result = NULL; 400 401 *count = 0; 402 403 for (;;) 404 { 405 int i; 406 GLFWvidmode mode; 407 DEVMODEW dm; 408 409 ZeroMemory(&dm, sizeof(dm)); 410 dm.dmSize = sizeof(dm); 411 412 if (!EnumDisplaySettingsW(monitor->win32.adapterName, modeIndex, &dm)) 413 break; 414 415 modeIndex++; 416 417 // Skip modes with less than 15 BPP 418 if (dm.dmBitsPerPel < 15) 419 continue; 420 421 mode.width = dm.dmPelsWidth; 422 mode.height = dm.dmPelsHeight; 423 mode.refreshRate = dm.dmDisplayFrequency; 424 _glfwSplitBPP(dm.dmBitsPerPel, 425 &mode.redBits, 426 &mode.greenBits, 427 &mode.blueBits); 428 429 for (i = 0; i < *count; i++) 430 { 431 if (_glfwCompareVideoModes(result + i, &mode) == 0) 432 break; 433 } 434 435 // Skip duplicate modes 436 if (i < *count) 437 continue; 438 439 if (monitor->win32.modesPruned) 440 { 441 // Skip modes not supported by the connected displays 442 if (ChangeDisplaySettingsExW(monitor->win32.adapterName, 443 &dm, 444 NULL, 445 CDS_TEST, 446 NULL) != DISP_CHANGE_SUCCESSFUL) 447 { 448 continue; 449 } 450 } 451 452 if (*count == size) 453 { 454 size += 128; 455 result = (GLFWvidmode*) _glfw_realloc(result, size * sizeof(GLFWvidmode)); 456 } 457 458 (*count)++; 459 result[*count - 1] = mode; 460 } 461 462 if (!*count) 463 { 464 // HACK: Report the current mode if no valid modes were found 465 result = _glfw_calloc(1, sizeof(GLFWvidmode)); 466 _glfwGetVideoModeWin32(monitor, result); 467 *count = 1; 468 } 469 470 return result; 471 } 472 473 GLFWbool _glfwGetVideoModeWin32(_GLFWmonitor* monitor, GLFWvidmode* mode) 474 { 475 DEVMODEW dm; 476 ZeroMemory(&dm, sizeof(dm)); 477 dm.dmSize = sizeof(dm); 478 479 if (!EnumDisplaySettingsW(monitor->win32.adapterName, ENUM_CURRENT_SETTINGS, &dm)) 480 { 481 _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to query display settings"); 482 return GLFW_FALSE; 483 } 484 485 mode->width = dm.dmPelsWidth; 486 mode->height = dm.dmPelsHeight; 487 mode->refreshRate = dm.dmDisplayFrequency; 488 _glfwSplitBPP(dm.dmBitsPerPel, 489 &mode->redBits, 490 &mode->greenBits, 491 &mode->blueBits); 492 493 return GLFW_TRUE; 494 } 495 496 GLFWbool _glfwGetGammaRampWin32(_GLFWmonitor* monitor, GLFWgammaramp* ramp) 497 { 498 HDC dc; 499 WORD values[3][256]; 500 501 dc = CreateDCW(L"DISPLAY", monitor->win32.adapterName, NULL, NULL); 502 GetDeviceGammaRamp(dc, values); 503 DeleteDC(dc); 504 505 _glfwAllocGammaArrays(ramp, 256); 506 507 memcpy(ramp->red, values[0], sizeof(values[0])); 508 memcpy(ramp->green, values[1], sizeof(values[1])); 509 memcpy(ramp->blue, values[2], sizeof(values[2])); 510 511 return GLFW_TRUE; 512 } 513 514 void _glfwSetGammaRampWin32(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) 515 { 516 HDC dc; 517 WORD values[3][256]; 518 519 if (ramp->size != 256) 520 { 521 _glfwInputError(GLFW_PLATFORM_ERROR, 522 "Win32: Gamma ramp size must be 256"); 523 return; 524 } 525 526 memcpy(values[0], ramp->red, sizeof(values[0])); 527 memcpy(values[1], ramp->green, sizeof(values[1])); 528 memcpy(values[2], ramp->blue, sizeof(values[2])); 529 530 dc = CreateDCW(L"DISPLAY", monitor->win32.adapterName, NULL, NULL); 531 SetDeviceGammaRamp(dc, values); 532 DeleteDC(dc); 533 } 534 535 536 ////////////////////////////////////////////////////////////////////////// 537 ////// GLFW native API ////// 538 ////////////////////////////////////////////////////////////////////////// 539 540 GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* handle) 541 { 542 _GLFWmonitor* monitor = (_GLFWmonitor*) handle; 543 _GLFW_REQUIRE_INIT_OR_RETURN(NULL); 544 545 if (_glfw.platform.platformID != GLFW_PLATFORM_WIN32) 546 { 547 _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, "Win32: Platform not initialized"); 548 return NULL; 549 } 550 551 return monitor->win32.publicAdapterName; 552 } 553 554 GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* handle) 555 { 556 _GLFWmonitor* monitor = (_GLFWmonitor*) handle; 557 _GLFW_REQUIRE_INIT_OR_RETURN(NULL); 558 559 if (_glfw.platform.platformID != GLFW_PLATFORM_WIN32) 560 { 561 _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, "Win32: Platform not initialized"); 562 return NULL; 563 } 564 565 return monitor->win32.publicDisplayName; 566 } 567 568 #endif // _GLFW_WIN32 569