Commit 9a0367675f44821c69b60341eed539fdfd13e7e7

Ryan C. Gordon 2022-06-12T15:28:49

windows: Get better name for the physical display, for Vista and later. Fixes #5321.

diff --git a/src/video/windows/SDL_windowsmodes.c b/src/video/windows/SDL_windowsmodes.c
index 03c2d4a..15d7724 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 f80ffde..50a4371 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