mpv/video/out/w32_common.c

1828 lines
57 KiB
C
Raw Normal View History

/*
* 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 <stdio.h>
#include <limits.h>
#include <pthread.h>
win32: fix window creation and size handling This commit fixes various issues with the way the window position and size is setup. Most importantly, it fixes some bugs with restoring from fullscreen state. Rename create_rendering_context() to reinit_window_state(). This function doesn't create anything, it just sets the window bounds and styles. Do not use vo_dx/dy for the window position, as video_out.c overwrites it with each vo_config() call. Use private variables window_x/y instead. A big cause for issues was that reinit_window_state() accidentally cleared the WS_VISIBLE style. I suspect that the API call to temporarily hide the window was a hack to deal with this. Another bug was that the window style was changed without calling SetWindowPos with SWP_FRAMECHANGED (as the MSDN documentation says). Properly initialize window position and size on vo_config following the same rules as the x11 backend: - Never change the window position. The window position should be kept, as the user might move the window, and resetting the window position e.g. during ordered chapter playback is not desired. - Never change the window size, unless the size of the video changes. These rules don't apply to fullscreen. When switching from fullscreen to windowed mode, the backend should restore the previous windowed size and position. When the VO was reconfigured during playback (vo_config() etc.), the saved window position and size should be changed according to the rules above, even if the window was in fullscreen mode during reconfiguring. Note that these rules might be perceived as awkward by some users: if you play multiple files with different resolutions, the window won't be centered when playing the files after the first. This is not a bug.
2011-10-22 00:11:14 +00:00
#include <assert.h>
#include <windows.h>
#include <windowsx.h>
#include <dwmapi.h>
#include <ole2.h>
#include <shobjidl.h>
#include <avrt.h>
#include "options/m_config.h"
#include "options/options.h"
2013-12-17 00:23:09 +00:00
#include "input/keycodes.h"
#include "input/input.h"
#include "input/event.h"
#include "stream/stream.h"
#include "common/msg.h"
#include "common/common.h"
#include "vo.h"
#include "win_state.h"
#include "w32_common.h"
#include "win32/displayconfig.h"
#include "win32/droptarget.h"
#include "osdep/io.h"
#include "osdep/threads.h"
#include "osdep/w32_keyboard.h"
#include "osdep/atomic.h"
#include "misc/dispatch.h"
#include "misc/rendezvous.h"
#include "mpv_talloc.h"
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
#define rect_w(r) ((r).right - (r).left)
#define rect_h(r) ((r).bottom - (r).top)
struct w32_api {
HRESULT (WINAPI *pGetDpiForMonitor)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*);
BOOL (WINAPI *pImmDisableIME)(DWORD);
};
struct vo_w32_state {
struct mp_log *log;
struct vo *vo;
struct mp_vo_opts *opts;
struct m_config_cache *opts_cache;
struct input_ctx *input_ctx;
pthread_t thread;
bool terminate;
struct mp_dispatch_queue *dispatch; // used to run stuff on the GUI thread
bool in_dispatch;
struct w32_api api; // stores functions from dynamically loaded DLLs
HWND window;
HWND parent; // 0 normally, set in embedding mode
HHOOK parent_win_hook;
HWINEVENTHOOK parent_evt_hook;
HMONITOR monitor; // Handle of the current screen
char *color_profile; // Path of the current screen's color profile
// Has the window seen a WM_DESTROY? If so, don't call DestroyWindow again.
bool destroyed;
// whether the window position and size were intialized
bool window_bounds_initialized;
bool current_fs;
bool toggle_fs; // whether the current fullscreen state needs to be switched
RECT windowrc; // currently known window rect
RECT prev_windowrc; // last non-fullscreen window rect
// video size
uint32_t o_dwidth;
uint32_t o_dheight;
int dpi;
bool disable_screensaver;
bool cursor_visible;
atomic_uint event_flags;
BOOL tracking;
TRACKMOUSEEVENT trackEvent;
int mouse_x;
int mouse_y;
// Should SetCursor be called when handling VOCTRL_SET_CURSOR_VISIBILITY?
bool can_set_cursor;
// UTF-16 decoding state for WM_CHAR and VK_PACKET
int high_surrogate;
w32_common: refactor and improve window state handling Refactored and split the `reinit_window_state` code into four separate functions: - `update_window_style` used to update window styles without modifying the window rect. - `fit_window_on_screen` used to adjust the window size when it is larger than the screen size. Added a helper function `fit_rect` to fit one rect on another without using any data from w32 struct. - `update_fullscreen_state` used to calculate the new fullscreen state and adjust the window rect accordingly. - `update_window_state` used to display the window on screen with new size, position and ontop state. This commit fixes three issues: - fixed #4753 by skipping `fit_window_on_screen` for a maximized window, since maximized window should already fit on the screen. It should be noted that this bug was only reproducible with `--fit-border` option which is enabled by default. The cause of the bug is that after calling the `add_window_borders` for a maximized window, the rect in result is slightly larger than the screen rect, which is okay, `SetWindowPos` will interpret it as a maximized state later, so no auto-fitting to screen size is needed here. - fixed #5215 by skipping `fit_window_on_screen` when leaving fullscreen. On a multi-monitor system if the mpv window was stretched to cover multiple monitors, its size was reset after switching back from fullscreen to fit the size of the active monitor. Also, when changing `--ontop` and `--border` options, now only the `update_window_style` and `update_window_state` functions are used, so `fit_window_on_screen` is not used for them too. - fixed #2451 by moving the `ITaskbarList2_MarkFullscreenWindow` below the `SetWindowPos`. If the taskbar is notified about fullscreen state before the window is shown on screen, the taskbar button could be missing until Alt-TAB is pressed, usually it was reproducible on Windows 8. Other changes: - In `update_fullscreen_state` the `reset window bounds` debug message now reports client area size and position, instead of window area size and position. This is done for consistency with debug messages in handling fullscreen state above in this function, since they also print window bounds of the client area. - Refactored `gui_thread_reconfig`. Added a new window flag `fit_on_screen` to fit the window on screen even when leaving fullscreen. This is needed for the case when the new video opened while the window is still in the fullscreen state. - Moved parent and fullscreen state checks out from the WM_MOVING to `snap_to_screen_edges` function for consistency with other functions. There's no point in keeping these checks out of the function body.
2017-12-17 00:20:53 +00:00
// Whether to fit the window on screen on next window state updating
bool fit_on_screen;
ITaskbarList2 *taskbar_list;
ITaskbarList3 *taskbar_list3;
UINT tbtnCreatedMsg;
bool tbtnCreated;
2015-03-10 18:25:30 +00:00
struct voctrl_playback_state current_pstate;
2015-03-10 18:25:30 +00:00
// updates on move/resize/displaychange
double display_fps;
bool moving;
bool snapped;
int snap_dx;
int snap_dy;
HANDLE avrt_handle;
};
static void add_window_borders(HWND hwnd, RECT *rc)
{
AdjustWindowRect(rc, GetWindowLongPtrW(hwnd, GWL_STYLE), 0);
}
// basically a reverse AdjustWindowRect (win32 doesn't appear to have this)
static void subtract_window_borders(HWND hwnd, RECT *rc)
{
RECT b = { 0, 0, 0, 0 };
add_window_borders(hwnd, &b);
rc->left -= b.left;
rc->top -= b.top;
rc->right -= b.right;
rc->bottom -= b.bottom;
}
static LRESULT borderless_nchittest(struct vo_w32_state *w32, int x, int y)
{
if (IsMaximized(w32->window))
return HTCLIENT;
POINT mouse = { x, y };
ScreenToClient(w32->window, &mouse);
// The horizontal frame should be the same size as the vertical frame,
// since the NONCLIENTMETRICS structure does not distinguish between them
int frame_size = GetSystemMetrics(SM_CXFRAME) +
GetSystemMetrics(SM_CXPADDEDBORDER);
// The diagonal size handles are slightly wider than the side borders
int diagonal_width = frame_size * 2 + GetSystemMetrics(SM_CXBORDER);
// Hit-test top border
if (mouse.y < frame_size) {
if (mouse.x < diagonal_width)
return HTTOPLEFT;
if (mouse.x >= rect_w(w32->windowrc) - diagonal_width)
return HTTOPRIGHT;
return HTTOP;
}
// Hit-test bottom border
if (mouse.y >= rect_h(w32->windowrc) - frame_size) {
if (mouse.x < diagonal_width)
return HTBOTTOMLEFT;
if (mouse.x >= rect_w(w32->windowrc) - diagonal_width)
return HTBOTTOMRIGHT;
return HTBOTTOM;
}
// Hit-test side borders
if (mouse.x < frame_size)
return HTLEFT;
if (mouse.x >= rect_w(w32->windowrc) - frame_size)
return HTRIGHT;
return HTCLIENT;
}
// turn a WMSZ_* input value in v into the border that should be resized
// returns: 0=left, 1=top, 2=right, 3=bottom, -1=undefined
static int get_resize_border(int v)
{
switch (v) {
case WMSZ_LEFT: return 3;
case WMSZ_TOP: return 2;
case WMSZ_RIGHT: return 3;
case WMSZ_BOTTOM: return 2;
case WMSZ_TOPLEFT: return 1;
case WMSZ_TOPRIGHT: return 1;
case WMSZ_BOTTOMLEFT: return 3;
case WMSZ_BOTTOMRIGHT: return 3;
default: return -1;
}
}
static bool key_state(int vk)
{
return GetKeyState(vk) & 0x8000;
}
static int mod_state(struct vo_w32_state *w32)
{
int res = 0;
// AltGr is represented as LCONTROL+RMENU on Windows
bool alt_gr = mp_input_use_alt_gr(w32->input_ctx) &&
key_state(VK_RMENU) && key_state(VK_LCONTROL);
if (key_state(VK_RCONTROL) || (key_state(VK_LCONTROL) && !alt_gr))
res |= MP_KEY_MODIFIER_CTRL;
if (key_state(VK_SHIFT))
res |= MP_KEY_MODIFIER_SHIFT;
if (key_state(VK_LMENU) || (key_state(VK_RMENU) && !alt_gr))
res |= MP_KEY_MODIFIER_ALT;
return res;
}
static int decode_surrogate_pair(wchar_t lead, wchar_t trail)
{
return 0x10000 + (((lead & 0x3ff) << 10) | (trail & 0x3ff));
}
static int decode_utf16(struct vo_w32_state *w32, wchar_t c)
{
// Decode UTF-16, keeping state in w32->high_surrogate
if (IS_HIGH_SURROGATE(c)) {
w32->high_surrogate = c;
return 0;
}
if (IS_LOW_SURROGATE(c)) {
if (!w32->high_surrogate) {
MP_ERR(w32, "Invalid UTF-16 input\n");
return 0;
}
int codepoint = decode_surrogate_pair(w32->high_surrogate, c);
w32->high_surrogate = 0;
return codepoint;
}
if (w32->high_surrogate != 0) {
w32->high_surrogate = 0;
MP_ERR(w32, "Invalid UTF-16 input\n");
return 0;
}
return c;
}
static void clear_keyboard_buffer(void)
{
static const UINT vkey = VK_DECIMAL;
static const BYTE keys[256] = { 0 };
UINT scancode = MapVirtualKey(vkey, MAPVK_VK_TO_VSC);
wchar_t buf[10];
int ret = 0;
// Use the method suggested by Michael Kaplan to clear any pending dead
// keys from the current keyboard layout. See:
// https://web.archive.org/web/20101004154432/http://blogs.msdn.com/b/michkap/archive/2006/04/06/569632.aspx
// https://web.archive.org/web/20100820152419/http://blogs.msdn.com/b/michkap/archive/2007/10/27/5717859.aspx
do {
ret = ToUnicode(vkey, scancode, keys, buf, MP_ARRAY_SIZE(buf), 0);
} while (ret < 0);
}
static int to_unicode(UINT vkey, UINT scancode, const BYTE keys[256])
{
// This wraps ToUnicode to be stateless and to return only one character
// Make the buffer 10 code units long to be safe, same as here:
// https://web.archive.org/web/20101013215215/http://blogs.msdn.com/b/michkap/archive/2006/03/24/559169.aspx
wchar_t buf[10] = { 0 };
// Dead keys aren't useful for key shortcuts, so clear the keyboard state
clear_keyboard_buffer();
int len = ToUnicode(vkey, scancode, keys, buf, MP_ARRAY_SIZE(buf), 0);
// Return the last complete UTF-16 code point. A negative return value
// indicates a dead key, however there should still be a non-combining
// version of the key in the buffer.
if (len < 0)
len = -len;
if (len >= 2 && IS_SURROGATE_PAIR(buf[len - 2], buf[len - 1]))
return decode_surrogate_pair(buf[len - 2], buf[len - 1]);
if (len >= 1)
return buf[len - 1];
return 0;
}
static int decode_key(struct vo_w32_state *w32, UINT vkey, UINT scancode)
{
BYTE keys[256];
GetKeyboardState(keys);
// If mp_input_use_alt_gr is false, detect and remove AltGr so normal
// characters are generated. Note that AltGr is represented as
// LCONTROL+RMENU on Windows.
if ((keys[VK_RMENU] & 0x80) && (keys[VK_LCONTROL] & 0x80) &&
!mp_input_use_alt_gr(w32->input_ctx))
{
keys[VK_RMENU] = keys[VK_LCONTROL] = 0;
keys[VK_MENU] = keys[VK_LMENU];
keys[VK_CONTROL] = keys[VK_RCONTROL];
}
int c = to_unicode(vkey, scancode, keys);
// Some shift states prevent ToUnicode from working or cause it to produce
// control characters. If this is detected, remove modifiers until it
// starts producing normal characters.
if (c < 0x20 && (keys[VK_MENU] & 0x80)) {
keys[VK_LMENU] = keys[VK_RMENU] = keys[VK_MENU] = 0;
c = to_unicode(vkey, scancode, keys);
}
if (c < 0x20 && (keys[VK_CONTROL] & 0x80)) {
keys[VK_LCONTROL] = keys[VK_RCONTROL] = keys[VK_CONTROL] = 0;
c = to_unicode(vkey, scancode, keys);
}
if (c < 0x20)
return 0;
// Decode lone UTF-16 surrogates (VK_PACKET can generate these)
if (c < 0x10000)
return decode_utf16(w32, c);
return c;
}
static bool handle_appcommand(struct vo_w32_state *w32, UINT cmd)
{
if (!mp_input_use_media_keys(w32->input_ctx))
return false;
int mpkey = mp_w32_appcmd_to_mpkey(cmd);
if (!mpkey)
return false;
mp_input_put_key(w32->input_ctx, mpkey | mod_state(w32));
return true;
}
static void handle_key_down(struct vo_w32_state *w32, UINT vkey, UINT scancode)
{
// Ignore key repeat
if (scancode & KF_REPEAT)
return;
int mpkey = mp_w32_vkey_to_mpkey(vkey, scancode & KF_EXTENDED);
if (!mpkey) {
mpkey = decode_key(w32, vkey, scancode & (0xff | KF_EXTENDED));
if (!mpkey)
return;
}
mp_input_put_key(w32->input_ctx, mpkey | mod_state(w32) | MP_KEY_STATE_DOWN);
}
static void handle_key_up(struct vo_w32_state *w32, UINT vkey, UINT scancode)
{
switch (vkey) {
case VK_MENU:
case VK_CONTROL:
case VK_SHIFT:
break;
default:
// Releasing all keys on key-up is simpler and ensures no keys can be
// get "stuck." This matches the behaviour of other VOs.
mp_input_put_key(w32->input_ctx, MP_INPUT_RELEASE_ALL);
}
}
static bool handle_char(struct vo_w32_state *w32, wchar_t wc)
{
int c = decode_utf16(w32, wc);
if (c == 0)
return true;
if (c < 0x20)
return false;
mp_input_put_key(w32->input_ctx, c | mod_state(w32));
return true;
}
static bool handle_mouse_down(struct vo_w32_state *w32, int btn, int x, int y)
{
btn |= mod_state(w32);
mp_input_put_key(w32->input_ctx, btn | MP_KEY_STATE_DOWN);
if (btn == MP_MBTN_LEFT && !w32->current_fs &&
!mp_input_test_dragging(w32->input_ctx, x, y))
{
// Window dragging hack
ReleaseCapture();
SendMessage(w32->window, WM_NCLBUTTONDOWN, HTCAPTION, 0);
mp_input_put_key(w32->input_ctx, MP_MBTN_LEFT | MP_KEY_STATE_UP);
// Indicate the message was handled, so DefWindowProc won't be called
return true;
}
SetCapture(w32->window);
return false;
}
static void handle_mouse_up(struct vo_w32_state *w32, int btn)
{
btn |= mod_state(w32);
mp_input_put_key(w32->input_ctx, btn | MP_KEY_STATE_UP);
ReleaseCapture();
}
static void handle_mouse_wheel(struct vo_w32_state *w32, bool horiz, int val)
{
int code;
if (horiz)
code = val > 0 ? MP_WHEEL_RIGHT : MP_WHEEL_LEFT;
else
code = val > 0 ? MP_WHEEL_UP : MP_WHEEL_DOWN;
mp_input_put_wheel(w32->input_ctx, code | mod_state(w32), abs(val) / 120.);
}
static void signal_events(struct vo_w32_state *w32, int events)
{
atomic_fetch_or(&w32->event_flags, events);
video: move display and timing to a separate thread The VO is run inside its own thread. It also does most of video timing. The playloop hands the image data and a realtime timestamp to the VO, and the VO does the rest. In particular, this allows the playloop to do other things, instead of blocking for video redraw. But if anything accesses the VO during video timing, it will block. This also fixes vo_sdl.c event handling; but that is only a side-effect, since reimplementing the broken way would require more effort. Also drop --softsleep. In theory, this option helps if the kernel's sleeping mechanism is too inaccurate for video timing. In practice, I haven't ever encountered a situation where it helps, and it just burns CPU cycles. On the other hand it's probably actively harmful, because it prevents the libavcodec decoder threads from doing real work. Side note: Originally, I intended that multiple frames can be queued to the VO. But this is not done, due to problems with OSD and other certain features. OSD in particular is simply designed in a way that it can be neither timed nor copied, so you do have to render it into the video frame before you can draw the next frame. (Subtitles have no such restriction. sd_lavc was even updated to fix this.) It seems the right solution to queuing multiple VO frames is rendering on VO-backed framebuffers, like vo_vdpau.c does. This requires VO driver support, and is out of scope of this commit. As consequence, the VO has a queue size of 1. The existing video queue is just needed to compute frame duration, and will be moved out in the next commit.
2014-08-12 21:02:08 +00:00
vo_wakeup(w32->vo);
}
static void wakeup_gui_thread(void *ctx)
{
struct vo_w32_state *w32 = ctx;
// Wake up the window procedure (which processes the dispatch queue)
if (GetWindowThreadProcessId(w32->window, NULL) == GetCurrentThreadId()) {
PostMessageW(w32->window, WM_NULL, 0, 0);
} else {
// Use a sent message when cross-thread, since the queue of sent
// messages is processed in some cases when posted messages are blocked
SendNotifyMessageW(w32->window, WM_NULL, 0, 0);
}
}
static double get_refresh_rate_from_gdi(const wchar_t *device)
2015-03-10 18:25:30 +00:00
{
DEVMODEW dm = { .dmSize = sizeof dm };
if (!EnumDisplaySettingsW(device, ENUM_CURRENT_SETTINGS, &dm))
return 0.0;
2015-03-10 18:25:30 +00:00
// 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.0;
2015-03-10 18:25:30 +00:00
// 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.
// A caller can always reproduce the original value by using floor.
double rv = dm.dmDisplayFrequency;
switch (dm.dmDisplayFrequency) {
case 23:
case 29:
case 47:
2015-03-10 18:25:30 +00:00
case 59:
case 71:
case 89:
case 95:
2015-03-10 18:25:30 +00:00
case 119:
case 143:
2015-03-10 18:25:30 +00:00
rv = (rv + 1) / 1.001;
}
return rv;
}
static char *get_color_profile(void *ctx, const wchar_t *device)
{
char *name = NULL;
HDC ic = CreateICW(device, NULL, NULL, NULL);
if (!ic)
goto done;
wchar_t wname[MAX_PATH + 1];
if (!GetICMProfileW(ic, &(DWORD){ MAX_PATH }, wname))
goto done;
name = mp_to_utf8(ctx, wname);
done:
if (ic)
DeleteDC(ic);
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)
2015-03-10 18:25:30 +00:00
{
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);
// 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;
2015-03-10 18:25:30 +00:00
signal_events(w32, VO_EVENT_WIN_STATE);
}
char *color_profile = get_color_profile(w32, mi.szDevice);
if ((color_profile == NULL) != (w32->color_profile == NULL) ||
(color_profile && strcmp(color_profile, w32->color_profile)))
{
if (color_profile)
MP_VERBOSE(w32, "color-profile: %s\n", color_profile);
talloc_free(w32->color_profile);
w32->color_profile = color_profile;
color_profile = NULL;
signal_events(w32, VO_EVENT_ICC_PROFILE_CHANGED);
}
talloc_free(color_profile);
2015-03-10 18:25:30 +00:00
}
static void force_update_display_info(struct vo_w32_state *w32)
{
w32->monitor = 0;
update_display_info(w32);
}
static void update_playback_state(struct vo_w32_state *w32)
{
struct voctrl_playback_state *pstate = &w32->current_pstate;
if (!w32->taskbar_list3 || !w32->tbtnCreated)
return;
if (!pstate->playing || !pstate->taskbar_progress) {
ITaskbarList3_SetProgressState(w32->taskbar_list3, w32->window,
TBPF_NOPROGRESS);
return;
}
ITaskbarList3_SetProgressValue(w32->taskbar_list3, w32->window,
pstate->percent_pos, 100);
ITaskbarList3_SetProgressState(w32->taskbar_list3, w32->window,
pstate->paused ? TBPF_PAUSED :
TBPF_NORMAL);
}
struct get_monitor_data {
int i;
int target;
HMONITOR mon;
};
static BOOL CALLBACK get_monitor_proc(HMONITOR mon, HDC dc, LPRECT r, LPARAM p)
{
struct get_monitor_data *data = (struct get_monitor_data*)p;
if (data->i == data->target) {
data->mon = mon;
return FALSE;
}
data->i++;
return TRUE;
}
static HMONITOR get_monitor(int id)
{
struct get_monitor_data data = { .target = id };
EnumDisplayMonitors(NULL, NULL, get_monitor_proc, (LPARAM)&data);
return data.mon;
}
static HMONITOR get_default_monitor(struct vo_w32_state *w32)
{
const int id = w32->current_fs ? w32->opts->fsscreen_id :
w32->opts->screen_id;
// Handle --fs-screen=<all|default> and --screen=default
if (id < 0)
return MonitorFromWindow(w32->window, MONITOR_DEFAULTTOPRIMARY);
HMONITOR mon = get_monitor(id);
if (mon)
return mon;
MP_VERBOSE(w32, "Screen %d does not exist, falling back to primary\n", id);
return MonitorFromPoint((POINT){0, 0}, MONITOR_DEFAULTTOPRIMARY);
}
static MONITORINFO get_monitor_info(struct vo_w32_state *w32)
{
HMONITOR mon;
if (IsWindowVisible(w32->window) && !w32->current_fs) {
mon = MonitorFromWindow(w32->window, MONITOR_DEFAULTTOPRIMARY);
} else {
// The window is not visible during initialization, so get the
// monitor by --screen or --fs-screen id, or fallback to primary.
mon = get_default_monitor(w32);
}
MONITORINFO mi = { .cbSize = sizeof(mi) };
GetMonitorInfoW(mon, &mi);
return mi;
}
static RECT get_screen_area(struct vo_w32_state *w32)
{
// Handle --fs-screen=all
if (w32->current_fs && w32->opts->fsscreen_id == -2) {
const int x = GetSystemMetrics(SM_XVIRTUALSCREEN);
const int y = GetSystemMetrics(SM_YVIRTUALSCREEN);
return (RECT) { x, y, x + GetSystemMetrics(SM_CXVIRTUALSCREEN),
y + GetSystemMetrics(SM_CYVIRTUALSCREEN) };
}
return get_monitor_info(w32).rcMonitor;
}
static RECT get_working_area(struct vo_w32_state *w32)
{
return w32->current_fs ? get_screen_area(w32) :
get_monitor_info(w32).rcWork;
}
static bool snap_to_screen_edges(struct vo_w32_state *w32, RECT *rc)
{
w32_common: refactor and improve window state handling Refactored and split the `reinit_window_state` code into four separate functions: - `update_window_style` used to update window styles without modifying the window rect. - `fit_window_on_screen` used to adjust the window size when it is larger than the screen size. Added a helper function `fit_rect` to fit one rect on another without using any data from w32 struct. - `update_fullscreen_state` used to calculate the new fullscreen state and adjust the window rect accordingly. - `update_window_state` used to display the window on screen with new size, position and ontop state. This commit fixes three issues: - fixed #4753 by skipping `fit_window_on_screen` for a maximized window, since maximized window should already fit on the screen. It should be noted that this bug was only reproducible with `--fit-border` option which is enabled by default. The cause of the bug is that after calling the `add_window_borders` for a maximized window, the rect in result is slightly larger than the screen rect, which is okay, `SetWindowPos` will interpret it as a maximized state later, so no auto-fitting to screen size is needed here. - fixed #5215 by skipping `fit_window_on_screen` when leaving fullscreen. On a multi-monitor system if the mpv window was stretched to cover multiple monitors, its size was reset after switching back from fullscreen to fit the size of the active monitor. Also, when changing `--ontop` and `--border` options, now only the `update_window_style` and `update_window_state` functions are used, so `fit_window_on_screen` is not used for them too. - fixed #2451 by moving the `ITaskbarList2_MarkFullscreenWindow` below the `SetWindowPos`. If the taskbar is notified about fullscreen state before the window is shown on screen, the taskbar button could be missing until Alt-TAB is pressed, usually it was reproducible on Windows 8. Other changes: - In `update_fullscreen_state` the `reset window bounds` debug message now reports client area size and position, instead of window area size and position. This is done for consistency with debug messages in handling fullscreen state above in this function, since they also print window bounds of the client area. - Refactored `gui_thread_reconfig`. Added a new window flag `fit_on_screen` to fit the window on screen even when leaving fullscreen. This is needed for the case when the new video opened while the window is still in the fullscreen state. - Moved parent and fullscreen state checks out from the WM_MOVING to `snap_to_screen_edges` function for consistency with other functions. There's no point in keeping these checks out of the function body.
2017-12-17 00:20:53 +00:00
if (w32->parent || w32->current_fs || IsMaximized(w32->window))
return false;
if (!w32->opts->snap_window) {
w32->snapped = false;
return false;
}
RECT rect;
POINT cursor;
if (!GetWindowRect(w32->window, &rect) || !GetCursorPos(&cursor))
return false;
// Check if window is going to be aero-snapped
if (rect_w(*rc) != rect_w(rect) || rect_h(*rc) != rect_h(rect))
return false;
// Check if window has already been aero-snapped
WINDOWPLACEMENT wp = {0};
wp.length = sizeof(wp);
if (!GetWindowPlacement(w32->window, &wp))
return false;
RECT wr = wp.rcNormalPosition;
if (rect_w(*rc) != rect_w(wr) || rect_h(*rc) != rect_h(wr))
return false;
// Get the work area to let the window snap to taskbar
wr = get_working_area(w32);
// 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 DWORD update_style(struct vo_w32_state *w32, DWORD style)
{
const DWORD NO_FRAME = WS_OVERLAPPED | WS_MINIMIZEBOX;
const DWORD FRAME = WS_OVERLAPPEDWINDOW;
const DWORD FULLSCREEN = NO_FRAME | WS_SYSMENU;
style &= ~(NO_FRAME | FRAME | FULLSCREEN);
if (w32->current_fs) {
style |= FULLSCREEN;
} else {
style |= w32->opts->border ? FRAME : NO_FRAME;
}
return style;
}
w32_common: refactor and improve window state handling Refactored and split the `reinit_window_state` code into four separate functions: - `update_window_style` used to update window styles without modifying the window rect. - `fit_window_on_screen` used to adjust the window size when it is larger than the screen size. Added a helper function `fit_rect` to fit one rect on another without using any data from w32 struct. - `update_fullscreen_state` used to calculate the new fullscreen state and adjust the window rect accordingly. - `update_window_state` used to display the window on screen with new size, position and ontop state. This commit fixes three issues: - fixed #4753 by skipping `fit_window_on_screen` for a maximized window, since maximized window should already fit on the screen. It should be noted that this bug was only reproducible with `--fit-border` option which is enabled by default. The cause of the bug is that after calling the `add_window_borders` for a maximized window, the rect in result is slightly larger than the screen rect, which is okay, `SetWindowPos` will interpret it as a maximized state later, so no auto-fitting to screen size is needed here. - fixed #5215 by skipping `fit_window_on_screen` when leaving fullscreen. On a multi-monitor system if the mpv window was stretched to cover multiple monitors, its size was reset after switching back from fullscreen to fit the size of the active monitor. Also, when changing `--ontop` and `--border` options, now only the `update_window_style` and `update_window_state` functions are used, so `fit_window_on_screen` is not used for them too. - fixed #2451 by moving the `ITaskbarList2_MarkFullscreenWindow` below the `SetWindowPos`. If the taskbar is notified about fullscreen state before the window is shown on screen, the taskbar button could be missing until Alt-TAB is pressed, usually it was reproducible on Windows 8. Other changes: - In `update_fullscreen_state` the `reset window bounds` debug message now reports client area size and position, instead of window area size and position. This is done for consistency with debug messages in handling fullscreen state above in this function, since they also print window bounds of the client area. - Refactored `gui_thread_reconfig`. Added a new window flag `fit_on_screen` to fit the window on screen even when leaving fullscreen. This is needed for the case when the new video opened while the window is still in the fullscreen state. - Moved parent and fullscreen state checks out from the WM_MOVING to `snap_to_screen_edges` function for consistency with other functions. There's no point in keeping these checks out of the function body.
2017-12-17 00:20:53 +00:00
static void update_window_style(struct vo_w32_state *w32)
{
if (w32->parent)
return;
w32_common: refactor and improve window state handling Refactored and split the `reinit_window_state` code into four separate functions: - `update_window_style` used to update window styles without modifying the window rect. - `fit_window_on_screen` used to adjust the window size when it is larger than the screen size. Added a helper function `fit_rect` to fit one rect on another without using any data from w32 struct. - `update_fullscreen_state` used to calculate the new fullscreen state and adjust the window rect accordingly. - `update_window_state` used to display the window on screen with new size, position and ontop state. This commit fixes three issues: - fixed #4753 by skipping `fit_window_on_screen` for a maximized window, since maximized window should already fit on the screen. It should be noted that this bug was only reproducible with `--fit-border` option which is enabled by default. The cause of the bug is that after calling the `add_window_borders` for a maximized window, the rect in result is slightly larger than the screen rect, which is okay, `SetWindowPos` will interpret it as a maximized state later, so no auto-fitting to screen size is needed here. - fixed #5215 by skipping `fit_window_on_screen` when leaving fullscreen. On a multi-monitor system if the mpv window was stretched to cover multiple monitors, its size was reset after switching back from fullscreen to fit the size of the active monitor. Also, when changing `--ontop` and `--border` options, now only the `update_window_style` and `update_window_state` functions are used, so `fit_window_on_screen` is not used for them too. - fixed #2451 by moving the `ITaskbarList2_MarkFullscreenWindow` below the `SetWindowPos`. If the taskbar is notified about fullscreen state before the window is shown on screen, the taskbar button could be missing until Alt-TAB is pressed, usually it was reproducible on Windows 8. Other changes: - In `update_fullscreen_state` the `reset window bounds` debug message now reports client area size and position, instead of window area size and position. This is done for consistency with debug messages in handling fullscreen state above in this function, since they also print window bounds of the client area. - Refactored `gui_thread_reconfig`. Added a new window flag `fit_on_screen` to fit the window on screen even when leaving fullscreen. This is needed for the case when the new video opened while the window is still in the fullscreen state. - Moved parent and fullscreen state checks out from the WM_MOVING to `snap_to_screen_edges` function for consistency with other functions. There's no point in keeping these checks out of the function body.
2017-12-17 00:20:53 +00:00
// SetWindowLongPtr can trigger a WM_SIZE event, so window rect
// has to be saved now and restored after setting the new style.
const RECT wr = w32->windowrc;
const DWORD style = GetWindowLongPtrW(w32->window, GWL_STYLE);
SetWindowLongPtrW(w32->window, GWL_STYLE, update_style(w32, style));
w32->windowrc = wr;
}
w32_common: refactor and improve window state handling Refactored and split the `reinit_window_state` code into four separate functions: - `update_window_style` used to update window styles without modifying the window rect. - `fit_window_on_screen` used to adjust the window size when it is larger than the screen size. Added a helper function `fit_rect` to fit one rect on another without using any data from w32 struct. - `update_fullscreen_state` used to calculate the new fullscreen state and adjust the window rect accordingly. - `update_window_state` used to display the window on screen with new size, position and ontop state. This commit fixes three issues: - fixed #4753 by skipping `fit_window_on_screen` for a maximized window, since maximized window should already fit on the screen. It should be noted that this bug was only reproducible with `--fit-border` option which is enabled by default. The cause of the bug is that after calling the `add_window_borders` for a maximized window, the rect in result is slightly larger than the screen rect, which is okay, `SetWindowPos` will interpret it as a maximized state later, so no auto-fitting to screen size is needed here. - fixed #5215 by skipping `fit_window_on_screen` when leaving fullscreen. On a multi-monitor system if the mpv window was stretched to cover multiple monitors, its size was reset after switching back from fullscreen to fit the size of the active monitor. Also, when changing `--ontop` and `--border` options, now only the `update_window_style` and `update_window_state` functions are used, so `fit_window_on_screen` is not used for them too. - fixed #2451 by moving the `ITaskbarList2_MarkFullscreenWindow` below the `SetWindowPos`. If the taskbar is notified about fullscreen state before the window is shown on screen, the taskbar button could be missing until Alt-TAB is pressed, usually it was reproducible on Windows 8. Other changes: - In `update_fullscreen_state` the `reset window bounds` debug message now reports client area size and position, instead of window area size and position. This is done for consistency with debug messages in handling fullscreen state above in this function, since they also print window bounds of the client area. - Refactored `gui_thread_reconfig`. Added a new window flag `fit_on_screen` to fit the window on screen even when leaving fullscreen. This is needed for the case when the new video opened while the window is still in the fullscreen state. - Moved parent and fullscreen state checks out from the WM_MOVING to `snap_to_screen_edges` function for consistency with other functions. There's no point in keeping these checks out of the function body.
2017-12-17 00:20:53 +00:00
// Adjust rc size and position if its size is larger than rc2.
// returns true if the rectangle was modified.
static bool fit_rect(RECT *rc, RECT *rc2)
{
// Calculate old size and maximum new size
int o_w = rect_w(*rc), o_h = rect_h(*rc);
int n_w = rect_w(*rc2), n_h = rect_h(*rc2);
if (o_w <= n_w && o_h <= n_h)
return false;
// Apply letterboxing
const float o_asp = o_w / (float)MPMAX(o_h, 1);
const float n_asp = n_w / (float)MPMAX(n_h, 1);
if (o_asp > n_asp) {
n_h = n_w / o_asp;
} else {
n_w = n_h * o_asp;
}
w32_common: refactor and improve window state handling Refactored and split the `reinit_window_state` code into four separate functions: - `update_window_style` used to update window styles without modifying the window rect. - `fit_window_on_screen` used to adjust the window size when it is larger than the screen size. Added a helper function `fit_rect` to fit one rect on another without using any data from w32 struct. - `update_fullscreen_state` used to calculate the new fullscreen state and adjust the window rect accordingly. - `update_window_state` used to display the window on screen with new size, position and ontop state. This commit fixes three issues: - fixed #4753 by skipping `fit_window_on_screen` for a maximized window, since maximized window should already fit on the screen. It should be noted that this bug was only reproducible with `--fit-border` option which is enabled by default. The cause of the bug is that after calling the `add_window_borders` for a maximized window, the rect in result is slightly larger than the screen rect, which is okay, `SetWindowPos` will interpret it as a maximized state later, so no auto-fitting to screen size is needed here. - fixed #5215 by skipping `fit_window_on_screen` when leaving fullscreen. On a multi-monitor system if the mpv window was stretched to cover multiple monitors, its size was reset after switching back from fullscreen to fit the size of the active monitor. Also, when changing `--ontop` and `--border` options, now only the `update_window_style` and `update_window_state` functions are used, so `fit_window_on_screen` is not used for them too. - fixed #2451 by moving the `ITaskbarList2_MarkFullscreenWindow` below the `SetWindowPos`. If the taskbar is notified about fullscreen state before the window is shown on screen, the taskbar button could be missing until Alt-TAB is pressed, usually it was reproducible on Windows 8. Other changes: - In `update_fullscreen_state` the `reset window bounds` debug message now reports client area size and position, instead of window area size and position. This is done for consistency with debug messages in handling fullscreen state above in this function, since they also print window bounds of the client area. - Refactored `gui_thread_reconfig`. Added a new window flag `fit_on_screen` to fit the window on screen even when leaving fullscreen. This is needed for the case when the new video opened while the window is still in the fullscreen state. - Moved parent and fullscreen state checks out from the WM_MOVING to `snap_to_screen_edges` function for consistency with other functions. There's no point in keeping these checks out of the function body.
2017-12-17 00:20:53 +00:00
// Calculate new position and save the rect
const int x = rc->left + o_w / 2 - n_w / 2;
const int y = rc->top + o_h / 2 - n_h / 2;
SetRect(rc, x, y, x + n_w, y + n_h);
return true;
}
w32_common: refactor and improve window state handling Refactored and split the `reinit_window_state` code into four separate functions: - `update_window_style` used to update window styles without modifying the window rect. - `fit_window_on_screen` used to adjust the window size when it is larger than the screen size. Added a helper function `fit_rect` to fit one rect on another without using any data from w32 struct. - `update_fullscreen_state` used to calculate the new fullscreen state and adjust the window rect accordingly. - `update_window_state` used to display the window on screen with new size, position and ontop state. This commit fixes three issues: - fixed #4753 by skipping `fit_window_on_screen` for a maximized window, since maximized window should already fit on the screen. It should be noted that this bug was only reproducible with `--fit-border` option which is enabled by default. The cause of the bug is that after calling the `add_window_borders` for a maximized window, the rect in result is slightly larger than the screen rect, which is okay, `SetWindowPos` will interpret it as a maximized state later, so no auto-fitting to screen size is needed here. - fixed #5215 by skipping `fit_window_on_screen` when leaving fullscreen. On a multi-monitor system if the mpv window was stretched to cover multiple monitors, its size was reset after switching back from fullscreen to fit the size of the active monitor. Also, when changing `--ontop` and `--border` options, now only the `update_window_style` and `update_window_state` functions are used, so `fit_window_on_screen` is not used for them too. - fixed #2451 by moving the `ITaskbarList2_MarkFullscreenWindow` below the `SetWindowPos`. If the taskbar is notified about fullscreen state before the window is shown on screen, the taskbar button could be missing until Alt-TAB is pressed, usually it was reproducible on Windows 8. Other changes: - In `update_fullscreen_state` the `reset window bounds` debug message now reports client area size and position, instead of window area size and position. This is done for consistency with debug messages in handling fullscreen state above in this function, since they also print window bounds of the client area. - Refactored `gui_thread_reconfig`. Added a new window flag `fit_on_screen` to fit the window on screen even when leaving fullscreen. This is needed for the case when the new video opened while the window is still in the fullscreen state. - Moved parent and fullscreen state checks out from the WM_MOVING to `snap_to_screen_edges` function for consistency with other functions. There's no point in keeping these checks out of the function body.
2017-12-17 00:20:53 +00:00
// Adjust window size and position if its size is larger than the screen size.
static void fit_window_on_screen(struct vo_w32_state *w32)
{
if (w32->parent || w32->current_fs || IsMaximized(w32->window))
return;
RECT screen = get_working_area(w32);
w32_common: refactor and improve window state handling Refactored and split the `reinit_window_state` code into four separate functions: - `update_window_style` used to update window styles without modifying the window rect. - `fit_window_on_screen` used to adjust the window size when it is larger than the screen size. Added a helper function `fit_rect` to fit one rect on another without using any data from w32 struct. - `update_fullscreen_state` used to calculate the new fullscreen state and adjust the window rect accordingly. - `update_window_state` used to display the window on screen with new size, position and ontop state. This commit fixes three issues: - fixed #4753 by skipping `fit_window_on_screen` for a maximized window, since maximized window should already fit on the screen. It should be noted that this bug was only reproducible with `--fit-border` option which is enabled by default. The cause of the bug is that after calling the `add_window_borders` for a maximized window, the rect in result is slightly larger than the screen rect, which is okay, `SetWindowPos` will interpret it as a maximized state later, so no auto-fitting to screen size is needed here. - fixed #5215 by skipping `fit_window_on_screen` when leaving fullscreen. On a multi-monitor system if the mpv window was stretched to cover multiple monitors, its size was reset after switching back from fullscreen to fit the size of the active monitor. Also, when changing `--ontop` and `--border` options, now only the `update_window_style` and `update_window_state` functions are used, so `fit_window_on_screen` is not used for them too. - fixed #2451 by moving the `ITaskbarList2_MarkFullscreenWindow` below the `SetWindowPos`. If the taskbar is notified about fullscreen state before the window is shown on screen, the taskbar button could be missing until Alt-TAB is pressed, usually it was reproducible on Windows 8. Other changes: - In `update_fullscreen_state` the `reset window bounds` debug message now reports client area size and position, instead of window area size and position. This is done for consistency with debug messages in handling fullscreen state above in this function, since they also print window bounds of the client area. - Refactored `gui_thread_reconfig`. Added a new window flag `fit_on_screen` to fit the window on screen even when leaving fullscreen. This is needed for the case when the new video opened while the window is still in the fullscreen state. - Moved parent and fullscreen state checks out from the WM_MOVING to `snap_to_screen_edges` function for consistency with other functions. There's no point in keeping these checks out of the function body.
2017-12-17 00:20:53 +00:00
if (w32->opts->border && w32->opts->fit_border)
subtract_window_borders(w32->window, &screen);
w32_common: refactor and improve window state handling Refactored and split the `reinit_window_state` code into four separate functions: - `update_window_style` used to update window styles without modifying the window rect. - `fit_window_on_screen` used to adjust the window size when it is larger than the screen size. Added a helper function `fit_rect` to fit one rect on another without using any data from w32 struct. - `update_fullscreen_state` used to calculate the new fullscreen state and adjust the window rect accordingly. - `update_window_state` used to display the window on screen with new size, position and ontop state. This commit fixes three issues: - fixed #4753 by skipping `fit_window_on_screen` for a maximized window, since maximized window should already fit on the screen. It should be noted that this bug was only reproducible with `--fit-border` option which is enabled by default. The cause of the bug is that after calling the `add_window_borders` for a maximized window, the rect in result is slightly larger than the screen rect, which is okay, `SetWindowPos` will interpret it as a maximized state later, so no auto-fitting to screen size is needed here. - fixed #5215 by skipping `fit_window_on_screen` when leaving fullscreen. On a multi-monitor system if the mpv window was stretched to cover multiple monitors, its size was reset after switching back from fullscreen to fit the size of the active monitor. Also, when changing `--ontop` and `--border` options, now only the `update_window_style` and `update_window_state` functions are used, so `fit_window_on_screen` is not used for them too. - fixed #2451 by moving the `ITaskbarList2_MarkFullscreenWindow` below the `SetWindowPos`. If the taskbar is notified about fullscreen state before the window is shown on screen, the taskbar button could be missing until Alt-TAB is pressed, usually it was reproducible on Windows 8. Other changes: - In `update_fullscreen_state` the `reset window bounds` debug message now reports client area size and position, instead of window area size and position. This is done for consistency with debug messages in handling fullscreen state above in this function, since they also print window bounds of the client area. - Refactored `gui_thread_reconfig`. Added a new window flag `fit_on_screen` to fit the window on screen even when leaving fullscreen. This is needed for the case when the new video opened while the window is still in the fullscreen state. - Moved parent and fullscreen state checks out from the WM_MOVING to `snap_to_screen_edges` function for consistency with other functions. There's no point in keeping these checks out of the function body.
2017-12-17 00:20:53 +00:00
if (fit_rect(&w32->windowrc, &screen)) {
MP_VERBOSE(w32, "adjusted window bounds: %d:%d:%d:%d\n",
(int)w32->windowrc.left, (int)w32->windowrc.top,
(int)rect_w(w32->windowrc), (int)rect_h(w32->windowrc));
}
w32_common: refactor and improve window state handling Refactored and split the `reinit_window_state` code into four separate functions: - `update_window_style` used to update window styles without modifying the window rect. - `fit_window_on_screen` used to adjust the window size when it is larger than the screen size. Added a helper function `fit_rect` to fit one rect on another without using any data from w32 struct. - `update_fullscreen_state` used to calculate the new fullscreen state and adjust the window rect accordingly. - `update_window_state` used to display the window on screen with new size, position and ontop state. This commit fixes three issues: - fixed #4753 by skipping `fit_window_on_screen` for a maximized window, since maximized window should already fit on the screen. It should be noted that this bug was only reproducible with `--fit-border` option which is enabled by default. The cause of the bug is that after calling the `add_window_borders` for a maximized window, the rect in result is slightly larger than the screen rect, which is okay, `SetWindowPos` will interpret it as a maximized state later, so no auto-fitting to screen size is needed here. - fixed #5215 by skipping `fit_window_on_screen` when leaving fullscreen. On a multi-monitor system if the mpv window was stretched to cover multiple monitors, its size was reset after switching back from fullscreen to fit the size of the active monitor. Also, when changing `--ontop` and `--border` options, now only the `update_window_style` and `update_window_state` functions are used, so `fit_window_on_screen` is not used for them too. - fixed #2451 by moving the `ITaskbarList2_MarkFullscreenWindow` below the `SetWindowPos`. If the taskbar is notified about fullscreen state before the window is shown on screen, the taskbar button could be missing until Alt-TAB is pressed, usually it was reproducible on Windows 8. Other changes: - In `update_fullscreen_state` the `reset window bounds` debug message now reports client area size and position, instead of window area size and position. This is done for consistency with debug messages in handling fullscreen state above in this function, since they also print window bounds of the client area. - Refactored `gui_thread_reconfig`. Added a new window flag `fit_on_screen` to fit the window on screen even when leaving fullscreen. This is needed for the case when the new video opened while the window is still in the fullscreen state. - Moved parent and fullscreen state checks out from the WM_MOVING to `snap_to_screen_edges` function for consistency with other functions. There's no point in keeping these checks out of the function body.
2017-12-17 00:20:53 +00:00
}
w32_common: refactor and improve window state handling Refactored and split the `reinit_window_state` code into four separate functions: - `update_window_style` used to update window styles without modifying the window rect. - `fit_window_on_screen` used to adjust the window size when it is larger than the screen size. Added a helper function `fit_rect` to fit one rect on another without using any data from w32 struct. - `update_fullscreen_state` used to calculate the new fullscreen state and adjust the window rect accordingly. - `update_window_state` used to display the window on screen with new size, position and ontop state. This commit fixes three issues: - fixed #4753 by skipping `fit_window_on_screen` for a maximized window, since maximized window should already fit on the screen. It should be noted that this bug was only reproducible with `--fit-border` option which is enabled by default. The cause of the bug is that after calling the `add_window_borders` for a maximized window, the rect in result is slightly larger than the screen rect, which is okay, `SetWindowPos` will interpret it as a maximized state later, so no auto-fitting to screen size is needed here. - fixed #5215 by skipping `fit_window_on_screen` when leaving fullscreen. On a multi-monitor system if the mpv window was stretched to cover multiple monitors, its size was reset after switching back from fullscreen to fit the size of the active monitor. Also, when changing `--ontop` and `--border` options, now only the `update_window_style` and `update_window_state` functions are used, so `fit_window_on_screen` is not used for them too. - fixed #2451 by moving the `ITaskbarList2_MarkFullscreenWindow` below the `SetWindowPos`. If the taskbar is notified about fullscreen state before the window is shown on screen, the taskbar button could be missing until Alt-TAB is pressed, usually it was reproducible on Windows 8. Other changes: - In `update_fullscreen_state` the `reset window bounds` debug message now reports client area size and position, instead of window area size and position. This is done for consistency with debug messages in handling fullscreen state above in this function, since they also print window bounds of the client area. - Refactored `gui_thread_reconfig`. Added a new window flag `fit_on_screen` to fit the window on screen even when leaving fullscreen. This is needed for the case when the new video opened while the window is still in the fullscreen state. - Moved parent and fullscreen state checks out from the WM_MOVING to `snap_to_screen_edges` function for consistency with other functions. There's no point in keeping these checks out of the function body.
2017-12-17 00:20:53 +00:00
// Calculate new fullscreen state and change window size and position.
// returns true if fullscreen state was changed.
static bool update_fullscreen_state(struct vo_w32_state *w32)
{
if (w32->parent)
return false;
bool new_fs = w32->opts->fullscreen;
if (w32->toggle_fs) {
new_fs = !w32->current_fs;
w32->toggle_fs = false;
}
w32_common: refactor and improve window state handling Refactored and split the `reinit_window_state` code into four separate functions: - `update_window_style` used to update window styles without modifying the window rect. - `fit_window_on_screen` used to adjust the window size when it is larger than the screen size. Added a helper function `fit_rect` to fit one rect on another without using any data from w32 struct. - `update_fullscreen_state` used to calculate the new fullscreen state and adjust the window rect accordingly. - `update_window_state` used to display the window on screen with new size, position and ontop state. This commit fixes three issues: - fixed #4753 by skipping `fit_window_on_screen` for a maximized window, since maximized window should already fit on the screen. It should be noted that this bug was only reproducible with `--fit-border` option which is enabled by default. The cause of the bug is that after calling the `add_window_borders` for a maximized window, the rect in result is slightly larger than the screen rect, which is okay, `SetWindowPos` will interpret it as a maximized state later, so no auto-fitting to screen size is needed here. - fixed #5215 by skipping `fit_window_on_screen` when leaving fullscreen. On a multi-monitor system if the mpv window was stretched to cover multiple monitors, its size was reset after switching back from fullscreen to fit the size of the active monitor. Also, when changing `--ontop` and `--border` options, now only the `update_window_style` and `update_window_state` functions are used, so `fit_window_on_screen` is not used for them too. - fixed #2451 by moving the `ITaskbarList2_MarkFullscreenWindow` below the `SetWindowPos`. If the taskbar is notified about fullscreen state before the window is shown on screen, the taskbar button could be missing until Alt-TAB is pressed, usually it was reproducible on Windows 8. Other changes: - In `update_fullscreen_state` the `reset window bounds` debug message now reports client area size and position, instead of window area size and position. This is done for consistency with debug messages in handling fullscreen state above in this function, since they also print window bounds of the client area. - Refactored `gui_thread_reconfig`. Added a new window flag `fit_on_screen` to fit the window on screen even when leaving fullscreen. This is needed for the case when the new video opened while the window is still in the fullscreen state. - Moved parent and fullscreen state checks out from the WM_MOVING to `snap_to_screen_edges` function for consistency with other functions. There's no point in keeping these checks out of the function body.
2017-12-17 00:20:53 +00:00
bool toggle_fs = w32->current_fs != new_fs;
w32->opts->fullscreen = w32->current_fs = new_fs;
m_config_cache_write_opt(w32->opts_cache,
&w32->opts->fullscreen);
w32_common: refactor and improve window state handling Refactored and split the `reinit_window_state` code into four separate functions: - `update_window_style` used to update window styles without modifying the window rect. - `fit_window_on_screen` used to adjust the window size when it is larger than the screen size. Added a helper function `fit_rect` to fit one rect on another without using any data from w32 struct. - `update_fullscreen_state` used to calculate the new fullscreen state and adjust the window rect accordingly. - `update_window_state` used to display the window on screen with new size, position and ontop state. This commit fixes three issues: - fixed #4753 by skipping `fit_window_on_screen` for a maximized window, since maximized window should already fit on the screen. It should be noted that this bug was only reproducible with `--fit-border` option which is enabled by default. The cause of the bug is that after calling the `add_window_borders` for a maximized window, the rect in result is slightly larger than the screen rect, which is okay, `SetWindowPos` will interpret it as a maximized state later, so no auto-fitting to screen size is needed here. - fixed #5215 by skipping `fit_window_on_screen` when leaving fullscreen. On a multi-monitor system if the mpv window was stretched to cover multiple monitors, its size was reset after switching back from fullscreen to fit the size of the active monitor. Also, when changing `--ontop` and `--border` options, now only the `update_window_style` and `update_window_state` functions are used, so `fit_window_on_screen` is not used for them too. - fixed #2451 by moving the `ITaskbarList2_MarkFullscreenWindow` below the `SetWindowPos`. If the taskbar is notified about fullscreen state before the window is shown on screen, the taskbar button could be missing until Alt-TAB is pressed, usually it was reproducible on Windows 8. Other changes: - In `update_fullscreen_state` the `reset window bounds` debug message now reports client area size and position, instead of window area size and position. This is done for consistency with debug messages in handling fullscreen state above in this function, since they also print window bounds of the client area. - Refactored `gui_thread_reconfig`. Added a new window flag `fit_on_screen` to fit the window on screen even when leaving fullscreen. This is needed for the case when the new video opened while the window is still in the fullscreen state. - Moved parent and fullscreen state checks out from the WM_MOVING to `snap_to_screen_edges` function for consistency with other functions. There's no point in keeping these checks out of the function body.
2017-12-17 00:20:53 +00:00
if (toggle_fs) {
if (w32->current_fs) {
// Save window rect when switching to fullscreen.
w32->prev_windowrc = w32->windowrc;
MP_VERBOSE(w32, "save window bounds: %d:%d:%d:%d\n",
(int)w32->windowrc.left, (int)w32->windowrc.top,
(int)rect_w(w32->windowrc), (int)rect_h(w32->windowrc));
} else {
w32_common: refactor and improve window state handling Refactored and split the `reinit_window_state` code into four separate functions: - `update_window_style` used to update window styles without modifying the window rect. - `fit_window_on_screen` used to adjust the window size when it is larger than the screen size. Added a helper function `fit_rect` to fit one rect on another without using any data from w32 struct. - `update_fullscreen_state` used to calculate the new fullscreen state and adjust the window rect accordingly. - `update_window_state` used to display the window on screen with new size, position and ontop state. This commit fixes three issues: - fixed #4753 by skipping `fit_window_on_screen` for a maximized window, since maximized window should already fit on the screen. It should be noted that this bug was only reproducible with `--fit-border` option which is enabled by default. The cause of the bug is that after calling the `add_window_borders` for a maximized window, the rect in result is slightly larger than the screen rect, which is okay, `SetWindowPos` will interpret it as a maximized state later, so no auto-fitting to screen size is needed here. - fixed #5215 by skipping `fit_window_on_screen` when leaving fullscreen. On a multi-monitor system if the mpv window was stretched to cover multiple monitors, its size was reset after switching back from fullscreen to fit the size of the active monitor. Also, when changing `--ontop` and `--border` options, now only the `update_window_style` and `update_window_state` functions are used, so `fit_window_on_screen` is not used for them too. - fixed #2451 by moving the `ITaskbarList2_MarkFullscreenWindow` below the `SetWindowPos`. If the taskbar is notified about fullscreen state before the window is shown on screen, the taskbar button could be missing until Alt-TAB is pressed, usually it was reproducible on Windows 8. Other changes: - In `update_fullscreen_state` the `reset window bounds` debug message now reports client area size and position, instead of window area size and position. This is done for consistency with debug messages in handling fullscreen state above in this function, since they also print window bounds of the client area. - Refactored `gui_thread_reconfig`. Added a new window flag `fit_on_screen` to fit the window on screen even when leaving fullscreen. This is needed for the case when the new video opened while the window is still in the fullscreen state. - Moved parent and fullscreen state checks out from the WM_MOVING to `snap_to_screen_edges` function for consistency with other functions. There's no point in keeping these checks out of the function body.
2017-12-17 00:20:53 +00:00
// Restore window rect when switching from fullscreen.
w32->windowrc = w32->prev_windowrc;
}
}
w32_common: refactor and improve window state handling Refactored and split the `reinit_window_state` code into four separate functions: - `update_window_style` used to update window styles without modifying the window rect. - `fit_window_on_screen` used to adjust the window size when it is larger than the screen size. Added a helper function `fit_rect` to fit one rect on another without using any data from w32 struct. - `update_fullscreen_state` used to calculate the new fullscreen state and adjust the window rect accordingly. - `update_window_state` used to display the window on screen with new size, position and ontop state. This commit fixes three issues: - fixed #4753 by skipping `fit_window_on_screen` for a maximized window, since maximized window should already fit on the screen. It should be noted that this bug was only reproducible with `--fit-border` option which is enabled by default. The cause of the bug is that after calling the `add_window_borders` for a maximized window, the rect in result is slightly larger than the screen rect, which is okay, `SetWindowPos` will interpret it as a maximized state later, so no auto-fitting to screen size is needed here. - fixed #5215 by skipping `fit_window_on_screen` when leaving fullscreen. On a multi-monitor system if the mpv window was stretched to cover multiple monitors, its size was reset after switching back from fullscreen to fit the size of the active monitor. Also, when changing `--ontop` and `--border` options, now only the `update_window_style` and `update_window_state` functions are used, so `fit_window_on_screen` is not used for them too. - fixed #2451 by moving the `ITaskbarList2_MarkFullscreenWindow` below the `SetWindowPos`. If the taskbar is notified about fullscreen state before the window is shown on screen, the taskbar button could be missing until Alt-TAB is pressed, usually it was reproducible on Windows 8. Other changes: - In `update_fullscreen_state` the `reset window bounds` debug message now reports client area size and position, instead of window area size and position. This is done for consistency with debug messages in handling fullscreen state above in this function, since they also print window bounds of the client area. - Refactored `gui_thread_reconfig`. Added a new window flag `fit_on_screen` to fit the window on screen even when leaving fullscreen. This is needed for the case when the new video opened while the window is still in the fullscreen state. - Moved parent and fullscreen state checks out from the WM_MOVING to `snap_to_screen_edges` function for consistency with other functions. There's no point in keeping these checks out of the function body.
2017-12-17 00:20:53 +00:00
if (w32->current_fs)
w32->windowrc = get_screen_area(w32);
w32_common: refactor and improve window state handling Refactored and split the `reinit_window_state` code into four separate functions: - `update_window_style` used to update window styles without modifying the window rect. - `fit_window_on_screen` used to adjust the window size when it is larger than the screen size. Added a helper function `fit_rect` to fit one rect on another without using any data from w32 struct. - `update_fullscreen_state` used to calculate the new fullscreen state and adjust the window rect accordingly. - `update_window_state` used to display the window on screen with new size, position and ontop state. This commit fixes three issues: - fixed #4753 by skipping `fit_window_on_screen` for a maximized window, since maximized window should already fit on the screen. It should be noted that this bug was only reproducible with `--fit-border` option which is enabled by default. The cause of the bug is that after calling the `add_window_borders` for a maximized window, the rect in result is slightly larger than the screen rect, which is okay, `SetWindowPos` will interpret it as a maximized state later, so no auto-fitting to screen size is needed here. - fixed #5215 by skipping `fit_window_on_screen` when leaving fullscreen. On a multi-monitor system if the mpv window was stretched to cover multiple monitors, its size was reset after switching back from fullscreen to fit the size of the active monitor. Also, when changing `--ontop` and `--border` options, now only the `update_window_style` and `update_window_state` functions are used, so `fit_window_on_screen` is not used for them too. - fixed #2451 by moving the `ITaskbarList2_MarkFullscreenWindow` below the `SetWindowPos`. If the taskbar is notified about fullscreen state before the window is shown on screen, the taskbar button could be missing until Alt-TAB is pressed, usually it was reproducible on Windows 8. Other changes: - In `update_fullscreen_state` the `reset window bounds` debug message now reports client area size and position, instead of window area size and position. This is done for consistency with debug messages in handling fullscreen state above in this function, since they also print window bounds of the client area. - Refactored `gui_thread_reconfig`. Added a new window flag `fit_on_screen` to fit the window on screen even when leaving fullscreen. This is needed for the case when the new video opened while the window is still in the fullscreen state. - Moved parent and fullscreen state checks out from the WM_MOVING to `snap_to_screen_edges` function for consistency with other functions. There's no point in keeping these checks out of the function body.
2017-12-17 00:20:53 +00:00
MP_VERBOSE(w32, "reset window bounds: %d:%d:%d:%d\n",
w32_common: refactor and improve window state handling Refactored and split the `reinit_window_state` code into four separate functions: - `update_window_style` used to update window styles without modifying the window rect. - `fit_window_on_screen` used to adjust the window size when it is larger than the screen size. Added a helper function `fit_rect` to fit one rect on another without using any data from w32 struct. - `update_fullscreen_state` used to calculate the new fullscreen state and adjust the window rect accordingly. - `update_window_state` used to display the window on screen with new size, position and ontop state. This commit fixes three issues: - fixed #4753 by skipping `fit_window_on_screen` for a maximized window, since maximized window should already fit on the screen. It should be noted that this bug was only reproducible with `--fit-border` option which is enabled by default. The cause of the bug is that after calling the `add_window_borders` for a maximized window, the rect in result is slightly larger than the screen rect, which is okay, `SetWindowPos` will interpret it as a maximized state later, so no auto-fitting to screen size is needed here. - fixed #5215 by skipping `fit_window_on_screen` when leaving fullscreen. On a multi-monitor system if the mpv window was stretched to cover multiple monitors, its size was reset after switching back from fullscreen to fit the size of the active monitor. Also, when changing `--ontop` and `--border` options, now only the `update_window_style` and `update_window_state` functions are used, so `fit_window_on_screen` is not used for them too. - fixed #2451 by moving the `ITaskbarList2_MarkFullscreenWindow` below the `SetWindowPos`. If the taskbar is notified about fullscreen state before the window is shown on screen, the taskbar button could be missing until Alt-TAB is pressed, usually it was reproducible on Windows 8. Other changes: - In `update_fullscreen_state` the `reset window bounds` debug message now reports client area size and position, instead of window area size and position. This is done for consistency with debug messages in handling fullscreen state above in this function, since they also print window bounds of the client area. - Refactored `gui_thread_reconfig`. Added a new window flag `fit_on_screen` to fit the window on screen even when leaving fullscreen. This is needed for the case when the new video opened while the window is still in the fullscreen state. - Moved parent and fullscreen state checks out from the WM_MOVING to `snap_to_screen_edges` function for consistency with other functions. There's no point in keeping these checks out of the function body.
2017-12-17 00:20:53 +00:00
(int)w32->windowrc.left, (int)w32->windowrc.top,
(int)rect_w(w32->windowrc), (int)rect_h(w32->windowrc));
return toggle_fs;
}
w32_common: support minimized and maximized properties Add support for setting window-minimized and window-maximized in Windows. The minimized and maximized state can be set independently. When the window is minimized, the value of window-maximized will determine whether the window is restored to the maximized state or not. Changing state is done with ShowWindow(), which has commands that change the window state and activate it (eg. SW_RESTORE) and commands that change the window state without activating it (eg. SW_SHOWNOACTIVATE.) It would be nice if we could use commands that don't activate the window, so scripts could change the window state in the backrgound without bringing it to the foreground, but there are some problems with that. There is no command to maximize a window without activating it, so SW_MAXIMIZE is used instead. Also, restoring a window from minimize without activating it seems buggy. On my Windows 10 1909 PC, it always moves the window to the back of the z-order. SW_RESTORE is used instead of SW_SHOWNOACTIVATE because of this. This also changes the way the window is initially shown. Previously, the window was made visible as a consequence of the SWP_SHOWWINDOW flag in the first call to SetWindowPos. In order to set the initial minimized or maximized state of the window, the window is shown with the ShowWindow function instead, where the ShowWindow command is determined by whether the window should be initially maximized or minimized. Even when showing the window normally, we should still call ShowWindow with the SW_SHOW command instead of using SetWindowPos, since the first call a process makes to ShowWindow(SW_SHOW) has special behaviour where it uses the show command in the process' STARTUPINFO instead of the command passed to the function, which should fix #5724. Note: While changes to window-minimized while in fullscreen mode should work as expected, changing window-maximized while in fullscreen does not work and won't result in the window changing state, even after leaving fullscreen. For this to work correctly, the fullscreen logic needs to be changed to apply the new maximized state on leaving fullscreen. Fixes: #5724 Fixes: #7351
2019-12-27 15:12:44 +00:00
static void update_minimized_state(struct vo_w32_state *w32)
{
if (w32->parent)
return;
if (!!IsMinimized(w32->window) != w32->opts->window_minimized) {
if (w32->opts->window_minimized) {
ShowWindow(w32->window, SW_SHOWMINNOACTIVE);
} else {
ShowWindow(w32->window, SW_RESTORE);
}
}
}
static void update_maximized_state(struct vo_w32_state *w32)
{
if (w32->parent)
return;
// Don't change the maximized state in fullscreen for now. In future, this
// should be made to apply the maximized state on leaving fullscreen.
if (w32->current_fs)
return;
WINDOWPLACEMENT wp = { .length = sizeof wp };
GetWindowPlacement(w32->window, &wp);
if (wp.showCmd == SW_SHOWMINIMIZED) {
// When the window is minimized, setting this property just changes
// whether it will be maximized when it's restored
if (w32->opts->window_maximized) {
wp.flags |= WPF_RESTORETOMAXIMIZED;
} else {
wp.flags &= ~WPF_RESTORETOMAXIMIZED;
}
SetWindowPlacement(w32->window, &wp);
} else if ((wp.showCmd == SW_SHOWMAXIMIZED) != w32->opts->window_maximized) {
if (w32->opts->window_maximized) {
ShowWindow(w32->window, SW_SHOWMAXIMIZED);
} else {
ShowWindow(w32->window, SW_SHOWNOACTIVATE);
}
}
}
static bool is_visible(HWND window)
{
// Unlike IsWindowVisible, this doesn't check the window's parents
return GetWindowLongPtrW(window, GWL_STYLE) & WS_VISIBLE;
}
w32_common: refactor and improve window state handling Refactored and split the `reinit_window_state` code into four separate functions: - `update_window_style` used to update window styles without modifying the window rect. - `fit_window_on_screen` used to adjust the window size when it is larger than the screen size. Added a helper function `fit_rect` to fit one rect on another without using any data from w32 struct. - `update_fullscreen_state` used to calculate the new fullscreen state and adjust the window rect accordingly. - `update_window_state` used to display the window on screen with new size, position and ontop state. This commit fixes three issues: - fixed #4753 by skipping `fit_window_on_screen` for a maximized window, since maximized window should already fit on the screen. It should be noted that this bug was only reproducible with `--fit-border` option which is enabled by default. The cause of the bug is that after calling the `add_window_borders` for a maximized window, the rect in result is slightly larger than the screen rect, which is okay, `SetWindowPos` will interpret it as a maximized state later, so no auto-fitting to screen size is needed here. - fixed #5215 by skipping `fit_window_on_screen` when leaving fullscreen. On a multi-monitor system if the mpv window was stretched to cover multiple monitors, its size was reset after switching back from fullscreen to fit the size of the active monitor. Also, when changing `--ontop` and `--border` options, now only the `update_window_style` and `update_window_state` functions are used, so `fit_window_on_screen` is not used for them too. - fixed #2451 by moving the `ITaskbarList2_MarkFullscreenWindow` below the `SetWindowPos`. If the taskbar is notified about fullscreen state before the window is shown on screen, the taskbar button could be missing until Alt-TAB is pressed, usually it was reproducible on Windows 8. Other changes: - In `update_fullscreen_state` the `reset window bounds` debug message now reports client area size and position, instead of window area size and position. This is done for consistency with debug messages in handling fullscreen state above in this function, since they also print window bounds of the client area. - Refactored `gui_thread_reconfig`. Added a new window flag `fit_on_screen` to fit the window on screen even when leaving fullscreen. This is needed for the case when the new video opened while the window is still in the fullscreen state. - Moved parent and fullscreen state checks out from the WM_MOVING to `snap_to_screen_edges` function for consistency with other functions. There's no point in keeping these checks out of the function body.
2017-12-17 00:20:53 +00:00
static void update_window_state(struct vo_w32_state *w32)
{
if (w32->parent)
return;
w32_common: refactor and improve window state handling Refactored and split the `reinit_window_state` code into four separate functions: - `update_window_style` used to update window styles without modifying the window rect. - `fit_window_on_screen` used to adjust the window size when it is larger than the screen size. Added a helper function `fit_rect` to fit one rect on another without using any data from w32 struct. - `update_fullscreen_state` used to calculate the new fullscreen state and adjust the window rect accordingly. - `update_window_state` used to display the window on screen with new size, position and ontop state. This commit fixes three issues: - fixed #4753 by skipping `fit_window_on_screen` for a maximized window, since maximized window should already fit on the screen. It should be noted that this bug was only reproducible with `--fit-border` option which is enabled by default. The cause of the bug is that after calling the `add_window_borders` for a maximized window, the rect in result is slightly larger than the screen rect, which is okay, `SetWindowPos` will interpret it as a maximized state later, so no auto-fitting to screen size is needed here. - fixed #5215 by skipping `fit_window_on_screen` when leaving fullscreen. On a multi-monitor system if the mpv window was stretched to cover multiple monitors, its size was reset after switching back from fullscreen to fit the size of the active monitor. Also, when changing `--ontop` and `--border` options, now only the `update_window_style` and `update_window_state` functions are used, so `fit_window_on_screen` is not used for them too. - fixed #2451 by moving the `ITaskbarList2_MarkFullscreenWindow` below the `SetWindowPos`. If the taskbar is notified about fullscreen state before the window is shown on screen, the taskbar button could be missing until Alt-TAB is pressed, usually it was reproducible on Windows 8. Other changes: - In `update_fullscreen_state` the `reset window bounds` debug message now reports client area size and position, instead of window area size and position. This is done for consistency with debug messages in handling fullscreen state above in this function, since they also print window bounds of the client area. - Refactored `gui_thread_reconfig`. Added a new window flag `fit_on_screen` to fit the window on screen even when leaving fullscreen. This is needed for the case when the new video opened while the window is still in the fullscreen state. - Moved parent and fullscreen state checks out from the WM_MOVING to `snap_to_screen_edges` function for consistency with other functions. There's no point in keeping these checks out of the function body.
2017-12-17 00:20:53 +00:00
RECT wr = w32->windowrc;
add_window_borders(w32->window, &wr);
SetWindowPos(w32->window, w32->opts->ontop ? HWND_TOPMOST : HWND_NOTOPMOST,
wr.left, wr.top, rect_w(wr), rect_h(wr),
w32_common: support minimized and maximized properties Add support for setting window-minimized and window-maximized in Windows. The minimized and maximized state can be set independently. When the window is minimized, the value of window-maximized will determine whether the window is restored to the maximized state or not. Changing state is done with ShowWindow(), which has commands that change the window state and activate it (eg. SW_RESTORE) and commands that change the window state without activating it (eg. SW_SHOWNOACTIVATE.) It would be nice if we could use commands that don't activate the window, so scripts could change the window state in the backrgound without bringing it to the foreground, but there are some problems with that. There is no command to maximize a window without activating it, so SW_MAXIMIZE is used instead. Also, restoring a window from minimize without activating it seems buggy. On my Windows 10 1909 PC, it always moves the window to the back of the z-order. SW_RESTORE is used instead of SW_SHOWNOACTIVATE because of this. This also changes the way the window is initially shown. Previously, the window was made visible as a consequence of the SWP_SHOWWINDOW flag in the first call to SetWindowPos. In order to set the initial minimized or maximized state of the window, the window is shown with the ShowWindow function instead, where the ShowWindow command is determined by whether the window should be initially maximized or minimized. Even when showing the window normally, we should still call ShowWindow with the SW_SHOW command instead of using SetWindowPos, since the first call a process makes to ShowWindow(SW_SHOW) has special behaviour where it uses the show command in the process' STARTUPINFO instead of the command passed to the function, which should fix #5724. Note: While changes to window-minimized while in fullscreen mode should work as expected, changing window-maximized while in fullscreen does not work and won't result in the window changing state, even after leaving fullscreen. For this to work correctly, the fullscreen logic needs to be changed to apply the new maximized state on leaving fullscreen. Fixes: #5724 Fixes: #7351
2019-12-27 15:12:44 +00:00
SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
// Show the window if it's not yet visible
if (!is_visible(w32->window)) {
if (w32->opts->window_minimized) {
ShowWindow(w32->window, SW_SHOWMINIMIZED);
update_maximized_state(w32); // Set the WPF_RESTORETOMAXIMIZED flag
} else if (w32->opts->window_maximized) {
ShowWindow(w32->window, SW_SHOWMAXIMIZED);
} else {
ShowWindow(w32->window, SW_SHOW);
}
}
w32_common: refactor and improve window state handling Refactored and split the `reinit_window_state` code into four separate functions: - `update_window_style` used to update window styles without modifying the window rect. - `fit_window_on_screen` used to adjust the window size when it is larger than the screen size. Added a helper function `fit_rect` to fit one rect on another without using any data from w32 struct. - `update_fullscreen_state` used to calculate the new fullscreen state and adjust the window rect accordingly. - `update_window_state` used to display the window on screen with new size, position and ontop state. This commit fixes three issues: - fixed #4753 by skipping `fit_window_on_screen` for a maximized window, since maximized window should already fit on the screen. It should be noted that this bug was only reproducible with `--fit-border` option which is enabled by default. The cause of the bug is that after calling the `add_window_borders` for a maximized window, the rect in result is slightly larger than the screen rect, which is okay, `SetWindowPos` will interpret it as a maximized state later, so no auto-fitting to screen size is needed here. - fixed #5215 by skipping `fit_window_on_screen` when leaving fullscreen. On a multi-monitor system if the mpv window was stretched to cover multiple monitors, its size was reset after switching back from fullscreen to fit the size of the active monitor. Also, when changing `--ontop` and `--border` options, now only the `update_window_style` and `update_window_state` functions are used, so `fit_window_on_screen` is not used for them too. - fixed #2451 by moving the `ITaskbarList2_MarkFullscreenWindow` below the `SetWindowPos`. If the taskbar is notified about fullscreen state before the window is shown on screen, the taskbar button could be missing until Alt-TAB is pressed, usually it was reproducible on Windows 8. Other changes: - In `update_fullscreen_state` the `reset window bounds` debug message now reports client area size and position, instead of window area size and position. This is done for consistency with debug messages in handling fullscreen state above in this function, since they also print window bounds of the client area. - Refactored `gui_thread_reconfig`. Added a new window flag `fit_on_screen` to fit the window on screen even when leaving fullscreen. This is needed for the case when the new video opened while the window is still in the fullscreen state. - Moved parent and fullscreen state checks out from the WM_MOVING to `snap_to_screen_edges` function for consistency with other functions. There's no point in keeping these checks out of the function body.
2017-12-17 00:20:53 +00:00
// Notify the taskbar about the fullscreen state only after the window
// is visible, to make sure the taskbar item has already been created
if (w32->taskbar_list) {
ITaskbarList2_MarkFullscreenWindow(w32->taskbar_list,
w32->window, w32->current_fs);
}
signal_events(w32, VO_EVENT_RESIZE);
}
w32_common: refactor and improve window state handling Refactored and split the `reinit_window_state` code into four separate functions: - `update_window_style` used to update window styles without modifying the window rect. - `fit_window_on_screen` used to adjust the window size when it is larger than the screen size. Added a helper function `fit_rect` to fit one rect on another without using any data from w32 struct. - `update_fullscreen_state` used to calculate the new fullscreen state and adjust the window rect accordingly. - `update_window_state` used to display the window on screen with new size, position and ontop state. This commit fixes three issues: - fixed #4753 by skipping `fit_window_on_screen` for a maximized window, since maximized window should already fit on the screen. It should be noted that this bug was only reproducible with `--fit-border` option which is enabled by default. The cause of the bug is that after calling the `add_window_borders` for a maximized window, the rect in result is slightly larger than the screen rect, which is okay, `SetWindowPos` will interpret it as a maximized state later, so no auto-fitting to screen size is needed here. - fixed #5215 by skipping `fit_window_on_screen` when leaving fullscreen. On a multi-monitor system if the mpv window was stretched to cover multiple monitors, its size was reset after switching back from fullscreen to fit the size of the active monitor. Also, when changing `--ontop` and `--border` options, now only the `update_window_style` and `update_window_state` functions are used, so `fit_window_on_screen` is not used for them too. - fixed #2451 by moving the `ITaskbarList2_MarkFullscreenWindow` below the `SetWindowPos`. If the taskbar is notified about fullscreen state before the window is shown on screen, the taskbar button could be missing until Alt-TAB is pressed, usually it was reproducible on Windows 8. Other changes: - In `update_fullscreen_state` the `reset window bounds` debug message now reports client area size and position, instead of window area size and position. This is done for consistency with debug messages in handling fullscreen state above in this function, since they also print window bounds of the client area. - Refactored `gui_thread_reconfig`. Added a new window flag `fit_on_screen` to fit the window on screen even when leaving fullscreen. This is needed for the case when the new video opened while the window is still in the fullscreen state. - Moved parent and fullscreen state checks out from the WM_MOVING to `snap_to_screen_edges` function for consistency with other functions. There's no point in keeping these checks out of the function body.
2017-12-17 00:20:53 +00:00
static void reinit_window_state(struct vo_w32_state *w32)
{
if (w32->parent)
return;
// The order matters: fs state should be updated prior to changing styles
bool toggle_fs = update_fullscreen_state(w32);
update_window_style(w32);
// Assume that the window has already been fit on screen before switching fs
if (!toggle_fs || w32->fit_on_screen) {
fit_window_on_screen(w32);
// The fullscreen state might still be active, so set the flag
// to fit on screen next time the window leaves the fullscreen.
w32->fit_on_screen = w32->current_fs;
}
// Show and activate the window after all window state parameters were set
update_window_state(w32);
}
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam)
{
struct vo_w32_state *w32 = (void*)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
if (!w32) {
// WM_NCCREATE is supposed to be the first message that a window
// receives. It allows struct vo_w32_state to be passed from
// CreateWindow's lpParam to the window procedure. However, as a
// longstanding Windows bug, overlapped top-level windows will get a
// WM_GETMINMAXINFO before WM_NCCREATE. This can be ignored.
if (message != WM_NCCREATE)
return DefWindowProcW(hWnd, message, wParam, lParam);
CREATESTRUCTW *cs = (CREATESTRUCTW *)lParam;
w32 = cs->lpCreateParams;
w32->window = hWnd;
SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LONG_PTR)w32);
}
// The dispatch queue should be processed as soon as possible to prevent
// playback glitches, since it is likely blocking the VO thread
if (!w32->in_dispatch) {
w32->in_dispatch = true;
mp_dispatch_queue_process(w32->dispatch, 0);
w32->in_dispatch = false;
}
switch (message) {
case WM_ERASEBKGND: // no need to erase background separately
return 1;
case WM_PAINT:
signal_events(w32, VO_EVENT_EXPOSE);
break;
case WM_MOVE: {
const int x = GET_X_LPARAM(lParam), y = GET_Y_LPARAM(lParam);
OffsetRect(&w32->windowrc, x - w32->windowrc.left,
y - w32->windowrc.top);
// Window may intersect with new monitors (see VOCTRL_GET_DISPLAY_NAMES)
signal_events(w32, VO_EVENT_WIN_STATE);
update_display_info(w32); // if we moved between monitors
MP_DBG(w32, "move window: %d:%d\n", x, y);
break;
}
case WM_MOVING: {
w32->moving = true;
RECT *rc = (RECT*)lParam;
w32_common: refactor and improve window state handling Refactored and split the `reinit_window_state` code into four separate functions: - `update_window_style` used to update window styles without modifying the window rect. - `fit_window_on_screen` used to adjust the window size when it is larger than the screen size. Added a helper function `fit_rect` to fit one rect on another without using any data from w32 struct. - `update_fullscreen_state` used to calculate the new fullscreen state and adjust the window rect accordingly. - `update_window_state` used to display the window on screen with new size, position and ontop state. This commit fixes three issues: - fixed #4753 by skipping `fit_window_on_screen` for a maximized window, since maximized window should already fit on the screen. It should be noted that this bug was only reproducible with `--fit-border` option which is enabled by default. The cause of the bug is that after calling the `add_window_borders` for a maximized window, the rect in result is slightly larger than the screen rect, which is okay, `SetWindowPos` will interpret it as a maximized state later, so no auto-fitting to screen size is needed here. - fixed #5215 by skipping `fit_window_on_screen` when leaving fullscreen. On a multi-monitor system if the mpv window was stretched to cover multiple monitors, its size was reset after switching back from fullscreen to fit the size of the active monitor. Also, when changing `--ontop` and `--border` options, now only the `update_window_style` and `update_window_state` functions are used, so `fit_window_on_screen` is not used for them too. - fixed #2451 by moving the `ITaskbarList2_MarkFullscreenWindow` below the `SetWindowPos`. If the taskbar is notified about fullscreen state before the window is shown on screen, the taskbar button could be missing until Alt-TAB is pressed, usually it was reproducible on Windows 8. Other changes: - In `update_fullscreen_state` the `reset window bounds` debug message now reports client area size and position, instead of window area size and position. This is done for consistency with debug messages in handling fullscreen state above in this function, since they also print window bounds of the client area. - Refactored `gui_thread_reconfig`. Added a new window flag `fit_on_screen` to fit the window on screen even when leaving fullscreen. This is needed for the case when the new video opened while the window is still in the fullscreen state. - Moved parent and fullscreen state checks out from the WM_MOVING to `snap_to_screen_edges` function for consistency with other functions. There's no point in keeping these checks out of the function body.
2017-12-17 00:20:53 +00:00
if (snap_to_screen_edges(w32, rc))
return TRUE;
break;
}
case WM_ENTERSIZEMOVE:
w32->moving = true;
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_EXITSIZEMOVE:
w32->moving = false;
break;
case WM_SIZE: {
if (w32->moving)
w32->snapped = false;
const int w = LOWORD(lParam), h = HIWORD(lParam);
if (w > 0 && h > 0) {
w32->windowrc.right = w32->windowrc.left + w;
w32->windowrc.bottom = w32->windowrc.top + h;
signal_events(w32, VO_EVENT_RESIZE);
MP_VERBOSE(w32, "resize window: %d:%d\n", w, h);
}
w32_common: support minimized and maximized properties Add support for setting window-minimized and window-maximized in Windows. The minimized and maximized state can be set independently. When the window is minimized, the value of window-maximized will determine whether the window is restored to the maximized state or not. Changing state is done with ShowWindow(), which has commands that change the window state and activate it (eg. SW_RESTORE) and commands that change the window state without activating it (eg. SW_SHOWNOACTIVATE.) It would be nice if we could use commands that don't activate the window, so scripts could change the window state in the backrgound without bringing it to the foreground, but there are some problems with that. There is no command to maximize a window without activating it, so SW_MAXIMIZE is used instead. Also, restoring a window from minimize without activating it seems buggy. On my Windows 10 1909 PC, it always moves the window to the back of the z-order. SW_RESTORE is used instead of SW_SHOWNOACTIVATE because of this. This also changes the way the window is initially shown. Previously, the window was made visible as a consequence of the SWP_SHOWWINDOW flag in the first call to SetWindowPos. In order to set the initial minimized or maximized state of the window, the window is shown with the ShowWindow function instead, where the ShowWindow command is determined by whether the window should be initially maximized or minimized. Even when showing the window normally, we should still call ShowWindow with the SW_SHOW command instead of using SetWindowPos, since the first call a process makes to ShowWindow(SW_SHOW) has special behaviour where it uses the show command in the process' STARTUPINFO instead of the command passed to the function, which should fix #5724. Note: While changes to window-minimized while in fullscreen mode should work as expected, changing window-maximized while in fullscreen does not work and won't result in the window changing state, even after leaving fullscreen. For this to work correctly, the fullscreen logic needs to be changed to apply the new maximized state on leaving fullscreen. Fixes: #5724 Fixes: #7351
2019-12-27 15:12:44 +00:00
// Window may have been minimized, maximized or restored
if (is_visible(w32->window)) {
WINDOWPLACEMENT wp = { .length = sizeof wp };
GetWindowPlacement(w32->window, &wp);
bool is_minimized = wp.showCmd == SW_SHOWMINIMIZED;
if (w32->opts->window_minimized != is_minimized) {
w32->opts->window_minimized = is_minimized;
m_config_cache_write_opt(w32->opts_cache,
&w32->opts->window_minimized);
}
bool is_maximized = wp.showCmd == SW_SHOWMAXIMIZED ||
(wp.showCmd == SW_SHOWMINIMIZED &&
(wp.flags & WPF_RESTORETOMAXIMIZED));
if (w32->opts->window_maximized != is_maximized) {
w32->opts->window_maximized = is_maximized;
m_config_cache_write_opt(w32->opts_cache,
&w32->opts->window_maximized);
}
}
signal_events(w32, VO_EVENT_WIN_STATE);
update_display_info(w32);
break;
}
case WM_SIZING:
if (w32->opts->keepaspect && w32->opts->keepaspect_window &&
!w32->current_fs && !w32->parent)
{
RECT *rc = (RECT*)lParam;
// get client area of the windows if it had the rect rc
// (subtracting the window borders)
RECT r = *rc;
subtract_window_borders(w32->window, &r);
int c_w = rect_w(r), c_h = rect_h(r);
float aspect = w32->o_dwidth / (float) MPMAX(w32->o_dheight, 1);
int d_w = c_h * aspect - c_w;
int d_h = c_w / aspect - c_h;
int d_corners[4] = { d_w, d_h, -d_w, -d_h };
int corners[4] = { rc->left, rc->top, rc->right, rc->bottom };
int corner = get_resize_border(wParam);
if (corner >= 0)
corners[corner] -= d_corners[corner];
*rc = (RECT) { corners[0], corners[1], corners[2], corners[3] };
return TRUE;
}
break;
case WM_DPICHANGED:
update_display_info(w32);
break;
case WM_CLOSE:
// Don't destroy the window yet to not lose wakeup events.
mp_input_put_key(w32->input_ctx, MP_KEY_CLOSE_WIN);
return 0;
case WM_NCDESTROY: // Sometimes only WM_NCDESTROY is received in --wid mode
case WM_DESTROY:
if (w32->destroyed)
break;
// If terminate is not set, something else destroyed the window. This
// can also happen in --wid mode when the parent window is destroyed.
if (!w32->terminate)
mp_input_put_key(w32->input_ctx, MP_KEY_CLOSE_WIN);
RevokeDragDrop(w32->window);
w32->destroyed = true;
w32->window = NULL;
PostQuitMessage(0);
break;
case WM_SYSCOMMAND:
switch (wParam & 0xFFF0) {
case SC_SCREENSAVE:
case SC_MONITORPOWER:
if (w32->disable_screensaver) {
MP_VERBOSE(w32, "killing screensaver\n");
return 0;
}
break;
case SC_RESTORE:
if (IsMaximized(w32->window) && w32->current_fs) {
w32->toggle_fs = true;
reinit_window_state(w32);
return 0;
}
break;
}
break;
case WM_NCHITTEST:
// Provide sizing handles for borderless windows
if (!w32->opts->border && !w32->current_fs) {
return borderless_nchittest(w32, GET_X_LPARAM(lParam),
GET_Y_LPARAM(lParam));
}
break;
case WM_APPCOMMAND:
if (handle_appcommand(w32, GET_APPCOMMAND_LPARAM(lParam)))
return TRUE;
break;
case WM_SYSKEYDOWN:
// Open the window menu on Alt+Space. Normally DefWindowProc opens the
// window menu in response to WM_SYSCHAR, but since mpv translates its
// own keyboard input, WM_SYSCHAR isn't generated, so the window menu
// must be opened manually.
if (wParam == VK_SPACE) {
SendMessage(w32->window, WM_SYSCOMMAND, SC_KEYMENU, ' ');
return 0;
}
handle_key_down(w32, wParam, HIWORD(lParam));
if (wParam == VK_F10)
return 0;
break;
case WM_KEYDOWN:
handle_key_down(w32, wParam, HIWORD(lParam));
break;
case WM_SYSKEYUP:
case WM_KEYUP:
handle_key_up(w32, wParam, HIWORD(lParam));
if (wParam == VK_F10)
return 0;
break;
case WM_CHAR:
case WM_SYSCHAR:
if (handle_char(w32, wParam))
return 0;
break;
case WM_KILLFOCUS:
mp_input_put_key(w32->input_ctx, MP_INPUT_RELEASE_ALL);
break;
case WM_SETCURSOR:
// The cursor should only be hidden if the mouse is in the client area
// and if the window isn't in menu mode (HIWORD(lParam) is non-zero)
w32->can_set_cursor = LOWORD(lParam) == HTCLIENT && HIWORD(lParam);
if (w32->can_set_cursor && !w32->cursor_visible) {
SetCursor(NULL);
return TRUE;
}
break;
case WM_MOUSELEAVE:
w32->tracking = FALSE;
mp_input_put_key(w32->input_ctx, MP_KEY_MOUSE_LEAVE);
break;
case WM_MOUSEMOVE: {
if (!w32->tracking) {
w32->tracking = TrackMouseEvent(&w32->trackEvent);
mp_input_put_key(w32->input_ctx, MP_KEY_MOUSE_ENTER);
}
// Windows can send spurious mouse events, which would make the mpv
// core unhide the mouse cursor on completely unrelated events. See:
// https://blogs.msdn.com/b/oldnewthing/archive/2003/10/01/55108.aspx
int x = GET_X_LPARAM(lParam);
int y = GET_Y_LPARAM(lParam);
if (x != w32->mouse_x || y != w32->mouse_y) {
w32->mouse_x = x;
w32->mouse_y = y;
mp_input_set_mouse_pos(w32->input_ctx, x, y);
}
break;
}
case WM_LBUTTONDOWN:
if (handle_mouse_down(w32, MP_MBTN_LEFT, GET_X_LPARAM(lParam),
GET_Y_LPARAM(lParam)))
return 0;
break;
case WM_LBUTTONUP:
handle_mouse_up(w32, MP_MBTN_LEFT);
break;
case WM_MBUTTONDOWN:
handle_mouse_down(w32, MP_MBTN_MID, GET_X_LPARAM(lParam),
GET_Y_LPARAM(lParam));
break;
case WM_MBUTTONUP:
handle_mouse_up(w32, MP_MBTN_MID);
break;
case WM_RBUTTONDOWN:
handle_mouse_down(w32, MP_MBTN_RIGHT, GET_X_LPARAM(lParam),
GET_Y_LPARAM(lParam));
break;
case WM_RBUTTONUP:
handle_mouse_up(w32, MP_MBTN_RIGHT);
break;
case WM_MOUSEWHEEL:
handle_mouse_wheel(w32, false, GET_WHEEL_DELTA_WPARAM(wParam));
return 0;
case WM_MOUSEHWHEEL:
handle_mouse_wheel(w32, true, GET_WHEEL_DELTA_WPARAM(wParam));
// Some buggy mouse drivers (SetPoint) stop delivering WM_MOUSEHWHEEL
// events when the message loop doesn't return TRUE (even on Windows 7)
return TRUE;
case WM_XBUTTONDOWN:
handle_mouse_down(w32,
HIWORD(wParam) == 1 ? MP_MBTN_BACK : MP_MBTN_FORWARD,
GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
break;
case WM_XBUTTONUP:
handle_mouse_up(w32,
HIWORD(wParam) == 1 ? MP_MBTN_BACK : MP_MBTN_FORWARD);
break;
2015-03-10 18:25:30 +00:00
case WM_DISPLAYCHANGE:
force_update_display_info(w32);
2015-03-10 18:25:30 +00:00
break;
}
if (message == w32->tbtnCreatedMsg) {
w32->tbtnCreated = true;
update_playback_state(w32);
return 0;
}
return DefWindowProcW(hWnd, message, wParam, lParam);
}
static pthread_once_t window_class_init_once = PTHREAD_ONCE_INIT;
static ATOM window_class;
static void register_window_class(void)
{
window_class = RegisterClassExW(&(WNDCLASSEXW) {
.cbSize = sizeof(WNDCLASSEXW),
.style = CS_HREDRAW | CS_VREDRAW,
.lpfnWndProc = WndProc,
.hInstance = HINST_THISCOMPONENT,
.hIcon = LoadIconW(HINST_THISCOMPONENT, L"IDI_ICON1"),
.hCursor = LoadCursor(NULL, IDC_ARROW),
.lpszClassName = L"mpv",
});
}
static ATOM get_window_class(void)
{
pthread_once(&window_class_init_once, register_window_class);
return window_class;
}
static void resize_child_win(HWND parent)
{
// Check if an mpv window is a child of this window. This will not
// necessarily be the case because the hook functions will run for all
// windows on the parent window's thread.
ATOM cls = get_window_class();
HWND child = FindWindowExW(parent, NULL, (LPWSTR)MAKEINTATOM(cls), NULL);
if (!child)
return;
// Make sure the window was created by this instance
if (GetWindowLongPtrW(child, GWLP_HINSTANCE) != (LONG_PTR)HINST_THISCOMPONENT)
return;
// Resize the mpv window to match its parent window's size
RECT rm, rp;
if (!GetClientRect(child, &rm))
return;
if (!GetClientRect(parent, &rp))
return;
if (EqualRect(&rm, &rp))
return;
SetWindowPos(child, NULL, 0, 0, rp.right, rp.bottom, SWP_ASYNCWINDOWPOS |
SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING);
}
static LRESULT CALLBACK parent_win_hook(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode != HC_ACTION)
goto done;
CWPSTRUCT *cwp = (CWPSTRUCT*)lParam;
if (cwp->message != WM_WINDOWPOSCHANGED)
goto done;
resize_child_win(cwp->hwnd);
done:
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
static void CALLBACK parent_evt_hook(HWINEVENTHOOK hWinEventHook, DWORD event,
HWND hwnd, LONG idObject, LONG idChild, DWORD dwEventThread,
DWORD dwmsEventTime)
{
if (event != EVENT_OBJECT_LOCATIONCHANGE)
return;
if (!hwnd || idObject != OBJID_WINDOW || idChild != CHILDID_SELF)
return;
resize_child_win(hwnd);
}
static void install_parent_hook(struct vo_w32_state *w32)
{
DWORD pid;
DWORD tid = GetWindowThreadProcessId(w32->parent, &pid);
// If the parent lives inside the current process, install a Windows hook
if (pid == GetCurrentProcessId()) {
w32->parent_win_hook = SetWindowsHookExW(WH_CALLWNDPROC,
parent_win_hook, NULL, tid);
} else {
// Otherwise, use a WinEvent hook. These don't seem to be as smooth as
// Windows hooks, but they can be delivered across process boundaries.
w32->parent_evt_hook = SetWinEventHook(
EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE,
NULL, parent_evt_hook, pid, tid, WINEVENT_OUTOFCONTEXT);
}
}
static void remove_parent_hook(struct vo_w32_state *w32)
{
if (w32->parent_win_hook)
UnhookWindowsHookEx(w32->parent_win_hook);
if (w32->parent_evt_hook)
UnhookWinEvent(w32->parent_evt_hook);
}
// Dispatch incoming window events and handle them.
// This returns only when the thread is asked to terminate.
static void run_message_loop(struct vo_w32_state *w32)
{
MSG msg;
while (GetMessageW(&msg, 0, 0, 0) > 0)
DispatchMessageW(&msg);
// Even if the message loop somehow exits, we still have to respond to
// external requests until termination is requested.
while (!w32->terminate)
mp_dispatch_queue_process(w32->dispatch, 1000);
}
static void gui_thread_reconfig(void *ptr)
{
struct vo_w32_state *w32 = ptr;
struct vo *vo = w32->vo;
RECT r = get_working_area(w32);
struct mp_rect screen = { r.left, r.top, r.right, r.bottom };
struct vo_win_geometry geo;
vo_calc_window_geometry(vo, &screen, &geo);
vo_apply_window_geometry(vo, &geo);
bool reset_size = w32->o_dwidth != vo->dwidth ||
w32->o_dheight != vo->dheight;
win32: fix window creation and size handling This commit fixes various issues with the way the window position and size is setup. Most importantly, it fixes some bugs with restoring from fullscreen state. Rename create_rendering_context() to reinit_window_state(). This function doesn't create anything, it just sets the window bounds and styles. Do not use vo_dx/dy for the window position, as video_out.c overwrites it with each vo_config() call. Use private variables window_x/y instead. A big cause for issues was that reinit_window_state() accidentally cleared the WS_VISIBLE style. I suspect that the API call to temporarily hide the window was a hack to deal with this. Another bug was that the window style was changed without calling SetWindowPos with SWP_FRAMECHANGED (as the MSDN documentation says). Properly initialize window position and size on vo_config following the same rules as the x11 backend: - Never change the window position. The window position should be kept, as the user might move the window, and resetting the window position e.g. during ordered chapter playback is not desired. - Never change the window size, unless the size of the video changes. These rules don't apply to fullscreen. When switching from fullscreen to windowed mode, the backend should restore the previous windowed size and position. When the VO was reconfigured during playback (vo_config() etc.), the saved window position and size should be changed according to the rules above, even if the window was in fullscreen mode during reconfiguring. Note that these rules might be perceived as awkward by some users: if you play multiple files with different resolutions, the window won't be centered when playing the files after the first. This is not a bug.
2011-10-22 00:11:14 +00:00
w32->o_dwidth = vo->dwidth;
w32->o_dheight = vo->dheight;
w32_common: refactor and improve window state handling Refactored and split the `reinit_window_state` code into four separate functions: - `update_window_style` used to update window styles without modifying the window rect. - `fit_window_on_screen` used to adjust the window size when it is larger than the screen size. Added a helper function `fit_rect` to fit one rect on another without using any data from w32 struct. - `update_fullscreen_state` used to calculate the new fullscreen state and adjust the window rect accordingly. - `update_window_state` used to display the window on screen with new size, position and ontop state. This commit fixes three issues: - fixed #4753 by skipping `fit_window_on_screen` for a maximized window, since maximized window should already fit on the screen. It should be noted that this bug was only reproducible with `--fit-border` option which is enabled by default. The cause of the bug is that after calling the `add_window_borders` for a maximized window, the rect in result is slightly larger than the screen rect, which is okay, `SetWindowPos` will interpret it as a maximized state later, so no auto-fitting to screen size is needed here. - fixed #5215 by skipping `fit_window_on_screen` when leaving fullscreen. On a multi-monitor system if the mpv window was stretched to cover multiple monitors, its size was reset after switching back from fullscreen to fit the size of the active monitor. Also, when changing `--ontop` and `--border` options, now only the `update_window_style` and `update_window_state` functions are used, so `fit_window_on_screen` is not used for them too. - fixed #2451 by moving the `ITaskbarList2_MarkFullscreenWindow` below the `SetWindowPos`. If the taskbar is notified about fullscreen state before the window is shown on screen, the taskbar button could be missing until Alt-TAB is pressed, usually it was reproducible on Windows 8. Other changes: - In `update_fullscreen_state` the `reset window bounds` debug message now reports client area size and position, instead of window area size and position. This is done for consistency with debug messages in handling fullscreen state above in this function, since they also print window bounds of the client area. - Refactored `gui_thread_reconfig`. Added a new window flag `fit_on_screen` to fit the window on screen even when leaving fullscreen. This is needed for the case when the new video opened while the window is still in the fullscreen state. - Moved parent and fullscreen state checks out from the WM_MOVING to `snap_to_screen_edges` function for consistency with other functions. There's no point in keeping these checks out of the function body.
2017-12-17 00:20:53 +00:00
if (!w32->parent && !w32->window_bounds_initialized) {
SetRect(&w32->windowrc, geo.win.x0, geo.win.y0,
geo.win.x0 + vo->dwidth, geo.win.y0 + vo->dheight);
w32->prev_windowrc = w32->windowrc;
w32->window_bounds_initialized = true;
w32->fit_on_screen = true;
goto finish;
}
w32_common: refactor and improve window state handling Refactored and split the `reinit_window_state` code into four separate functions: - `update_window_style` used to update window styles without modifying the window rect. - `fit_window_on_screen` used to adjust the window size when it is larger than the screen size. Added a helper function `fit_rect` to fit one rect on another without using any data from w32 struct. - `update_fullscreen_state` used to calculate the new fullscreen state and adjust the window rect accordingly. - `update_window_state` used to display the window on screen with new size, position and ontop state. This commit fixes three issues: - fixed #4753 by skipping `fit_window_on_screen` for a maximized window, since maximized window should already fit on the screen. It should be noted that this bug was only reproducible with `--fit-border` option which is enabled by default. The cause of the bug is that after calling the `add_window_borders` for a maximized window, the rect in result is slightly larger than the screen rect, which is okay, `SetWindowPos` will interpret it as a maximized state later, so no auto-fitting to screen size is needed here. - fixed #5215 by skipping `fit_window_on_screen` when leaving fullscreen. On a multi-monitor system if the mpv window was stretched to cover multiple monitors, its size was reset after switching back from fullscreen to fit the size of the active monitor. Also, when changing `--ontop` and `--border` options, now only the `update_window_style` and `update_window_state` functions are used, so `fit_window_on_screen` is not used for them too. - fixed #2451 by moving the `ITaskbarList2_MarkFullscreenWindow` below the `SetWindowPos`. If the taskbar is notified about fullscreen state before the window is shown on screen, the taskbar button could be missing until Alt-TAB is pressed, usually it was reproducible on Windows 8. Other changes: - In `update_fullscreen_state` the `reset window bounds` debug message now reports client area size and position, instead of window area size and position. This is done for consistency with debug messages in handling fullscreen state above in this function, since they also print window bounds of the client area. - Refactored `gui_thread_reconfig`. Added a new window flag `fit_on_screen` to fit the window on screen even when leaving fullscreen. This is needed for the case when the new video opened while the window is still in the fullscreen state. - Moved parent and fullscreen state checks out from the WM_MOVING to `snap_to_screen_edges` function for consistency with other functions. There's no point in keeping these checks out of the function body.
2017-12-17 00:20:53 +00:00
// The rect which size is going to be modified.
RECT *rc = &w32->windowrc;
// The desired size always matches the window size in wid mode.
if (!reset_size || w32->parent) {
GetClientRect(w32->window, &r);
w32_common: refactor and improve window state handling Refactored and split the `reinit_window_state` code into four separate functions: - `update_window_style` used to update window styles without modifying the window rect. - `fit_window_on_screen` used to adjust the window size when it is larger than the screen size. Added a helper function `fit_rect` to fit one rect on another without using any data from w32 struct. - `update_fullscreen_state` used to calculate the new fullscreen state and adjust the window rect accordingly. - `update_window_state` used to display the window on screen with new size, position and ontop state. This commit fixes three issues: - fixed #4753 by skipping `fit_window_on_screen` for a maximized window, since maximized window should already fit on the screen. It should be noted that this bug was only reproducible with `--fit-border` option which is enabled by default. The cause of the bug is that after calling the `add_window_borders` for a maximized window, the rect in result is slightly larger than the screen rect, which is okay, `SetWindowPos` will interpret it as a maximized state later, so no auto-fitting to screen size is needed here. - fixed #5215 by skipping `fit_window_on_screen` when leaving fullscreen. On a multi-monitor system if the mpv window was stretched to cover multiple monitors, its size was reset after switching back from fullscreen to fit the size of the active monitor. Also, when changing `--ontop` and `--border` options, now only the `update_window_style` and `update_window_state` functions are used, so `fit_window_on_screen` is not used for them too. - fixed #2451 by moving the `ITaskbarList2_MarkFullscreenWindow` below the `SetWindowPos`. If the taskbar is notified about fullscreen state before the window is shown on screen, the taskbar button could be missing until Alt-TAB is pressed, usually it was reproducible on Windows 8. Other changes: - In `update_fullscreen_state` the `reset window bounds` debug message now reports client area size and position, instead of window area size and position. This is done for consistency with debug messages in handling fullscreen state above in this function, since they also print window bounds of the client area. - Refactored `gui_thread_reconfig`. Added a new window flag `fit_on_screen` to fit the window on screen even when leaving fullscreen. This is needed for the case when the new video opened while the window is still in the fullscreen state. - Moved parent and fullscreen state checks out from the WM_MOVING to `snap_to_screen_edges` function for consistency with other functions. There's no point in keeping these checks out of the function body.
2017-12-17 00:20:53 +00:00
// Restore vo_dwidth and vo_dheight, which were reset in vo_config()
vo->dwidth = r.right;
vo->dheight = r.bottom;
w32_common: refactor and improve window state handling Refactored and split the `reinit_window_state` code into four separate functions: - `update_window_style` used to update window styles without modifying the window rect. - `fit_window_on_screen` used to adjust the window size when it is larger than the screen size. Added a helper function `fit_rect` to fit one rect on another without using any data from w32 struct. - `update_fullscreen_state` used to calculate the new fullscreen state and adjust the window rect accordingly. - `update_window_state` used to display the window on screen with new size, position and ontop state. This commit fixes three issues: - fixed #4753 by skipping `fit_window_on_screen` for a maximized window, since maximized window should already fit on the screen. It should be noted that this bug was only reproducible with `--fit-border` option which is enabled by default. The cause of the bug is that after calling the `add_window_borders` for a maximized window, the rect in result is slightly larger than the screen rect, which is okay, `SetWindowPos` will interpret it as a maximized state later, so no auto-fitting to screen size is needed here. - fixed #5215 by skipping `fit_window_on_screen` when leaving fullscreen. On a multi-monitor system if the mpv window was stretched to cover multiple monitors, its size was reset after switching back from fullscreen to fit the size of the active monitor. Also, when changing `--ontop` and `--border` options, now only the `update_window_style` and `update_window_state` functions are used, so `fit_window_on_screen` is not used for them too. - fixed #2451 by moving the `ITaskbarList2_MarkFullscreenWindow` below the `SetWindowPos`. If the taskbar is notified about fullscreen state before the window is shown on screen, the taskbar button could be missing until Alt-TAB is pressed, usually it was reproducible on Windows 8. Other changes: - In `update_fullscreen_state` the `reset window bounds` debug message now reports client area size and position, instead of window area size and position. This is done for consistency with debug messages in handling fullscreen state above in this function, since they also print window bounds of the client area. - Refactored `gui_thread_reconfig`. Added a new window flag `fit_on_screen` to fit the window on screen even when leaving fullscreen. This is needed for the case when the new video opened while the window is still in the fullscreen state. - Moved parent and fullscreen state checks out from the WM_MOVING to `snap_to_screen_edges` function for consistency with other functions. There's no point in keeping these checks out of the function body.
2017-12-17 00:20:53 +00:00
} else {
if (w32->current_fs)
rc = &w32->prev_windowrc;
w32->fit_on_screen = true;
}
w32_common: refactor and improve window state handling Refactored and split the `reinit_window_state` code into four separate functions: - `update_window_style` used to update window styles without modifying the window rect. - `fit_window_on_screen` used to adjust the window size when it is larger than the screen size. Added a helper function `fit_rect` to fit one rect on another without using any data from w32 struct. - `update_fullscreen_state` used to calculate the new fullscreen state and adjust the window rect accordingly. - `update_window_state` used to display the window on screen with new size, position and ontop state. This commit fixes three issues: - fixed #4753 by skipping `fit_window_on_screen` for a maximized window, since maximized window should already fit on the screen. It should be noted that this bug was only reproducible with `--fit-border` option which is enabled by default. The cause of the bug is that after calling the `add_window_borders` for a maximized window, the rect in result is slightly larger than the screen rect, which is okay, `SetWindowPos` will interpret it as a maximized state later, so no auto-fitting to screen size is needed here. - fixed #5215 by skipping `fit_window_on_screen` when leaving fullscreen. On a multi-monitor system if the mpv window was stretched to cover multiple monitors, its size was reset after switching back from fullscreen to fit the size of the active monitor. Also, when changing `--ontop` and `--border` options, now only the `update_window_style` and `update_window_state` functions are used, so `fit_window_on_screen` is not used for them too. - fixed #2451 by moving the `ITaskbarList2_MarkFullscreenWindow` below the `SetWindowPos`. If the taskbar is notified about fullscreen state before the window is shown on screen, the taskbar button could be missing until Alt-TAB is pressed, usually it was reproducible on Windows 8. Other changes: - In `update_fullscreen_state` the `reset window bounds` debug message now reports client area size and position, instead of window area size and position. This is done for consistency with debug messages in handling fullscreen state above in this function, since they also print window bounds of the client area. - Refactored `gui_thread_reconfig`. Added a new window flag `fit_on_screen` to fit the window on screen even when leaving fullscreen. This is needed for the case when the new video opened while the window is still in the fullscreen state. - Moved parent and fullscreen state checks out from the WM_MOVING to `snap_to_screen_edges` function for consistency with other functions. There's no point in keeping these checks out of the function body.
2017-12-17 00:20:53 +00:00
// Save new window size and position.
const int x = rc->left + rect_w(*rc) / 2 - vo->dwidth / 2;
const int y = rc->top + rect_h(*rc) / 2 - vo->dheight / 2;
SetRect(rc, x, y, x + vo->dwidth, y + vo->dheight);
w32_common: refactor and improve window state handling Refactored and split the `reinit_window_state` code into four separate functions: - `update_window_style` used to update window styles without modifying the window rect. - `fit_window_on_screen` used to adjust the window size when it is larger than the screen size. Added a helper function `fit_rect` to fit one rect on another without using any data from w32 struct. - `update_fullscreen_state` used to calculate the new fullscreen state and adjust the window rect accordingly. - `update_window_state` used to display the window on screen with new size, position and ontop state. This commit fixes three issues: - fixed #4753 by skipping `fit_window_on_screen` for a maximized window, since maximized window should already fit on the screen. It should be noted that this bug was only reproducible with `--fit-border` option which is enabled by default. The cause of the bug is that after calling the `add_window_borders` for a maximized window, the rect in result is slightly larger than the screen rect, which is okay, `SetWindowPos` will interpret it as a maximized state later, so no auto-fitting to screen size is needed here. - fixed #5215 by skipping `fit_window_on_screen` when leaving fullscreen. On a multi-monitor system if the mpv window was stretched to cover multiple monitors, its size was reset after switching back from fullscreen to fit the size of the active monitor. Also, when changing `--ontop` and `--border` options, now only the `update_window_style` and `update_window_state` functions are used, so `fit_window_on_screen` is not used for them too. - fixed #2451 by moving the `ITaskbarList2_MarkFullscreenWindow` below the `SetWindowPos`. If the taskbar is notified about fullscreen state before the window is shown on screen, the taskbar button could be missing until Alt-TAB is pressed, usually it was reproducible on Windows 8. Other changes: - In `update_fullscreen_state` the `reset window bounds` debug message now reports client area size and position, instead of window area size and position. This is done for consistency with debug messages in handling fullscreen state above in this function, since they also print window bounds of the client area. - Refactored `gui_thread_reconfig`. Added a new window flag `fit_on_screen` to fit the window on screen even when leaving fullscreen. This is needed for the case when the new video opened while the window is still in the fullscreen state. - Moved parent and fullscreen state checks out from the WM_MOVING to `snap_to_screen_edges` function for consistency with other functions. There's no point in keeping these checks out of the function body.
2017-12-17 00:20:53 +00:00
finish:
reinit_window_state(w32);
}
// Resize the window. On the first call, it's also made visible.
void vo_w32_config(struct vo *vo)
{
struct vo_w32_state *w32 = vo->w32;
mp_dispatch_run(w32->dispatch, gui_thread_reconfig, w32);
}
static void w32_api_load(struct vo_w32_state *w32)
{
HMODULE shcore_dll = LoadLibraryW(L"shcore.dll");
// Available since Win8.1
w32->api.pGetDpiForMonitor = !shcore_dll ? NULL :
(void *)GetProcAddress(shcore_dll, "GetDpiForMonitor");
// imm32.dll must be loaded dynamically
// to account for machines without East Asian language support
HMODULE imm32_dll = LoadLibraryW(L"imm32.dll");
w32->api.pImmDisableIME = !imm32_dll ? NULL :
(void *)GetProcAddress(imm32_dll, "ImmDisableIME");
}
static void *gui_thread(void *ptr)
{
struct vo_w32_state *w32 = ptr;
bool ole_ok = false;
int res = 0;
mpthread_set_name("win32 window");
w32_api_load(w32);
// Disables the IME for windows on this thread
if (w32->api.pImmDisableIME)
w32->api.pImmDisableIME(0);
if (w32->opts->WinID >= 0)
w32->parent = (HWND)(intptr_t)(w32->opts->WinID);
ATOM cls = get_window_class();
if (w32->parent) {
RECT r;
GetClientRect(w32->parent, &r);
CreateWindowExW(WS_EX_NOPARENTNOTIFY, (LPWSTR)MAKEINTATOM(cls), L"mpv",
WS_CHILD | WS_VISIBLE, 0, 0, r.right, r.bottom,
w32->parent, 0, HINST_THISCOMPONENT, w32);
// Install a hook to get notifications when the parent changes size
if (w32->window)
install_parent_hook(w32);
} else {
CreateWindowExW(0, (LPWSTR)MAKEINTATOM(cls), L"mpv",
update_style(w32, 0), CW_USEDEFAULT, SW_HIDE, 100, 100,
0, 0, HINST_THISCOMPONENT, w32);
}
if (!w32->window) {
MP_ERR(w32, "unable to create window!\n");
goto done;
}
if (SUCCEEDED(OleInitialize(NULL))) {
ole_ok = true;
IDropTarget *dt = mp_w32_droptarget_create(w32->log, w32->input_ctx);
RegisterDragDrop(w32->window, dt);
// ITaskbarList2 has the MarkFullscreenWindow method, which is used to
// make sure the taskbar is hidden when mpv goes fullscreen
if (SUCCEEDED(CoCreateInstance(&CLSID_TaskbarList, NULL,
CLSCTX_INPROC_SERVER, &IID_ITaskbarList2,
(void**)&w32->taskbar_list)))
{
if (FAILED(ITaskbarList2_HrInit(w32->taskbar_list))) {
ITaskbarList2_Release(w32->taskbar_list);
w32->taskbar_list = NULL;
}
}
// ITaskbarList3 has methods for status indication on taskbar buttons,
// however that interface is only available on Win7/2008 R2 or newer
if (SUCCEEDED(CoCreateInstance(&CLSID_TaskbarList, NULL,
CLSCTX_INPROC_SERVER, &IID_ITaskbarList3,
(void**)&w32->taskbar_list3)))
{
if (FAILED(ITaskbarList3_HrInit(w32->taskbar_list3))) {
ITaskbarList3_Release(w32->taskbar_list3);
w32->taskbar_list3 = NULL;
} else {
w32->tbtnCreatedMsg = RegisterWindowMessage(L"TaskbarButtonCreated");
}
}
} else {
MP_ERR(w32, "Failed to initialize OLE/COM\n");
}
w32->tracking = FALSE;
w32->trackEvent = (TRACKMOUSEEVENT){
.cbSize = sizeof(TRACKMOUSEEVENT),
.dwFlags = TME_LEAVE,
.hwndTrack = w32->window,
};
if (w32->parent)
EnableWindow(w32->window, 0);
w32->cursor_visible = true;
w32->moving = false;
w32->snapped = false;
w32->snap_dx = w32->snap_dy = 0;
mp_dispatch_set_wakeup_fn(w32->dispatch, wakeup_gui_thread, w32);
res = 1;
done:
mp_rendezvous(w32, res); // init barrier
// This blocks until the GUI thread is to be exited.
if (res)
run_message_loop(w32);
MP_VERBOSE(w32, "uninit\n");
remove_parent_hook(w32);
if (w32->window && !w32->destroyed)
DestroyWindow(w32->window);
if (w32->taskbar_list)
ITaskbarList2_Release(w32->taskbar_list);
if (w32->taskbar_list3)
ITaskbarList3_Release(w32->taskbar_list3);
if (ole_ok)
OleUninitialize();
SetThreadExecutionState(ES_CONTINUOUS);
return NULL;
}
// Returns: 1 = Success, 0 = Failure
int vo_w32_init(struct vo *vo)
{
assert(!vo->w32);
struct vo_w32_state *w32 = talloc_ptrtype(vo, w32);
*w32 = (struct vo_w32_state){
.log = mp_log_new(w32, vo->log, "win32"),
.vo = vo,
.opts_cache = m_config_cache_alloc(w32, vo->global, &vo_sub_opts),
.input_ctx = vo->input_ctx,
.dispatch = mp_dispatch_create(w32),
};
w32->opts = w32->opts_cache->opts;
vo->w32 = w32;
if (pthread_create(&w32->thread, NULL, gui_thread, w32))
goto fail;
if (!mp_rendezvous(w32, 0)) { // init barrier
pthread_join(w32->thread, NULL);
goto fail;
}
// While the UI runs in its own thread, the thread in which this function
// runs in will be the renderer thread. Apply magic MMCSS cargo-cult,
// which might stop Windows from throttling clock rate and so on.
if (vo->opts->mmcss_profile[0]) {
wchar_t *profile = mp_from_utf8(NULL, vo->opts->mmcss_profile);
w32->avrt_handle = AvSetMmThreadCharacteristicsW(profile, &(DWORD){0});
talloc_free(profile);
}
return 1;
fail:
talloc_free(w32);
vo->w32 = NULL;
return 0;
}
struct disp_names_data {
HMONITOR assoc;
int count;
char **names;
};
static BOOL CALLBACK disp_names_proc(HMONITOR mon, HDC dc, LPRECT r, LPARAM p)
{
struct disp_names_data *data = (struct disp_names_data*)p;
// get_disp_names() adds data->assoc to the list, so skip it here
if (mon == data->assoc)
return TRUE;
MONITORINFOEXW mi = { .cbSize = sizeof mi };
if (GetMonitorInfoW(mon, (MONITORINFO*)&mi)) {
MP_TARRAY_APPEND(NULL, data->names, data->count,
mp_to_utf8(NULL, mi.szDevice));
}
return TRUE;
}
static char **get_disp_names(struct vo_w32_state *w32)
{
// Get the client area of the window in screen space
RECT rect = { 0 };
GetClientRect(w32->window, &rect);
MapWindowPoints(w32->window, NULL, (POINT*)&rect, 2);
struct disp_names_data data = { .assoc = w32->monitor };
// Make sure the monitor that Windows considers to be associated with the
// window is first in the list
MONITORINFOEXW mi = { .cbSize = sizeof mi };
if (GetMonitorInfoW(data.assoc, (MONITORINFO*)&mi)) {
MP_TARRAY_APPEND(NULL, data.names, data.count,
mp_to_utf8(NULL, mi.szDevice));
}
// Get the names of the other monitors that intersect the client rect
EnumDisplayMonitors(NULL, &rect, disp_names_proc, (LPARAM)&data);
MP_TARRAY_APPEND(NULL, data.names, data.count, NULL);
return data.names;
}
static int gui_thread_control(struct vo_w32_state *w32, int request, void *arg)
{
switch (request) {
case VOCTRL_VO_OPTS_CHANGED: {
void *changed_option;
while (m_config_cache_get_next_changed(w32->opts_cache,
&changed_option))
{
struct mp_vo_opts *vo_opts = w32->opts_cache->opts;
if (changed_option == &vo_opts->fullscreen) {
reinit_window_state(w32);
} else if (changed_option == &vo_opts->ontop) {
update_window_state(w32);
} else if (changed_option == &vo_opts->border) {
update_window_style(w32);
update_window_state(w32);
w32_common: support minimized and maximized properties Add support for setting window-minimized and window-maximized in Windows. The minimized and maximized state can be set independently. When the window is minimized, the value of window-maximized will determine whether the window is restored to the maximized state or not. Changing state is done with ShowWindow(), which has commands that change the window state and activate it (eg. SW_RESTORE) and commands that change the window state without activating it (eg. SW_SHOWNOACTIVATE.) It would be nice if we could use commands that don't activate the window, so scripts could change the window state in the backrgound without bringing it to the foreground, but there are some problems with that. There is no command to maximize a window without activating it, so SW_MAXIMIZE is used instead. Also, restoring a window from minimize without activating it seems buggy. On my Windows 10 1909 PC, it always moves the window to the back of the z-order. SW_RESTORE is used instead of SW_SHOWNOACTIVATE because of this. This also changes the way the window is initially shown. Previously, the window was made visible as a consequence of the SWP_SHOWWINDOW flag in the first call to SetWindowPos. In order to set the initial minimized or maximized state of the window, the window is shown with the ShowWindow function instead, where the ShowWindow command is determined by whether the window should be initially maximized or minimized. Even when showing the window normally, we should still call ShowWindow with the SW_SHOW command instead of using SetWindowPos, since the first call a process makes to ShowWindow(SW_SHOW) has special behaviour where it uses the show command in the process' STARTUPINFO instead of the command passed to the function, which should fix #5724. Note: While changes to window-minimized while in fullscreen mode should work as expected, changing window-maximized while in fullscreen does not work and won't result in the window changing state, even after leaving fullscreen. For this to work correctly, the fullscreen logic needs to be changed to apply the new maximized state on leaving fullscreen. Fixes: #5724 Fixes: #7351
2019-12-27 15:12:44 +00:00
} else if (changed_option == &vo_opts->window_minimized) {
update_minimized_state(w32);
} else if (changed_option == &vo_opts->window_maximized) {
update_maximized_state(w32);
}
}
return VO_TRUE;
}
case VOCTRL_GET_UNFS_WINDOW_SIZE: {
int *s = arg;
if (!w32->window_bounds_initialized)
return VO_FALSE;
RECT *rc = w32->current_fs ? &w32->prev_windowrc : &w32->windowrc;
s[0] = rect_w(*rc);
s[1] = rect_h(*rc);
return VO_TRUE;
}
case VOCTRL_SET_UNFS_WINDOW_SIZE: {
int *s = arg;
if (!w32->window_bounds_initialized)
return VO_FALSE;
RECT *rc = w32->current_fs ? &w32->prev_windowrc : &w32->windowrc;
const int x = rc->left + rect_w(*rc) / 2 - s[0] / 2;
const int y = rc->top + rect_h(*rc) / 2 - s[1] / 2;
SetRect(rc, x, y, x + s[0], y + s[1]);
w32_common: refactor and improve window state handling Refactored and split the `reinit_window_state` code into four separate functions: - `update_window_style` used to update window styles without modifying the window rect. - `fit_window_on_screen` used to adjust the window size when it is larger than the screen size. Added a helper function `fit_rect` to fit one rect on another without using any data from w32 struct. - `update_fullscreen_state` used to calculate the new fullscreen state and adjust the window rect accordingly. - `update_window_state` used to display the window on screen with new size, position and ontop state. This commit fixes three issues: - fixed #4753 by skipping `fit_window_on_screen` for a maximized window, since maximized window should already fit on the screen. It should be noted that this bug was only reproducible with `--fit-border` option which is enabled by default. The cause of the bug is that after calling the `add_window_borders` for a maximized window, the rect in result is slightly larger than the screen rect, which is okay, `SetWindowPos` will interpret it as a maximized state later, so no auto-fitting to screen size is needed here. - fixed #5215 by skipping `fit_window_on_screen` when leaving fullscreen. On a multi-monitor system if the mpv window was stretched to cover multiple monitors, its size was reset after switching back from fullscreen to fit the size of the active monitor. Also, when changing `--ontop` and `--border` options, now only the `update_window_style` and `update_window_state` functions are used, so `fit_window_on_screen` is not used for them too. - fixed #2451 by moving the `ITaskbarList2_MarkFullscreenWindow` below the `SetWindowPos`. If the taskbar is notified about fullscreen state before the window is shown on screen, the taskbar button could be missing until Alt-TAB is pressed, usually it was reproducible on Windows 8. Other changes: - In `update_fullscreen_state` the `reset window bounds` debug message now reports client area size and position, instead of window area size and position. This is done for consistency with debug messages in handling fullscreen state above in this function, since they also print window bounds of the client area. - Refactored `gui_thread_reconfig`. Added a new window flag `fit_on_screen` to fit the window on screen even when leaving fullscreen. This is needed for the case when the new video opened while the window is still in the fullscreen state. - Moved parent and fullscreen state checks out from the WM_MOVING to `snap_to_screen_edges` function for consistency with other functions. There's no point in keeping these checks out of the function body.
2017-12-17 00:20:53 +00:00
w32->fit_on_screen = true;
reinit_window_state(w32);
return VO_TRUE;
}
case VOCTRL_SET_CURSOR_VISIBILITY:
w32->cursor_visible = *(bool *)arg;
if (w32->can_set_cursor && w32->tracking) {
if (w32->cursor_visible)
SetCursor(LoadCursor(NULL, IDC_ARROW));
else
SetCursor(NULL);
}
return VO_TRUE;
case VOCTRL_KILL_SCREENSAVER:
w32->disable_screensaver = true;
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED |
ES_DISPLAY_REQUIRED);
return VO_TRUE;
case VOCTRL_RESTORE_SCREENSAVER:
w32->disable_screensaver = false;
SetThreadExecutionState(ES_CONTINUOUS);
return VO_TRUE;
case VOCTRL_UPDATE_WINDOW_TITLE: {
wchar_t *title = mp_from_utf8(NULL, (char *)arg);
SetWindowTextW(w32->window, title);
talloc_free(title);
return VO_TRUE;
}
case VOCTRL_UPDATE_PLAYBACK_STATE: {
w32->current_pstate = *(struct voctrl_playback_state *)arg;
update_playback_state(w32);
return VO_TRUE;
}
2015-03-10 18:25:30 +00:00
case VOCTRL_GET_DISPLAY_FPS:
update_display_info(w32);
2015-03-10 18:25:30 +00:00
*(double*) arg = w32->display_fps;
return VO_TRUE;
case VOCTRL_GET_DISPLAY_NAMES:
*(char ***)arg = get_disp_names(w32);
return VO_TRUE;
case VOCTRL_GET_ICC_PROFILE:
update_display_info(w32);
if (w32->color_profile) {
bstr *p = arg;
*p = stream_read_file(w32->color_profile, NULL,
w32->vo->global, 100000000); // 100 MB
return p->len ? VO_TRUE : VO_FALSE;
}
return VO_FALSE;
}
return VO_NOTIMPL;
}
static void do_control(void *ptr)
{
void **p = ptr;
struct vo_w32_state *w32 = p[0];
int *events = p[1];
int request = *(int *)p[2];
void *arg = p[3];
int *ret = p[4];
*ret = gui_thread_control(w32, request, arg);
*events |= atomic_fetch_and(&w32->event_flags, 0);
// Safe access, since caller (owner of vo) is blocked.
if (*events & VO_EVENT_RESIZE) {
w32->vo->dwidth = rect_w(w32->windowrc);
w32->vo->dheight = rect_h(w32->windowrc);
}
}
int vo_w32_control(struct vo *vo, int *events, int request, void *arg)
{
struct vo_w32_state *w32 = vo->w32;
if (request == VOCTRL_CHECK_EVENTS) {
*events |= atomic_fetch_and(&w32->event_flags, 0);
if (*events & VO_EVENT_RESIZE) {
mp_dispatch_lock(w32->dispatch);
vo->dwidth = rect_w(w32->windowrc);
vo->dheight = rect_h(w32->windowrc);
mp_dispatch_unlock(w32->dispatch);
}
return VO_TRUE;
} else {
int r;
void *p[] = {w32, events, &request, arg, &r};
mp_dispatch_run(w32->dispatch, do_control, p);
return r;
}
}
static void do_terminate(void *ptr)
{
struct vo_w32_state *w32 = ptr;
w32->terminate = true;
if (!w32->destroyed)
DestroyWindow(w32->window);
mp_dispatch_interrupt(w32->dispatch);
}
void vo_w32_uninit(struct vo *vo)
{
struct vo_w32_state *w32 = vo->w32;
if (!w32)
return;
mp_dispatch_run(w32->dispatch, do_terminate, w32);
pthread_join(w32->thread, NULL);
AvRevertMmThreadCharacteristics(w32->avrt_handle);
talloc_free(w32);
vo->w32 = NULL;
}
HWND vo_w32_hwnd(struct vo *vo)
{
struct vo_w32_state *w32 = vo->w32;
return w32->window; // immutable, so no synchronization needed
}
void vo_w32_run_on_thread(struct vo *vo, void (*cb)(void *ctx), void *ctx)
{
struct vo_w32_state *w32 = vo->w32;
mp_dispatch_run(w32->dispatch, cb, ctx);
}