Skip to content

Commit

Permalink
win32: snap to screen edges
Browse files Browse the repository at this point in the history
Disabled by default. The snap sensitivity value depends on
the screen DPI. The default value is 16px on a 96 DPI screen.

Fixes #2248
  • Loading branch information
pavelxdd authored and wm4 committed Jan 27, 2017
1 parent cfda696 commit 9c90c90
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 0 deletions.
3 changes: 3 additions & 0 deletions DOCS/man/options.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
2 changes: 2 additions & 0 deletions options/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions options/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions player/command.c
Original file line number Diff line number Diff line change
Expand Up @@ -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" },
Expand Down
146 changes: 146 additions & 0 deletions video/out/w32_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
};

Expand Down Expand Up @@ -630,13 +656,35 @@ static char *get_color_profile(void *ctx, const wchar_t *device)
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);
if (w32->monitor == monitor)
return;
w32->monitor = monitor;

update_dpi(w32);

MONITORINFOEXW mi = { .cbSize = sizeof mi };
GetMonitorInfoW(monitor, (MONITORINFO*)&mi);

Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down

0 comments on commit 9c90c90

Please sign in to comment.