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