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