Atlas - SDL_windowsmodes.c

Home / ext / SDL / src / video / windows Lines: 1 | Size: 33263 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "SDL_internal.h" 22 23#if defined(SDL_VIDEO_DRIVER_WINDOWS) && !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 24 25#include "SDL_windowsvideo.h" 26#include "../../events/SDL_displayevents_c.h" 27 28#ifdef HAVE_DXGI1_6_H 29#define COBJMACROS 30#include <dxgi1_6.h> 31#endif 32 33// Windows CE compatibility 34#ifndef CDS_FULLSCREEN 35#define CDS_FULLSCREEN 0 36#endif 37#ifndef USER_DEFAULT_SCREEN_DPI 38#define USER_DEFAULT_SCREEN_DPI 96 39#endif 40 41// #define DEBUG_MODES 42// #define HIGHDPI_DEBUG_VERBOSE 43 44static void WIN_UpdateDisplayMode(SDL_VideoDevice *_this, LPCWSTR deviceName, DWORD index, SDL_DisplayMode *mode) 45{ 46 SDL_DisplayModeData *data = (SDL_DisplayModeData *)mode->internal; 47 HDC hdc; 48 49 data->DeviceMode.dmFields = (DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS); 50 51 // NOLINTNEXTLINE(bugprone-assignment-in-if-condition): No simple way to extract the assignment 52 if (index == ENUM_CURRENT_SETTINGS && (hdc = CreateDCW(deviceName, NULL, NULL, NULL)) != NULL) { 53 char bmi_data[sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)]; 54 LPBITMAPINFO bmi; 55 HBITMAP hbm; 56 57 SDL_zeroa(bmi_data); 58 bmi = (LPBITMAPINFO)bmi_data; 59 bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 60 61 hbm = CreateCompatibleBitmap(hdc, 1, 1); 62 GetDIBits(hdc, hbm, 0, 1, NULL, bmi, DIB_RGB_COLORS); 63 GetDIBits(hdc, hbm, 0, 1, NULL, bmi, DIB_RGB_COLORS); 64 DeleteObject(hbm); 65 DeleteDC(hdc); 66 if (bmi->bmiHeader.biCompression == BI_BITFIELDS) { 67 switch (*(Uint32 *)bmi->bmiColors) { 68 case 0x00FF0000: 69 mode->format = SDL_PIXELFORMAT_XRGB8888; 70 break; 71 case 0x000000FF: 72 mode->format = SDL_PIXELFORMAT_XBGR8888; 73 break; 74 case 0xF800: 75 mode->format = SDL_PIXELFORMAT_RGB565; 76 break; 77 case 0x7C00: 78 mode->format = SDL_PIXELFORMAT_XRGB1555; 79 break; 80 } 81 } else if (bmi->bmiHeader.biCompression == BI_RGB) { 82 if (bmi->bmiHeader.biBitCount == 24) { 83 mode->format = SDL_PIXELFORMAT_RGB24; 84 } else if (bmi->bmiHeader.biBitCount == 8) { 85 mode->format = SDL_PIXELFORMAT_INDEX8; 86 } else if (bmi->bmiHeader.biBitCount == 4) { 87 mode->format = SDL_PIXELFORMAT_INDEX4LSB; 88 } 89 } 90 } else if (mode->format == SDL_PIXELFORMAT_UNKNOWN) { 91 // FIXME: Can we tell what this will be? 92 if ((data->DeviceMode.dmFields & DM_BITSPERPEL) == DM_BITSPERPEL) { 93 switch (data->DeviceMode.dmBitsPerPel) { 94 case 32: 95 mode->format = SDL_PIXELFORMAT_XRGB8888; 96 break; 97 case 24: 98 mode->format = SDL_PIXELFORMAT_RGB24; 99 break; 100 case 16: 101 mode->format = SDL_PIXELFORMAT_RGB565; 102 break; 103 case 15: 104 mode->format = SDL_PIXELFORMAT_XRGB1555; 105 break; 106 case 8: 107 mode->format = SDL_PIXELFORMAT_INDEX8; 108 break; 109 case 4: 110 mode->format = SDL_PIXELFORMAT_INDEX4LSB; 111 break; 112 } 113 } 114 } 115} 116 117static void *WIN_GetDXGIOutput(SDL_VideoDevice *_this, const WCHAR *DeviceName) 118{ 119 void *result = NULL; 120 121#ifdef HAVE_DXGI_H 122 const SDL_VideoData *videodata = (const SDL_VideoData *)_this->internal; 123 int nAdapter, nOutput; 124 IDXGIAdapter *pDXGIAdapter; 125 IDXGIOutput *pDXGIOutput; 126 127 if (!videodata->pDXGIFactory) { 128 return NULL; 129 } 130 131 nAdapter = 0; 132 while (!result && SUCCEEDED(IDXGIFactory_EnumAdapters(videodata->pDXGIFactory, nAdapter, &pDXGIAdapter))) { 133 nOutput = 0; 134 while (!result && SUCCEEDED(IDXGIAdapter_EnumOutputs(pDXGIAdapter, nOutput, &pDXGIOutput))) { 135 DXGI_OUTPUT_DESC outputDesc; 136 if (SUCCEEDED(IDXGIOutput_GetDesc(pDXGIOutput, &outputDesc))) { 137 if (SDL_wcscmp(outputDesc.DeviceName, DeviceName) == 0) { 138 result = pDXGIOutput; 139 } 140 } 141 if (pDXGIOutput != result) { 142 IDXGIOutput_Release(pDXGIOutput); 143 } 144 nOutput++; 145 } 146 IDXGIAdapter_Release(pDXGIAdapter); 147 nAdapter++; 148 } 149#endif 150 return result; 151} 152 153static void WIN_ReleaseDXGIOutput(void *dxgi_output) 154{ 155#ifdef HAVE_DXGI_H 156 IDXGIOutput *pDXGIOutput = (IDXGIOutput *)dxgi_output; 157 158 if (pDXGIOutput) { 159 IDXGIOutput_Release(pDXGIOutput); 160 } 161#endif 162} 163 164static SDL_DisplayOrientation WIN_GetNaturalOrientation(DEVMODEW *mode) 165{ 166 int width = mode->dmPelsWidth; 167 int height = mode->dmPelsHeight; 168 169 // Use unrotated width/height to guess orientation 170 if (mode->dmDisplayOrientation == DMDO_90 || mode->dmDisplayOrientation == DMDO_270) { 171 int temp = width; 172 width = height; 173 height = temp; 174 } 175 176 if (width >= height) { 177 return SDL_ORIENTATION_LANDSCAPE; 178 } else { 179 return SDL_ORIENTATION_PORTRAIT; 180 } 181} 182 183static SDL_DisplayOrientation WIN_GetDisplayOrientation(DEVMODEW *mode) 184{ 185 if (WIN_GetNaturalOrientation(mode) == SDL_ORIENTATION_LANDSCAPE) { 186 switch (mode->dmDisplayOrientation) { 187 case DMDO_DEFAULT: 188 return SDL_ORIENTATION_LANDSCAPE; 189 case DMDO_90: 190 return SDL_ORIENTATION_PORTRAIT; 191 case DMDO_180: 192 return SDL_ORIENTATION_LANDSCAPE_FLIPPED; 193 case DMDO_270: 194 return SDL_ORIENTATION_PORTRAIT_FLIPPED; 195 default: 196 return SDL_ORIENTATION_UNKNOWN; 197 } 198 } else { 199 switch (mode->dmDisplayOrientation) { 200 case DMDO_DEFAULT: 201 return SDL_ORIENTATION_PORTRAIT; 202 case DMDO_90: 203 return SDL_ORIENTATION_LANDSCAPE_FLIPPED; 204 case DMDO_180: 205 return SDL_ORIENTATION_PORTRAIT_FLIPPED; 206 case DMDO_270: 207 return SDL_ORIENTATION_LANDSCAPE; 208 default: 209 return SDL_ORIENTATION_UNKNOWN; 210 } 211 } 212} 213 214static void WIN_GetRefreshRate(void *dxgi_output, DEVMODEW *mode, int *numerator, int *denominator) 215{ 216 // We're not currently using DXGI to query display modes, so fake NTSC timings 217 switch (mode->dmDisplayFrequency) { 218 case 119: 219 case 59: 220 case 29: 221 *numerator = (mode->dmDisplayFrequency + 1) * 1000; 222 *denominator = 1001; 223 break; 224 default: 225 *numerator = mode->dmDisplayFrequency; 226 *denominator = 1; 227 break; 228 } 229 230#ifdef HAVE_DXGI_H 231 if (dxgi_output) { 232 IDXGIOutput *pDXGIOutput = (IDXGIOutput *)dxgi_output; 233 DXGI_MODE_DESC modeToMatch; 234 DXGI_MODE_DESC closestMatch; 235 236 SDL_zero(modeToMatch); 237 modeToMatch.Width = mode->dmPelsWidth; 238 modeToMatch.Height = mode->dmPelsHeight; 239 modeToMatch.RefreshRate.Numerator = *numerator; 240 modeToMatch.RefreshRate.Denominator = *denominator; 241 modeToMatch.Format = DXGI_FORMAT_R8G8B8A8_UNORM; 242 243 if (SUCCEEDED(IDXGIOutput_FindClosestMatchingMode(pDXGIOutput, &modeToMatch, &closestMatch, NULL))) { 244 *numerator = closestMatch.RefreshRate.Numerator; 245 *denominator = closestMatch.RefreshRate.Denominator; 246 } 247 } 248#endif // HAVE_DXGI_H 249} 250 251static float WIN_GetContentScale(SDL_VideoDevice *_this, HMONITOR hMonitor) 252{ 253 int dpi = 0; 254 255 const SDL_VideoData *videodata = (const SDL_VideoData *)_this->internal; 256 if (videodata->GetDpiForMonitor) { 257 UINT hdpi_uint, vdpi_uint; 258 if (videodata->GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &hdpi_uint, &vdpi_uint) == S_OK) { 259 dpi = (int)hdpi_uint; 260 } 261 } 262 if (dpi == 0) { 263 // Window 8.0 and below: same DPI for all monitors 264 HDC hdc = GetDC(NULL); 265 if (hdc) { 266 dpi = GetDeviceCaps(hdc, LOGPIXELSX); 267 ReleaseDC(NULL, hdc); 268 } 269 } 270 if (dpi == 0) { 271 // Safe default 272 dpi = USER_DEFAULT_SCREEN_DPI; 273 } 274 return dpi / (float)USER_DEFAULT_SCREEN_DPI; 275} 276 277static bool WIN_GetDisplayMode(SDL_VideoDevice *_this, void *dxgi_output, HMONITOR hMonitor, LPCWSTR deviceName, DWORD index, SDL_DisplayMode *mode, SDL_DisplayOrientation *natural_orientation, SDL_DisplayOrientation *current_orientation) 278{ 279 SDL_DisplayModeData *data; 280 DEVMODEW devmode; 281 282 devmode.dmSize = sizeof(devmode); 283 devmode.dmDriverExtra = 0; 284 if (!EnumDisplaySettingsW(deviceName, index, &devmode)) { 285 return false; 286 } 287 288 data = (SDL_DisplayModeData *)SDL_malloc(sizeof(*data)); 289 if (!data) { 290 return false; 291 } 292 293 SDL_zerop(mode); 294 mode->internal = data; 295 data->DeviceMode = devmode; 296 297 mode->format = SDL_PIXELFORMAT_UNKNOWN; 298 mode->w = data->DeviceMode.dmPelsWidth; 299 mode->h = data->DeviceMode.dmPelsHeight; 300 WIN_GetRefreshRate(dxgi_output, &data->DeviceMode, &mode->refresh_rate_numerator, &mode->refresh_rate_denominator); 301 302 // Fill in the mode information 303 WIN_UpdateDisplayMode(_this, deviceName, index, mode); 304 305 if (natural_orientation) { 306 *natural_orientation = WIN_GetNaturalOrientation(&devmode); 307 } 308 if (current_orientation) { 309 *current_orientation = WIN_GetDisplayOrientation(&devmode); 310 } 311 312 return true; 313} 314 315static char *WIN_GetDisplayNameVista(SDL_VideoData *videodata, const WCHAR *deviceName) 316{ 317 DISPLAYCONFIG_PATH_INFO *paths = NULL; 318 DISPLAYCONFIG_MODE_INFO *modes = NULL; 319 char *result = NULL; 320 UINT32 pathCount = 0; 321 UINT32 modeCount = 0; 322 UINT32 i; 323 LONG rc; 324 325 if (!videodata->GetDisplayConfigBufferSizes || !videodata->QueryDisplayConfig || !videodata->DisplayConfigGetDeviceInfo) { 326 return NULL; 327 } 328 329 do { 330 rc = videodata->GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &pathCount, &modeCount); 331 if (rc != ERROR_SUCCESS) { 332 goto WIN_GetDisplayNameVista_failed; 333 } 334 335 SDL_free(paths); 336 SDL_free(modes); 337 338 paths = (DISPLAYCONFIG_PATH_INFO *)SDL_malloc(sizeof(DISPLAYCONFIG_PATH_INFO) * pathCount); 339 modes = (DISPLAYCONFIG_MODE_INFO *)SDL_malloc(sizeof(DISPLAYCONFIG_MODE_INFO) * modeCount); 340 if ((!paths) || (!modes)) { 341 goto WIN_GetDisplayNameVista_failed; 342 } 343 344 rc = videodata->QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &pathCount, paths, &modeCount, modes, 0); 345 } while (rc == ERROR_INSUFFICIENT_BUFFER); 346 347 if (rc == ERROR_SUCCESS) { 348 for (i = 0; i < pathCount; i++) { 349 DISPLAYCONFIG_SOURCE_DEVICE_NAME sourceName; 350 DISPLAYCONFIG_TARGET_DEVICE_NAME targetName; 351 352 SDL_zero(sourceName); 353 sourceName.header.adapterId = paths[i].targetInfo.adapterId; 354 sourceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME; 355 sourceName.header.size = sizeof(sourceName); 356 sourceName.header.id = paths[i].sourceInfo.id; 357 rc = videodata->DisplayConfigGetDeviceInfo(&sourceName.header); 358 if (rc != ERROR_SUCCESS) { 359 break; 360 } else if (SDL_wcscmp(deviceName, sourceName.viewGdiDeviceName) != 0) { 361 continue; 362 } 363 364 SDL_zero(targetName); 365 targetName.header.adapterId = paths[i].targetInfo.adapterId; 366 targetName.header.id = paths[i].targetInfo.id; 367 targetName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME; 368 targetName.header.size = sizeof(targetName); 369 rc = videodata->DisplayConfigGetDeviceInfo(&targetName.header); 370 if (rc == ERROR_SUCCESS) { 371 result = WIN_StringToUTF8W(targetName.monitorFriendlyDeviceName); 372 /* if we got an empty string, treat it as failure so we'll fallback 373 to getting the generic name. */ 374 if (result && (*result == '\0')) { 375 SDL_free(result); 376 result = NULL; 377 } 378 } 379 break; 380 } 381 } 382 383 SDL_free(paths); 384 SDL_free(modes); 385 return result; 386 387WIN_GetDisplayNameVista_failed: 388 SDL_free(result); 389 SDL_free(paths); 390 SDL_free(modes); 391 return NULL; 392} 393 394#ifdef HAVE_DXGI1_6_H 395static bool WIN_GetMonitorDESC1(HMONITOR hMonitor, DXGI_OUTPUT_DESC1 *desc) 396{ 397 typedef HRESULT (WINAPI *pfnCreateDXGIFactory1)(REFIID riid, void **ppFactory); 398 pfnCreateDXGIFactory1 pCreateDXGIFactory1 = NULL; 399 SDL_SharedObject *hDXGIMod = NULL; 400 bool found = false; 401 402 hDXGIMod = SDL_LoadObject("dxgi.dll"); 403 if (hDXGIMod) { 404 pCreateDXGIFactory1 = (pfnCreateDXGIFactory1)SDL_LoadFunction(hDXGIMod, "CreateDXGIFactory1"); 405 } 406 if (pCreateDXGIFactory1) { 407 static const GUID SDL_IID_IDXGIFactory1 = { 0x770aae78, 0xf26f, 0x4dba, { 0xa8, 0x29, 0x25, 0x3c, 0x83, 0xd1, 0xb3, 0x87 } }; 408 static const GUID SDL_IID_IDXGIOutput6 = { 0x068346e8, 0xaaec, 0x4b84, { 0xad, 0xd7, 0x13, 0x7f, 0x51, 0x3f, 0x77, 0xa1 } }; 409 IDXGIFactory1 *dxgiFactory; 410 411 if (SUCCEEDED(pCreateDXGIFactory1(&SDL_IID_IDXGIFactory1, (void **)&dxgiFactory))) { 412 IDXGIAdapter1 *dxgiAdapter; 413 UINT adapter = 0; 414 while (!found && SUCCEEDED(IDXGIFactory1_EnumAdapters1(dxgiFactory, adapter, &dxgiAdapter))) { 415 IDXGIOutput *dxgiOutput; 416 UINT output = 0; 417 while (!found && SUCCEEDED(IDXGIAdapter1_EnumOutputs(dxgiAdapter, output, &dxgiOutput))) { 418 IDXGIOutput6 *dxgiOutput6; 419 if (SUCCEEDED(IDXGIOutput_QueryInterface(dxgiOutput, &SDL_IID_IDXGIOutput6, (void **)&dxgiOutput6))) { 420 if (SUCCEEDED(IDXGIOutput6_GetDesc1(dxgiOutput6, desc))) { 421 if (desc->Monitor == hMonitor) { 422 found = true; 423 } 424 } 425 IDXGIOutput6_Release(dxgiOutput6); 426 } 427 IDXGIOutput_Release(dxgiOutput); 428 ++output; 429 } 430 IDXGIAdapter1_Release(dxgiAdapter); 431 ++adapter; 432 } 433 IDXGIFactory2_Release(dxgiFactory); 434 } 435 } 436 if (hDXGIMod) { 437 SDL_UnloadObject(hDXGIMod); 438 } 439 return found; 440} 441 442static bool WIN_GetMonitorPathInfo(SDL_VideoData *videodata, HMONITOR hMonitor, DISPLAYCONFIG_PATH_INFO *path_info) 443{ 444 LONG result; 445 MONITORINFOEXW view_info; 446 UINT32 i; 447 UINT32 num_path_array_elements = 0; 448 UINT32 num_mode_info_array_elements = 0; 449 DISPLAYCONFIG_PATH_INFO *path_infos = NULL, *new_path_infos; 450 DISPLAYCONFIG_MODE_INFO *mode_infos = NULL, *new_mode_infos; 451 bool found = false; 452 453 if (!videodata->GetDisplayConfigBufferSizes || !videodata->QueryDisplayConfig || !videodata->DisplayConfigGetDeviceInfo) { 454 return false; 455 } 456 457 SDL_zero(view_info); 458 view_info.cbSize = sizeof(view_info); 459 if (!GetMonitorInfoW(hMonitor, (MONITORINFO *)&view_info)) { 460 goto done; 461 } 462 463 do { 464 if (videodata->GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &num_path_array_elements, &num_mode_info_array_elements) != ERROR_SUCCESS) { 465 SDL_free(path_infos); 466 SDL_free(mode_infos); 467 return false; 468 } 469 470 new_path_infos = (DISPLAYCONFIG_PATH_INFO *)SDL_realloc(path_infos, num_path_array_elements * sizeof(*path_infos)); 471 if (!new_path_infos) { 472 goto done; 473 } 474 path_infos = new_path_infos; 475 476 new_mode_infos = (DISPLAYCONFIG_MODE_INFO *)SDL_realloc(mode_infos, num_mode_info_array_elements * sizeof(*mode_infos)); 477 if (!new_mode_infos) { 478 goto done; 479 } 480 mode_infos = new_mode_infos; 481 482 result = videodata->QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &num_path_array_elements, path_infos, &num_mode_info_array_elements, mode_infos, NULL); 483 484 } while (result == ERROR_INSUFFICIENT_BUFFER); 485 486 if (result == ERROR_SUCCESS) { 487 for (i = 0; i < num_path_array_elements; ++i) { 488 DISPLAYCONFIG_SOURCE_DEVICE_NAME device_name; 489 490 SDL_zero(device_name); 491 device_name.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME; 492 device_name.header.size = sizeof(device_name); 493 device_name.header.adapterId = path_infos[i].sourceInfo.adapterId; 494 device_name.header.id = path_infos[i].sourceInfo.id; 495 if (videodata->DisplayConfigGetDeviceInfo(&device_name.header) == ERROR_SUCCESS) { 496 if (SDL_wcscmp(view_info.szDevice, device_name.viewGdiDeviceName) == 0) { 497 SDL_copyp(path_info, &path_infos[i]); 498 found = true; 499 break; 500 } 501 } 502 } 503 } 504 505done: 506 SDL_free(path_infos); 507 SDL_free(mode_infos); 508 509 return found; 510} 511 512static float WIN_GetSDRWhitePoint(SDL_VideoDevice *_this, HMONITOR hMonitor) 513{ 514 DISPLAYCONFIG_PATH_INFO path_info; 515 SDL_VideoData *videodata = _this->internal; 516 float SDR_white_level = 1.0f; 517 518 if (WIN_GetMonitorPathInfo(videodata, hMonitor, &path_info)) { 519 /* workarounds for https://github.com/libsdl-org/SDL/issues/11193 */ 520 struct SDL_DISPLAYCONFIG_SDR_WHITE_LEVEL { 521 DISPLAYCONFIG_DEVICE_INFO_HEADER header; 522 ULONG SDRWhiteLevel; 523 } white_level; 524 #define DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL 11 525 526 SDL_zero(white_level); 527 white_level.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL; 528 white_level.header.size = sizeof(white_level); 529 white_level.header.adapterId = path_info.targetInfo.adapterId; 530 white_level.header.id = path_info.targetInfo.id; 531 // WIN_GetMonitorPathInfo() succeeded: DisplayConfigGetDeviceInfo is not NULL 532 if (videodata->DisplayConfigGetDeviceInfo(&white_level.header) == ERROR_SUCCESS && 533 white_level.SDRWhiteLevel > 0) { 534 SDR_white_level = (white_level.SDRWhiteLevel / 1000.0f); 535 } 536 } 537 return SDR_white_level; 538} 539 540static void WIN_GetHDRProperties(SDL_VideoDevice *_this, HMONITOR hMonitor, SDL_HDROutputProperties *HDR) 541{ 542 DXGI_OUTPUT_DESC1 desc; 543 544 SDL_zerop(HDR); 545 546 if (WIN_GetMonitorDESC1(hMonitor, &desc)) { 547 if (desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) { 548 HDR->SDR_white_level = WIN_GetSDRWhitePoint(_this, hMonitor); 549 HDR->HDR_headroom = (desc.MaxLuminance / 80.0f) / HDR->SDR_white_level; 550 } 551 } 552} 553#endif // HAVE_DXGI1_6_H 554 555static void WIN_AddDisplay(SDL_VideoDevice *_this, HMONITOR hMonitor, const MONITORINFOEXW *info, int *display_index) 556{ 557 int i, index = *display_index; 558 SDL_VideoDisplay display; 559 SDL_DisplayData *displaydata; 560 void *dxgi_output = NULL; 561 SDL_DisplayMode mode; 562 SDL_DisplayOrientation natural_orientation; 563 SDL_DisplayOrientation current_orientation; 564 float content_scale = WIN_GetContentScale(_this, hMonitor); 565 566#ifdef DEBUG_MODES 567 SDL_Log("Display: %s", WIN_StringToUTF8W(info->szDevice)); 568#endif 569 570 dxgi_output = WIN_GetDXGIOutput(_this, info->szDevice); 571 bool found = WIN_GetDisplayMode(_this, dxgi_output, hMonitor, info->szDevice, ENUM_CURRENT_SETTINGS, &mode, &natural_orientation, &current_orientation); 572 WIN_ReleaseDXGIOutput(dxgi_output); 573 if (!found) { 574 return; 575 } 576 577 // Prevent adding duplicate displays. Do this after we know the display is 578 // ready to be added to allow any displays that we can't fully query to be 579 // removed 580 for (i = 0; i < _this->num_displays; ++i) { 581 SDL_DisplayData *internal = _this->displays[i]->internal; 582 if (SDL_wcscmp(internal->DeviceName, info->szDevice) == 0) { 583 bool moved = (index != i); 584 bool changed_bounds = false; 585 586 if (internal->state != DisplayRemoved) { 587 // We've already enumerated this display, don't move it 588 goto cleanup; 589 } 590 591 if (index >= _this->num_displays) { 592 // This should never happen due to the check above, but just in case... 593 goto cleanup; 594 } 595 596 if (moved) { 597 SDL_VideoDisplay *tmp; 598 599 tmp = _this->displays[index]; 600 _this->displays[index] = _this->displays[i]; 601 _this->displays[i] = tmp; 602 i = index; 603 } 604 605 internal->MonitorHandle = hMonitor; 606 internal->state = DisplayUnchanged; 607 608 SDL_PropertiesID props = SDL_GetDisplayProperties(_this->displays[i]->id); 609 SDL_SetPointerProperty(props, SDL_PROP_DISPLAY_WINDOWS_HMONITOR_POINTER, hMonitor); 610 611 if (!_this->setting_display_mode) { 612 SDL_VideoDisplay *existing_display = _this->displays[i]; 613 SDL_Rect bounds; 614 615 SDL_ResetFullscreenDisplayModes(existing_display); 616 SDL_SetDesktopDisplayMode(existing_display, &mode); 617 // The mode is owned by the video subsystem 618 mode.internal = NULL; 619 if (WIN_GetDisplayBounds(_this, existing_display, &bounds) && 620 SDL_memcmp(&internal->bounds, &bounds, sizeof(bounds)) != 0) { 621 changed_bounds = true; 622 SDL_copyp(&internal->bounds, &bounds); 623 } 624 if (moved || changed_bounds) { 625 SDL_SendDisplayEvent(existing_display, SDL_EVENT_DISPLAY_MOVED, 0, 0); 626 } 627 SDL_SendDisplayEvent(existing_display, SDL_EVENT_DISPLAY_ORIENTATION, current_orientation, 0); 628 SDL_SetDisplayContentScale(existing_display, content_scale); 629#ifdef HAVE_DXGI1_6_H 630 SDL_HDROutputProperties HDR; 631 WIN_GetHDRProperties(_this, hMonitor, &HDR); 632 SDL_SetDisplayHDRProperties(existing_display, &HDR); 633#endif 634 } 635 goto done; 636 } 637 } 638 639 displaydata = (SDL_DisplayData *)SDL_calloc(1, sizeof(*displaydata)); 640 if (!displaydata) { 641 goto cleanup; 642 } 643 SDL_memcpy(displaydata->DeviceName, info->szDevice, sizeof(displaydata->DeviceName)); 644 displaydata->MonitorHandle = hMonitor; 645 displaydata->state = DisplayAdded; 646 647 SDL_zero(display); 648 display.name = WIN_GetDisplayNameVista(_this->internal, info->szDevice); 649 if (!display.name) { 650 DISPLAY_DEVICEW device; 651 SDL_zero(device); 652 device.cb = sizeof(device); 653 if (EnumDisplayDevicesW(info->szDevice, 0, &device, 0)) { 654 display.name = WIN_StringToUTF8W(device.DeviceString); 655 } 656 } 657 658 display.desktop_mode = mode; 659 display.natural_orientation = natural_orientation; 660 display.current_orientation = current_orientation; 661 display.content_scale = content_scale; 662 display.device = _this; 663 display.internal = displaydata; 664 WIN_GetDisplayBounds(_this, &display, &displaydata->bounds); 665#ifdef HAVE_DXGI1_6_H 666 WIN_GetHDRProperties(_this, hMonitor, &display.HDR); 667#endif 668 SDL_DisplayID displayID = SDL_AddVideoDisplay(&display, false); 669 if (displayID) { 670 // The mode is owned by the video subsystem 671 mode.internal = NULL; 672 673 SDL_PropertiesID props = SDL_GetDisplayProperties(displayID); 674 SDL_SetPointerProperty(props, SDL_PROP_DISPLAY_WINDOWS_HMONITOR_POINTER, hMonitor); 675 } else { 676 SDL_free(displaydata); 677 } 678 SDL_free(display.name); 679 680done: 681 *display_index += 1; 682 683cleanup: 684 SDL_free(mode.internal); 685} 686 687typedef struct _WIN_AddDisplaysData 688{ 689 SDL_VideoDevice *video_device; 690 int display_index; 691 bool want_primary; 692} WIN_AddDisplaysData; 693 694static BOOL CALLBACK WIN_AddDisplaysCallback(HMONITOR hMonitor, 695 HDC hdcMonitor, 696 LPRECT lprcMonitor, 697 LPARAM dwData) 698{ 699 WIN_AddDisplaysData *data = (WIN_AddDisplaysData *)dwData; 700 MONITORINFOEXW info; 701 702 SDL_zero(info); 703 info.cbSize = sizeof(info); 704 705 if (GetMonitorInfoW(hMonitor, (LPMONITORINFO)&info) != 0) { 706 const bool is_primary = ((info.dwFlags & MONITORINFOF_PRIMARY) == MONITORINFOF_PRIMARY); 707 708 if (is_primary == data->want_primary) { 709 WIN_AddDisplay(data->video_device, hMonitor, &info, &data->display_index); 710 } 711 } 712 713 // continue enumeration 714 return TRUE; 715} 716 717static void WIN_AddDisplays(SDL_VideoDevice *_this) 718{ 719 WIN_AddDisplaysData callback_data; 720 callback_data.video_device = _this; 721 callback_data.display_index = 0; 722 723 callback_data.want_primary = true; 724 EnumDisplayMonitors(NULL, NULL, WIN_AddDisplaysCallback, (LPARAM)&callback_data); 725 726 callback_data.want_primary = false; 727 EnumDisplayMonitors(NULL, NULL, WIN_AddDisplaysCallback, (LPARAM)&callback_data); 728} 729 730bool WIN_InitModes(SDL_VideoDevice *_this) 731{ 732 WIN_AddDisplays(_this); 733 734 if (_this->num_displays == 0) { 735 return SDL_SetError("No displays available"); 736 } 737 return true; 738} 739 740bool WIN_GetDisplayBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect) 741{ 742 const SDL_DisplayData *data = display->internal; 743 MONITORINFO minfo; 744 BOOL rc; 745 746 SDL_zero(minfo); 747 minfo.cbSize = sizeof(MONITORINFO); 748 rc = GetMonitorInfo(data->MonitorHandle, &minfo); 749 750 if (!rc) { 751 return SDL_SetError("Couldn't find monitor data"); 752 } 753 754 rect->x = minfo.rcMonitor.left; 755 rect->y = minfo.rcMonitor.top; 756 rect->w = minfo.rcMonitor.right - minfo.rcMonitor.left; 757 rect->h = minfo.rcMonitor.bottom - minfo.rcMonitor.top; 758 759 return true; 760} 761 762bool WIN_GetDisplayUsableBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect) 763{ 764 const SDL_DisplayData *data = display->internal; 765 MONITORINFO minfo; 766 BOOL rc; 767 768 SDL_zero(minfo); 769 minfo.cbSize = sizeof(MONITORINFO); 770 rc = GetMonitorInfo(data->MonitorHandle, &minfo); 771 772 if (!rc) { 773 return SDL_SetError("Couldn't find monitor data"); 774 } 775 776 rect->x = minfo.rcWork.left; 777 rect->y = minfo.rcWork.top; 778 rect->w = minfo.rcWork.right - minfo.rcWork.left; 779 rect->h = minfo.rcWork.bottom - minfo.rcWork.top; 780 781 return true; 782} 783 784bool WIN_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display) 785{ 786 SDL_DisplayData *data = display->internal; 787 void *dxgi_output; 788 DWORD i; 789 SDL_DisplayMode mode; 790 791 dxgi_output = WIN_GetDXGIOutput(_this, data->DeviceName); 792 793 // Make sure we add the current mode to the list in case it's a custom mode that doesn't enumerate 794 for (i = ENUM_CURRENT_SETTINGS; ; ++i) { 795 if (!WIN_GetDisplayMode(_this, dxgi_output, data->MonitorHandle, data->DeviceName, i, &mode, NULL, NULL)) { 796 break; 797 } 798 if (SDL_ISPIXELFORMAT_INDEXED(mode.format)) { 799 // We don't support palettized modes now 800 SDL_free(mode.internal); 801 continue; 802 } 803 if (mode.format != SDL_PIXELFORMAT_UNKNOWN) { 804 if (!SDL_AddFullscreenDisplayMode(display, &mode)) { 805 SDL_free(mode.internal); 806 } 807 } else { 808 SDL_free(mode.internal); 809 } 810 } 811 812 WIN_ReleaseDXGIOutput(dxgi_output); 813 814 return true; 815} 816 817#ifdef DEBUG_MODES 818static void WIN_LogMonitor(SDL_VideoDevice *_this, HMONITOR mon) 819{ 820 const SDL_VideoData *vid_data = (const SDL_VideoData *)_this->internal; 821 MONITORINFOEX minfo; 822 UINT xdpi = 0, ydpi = 0; 823 char *name_utf8; 824 825 if (vid_data->GetDpiForMonitor) { 826 vid_data->GetDpiForMonitor(mon, MDT_EFFECTIVE_DPI, &xdpi, &ydpi); 827 } 828 829 SDL_zero(minfo); 830 minfo.cbSize = sizeof(minfo); 831 GetMonitorInfo(mon, (LPMONITORINFO)&minfo); 832 833 name_utf8 = WIN_StringToUTF8(minfo.szDevice); 834 835 SDL_Log("WIN_LogMonitor: monitor \"%s\": dpi: %d windows screen coordinates: %d, %d, %dx%d", 836 name_utf8, 837 xdpi, 838 minfo.rcMonitor.left, 839 minfo.rcMonitor.top, 840 minfo.rcMonitor.right - minfo.rcMonitor.left, 841 minfo.rcMonitor.bottom - minfo.rcMonitor.top); 842 843 SDL_free(name_utf8); 844} 845#endif 846 847bool WIN_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_DisplayMode *mode) 848{ 849 SDL_DisplayData *displaydata = display->internal; 850 SDL_DisplayModeData *data = (SDL_DisplayModeData *)mode->internal; 851 LONG status; 852 853#ifdef DEBUG_MODES 854 SDL_Log("WIN_SetDisplayMode: monitor state before mode change:"); 855 WIN_LogMonitor(_this, displaydata->MonitorHandle); 856#endif 857 858 /* High-DPI notes: 859 860 - ChangeDisplaySettingsEx always takes pixels. 861 - e.g. if the display is set to 2880x1800 with 200% scaling in Display Settings 862 - calling ChangeDisplaySettingsEx with a dmPelsWidth/Height other than 2880x1800 will 863 change the monitor DPI to 96. (100% scaling) 864 - calling ChangeDisplaySettingsEx with a dmPelsWidth/Height of 2880x1800 (or a NULL DEVMODE*) will 865 reset the monitor DPI to 192. (200% scaling) 866 867 NOTE: these are temporary changes in DPI, not modifications to the Control Panel setting. */ 868 if (mode->internal == display->desktop_mode.internal) { 869#ifdef DEBUG_MODES 870 SDL_Log("WIN_SetDisplayMode: resetting to original resolution"); 871#endif 872 status = ChangeDisplaySettingsExW(displaydata->DeviceName, NULL, NULL, CDS_FULLSCREEN, NULL); 873 } else { 874#ifdef DEBUG_MODES 875 SDL_Log("WIN_SetDisplayMode: changing to %dx%d pixels", data->DeviceMode.dmPelsWidth, data->DeviceMode.dmPelsHeight); 876#endif 877 status = ChangeDisplaySettingsExW(displaydata->DeviceName, &data->DeviceMode, NULL, CDS_FULLSCREEN, NULL); 878 } 879 if (status != DISP_CHANGE_SUCCESSFUL) { 880 const char *reason = "Unknown reason"; 881 switch (status) { 882 case DISP_CHANGE_BADFLAGS: 883 reason = "DISP_CHANGE_BADFLAGS"; 884 break; 885 case DISP_CHANGE_BADMODE: 886 reason = "DISP_CHANGE_BADMODE"; 887 break; 888 case DISP_CHANGE_BADPARAM: 889 reason = "DISP_CHANGE_BADPARAM"; 890 break; 891 case DISP_CHANGE_FAILED: 892 reason = "DISP_CHANGE_FAILED"; 893 break; 894 } 895 return SDL_SetError("ChangeDisplaySettingsEx() failed: %s", reason); 896 } 897 898#ifdef DEBUG_MODES 899 SDL_Log("WIN_SetDisplayMode: monitor state after mode change:"); 900 WIN_LogMonitor(_this, displaydata->MonitorHandle); 901#endif 902 903 EnumDisplaySettingsW(displaydata->DeviceName, ENUM_CURRENT_SETTINGS, &data->DeviceMode); 904 WIN_UpdateDisplayMode(_this, displaydata->DeviceName, ENUM_CURRENT_SETTINGS, mode); 905 return true; 906} 907 908void WIN_RefreshDisplays(SDL_VideoDevice *_this) 909{ 910 int i; 911 912 // Mark all displays as potentially invalid to detect 913 // entries that have actually been removed 914 for (i = 0; i < _this->num_displays; ++i) { 915 SDL_DisplayData *internal = _this->displays[i]->internal; 916 internal->state = DisplayRemoved; 917 } 918 919 // Enumerate displays to add any new ones and mark still 920 // connected entries as valid 921 WIN_AddDisplays(_this); 922 923 // Delete any entries still marked as invalid, iterate 924 // in reverse as each delete takes effect immediately 925 for (i = _this->num_displays - 1; i >= 0; --i) { 926 SDL_VideoDisplay *display = _this->displays[i]; 927 SDL_DisplayData *internal = display->internal; 928 if (internal->state == DisplayRemoved) { 929 SDL_DelVideoDisplay(display->id, true); 930 } 931 } 932 933 // Send events for any newly added displays 934 for (i = 0; i < _this->num_displays; ++i) { 935 SDL_VideoDisplay *display = _this->displays[i]; 936 SDL_DisplayData *internal = display->internal; 937 if (internal->state == DisplayAdded) { 938 SDL_SendDisplayEvent(display, SDL_EVENT_DISPLAY_ADDED, 0, 0); 939 } 940 } 941} 942 943void WIN_UpdateDisplayUsableBounds(SDL_VideoDevice *_this) 944{ 945 // This almost never happens, so just go ahead and send update events for all displays 946 for (int i = 0; i < _this->num_displays; ++i) { 947 SDL_SendDisplayEvent(_this->displays[i], SDL_EVENT_DISPLAY_USABLE_BOUNDS_CHANGED, 0, 0); 948 } 949} 950 951void WIN_QuitModes(SDL_VideoDevice *_this) 952{ 953 // All fullscreen windows should have restored modes by now 954} 955 956#endif // SDL_VIDEO_DRIVER_WINDOWS 957
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.