summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpavelxdd <pavel.otchertsov@gmail.com>2016-12-09 21:22:33 +0300
committerwm4 <wm4@nowhere>2017-01-27 12:00:32 +0100
commit9c90c902c11f557577f22e72a3a2db0034c511d8 (patch)
tree6297152519ff7e18c5abfc99e9d9f343a13e2274
parentcfda696580a994055e4532a273ce86c867ffdac4 (diff)
downloadmpv-9c90c902c11f557577f22e72a3a2db0034c511d8.tar.bz2
mpv-9c90c902c11f557577f22e72a3a2db0034c511d8.tar.xz
win32: snap to screen edges
Disabled by default. The snap sensitivity value depends on the screen DPI. The default value is 16px on a 96 DPI screen. Fixes #2248
-rw-r--r--DOCS/man/options.rst3
-rw-r--r--options/options.c2
-rw-r--r--options/options.h1
-rw-r--r--player/command.c1
-rw-r--r--video/out/w32_common.c146
5 files changed, 153 insertions, 0 deletions
diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst
index c1942cf0c6..a6034e512e 100644
--- a/DOCS/man/options.rst
+++ b/DOCS/man/options.rst
@@ -2112,6 +2112,9 @@ Window
Enabled by default.
+``--snap-window``
+ (Windows only) Snap the player window to screen edges.
+
``--ontop``
Makes the player window stay on top of other windows.
diff --git a/options/options.c b/options/options.c
index ed10299480..6547f06074 100644
--- a/options/options.c
+++ b/options/options.c
@@ -155,6 +155,7 @@ static const m_option_t mp_vo_opt_list[] = {
OPT_SETTINGSLIST("vo", video_driver_list, 0, &vo_obj_list, ),
OPT_SUBSTRUCT("sws", sws_opts, sws_conf, 0),
OPT_FLAG("taskbar-progress", taskbar_progress, 0),
+ OPT_FLAG("snap-window", snap_window, 0),
OPT_FLAG("ontop", ontop, 0),
OPT_FLAG("border", border, 0),
OPT_FLAG("fit-border", fit_border, 0),
@@ -222,6 +223,7 @@ const struct m_sub_options vo_sub_opts = {
.keepaspect_window = 1,
.hidpi_window_scale = 1,
.taskbar_progress = 1,
+ .snap_window = 0,
.border = 1,
.fit_border = 1,
.WinID = -1,
diff --git a/options/options.h b/options/options.h
index 6199eae5fe..b0687ac9e5 100644
--- a/options/options.h
+++ b/options/options.h
@@ -10,6 +10,7 @@ typedef struct mp_vo_opts {
struct m_obj_settings *video_driver_list;
int taskbar_progress;
+ int snap_window;
int ontop;
int fullscreen;
int border;
diff --git a/player/command.c b/player/command.c
index da5d985c17..7023111556 100644
--- a/player/command.c
+++ b/player/command.c
@@ -4270,6 +4270,7 @@ static const struct property_osd_display {
// video
{ "panscan", "Panscan", .osd_progbar = OSD_PANSCAN },
{ "taskbar-progress", "Progress in taskbar" },
+ { "snap-window", "Snap to screen edges" },
{ "ontop", "Stay on top" },
{ "border", "Border" },
{ "framedrop", "Framedrop" },
diff --git a/video/out/w32_common.c b/video/out/w32_common.c
index dad9b10799..4ff8d7b5bd 100644
--- a/video/out/w32_common.c
+++ b/video/out/w32_common.c
@@ -21,6 +21,7 @@
#include <assert.h>
#include <windows.h>
#include <windowsx.h>
+#include <dwmapi.h>
#include <ole2.h>
#include <shobjidl.h>
#include <avrt.h>
@@ -47,8 +48,25 @@
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
#define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase)
+#ifndef WM_DPICHANGED
+#define WM_DPICHANGED (0x02E0)
+#endif
+
+#ifndef DPI_ENUMS_DECLARED
+typedef enum MONITOR_DPI_TYPE {
+ MDT_EFFECTIVE_DPI = 0,
+ MDT_ANGULAR_DPI = 1,
+ MDT_RAW_DPI = 2,
+ MDT_DEFAULT = MDT_EFFECTIVE_DPI
+} MONITOR_DPI_TYPE;
+#endif
+
static __thread struct vo_w32_state *w32_thread_context;
+struct w32_api {
+ HRESULT (WINAPI *pGetDpiForMonitor)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*);
+};
+
struct vo_w32_state {
struct mp_log *log;
struct vo *vo;
@@ -59,6 +77,8 @@ struct vo_w32_state {
bool terminate;
struct mp_dispatch_queue *dispatch; // used to run stuff on the GUI thread
+ struct w32_api api; // stores functions from dynamically loaded DLLs
+
HWND window;
HWND parent; // 0 normally, set in embedding mode
HHOOK parent_win_hook;
@@ -92,6 +112,8 @@ struct vo_w32_state {
uint32_t o_dwidth;
uint32_t o_dheight;
+ int dpi;
+
bool disable_screensaver;
bool cursor_visible;
atomic_uint event_flags;
@@ -118,6 +140,10 @@ struct vo_w32_state {
// updates on move/resize/displaychange
double display_fps;
+ bool snapped;
+ int snap_dx;
+ int snap_dy;
+
HANDLE avrt_handle;
};
@@ -630,6 +656,26 @@ done:
return name;
}
+static void update_dpi(struct vo_w32_state *w32)
+{
+ UINT dpiX, dpiY;
+ if (w32->api.pGetDpiForMonitor && w32->api.pGetDpiForMonitor(w32->monitor,
+ MDT_EFFECTIVE_DPI, &dpiX, &dpiY) == S_OK) {
+ w32->dpi = (int)dpiX;
+ MP_VERBOSE(w32, "DPI detected from the new API: %d\n", w32->dpi);
+ return;
+ }
+ HDC hdc = GetDC(NULL);
+ if (hdc) {
+ w32->dpi = GetDeviceCaps(hdc, LOGPIXELSX);
+ ReleaseDC(NULL, hdc);
+ MP_VERBOSE(w32, "DPI detected from the old API: %d\n", w32->dpi);
+ } else {
+ w32->dpi = 96;
+ MP_VERBOSE(w32, "Couldn't determine DPI, falling back to %d\n", w32->dpi);
+ }
+}
+
static void update_display_info(struct vo_w32_state *w32)
{
HMONITOR monitor = MonitorFromWindow(w32->window, MONITOR_DEFAULTTOPRIMARY);
@@ -637,6 +683,8 @@ static void update_display_info(struct vo_w32_state *w32)
return;
w32->monitor = monitor;
+ update_dpi(w32);
+
MONITORINFOEXW mi = { .cbSize = sizeof mi };
GetMonitorInfoW(monitor, (MONITORINFO*)&mi);
@@ -697,6 +745,75 @@ static void update_playback_state(struct vo_w32_state *w32)
TBPF_NORMAL);
}
+static bool snap_to_screen_edges(struct vo_w32_state *w32, RECT *rc)
+{
+ if (!w32->opts->snap_window) {
+ w32->snapped = false;
+ return false;
+ }
+
+ RECT rect;
+ POINT cursor;
+ if (!GetWindowRect(w32->window, &rect) || !GetCursorPos(&cursor))
+ return false;
+ // Check for aero snapping
+ if ((rc->right - rc->left != rect.right - rect.left) ||
+ (rc->bottom - rc->top != rect.bottom - rect.top))
+ return false;
+
+ MONITORINFO mi = { .cbSize = sizeof(mi) };
+ if (!GetMonitorInfoW(w32->monitor, &mi))
+ return false;
+ // Get the work area to let the window snap to taskbar
+ RECT wr = mi.rcWork;
+
+ // Check for invisible borders and adjust the work area size
+ RECT frame = {0};
+ if (DwmGetWindowAttribute(w32->window, DWMWA_EXTENDED_FRAME_BOUNDS,
+ &frame, sizeof(RECT)) == S_OK) {
+ wr.left -= frame.left - rect.left;
+ wr.top -= frame.top - rect.top;
+ wr.right += rect.right - frame.right;
+ wr.bottom += rect.bottom - frame.bottom;
+ }
+
+ // Let the window to unsnap by changing its position,
+ // otherwise it will stick to the screen edges forever
+ rect = *rc;
+ if (w32->snapped) {
+ OffsetRect(&rect, cursor.x - rect.left - w32->snap_dx,
+ cursor.y - rect.top - w32->snap_dy);
+ }
+
+ int threshold = (w32->dpi * 16) / 96;
+ bool snapped = false;
+ // Adjust X position
+ if (abs(rect.left - wr.left) < threshold) {
+ snapped = true;
+ OffsetRect(&rect, wr.left - rect.left, 0);
+ } else if (abs(rect.right - wr.right) < threshold) {
+ snapped = true;
+ OffsetRect(&rect, wr.right - rect.right, 0);
+ }
+ // Adjust Y position
+ if (abs(rect.top - wr.top) < threshold) {
+ snapped = true;
+ OffsetRect(&rect, 0, wr.top - rect.top);
+ } else if (abs(rect.bottom - wr.bottom) < threshold) {
+ snapped = true;
+ OffsetRect(&rect, 0, wr.bottom - rect.bottom);
+ }
+
+ if (!w32->snapped && snapped) {
+ w32->snap_dx = cursor.x - rc->left;
+ w32->snap_dy = cursor.y - rc->top;
+ }
+
+ w32->snapped = snapped;
+ *rc = rect;
+ return true;
+}
+
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam)
{
@@ -730,6 +847,24 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
MP_DBG(w32, "move window: %d:%d\n", w32->window_x, w32->window_y);
break;
}
+ case WM_MOVING: {
+ RECT *rc = (RECT*)lParam;
+ if (snap_to_screen_edges(w32, rc))
+ return TRUE;
+ break;
+ }
+ case WM_ENTERSIZEMOVE:
+ if (w32->snapped) {
+ // Save the cursor offset from the window borders,
+ // so the player window can be unsnapped later
+ RECT rc;
+ POINT cursor;
+ if (GetWindowRect(w32->window, &rc) && GetCursorPos(&cursor)) {
+ w32->snap_dx = cursor.x - rc.left;
+ w32->snap_dy = cursor.y - rc.top;
+ }
+ }
+ break;
case WM_SIZE: {
RECT r;
if (GetClientRect(w32->window, &r) && r.right > 0 && r.bottom > 0) {
@@ -767,6 +902,9 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
return TRUE;
}
break;
+ case WM_DPICHANGED:
+ update_display_info(w32);
+ break;
case WM_CLOSE:
// Don't actually allow it to destroy the window, or whatever else it
// is that will make us lose WM_USER wakeups.
@@ -1333,6 +1471,13 @@ static void thread_disable_ime(void)
FreeLibrary(imm32);
}
+static void w32_api_load(struct vo_w32_state *w32)
+{
+ HMODULE shcore_dll = LoadLibraryW(L"shcore.dll");
+ w32->api.pGetDpiForMonitor = !shcore_dll ? NULL :
+ (void *)GetProcAddress(shcore_dll, "GetDpiForMonitor");
+}
+
static void *gui_thread(void *ptr)
{
struct vo_w32_state *w32 = ptr;
@@ -1341,6 +1486,7 @@ static void *gui_thread(void *ptr)
mpthread_set_name("win32 window");
+ w32_api_load(w32);
thread_disable_ime();
w32_thread_context = w32;