summaryrefslogtreecommitdiffstats
path: root/video/out/w32_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'video/out/w32_common.c')
-rw-r--r--video/out/w32_common.c171
1 files changed, 169 insertions, 2 deletions
diff --git a/video/out/w32_common.c b/video/out/w32_common.c
index 0de9148257..287bb24618 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;
@@ -81,6 +101,7 @@ struct vo_w32_state {
bool window_bounds_initialized;
bool current_fs;
+ bool toggle_fs; // whether the current fullscreen state needs to be switched
// currently known window state
int window_x;
@@ -92,6 +113,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 +141,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 +657,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 +684,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 +746,81 @@ 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 void toggle_fullscreen(struct vo_w32_state *w32)
+{
+ w32->toggle_fs = true;
+ signal_events(w32, VO_EVENT_FULLSCREEN_STATE);
+}
+
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam)
{
@@ -730,6 +854,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 +909,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.
@@ -786,7 +931,7 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
PostQuitMessage(0);
break;
case WM_SYSCOMMAND:
- switch (wParam) {
+ switch (wParam & 0xFFF0) {
case SC_SCREENSAVE:
case SC_MONITORPOWER:
if (w32->disable_screensaver) {
@@ -794,6 +939,12 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
return 0;
}
break;
+ case SC_RESTORE:
+ if (IsMaximized(w32->window) && w32->current_fs) {
+ toggle_fullscreen(w32);
+ return 0;
+ }
+ break;
}
break;
case WM_NCHITTEST:
@@ -1133,9 +1284,10 @@ static void reinit_window_state(struct vo_w32_state *w32)
if (w32->parent)
return;
- bool new_fs = w32->opts->fullscreen;
+ bool new_fs = w32->toggle_fs ? !w32->current_fs : w32->opts->fullscreen;
bool toggle_fs = w32->current_fs != new_fs;
w32->current_fs = new_fs;
+ w32->toggle_fs = false;
if (w32->taskbar_list) {
ITaskbarList2_MarkFullscreenWindow(w32->taskbar_list,
@@ -1333,6 +1485,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 +1500,7 @@ static void *gui_thread(void *ptr)
mpthread_set_name("win32 window");
+ w32_api_load(w32);
thread_disable_ime();
w32_thread_context = w32;
@@ -1549,6 +1709,9 @@ static int gui_thread_control(struct vo_w32_state *w32, int request, void *arg)
case VOCTRL_BORDER:
reinit_window_state(w32);
return VO_TRUE;
+ case VOCTRL_GET_FULLSCREEN:
+ *(bool *)arg = w32->toggle_fs != w32->current_fs;
+ return VO_TRUE;
case VOCTRL_GET_UNFS_WINDOW_SIZE: {
int *s = arg;
@@ -1648,6 +1811,8 @@ static void do_control(void *ptr)
w32->vo->dwidth = w32->dw;
w32->vo->dheight = w32->dh;
}
+ if (*events & VO_EVENT_FULLSCREEN_STATE)
+ reinit_window_state(w32);
}
int vo_w32_control(struct vo *vo, int *events, int request, void *arg)
@@ -1661,6 +1826,8 @@ int vo_w32_control(struct vo *vo, int *events, int request, void *arg)
vo->dheight = w32->dh;
mp_dispatch_unlock(w32->dispatch);
}
+ if (*events & VO_EVENT_FULLSCREEN_STATE)
+ reinit_window_state(w32);
return VO_TRUE;
} else {
int r;