diff options
Diffstat (limited to 'video/out/w32_common.c')
-rw-r--r-- | video/out/w32_common.c | 171 |
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; |