From 9a0367675f44821c69b60341eed539fdfd13e7e7 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Sun, 12 Jun 2022 15:28:49 -0400
Subject: [PATCH] windows: Get better name for the physical display, for Vista
and later.
Fixes #5321.
---
src/video/windows/SDL_windowsmodes.c | 107 ++++++++++++++-
src/video/windows/SDL_windowsvideo.h | 197 +++++++++++++++++++++++++++
2 files changed, 300 insertions(+), 4 deletions(-)
diff --git a/src/video/windows/SDL_windowsmodes.c b/src/video/windows/SDL_windowsmodes.c
index 03c2d4aa59b..15d7724bf87 100644
--- a/src/video/windows/SDL_windowsmodes.c
+++ b/src/video/windows/SDL_windowsmodes.c
@@ -196,6 +196,100 @@ WIN_GetDisplayMode(_THIS, LPCWSTR deviceName, DWORD index, SDL_DisplayMode * mod
return SDL_TRUE;
}
+/* The win32 API calls in this function require Windows Vista or later. */
+/* these are normally WINAPI but VS2017 is saying "warning C4229: anachronism used: modifiers on data are ignored" */
+typedef LONG (*SDL_WIN32PROC_GetDisplayConfigBufferSizes)(UINT32 flags, UINT32* numPathArrayElements, UINT32* numModeInfoArrayElements);
+typedef LONG (*SDL_WIN32PROC_QueryDisplayConfig)(UINT32 flags, UINT32* numPathArrayElements, DISPLAYCONFIG_PATH_INFO* pathArray, UINT32* numModeInfoArrayElements, DISPLAYCONFIG_MODE_INFO* modeInfoArray, DISPLAYCONFIG_TOPOLOGY_ID* currentTopologyId);
+typedef LONG (*SDL_WIN32PROC_DisplayConfigGetDeviceInfo)(DISPLAYCONFIG_DEVICE_INFO_HEADER* requestPacket);
+
+static char *
+WIN_GetDisplayNameVista(const WCHAR *deviceName)
+{
+ void *dll;
+ SDL_WIN32PROC_GetDisplayConfigBufferSizes pGetDisplayConfigBufferSizes;
+ SDL_WIN32PROC_QueryDisplayConfig pQueryDisplayConfig;
+ SDL_WIN32PROC_DisplayConfigGetDeviceInfo pDisplayConfigGetDeviceInfo;
+ DISPLAYCONFIG_PATH_INFO *paths = NULL;
+ DISPLAYCONFIG_MODE_INFO *modes = NULL;
+ char *retval = NULL;
+ UINT32 pathCount = 0;
+ UINT32 modeCount = 0;
+ UINT32 i;
+ LONG rc;
+
+ dll = SDL_LoadObject("USER32.DLL");
+ if (!dll) {
+ return NULL;
+ }
+
+ pGetDisplayConfigBufferSizes = (SDL_WIN32PROC_GetDisplayConfigBufferSizes) SDL_LoadFunction(dll, "GetDisplayConfigBufferSizes");
+ pQueryDisplayConfig = (SDL_WIN32PROC_QueryDisplayConfig) SDL_LoadFunction(dll, "QueryDisplayConfig");
+ pDisplayConfigGetDeviceInfo = (SDL_WIN32PROC_DisplayConfigGetDeviceInfo) SDL_LoadFunction(dll, "DisplayConfigGetDeviceInfo");
+
+ if (!pGetDisplayConfigBufferSizes || !pQueryDisplayConfig || !pDisplayConfigGetDeviceInfo) {
+ goto WIN_GetDisplayNameVista_failed;
+ }
+
+ do {
+ rc = pGetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &pathCount, &modeCount);
+ if (rc != ERROR_SUCCESS) {
+ goto WIN_GetDisplayNameVista_failed;
+ }
+
+ SDL_free(paths);
+ SDL_free(modes);
+
+ paths = (DISPLAYCONFIG_PATH_INFO *) SDL_malloc(sizeof (DISPLAYCONFIG_PATH_INFO) * pathCount);
+ modes = (DISPLAYCONFIG_MODE_INFO *) SDL_malloc(sizeof (DISPLAYCONFIG_MODE_INFO) * modeCount);
+ if ((paths == NULL) || (modes == NULL)) {
+ goto WIN_GetDisplayNameVista_failed;
+ }
+
+ rc = pQueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &pathCount, paths, &modeCount, modes, 0);
+ } while (rc == ERROR_INSUFFICIENT_BUFFER);
+
+ if (rc == ERROR_SUCCESS) {
+ for (i = 0; i < pathCount; i++) {
+ DISPLAYCONFIG_SOURCE_DEVICE_NAME sourceName;
+ DISPLAYCONFIG_TARGET_DEVICE_NAME targetName;
+
+ SDL_zero(sourceName);
+ sourceName.header.adapterId = paths[i].targetInfo.adapterId;
+ sourceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
+ sourceName.header.size = sizeof (sourceName);
+ rc = pDisplayConfigGetDeviceInfo(&sourceName.header);
+ if (rc != ERROR_SUCCESS) {
+ break;
+ } else if (SDL_wcscmp(deviceName, sourceName.viewGdiDeviceName) != 0) {
+ continue;
+ }
+
+ SDL_zero(targetName);
+ targetName.header.adapterId = paths[i].targetInfo.adapterId;
+ targetName.header.id = paths[i].targetInfo.id;
+ targetName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
+ targetName.header.size = sizeof (targetName);
+ rc = pDisplayConfigGetDeviceInfo(&targetName.header);
+ if (rc == ERROR_SUCCESS) {
+ retval = WIN_StringToUTF8W(targetName.monitorFriendlyDeviceName);
+ }
+ break;
+ }
+ }
+
+ SDL_free(paths);
+ SDL_free(modes);
+ SDL_UnloadObject(dll);
+ return retval;
+
+WIN_GetDisplayNameVista_failed:
+ SDL_free(retval);
+ SDL_free(paths);
+ SDL_free(modes);
+ SDL_UnloadObject(dll);
+ return NULL;
+}
+
static SDL_bool
WIN_AddDisplay(_THIS, HMONITOR hMonitor, const MONITORINFOEXW *info, SDL_bool send_event)
{
@@ -204,7 +298,6 @@ WIN_AddDisplay(_THIS, HMONITOR hMonitor, const MONITORINFOEXW *info, SDL_bool se
SDL_DisplayData *displaydata;
SDL_DisplayMode mode;
SDL_DisplayOrientation orientation;
- DISPLAY_DEVICEW device;
#ifdef DEBUG_MODES
SDL_Log("Display: %s\n", WIN_StringToUTF8W(info->szDevice));
@@ -243,10 +336,16 @@ WIN_AddDisplay(_THIS, HMONITOR hMonitor, const MONITORINFOEXW *info, SDL_bool se
displaydata->IsValid = SDL_TRUE;
SDL_zero(display);
- device.cb = sizeof(device);
- if (EnumDisplayDevicesW(info->szDevice, 0, &device, 0)) {
- display.name = WIN_StringToUTF8W(device.DeviceString);
+ display.name = WIN_GetDisplayNameVista(info->szDevice);
+ if (display.name == NULL) {
+ DISPLAY_DEVICEW device;
+ SDL_zero(device);
+ device.cb = sizeof (device);
+ if (EnumDisplayDevicesW(info->szDevice, 0, &device, 0)) {
+ display.name = WIN_StringToUTF8W(device.DeviceString);
+ }
}
+
display.desktop_mode = mode;
display.current_mode = mode;
display.orientation = orientation;
diff --git a/src/video/windows/SDL_windowsvideo.h b/src/video/windows/SDL_windowsvideo.h
index f80ffde8eb5..50a4371ac98 100644
--- a/src/video/windows/SDL_windowsvideo.h
+++ b/src/video/windows/SDL_windowsvideo.h
@@ -75,6 +75,203 @@ typedef struct _TOUCHINPUT {
DWORD cyContact;
} TOUCHINPUT, *PTOUCHINPUT;
+
+/* More-robust display information in Vista... */
+/* This is a huge amount of data to be stuffing into three API calls. :( */
+typedef struct DISPLAYCONFIG_PATH_SOURCE_INFO
+{
+ LUID adapterId;
+ UINT32 id;
+ union
+ {
+ UINT32 modeInfoIdx;
+ struct
+ {
+ UINT32 cloneGroupId : 16;
+ UINT32 sourceModeInfoIdx : 16;
+ } DUMMYSTRUCTNAME;
+ } DUMMYUNIONNAME;
+
+ UINT32 statusFlags;
+} DISPLAYCONFIG_PATH_SOURCE_INFO;
+
+typedef struct DISPLAYCONFIG_RATIONAL
+{
+ UINT32 Numerator;
+ UINT32 Denominator;
+} DISPLAYCONFIG_RATIONAL;
+
+typedef struct DISPLAYCONFIG_PATH_TARGET_INFO
+{
+ LUID adapterId;
+ UINT32 id;
+ union
+ {
+ UINT32 modeInfoIdx;
+ struct
+ {
+ UINT32 desktopModeInfoIdx : 16;
+ UINT32 targetModeInfoIdx : 16;
+ } DUMMYSTRUCTNAME;
+ } DUMMYUNIONNAME;
+ UINT32 /*DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY*/ outputTechnology;
+ UINT32 /*DISPLAYCONFIG_ROTATION*/ rotation;
+ UINT32 /*DISPLAYCONFIG_SCALING*/ scaling;
+ DISPLAYCONFIG_RATIONAL refreshRate;
+ UINT32 /*DISPLAYCONFIG_SCANLINE_ORDERING*/ scanLineOrdering;
+ BOOL targetAvailable;
+ UINT32 statusFlags;
+} DISPLAYCONFIG_PATH_TARGET_INFO;
+
+typedef struct DISPLAYCONFIG_PATH_INFO
+{
+ DISPLAYCONFIG_PATH_SOURCE_INFO sourceInfo;
+ DISPLAYCONFIG_PATH_TARGET_INFO targetInfo;
+ UINT32 flags;
+} DISPLAYCONFIG_PATH_INFO;
+
+typedef enum
+{
+ DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE = 1,
+ DISPLAYCONFIG_MODE_INFO_TYPE_TARGET = 2,
+ DISPLAYCONFIG_MODE_INFO_TYPE_DESKTOP_IMAGE = 3,
+ DISPLAYCONFIG_MODE_INFO_TYPE_FORCE_UINT32 = 0xFFFFFFFF
+} DISPLAYCONFIG_MODE_INFO_TYPE;
+
+typedef struct DISPLAYCONFIG_2DREGION
+{
+ UINT32 cx;
+ UINT32 cy;
+} DISPLAYCONFIG_2DREGION;
+
+typedef struct DISPLAYCONFIG_VIDEO_SIGNAL_INFO
+{
+ UINT64 pixelRate;
+ DISPLAYCONFIG_RATIONAL hSyncFreq;
+ DISPLAYCONFIG_RATIONAL vSyncFreq;
+ DISPLAYCONFIG_2DREGION activeSize;
+ DISPLAYCONFIG_2DREGION totalSize;
+
+ union
+ {
+ struct
+ {
+ UINT32 videoStandard : 16;
+
+ // Vertical refresh frequency divider
+ UINT32 vSyncFreqDivider : 6;
+
+ UINT32 reserved : 10;
+ } AdditionalSignalInfo;
+
+ UINT32 videoStandard;
+ } DUMMYUNIONNAME;
+
+ // Scan line ordering (e.g. progressive, interlaced).
+ UINT32 /*DISPLAYCONFIG_SCANLINE_ORDERING*/ scanLineOrdering;
+} DISPLAYCONFIG_VIDEO_SIGNAL_INFO;
+
+typedef struct DISPLAYCONFIG_SOURCE_MODE
+{
+ UINT32 width;
+ UINT32 height;
+ UINT32 /*DISPLAYCONFIG_PIXELFORMAT*/ pixelFormat;
+ POINTL position;
+} DISPLAYCONFIG_SOURCE_MODE;
+
+typedef struct DISPLAYCONFIG_TARGET_MODE
+{
+ DISPLAYCONFIG_VIDEO_SIGNAL_INFO targetVideoSignalInfo;
+} DISPLAYCONFIG_TARGET_MODE;
+
+typedef struct DISPLAYCONFIG_DESKTOP_IMAGE_INFO
+{
+ POINTL PathSourceSize;
+ RECTL DesktopImageRegion;
+ RECTL DesktopImageClip;
+} DISPLAYCONFIG_DESKTOP_IMAGE_INFO;
+
+typedef struct DISPLAYCONFIG_MODE_INFO
+{
+ DISPLAYCONFIG_MODE_INFO_TYPE infoType;
+ UINT32 id;
+ LUID adapterId;
+ union
+ {
+ DISPLAYCONFIG_TARGET_MODE targetMode;
+ DISPLAYCONFIG_SOURCE_MODE sourceMode;
+ DISPLAYCONFIG_DESKTOP_IMAGE_INFO desktopImageInfo;
+ } DUMMYUNIONNAME;
+} DISPLAYCONFIG_MODE_INFO;
+
+typedef enum DISPLAYCONFIG_TOPOLOGY_ID
+{
+ DISPLAYCONFIG_TOPOLOGY_INTERNAL = 0x00000001,
+ DISPLAYCONFIG_TOPOLOGY_CLONE = 0x00000002,
+ DISPLAYCONFIG_TOPOLOGY_EXTEND = 0x00000004,
+ DISPLAYCONFIG_TOPOLOGY_EXTERNAL = 0x00000008,
+ DISPLAYCONFIG_TOPOLOGY_FORCE_UINT32 = 0xFFFFFFFF
+} DISPLAYCONFIG_TOPOLOGY_ID;
+
+typedef enum
+{
+ DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME = 1,
+ DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME = 2,
+ DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE = 3,
+ DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME = 4,
+ DISPLAYCONFIG_DEVICE_INFO_SET_TARGET_PERSISTENCE = 5,
+ DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_BASE_TYPE = 6,
+ DISPLAYCONFIG_DEVICE_INFO_GET_SUPPORT_VIRTUAL_RESOLUTION = 7,
+ DISPLAYCONFIG_DEVICE_INFO_SET_SUPPORT_VIRTUAL_RESOLUTION = 8,
+ DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO = 9,
+ DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE = 10,
+ DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL = 11,
+ DISPLAYCONFIG_DEVICE_INFO_FORCE_UINT32 = 0xFFFFFFFF
+} DISPLAYCONFIG_DEVICE_INFO_TYPE;
+
+typedef struct DISPLAYCONFIG_DEVICE_INFO_HEADER
+{
+ DISPLAYCONFIG_DEVICE_INFO_TYPE type;
+ UINT32 size;
+ LUID adapterId;
+ UINT32 id;
+} DISPLAYCONFIG_DEVICE_INFO_HEADER;
+
+typedef struct DISPLAYCONFIG_SOURCE_DEVICE_NAME
+{
+ DISPLAYCONFIG_DEVICE_INFO_HEADER header;
+ WCHAR viewGdiDeviceName[CCHDEVICENAME];
+} DISPLAYCONFIG_SOURCE_DEVICE_NAME;
+
+typedef struct DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS
+{
+ union
+ {
+ struct
+ {
+ UINT32 friendlyNameFromEdid : 1;
+ UINT32 friendlyNameForced : 1;
+ UINT32 edidIdsValid : 1;
+ UINT32 reserved : 29;
+ } DUMMYSTRUCTNAME;
+ UINT32 value;
+ } DUMMYUNIONNAME;
+} DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS;
+
+typedef struct DISPLAYCONFIG_TARGET_DEVICE_NAME
+{
+ DISPLAYCONFIG_DEVICE_INFO_HEADER header;
+ DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS flags;
+ UINT32 /*DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY*/ outputTechnology;
+ UINT16 edidManufactureId;
+ UINT16 edidProductCodeId;
+ UINT32 connectorInstance;
+ WCHAR monitorFriendlyDeviceName[64];
+ WCHAR monitorDevicePath[128];
+} DISPLAYCONFIG_TARGET_DEVICE_NAME;
+
+#define QDC_ONLY_ACTIVE_PATHS 0x00000002
+
#endif /* WINVER < 0x0601 */
#if WINVER < 0x0603