win32_monitor.c (16234B)
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 // 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 = (_GLFWmonitor**)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 break; 189 } 190 } 191 192 if (i < disconnectedCount) 193 continue; 194 195 monitor = createMonitor(&adapter, &display); 196 if (!monitor) 197 { 198 free(disconnected); 199 return; 200 } 201 202 _glfwInputMonitor(monitor, GLFW_CONNECTED, type); 203 204 type = _GLFW_INSERT_LAST; 205 } 206 207 // HACK: If an active adapter does not have any display devices 208 // (as sometimes happens), add it directly as a monitor 209 if (displayIndex == 0) 210 { 211 for (i = 0; i < disconnectedCount; i++) 212 { 213 if (disconnected[i] && 214 wcscmp(disconnected[i]->win32.adapterName, 215 adapter.DeviceName) == 0) 216 { 217 disconnected[i] = NULL; 218 break; 219 } 220 } 221 222 if (i < disconnectedCount) 223 continue; 224 225 monitor = createMonitor(&adapter, NULL); 226 if (!monitor) 227 { 228 free(disconnected); 229 return; 230 } 231 232 _glfwInputMonitor(monitor, GLFW_CONNECTED, type); 233 } 234 } 235 236 for (i = 0; i < disconnectedCount; i++) 237 { 238 if (disconnected[i]) 239 _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0); 240 } 241 242 free(disconnected); 243 } 244 245 // Change the current video mode 246 // 247 void _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired) 248 { 249 GLFWvidmode current; 250 const GLFWvidmode* best; 251 DEVMODEW dm; 252 LONG result; 253 254 best = _glfwChooseVideoMode(monitor, desired); 255 _glfwPlatformGetVideoMode(monitor, ¤t); 256 if (_glfwCompareVideoModes(¤t, best) == 0) 257 return; 258 259 ZeroMemory(&dm, sizeof(dm)); 260 dm.dmSize = sizeof(dm); 261 dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | 262 DM_DISPLAYFREQUENCY; 263 dm.dmPelsWidth = best->width; 264 dm.dmPelsHeight = best->height; 265 dm.dmBitsPerPel = best->redBits + best->greenBits + best->blueBits; 266 dm.dmDisplayFrequency = best->refreshRate; 267 268 if (dm.dmBitsPerPel < 15 || dm.dmBitsPerPel >= 24) 269 dm.dmBitsPerPel = 32; 270 271 result = ChangeDisplaySettingsExW(monitor->win32.adapterName, 272 &dm, 273 NULL, 274 CDS_FULLSCREEN, 275 NULL); 276 if (result == DISP_CHANGE_SUCCESSFUL) 277 monitor->win32.modeChanged = GLFW_TRUE; 278 else 279 { 280 const char* description = "Unknown error"; 281 282 if (result == DISP_CHANGE_BADDUALVIEW) 283 description = "The system uses DualView"; 284 else if (result == DISP_CHANGE_BADFLAGS) 285 description = "Invalid flags"; 286 else if (result == DISP_CHANGE_BADMODE) 287 description = "Graphics mode not supported"; 288 else if (result == DISP_CHANGE_BADPARAM) 289 description = "Invalid parameter"; 290 else if (result == DISP_CHANGE_FAILED) 291 description = "Graphics mode failed"; 292 else if (result == DISP_CHANGE_NOTUPDATED) 293 description = "Failed to write to registry"; 294 else if (result == DISP_CHANGE_RESTART) 295 description = "Computer restart required"; 296 297 _glfwInputError(GLFW_PLATFORM_ERROR, 298 "Win32: Failed to set video mode: %s", 299 description); 300 } 301 } 302 303 // Restore the previously saved (original) video mode 304 // 305 void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor) 306 { 307 if (monitor->win32.modeChanged) 308 { 309 ChangeDisplaySettingsExW(monitor->win32.adapterName, 310 NULL, NULL, CDS_FULLSCREEN, NULL); 311 monitor->win32.modeChanged = GLFW_FALSE; 312 } 313 } 314 315 void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* yscale) 316 { 317 UINT xdpi, ydpi; 318 319 if (IsWindows8Point1OrGreater()) 320 GetDpiForMonitor(handle, MDT_EFFECTIVE_DPI, &xdpi, &ydpi); 321 else 322 { 323 const HDC dc = GetDC(NULL); 324 xdpi = GetDeviceCaps(dc, LOGPIXELSX); 325 ydpi = GetDeviceCaps(dc, LOGPIXELSY); 326 ReleaseDC(NULL, dc); 327 } 328 329 if (xscale) 330 *xscale = xdpi / (float) USER_DEFAULT_SCREEN_DPI; 331 if (yscale) 332 *yscale = ydpi / (float) USER_DEFAULT_SCREEN_DPI; 333 } 334 335 336 ////////////////////////////////////////////////////////////////////////// 337 ////// GLFW platform API ////// 338 ////////////////////////////////////////////////////////////////////////// 339 340 void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) 341 { 342 } 343 344 void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) 345 { 346 DEVMODEW dm; 347 ZeroMemory(&dm, sizeof(dm)); 348 dm.dmSize = sizeof(dm); 349 350 EnumDisplaySettingsExW(monitor->win32.adapterName, 351 ENUM_CURRENT_SETTINGS, 352 &dm, 353 EDS_ROTATEDMODE); 354 355 if (xpos) 356 *xpos = dm.dmPosition.x; 357 if (ypos) 358 *ypos = dm.dmPosition.y; 359 } 360 361 void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, 362 float* xscale, float* yscale) 363 { 364 _glfwGetMonitorContentScaleWin32(monitor->win32.handle, xscale, yscale); 365 } 366 367 void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, 368 int* xpos, int* ypos, 369 int* width, int* height) 370 { 371 MONITORINFO mi = { sizeof(mi) }; 372 GetMonitorInfo(monitor->win32.handle, &mi); 373 374 if (xpos) 375 *xpos = mi.rcWork.left; 376 if (ypos) 377 *ypos = mi.rcWork.top; 378 if (width) 379 *width = mi.rcWork.right - mi.rcWork.left; 380 if (height) 381 *height = mi.rcWork.bottom - mi.rcWork.top; 382 } 383 384 GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) 385 { 386 int modeIndex = 0, size = 0; 387 GLFWvidmode* result = NULL; 388 389 *count = 0; 390 391 for (;;) 392 { 393 int i; 394 GLFWvidmode mode; 395 DEVMODEW dm; 396 397 ZeroMemory(&dm, sizeof(dm)); 398 dm.dmSize = sizeof(dm); 399 400 if (!EnumDisplaySettingsW(monitor->win32.adapterName, modeIndex, &dm)) 401 break; 402 403 modeIndex++; 404 405 // Skip modes with less than 15 BPP 406 if (dm.dmBitsPerPel < 15) 407 continue; 408 409 mode.width = dm.dmPelsWidth; 410 mode.height = dm.dmPelsHeight; 411 mode.refreshRate = dm.dmDisplayFrequency; 412 _glfwSplitBPP(dm.dmBitsPerPel, 413 &mode.redBits, 414 &mode.greenBits, 415 &mode.blueBits); 416 417 for (i = 0; i < *count; i++) 418 { 419 if (_glfwCompareVideoModes(result + i, &mode) == 0) 420 break; 421 } 422 423 // Skip duplicate modes 424 if (i < *count) 425 continue; 426 427 if (monitor->win32.modesPruned) 428 { 429 // Skip modes not supported by the connected displays 430 if (ChangeDisplaySettingsExW(monitor->win32.adapterName, 431 &dm, 432 NULL, 433 CDS_TEST, 434 NULL) != DISP_CHANGE_SUCCESSFUL) 435 { 436 continue; 437 } 438 } 439 440 if (*count == size) 441 { 442 size += 128; 443 result = (GLFWvidmode*) realloc(result, size * sizeof(GLFWvidmode)); 444 } 445 446 (*count)++; 447 result[*count - 1] = mode; 448 } 449 450 if (!*count) 451 { 452 // HACK: Report the current mode if no valid modes were found 453 result = (GLFWvidmode*)calloc(1, sizeof(GLFWvidmode)); 454 _glfwPlatformGetVideoMode(monitor, result); 455 *count = 1; 456 } 457 458 return result; 459 } 460 461 void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) 462 { 463 DEVMODEW dm; 464 ZeroMemory(&dm, sizeof(dm)); 465 dm.dmSize = sizeof(dm); 466 467 EnumDisplaySettingsW(monitor->win32.adapterName, ENUM_CURRENT_SETTINGS, &dm); 468 469 mode->width = dm.dmPelsWidth; 470 mode->height = dm.dmPelsHeight; 471 mode->refreshRate = dm.dmDisplayFrequency; 472 _glfwSplitBPP(dm.dmBitsPerPel, 473 &mode->redBits, 474 &mode->greenBits, 475 &mode->blueBits); 476 } 477 478 GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) 479 { 480 HDC dc; 481 WORD values[3][256]; 482 483 dc = CreateDCW(L"DISPLAY", monitor->win32.adapterName, NULL, NULL); 484 GetDeviceGammaRamp(dc, values); 485 DeleteDC(dc); 486 487 _glfwAllocGammaArrays(ramp, 256); 488 489 memcpy(ramp->red, values[0], sizeof(values[0])); 490 memcpy(ramp->green, values[1], sizeof(values[1])); 491 memcpy(ramp->blue, values[2], sizeof(values[2])); 492 493 return GLFW_TRUE; 494 } 495 496 void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) 497 { 498 HDC dc; 499 WORD values[3][256]; 500 501 if (ramp->size != 256) 502 { 503 _glfwInputError(GLFW_PLATFORM_ERROR, 504 "Win32: Gamma ramp size must be 256"); 505 return; 506 } 507 508 memcpy(values[0], ramp->red, sizeof(values[0])); 509 memcpy(values[1], ramp->green, sizeof(values[1])); 510 memcpy(values[2], ramp->blue, sizeof(values[2])); 511 512 dc = CreateDCW(L"DISPLAY", monitor->win32.adapterName, NULL, NULL); 513 SetDeviceGammaRamp(dc, values); 514 DeleteDC(dc); 515 } 516 517 518 ////////////////////////////////////////////////////////////////////////// 519 ////// GLFW native API ////// 520 ////////////////////////////////////////////////////////////////////////// 521 522 GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* handle) 523 { 524 _GLFWmonitor* monitor = (_GLFWmonitor*) handle; 525 _GLFW_REQUIRE_INIT_OR_RETURN(NULL); 526 return monitor->win32.publicAdapterName; 527 } 528 529 GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* handle) 530 { 531 _GLFWmonitor* monitor = (_GLFWmonitor*) handle; 532 _GLFW_REQUIRE_INIT_OR_RETURN(NULL); 533 return monitor->win32.publicDisplayName; 534 } 535