summaryrefslogtreecommitdiffstats
path: root/video/out/win32/displayconfig.c
diff options
context:
space:
mode:
Diffstat (limited to 'video/out/win32/displayconfig.c')
-rw-r--r--video/out/win32/displayconfig.c236
1 files changed, 236 insertions, 0 deletions
diff --git a/video/out/win32/displayconfig.c b/video/out/win32/displayconfig.c
new file mode 100644
index 0000000000..635e8b6b36
--- /dev/null
+++ b/video/out/win32/displayconfig.c
@@ -0,0 +1,236 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <windows.h>
+#include <stdbool.h>
+#include <string.h>
+#include <pthread.h>
+
+#include "displayconfig.h"
+
+#include "talloc.h"
+
+// Some DisplayConfig definitions are broken in mingw-w64 (as of 2015-3-13.) To
+// get the correct struct alignment, it's necessary to define them properly.
+#include <pshpack1.h>
+
+typedef enum {
+ MP_DISPLAYCONFIG_OUTPUT_TECHNOLOGY_OTHER = -1,
+ MP_DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HD15 = 0,
+ MP_DISPLAYCONFIG_OUTPUT_TECHNOLOGY_SVIDEO = 1,
+ MP_DISPLAYCONFIG_OUTPUT_TECHNOLOGY_COMPOSITE_VIDEO = 2,
+ MP_DISPLAYCONFIG_OUTPUT_TECHNOLOGY_COMPONENT_VIDEO = 3,
+ MP_DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DVI = 4,
+ MP_DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HDMI = 5,
+ MP_DISPLAYCONFIG_OUTPUT_TECHNOLOGY_LVDS = 6,
+ MP_DISPLAYCONFIG_OUTPUT_TECHNOLOGY_D_JPN = 8,
+ MP_DISPLAYCONFIG_OUTPUT_TECHNOLOGY_SDI = 9,
+ MP_DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EXTERNAL = 10,
+ MP_DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EMBEDDED = 11,
+ MP_DISPLAYCONFIG_OUTPUT_TECHNOLOGY_UDI_EXTERNAL = 12,
+ MP_DISPLAYCONFIG_OUTPUT_TECHNOLOGY_UDI_EMBEDDED = 13,
+ MP_DISPLAYCONFIG_OUTPUT_TECHNOLOGY_SDTVDONGLE = 14,
+ MP_DISPLAYCONFIG_OUTPUT_TECHNOLOGY_MIRACAST = 15,
+ MP_DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL = -2147483647 - 1,
+ MP_DISPLAYCONFIG_OUTPUT_TECHNOLOGY_FORCE_UINT32 = 0x7FFFFFFF
+} MP_DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY;
+
+typedef struct MP_DISPLAYCONFIG_PATH_TARGET_INFO {
+ LUID adapterId;
+ UINT32 id;
+ UINT32 modeInfoIdx;
+ MP_DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY outputTechnology;
+ DISPLAYCONFIG_ROTATION rotation;
+ DISPLAYCONFIG_SCALING scaling;
+ DISPLAYCONFIG_RATIONAL refreshRate;
+ DISPLAYCONFIG_SCANLINE_ORDERING scanLineOrdering;
+ WINBOOL targetAvailable;
+ UINT32 statusFlags;
+} MP_DISPLAYCONFIG_PATH_TARGET_INFO;
+#define DISPLAYCONFIG_PATH_TARGET_INFO MP_DISPLAYCONFIG_PATH_TARGET_INFO
+
+typedef struct MP_DISPLAYCONFIG_PATH_INFO {
+ DISPLAYCONFIG_PATH_SOURCE_INFO sourceInfo;
+ MP_DISPLAYCONFIG_PATH_TARGET_INFO targetInfo;
+ UINT32 flags;
+} MP_DISPLAYCONFIG_PATH_INFO;
+#define DISPLAYCONFIG_PATH_INFO MP_DISPLAYCONFIG_PATH_INFO
+
+typedef struct MP_DISPLAYCONFIG_TARGET_DEVICE_NAME {
+ DISPLAYCONFIG_DEVICE_INFO_HEADER header;
+ DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS flags;
+ MP_DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY outputTechnology;
+ UINT16 edidManufactureId;
+ UINT16 edidProductCodeId;
+ UINT32 connectorInstance;
+ WCHAR monitorFriendlyDeviceName[64];
+ WCHAR monitorDevicePath[128];
+} MP_DISPLAYCONFIG_TARGET_DEVICE_NAME;
+#define DISPLAYCONFIG_TARGET_DEVICE_NAME MP_DISPLAYCONFIG_TARGET_DEVICE_NAME
+
+#include <poppack.h>
+
+static pthread_once_t displayconfig_load_ran = PTHREAD_ONCE_INIT;
+static bool displayconfig_loaded = false;
+
+static LONG (WINAPI *pDisplayConfigGetDeviceInfo)(
+ DISPLAYCONFIG_DEVICE_INFO_HEADER*);
+static LONG (WINAPI *pGetDisplayConfigBufferSizes)(UINT32, UINT32*, UINT32*);
+static LONG (WINAPI *pQueryDisplayConfig)(UINT32, UINT32*,
+ DISPLAYCONFIG_PATH_INFO*, UINT32*, DISPLAYCONFIG_MODE_INFO*,
+ DISPLAYCONFIG_TOPOLOGY_ID*);
+
+static void displayconfig_load(void)
+{
+ HMODULE user32 = GetModuleHandleW(L"user32.dll");
+ if (!user32)
+ return;
+
+ pDisplayConfigGetDeviceInfo =
+ (LONG (WINAPI*)(DISPLAYCONFIG_DEVICE_INFO_HEADER*))
+ GetProcAddress(user32, "DisplayConfigGetDeviceInfo");
+ if (!pDisplayConfigGetDeviceInfo)
+ return;
+ pGetDisplayConfigBufferSizes =
+ (LONG (WINAPI*)(UINT32, UINT32*, UINT32*))
+ GetProcAddress(user32, "GetDisplayConfigBufferSizes");
+ if (!pGetDisplayConfigBufferSizes)
+ return;
+ pQueryDisplayConfig =
+ (LONG (WINAPI*)(UINT32, UINT32*, DISPLAYCONFIG_PATH_INFO*, UINT32*,
+ DISPLAYCONFIG_MODE_INFO*, DISPLAYCONFIG_TOPOLOGY_ID*))
+ GetProcAddress(user32, "QueryDisplayConfig");
+ if (!pQueryDisplayConfig)
+ return;
+
+ displayconfig_loaded = true;
+}
+
+static int get_config(void *ctx,
+ UINT32 *num_paths, DISPLAYCONFIG_PATH_INFO** paths,
+ UINT32 *num_modes, DISPLAYCONFIG_MODE_INFO** modes)
+{
+ LONG res;
+ *paths = NULL;
+ *modes = NULL;
+
+ // The display configuration could change between the call to
+ // GetDisplayConfigBufferSizes and the call to QueryDisplayConfig, so call
+ // them in a loop until the correct buffer size is chosen
+ do {
+ res = pGetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, num_paths,
+ num_modes);
+ if (res != ERROR_SUCCESS)
+ goto fail;
+
+ // Free old buffers if they exist and allocate new ones
+ talloc_free(*paths);
+ talloc_free(*modes);
+ *paths = talloc_array(ctx, DISPLAYCONFIG_PATH_INFO, *num_paths);
+ *modes = talloc_array(ctx, DISPLAYCONFIG_MODE_INFO, *num_modes);
+
+ res = pQueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, num_paths, *paths,
+ num_modes, *modes, NULL);
+ } while (res == ERROR_INSUFFICIENT_BUFFER);
+ if (res != ERROR_SUCCESS)
+ goto fail;
+
+ return 0;
+fail:
+ talloc_free(*paths);
+ talloc_free(*modes);
+ return -1;
+}
+
+static DISPLAYCONFIG_PATH_INFO *get_path(UINT32 num_paths,
+ DISPLAYCONFIG_PATH_INFO* paths,
+ const wchar_t *device)
+{
+ // Search for a path with a matching device name
+ for (UINT32 i = 0; i < num_paths; i++) {
+ // Send a GET_SOURCE_NAME request
+ DISPLAYCONFIG_SOURCE_DEVICE_NAME source = {
+ .header = {
+ .size = sizeof source,
+ .type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME,
+ .adapterId = paths[i].sourceInfo.adapterId,
+ .id = paths[i].sourceInfo.id,
+ }
+ };
+ if (pDisplayConfigGetDeviceInfo(&source.header) != ERROR_SUCCESS)
+ return NULL;
+
+ // Check if the device name matches
+ if (!wcscmp(device, source.viewGdiDeviceName))
+ return &paths[i];
+ }
+
+ return NULL;
+}
+
+static double get_refresh_rate_from_mode(DISPLAYCONFIG_MODE_INFO *mode)
+{
+ if (mode->infoType != DISPLAYCONFIG_MODE_INFO_TYPE_TARGET)
+ return 0.0;
+
+ DISPLAYCONFIG_VIDEO_SIGNAL_INFO *info =
+ &mode->targetMode.targetVideoSignalInfo;
+ if (info->vSyncFreq.Denominator == 0)
+ return 0.0;
+
+ return ((double)info->vSyncFreq.Numerator) /
+ ((double)info->vSyncFreq.Denominator);
+}
+
+double mp_w32_displayconfig_get_refresh_rate(const wchar_t *device)
+{
+ // Load Windows 7 DisplayConfig API
+ pthread_once(&displayconfig_load_ran, displayconfig_load);
+ if (!displayconfig_loaded)
+ return 0.0;
+
+ void *ctx = talloc_new(NULL);
+ double freq = 0.0;
+
+ // Get the current display configuration
+ UINT32 num_paths;
+ DISPLAYCONFIG_PATH_INFO* paths;
+ UINT32 num_modes;
+ DISPLAYCONFIG_MODE_INFO* modes;
+ if (get_config(ctx, &num_paths, &paths, &num_modes, &modes))
+ goto end;
+
+ // Get the path for the specified monitor
+ DISPLAYCONFIG_PATH_INFO* path;
+ if (!(path = get_path(num_paths, paths, device)))
+ goto end;
+
+ // Try getting the refresh rate from the mode first. The value in the mode
+ // overrides the value in the path.
+ if (path->targetInfo.modeInfoIdx != DISPLAYCONFIG_PATH_MODE_IDX_INVALID)
+ freq = get_refresh_rate_from_mode(&modes[path->targetInfo.modeInfoIdx]);
+
+ // If the mode didn't contain a valid refresh rate, try the path
+ if (freq == 0.0 && path->targetInfo.refreshRate.Denominator != 0) {
+ freq = ((double)path->targetInfo.refreshRate.Numerator) /
+ ((double)path->targetInfo.refreshRate.Denominator);
+ }
+
+end:
+ talloc_free(ctx);
+ return freq;
+}