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