summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--video/out/w32_common.c56
-rw-r--r--video/out/win32/displayconfig.c236
-rw-r--r--video/out/win32/displayconfig.h27
-rw-r--r--waftools/detections/compiler.py2
-rw-r--r--wscript_build.py1
5 files changed, 303 insertions, 19 deletions
diff --git a/video/out/w32_common.c b/video/out/w32_common.c
index f76384b451..09088df79d 100644
--- a/video/out/w32_common.c
+++ b/video/out/w32_common.c
@@ -34,6 +34,7 @@
#include "vo.h"
#include "win_state.h"
#include "w32_common.h"
+#include "win32/displayconfig.h"
#include "osdep/io.h"
#include "osdep/threads.h"
#include "osdep/w32_keyboard.h"
@@ -59,8 +60,8 @@ struct vo_w32_state {
HWND window;
HWND parent; // 0 normally, set in embedding mode
- // Size and virtual position of the current screen.
- struct mp_rect screenrc;
+ HMONITOR monitor; // Handle of the current screen
+ struct mp_rect screenrc; // Size and virtual position of the current screen
// last non-fullscreen extends (updated only on fullscreen or on initialization)
int prev_width;
@@ -567,22 +568,17 @@ static void wakeup_gui_thread(void *ctx)
PostMessage(w32->window, WM_USER, 0, 0);
}
-static double vo_w32_get_display_fps(struct vo_w32_state *w32)
+static double get_refresh_rate_from_gdi(const wchar_t *device)
{
- // Get the device name of the monitor containing the window
- HMONITOR mon = MonitorFromWindow(w32->window, MONITOR_DEFAULTTOPRIMARY);
- MONITORINFOEXW mi = { .cbSize = sizeof mi };
- GetMonitorInfoW(mon, (MONITORINFO*)&mi);
-
- DEVMODE dm = { .dmSize = sizeof dm };
- if (!EnumDisplaySettingsW(mi.szDevice, ENUM_CURRENT_SETTINGS, &dm))
- return -1;
+ DEVMODEW dm = { .dmSize = sizeof dm };
+ if (!EnumDisplaySettingsW(device, ENUM_CURRENT_SETTINGS, &dm))
+ return 0.0;
// May return 0 or 1 which "represent the display hardware's default refresh rate"
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd183565%28v=vs.85%29.aspx
// mpv validates this value with a threshold of 1, so don't return exactly 1
if (dm.dmDisplayFrequency == 1)
- return 0;
+ return 0.0;
// dm.dmDisplayFrequency is an integer which is rounded down, so it's
// highly likely that 23 represents 24/1.001, 59 represents 60/1.001, etc.
@@ -606,14 +602,37 @@ static double vo_w32_get_display_fps(struct vo_w32_state *w32)
static void update_display_fps(struct vo_w32_state *w32)
{
- double fps = vo_w32_get_display_fps(w32);
- if (fps != w32->display_fps) {
- w32->display_fps = fps;
+ HMONITOR monitor = MonitorFromWindow(w32->window, MONITOR_DEFAULTTOPRIMARY);
+ if (w32->monitor == monitor)
+ return;
+ w32->monitor = monitor;
+
+ MONITORINFOEXW mi = { .cbSize = sizeof mi };
+ GetMonitorInfoW(monitor, (MONITORINFO*)&mi);
+
+ // Try to get the monitor refresh rate.
+ double freq = 0.0;
+
+ if (freq == 0.0)
+ freq = mp_w32_displayconfig_get_refresh_rate(mi.szDevice);
+ if (freq == 0.0)
+ freq = get_refresh_rate_from_gdi(mi.szDevice);
+
+ if (freq != w32->display_fps) {
+ MP_VERBOSE(w32, "display-fps: %f\n", freq);
+ if (freq == 0.0)
+ MP_WARN(w32, "Couldn't determine monitor refresh rate\n");
+ w32->display_fps = freq;
signal_events(w32, VO_EVENT_WIN_STATE);
- MP_VERBOSE(w32, "display-fps: %f\n", fps);
}
}
+static void force_update_display_fps(struct vo_w32_state *w32)
+{
+ w32->monitor = 0;
+ update_display_fps(w32);
+}
+
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam)
{
@@ -648,13 +667,14 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
if (GetClientRect(w32->window, &r) && r.right > 0 && r.bottom > 0) {
w32->dw = r.right;
w32->dh = r.bottom;
- update_display_fps(w32); // if we moved between monitors
signal_events(w32, VO_EVENT_RESIZE);
MP_VERBOSE(w32, "resize window: %d:%d\n", w32->dw, w32->dh);
}
// Window may have been minimized or restored
signal_events(w32, VO_EVENT_WIN_STATE);
+
+ update_display_fps(w32);
break;
}
case WM_SIZING:
@@ -799,7 +819,7 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
mouse_button |= MP_KEY_STATE_UP;
break;
case WM_DISPLAYCHANGE:
- update_display_fps(w32);
+ force_update_display_fps(w32);
break;
}
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;
+}
diff --git a/video/out/win32/displayconfig.h b/video/out/win32/displayconfig.h
new file mode 100644
index 0000000000..ee6cd037f0
--- /dev/null
+++ b/video/out/win32/displayconfig.h
@@ -0,0 +1,27 @@
+/*
+ * 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/>.
+ */
+
+#ifndef MP_WIN32_DISPLAYCONFIG_H_
+#define MP_WIN32_DISPLAYCONFIG_H_
+
+#include <wchar.h>
+
+// Given a GDI monitor device name, get the precise refresh rate using the
+// Windows 7 DisplayConfig API. Returns 0.0 on failure.
+double mp_w32_displayconfig_get_refresh_rate(const wchar_t *device);
+
+#endif
diff --git a/waftools/detections/compiler.py b/waftools/detections/compiler.py
index d3e6b34455..284a7a5b9e 100644
--- a/waftools/detections/compiler.py
+++ b/waftools/detections/compiler.py
@@ -49,7 +49,7 @@ def __add_clang_flags__(ctx):
"-Wno-tautological-constant-out-of-range-compare" ]
def __add_mswin_flags__(ctx):
- ctx.env.CFLAGS += ['-D_WIN32_WINNT=0x600', '-DUNICODE', '-DCOBJMACROS',
+ ctx.env.CFLAGS += ['-D_WIN32_WINNT=0x0601', '-DUNICODE', '-DCOBJMACROS',
'-U__STRICT_ANSI__']
def __add_mingw_flags__(ctx):
diff --git a/wscript_build.py b/wscript_build.py
index 0bb0bc0620..d33c07db68 100644
--- a/wscript_build.py
+++ b/wscript_build.py
@@ -355,6 +355,7 @@ def build(ctx):
( "video/out/vo_x11.c" , "x11" ),
( "video/out/vo_xv.c", "xv" ),
( "video/out/w32_common.c", "win32" ),
+ ( "video/out/win32/displayconfig.c", "win32" ),
( "video/out/wayland_common.c", "wayland" ),
( "video/out/wayland/buffer.c", "wayland" ),
( "video/out/wayland/memfile.c", "wayland" ),