summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Ross-Gowan <rossymiles@gmail.com>2017-04-02 00:13:18 +1100
committerJames Ross-Gowan <rossymiles@gmail.com>2017-04-02 00:41:49 +1100
commit16ff72cc14dc8ca0fc44083dbf967e7366d6c185 (patch)
tree8d0b10c3a6f1349b3c118191291955fb29982dfb
parent8aaef132f60b13054587c30045135fb2afe7523a (diff)
downloadmpv-16ff72cc14dc8ca0fc44083dbf967e7366d6c185.tar.bz2
mpv-16ff72cc14dc8ca0fc44083dbf967e7366d6c185.tar.xz
w32_common: fix undefined behaviour when toggling fullscreen
The old code called reinit_window_state() from the VO thread, which is not safe. Instead of calling reinit_window_state() when VO_EVENT_FULLSCREEN_STATE is captured, it should be called when sending the event from the Win32 thread instead.
-rw-r--r--video/out/w32_common.c436
1 files changed, 214 insertions, 222 deletions
diff --git a/video/out/w32_common.c b/video/out/w32_common.c
index 18acaa4423..7fdad59668 100644
--- a/video/out/w32_common.c
+++ b/video/out/w32_common.c
@@ -619,10 +619,217 @@ static bool snap_to_screen_edges(struct vo_w32_state *w32, RECT *rc)
return true;
}
-static void toggle_fullscreen(struct vo_w32_state *w32)
+struct get_monitor_data {
+ int i;
+ int target;
+ HMONITOR mon;
+};
+
+static BOOL CALLBACK get_monitor_proc(HMONITOR mon, HDC dc, LPRECT r, LPARAM p)
{
- w32->toggle_fs = true;
- signal_events(w32, VO_EVENT_FULLSCREEN_STATE);
+ 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 void update_screen_rect(struct vo_w32_state *w32)
+{
+ struct mp_vo_opts *opts = w32->opts;
+ int screen = w32->current_fs ? opts->fsscreen_id : opts->screen_id;
+
+ // Handle --fs-screen=all
+ if (w32->current_fs && screen == -2) {
+ struct mp_rect rc = {
+ GetSystemMetrics(SM_XVIRTUALSCREEN),
+ GetSystemMetrics(SM_YVIRTUALSCREEN),
+ GetSystemMetrics(SM_CXVIRTUALSCREEN),
+ GetSystemMetrics(SM_CYVIRTUALSCREEN),
+ };
+ rc.x1 += rc.x0;
+ rc.y1 += rc.y0;
+ w32->screenrc = rc;
+ return;
+ }
+
+ // When not using --fs-screen=all, mpv belongs to a specific HMONITOR
+ HMONITOR mon;
+ if (screen == -1) {
+ // Handle --fs-screen=current and --screen=default
+ mon = MonitorFromWindow(w32->window, MONITOR_DEFAULTTOPRIMARY);
+ } else {
+ mon = get_monitor(screen);
+ if (!mon) {
+ MP_INFO(w32, "Screen %d does not exist, falling back to primary\n",
+ screen);
+ mon = MonitorFromPoint((POINT){0, 0}, MONITOR_DEFAULTTOPRIMARY);
+ }
+ }
+
+ MONITORINFO mi = { .cbSize = sizeof(mi) };
+ GetMonitorInfoW(mon, &mi);
+ w32->screenrc = (struct mp_rect){
+ mi.rcMonitor.left, mi.rcMonitor.top,
+ mi.rcMonitor.right, mi.rcMonitor.bottom,
+ };
+}
+
+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;
+}
+
+// Update the window title, position, size, and border style.
+static void reinit_window_state(struct vo_w32_state *w32)
+{
+ HWND layer = HWND_NOTOPMOST;
+ RECT r;
+
+ if (w32->parent)
+ return;
+
+ bool new_fs = w32->toggle_fs ? !w32->current_fs : w32->opts->fullscreen;
+ bool toggle_fs = w32->current_fs != new_fs;
+ w32->current_fs = new_fs;
+ w32->toggle_fs = false;
+
+ if (w32->taskbar_list) {
+ ITaskbarList2_MarkFullscreenWindow(w32->taskbar_list,
+ w32->window, w32->current_fs);
+ }
+
+ DWORD style = update_style(w32, GetWindowLongPtrW(w32->window, GWL_STYLE));
+
+ if (w32->opts->ontop)
+ layer = HWND_TOPMOST;
+
+ // xxx not sure if this can trigger any unwanted messages (WM_MOVE/WM_SIZE)
+ update_screen_rect(w32);
+
+ int screen_w = w32->screenrc.x1 - w32->screenrc.x0;
+ int screen_h = w32->screenrc.y1 - w32->screenrc.y0;
+
+ if (w32->current_fs) {
+ // Save window position and size when switching to fullscreen.
+ if (toggle_fs) {
+ w32->prev_width = w32->dw;
+ w32->prev_height = w32->dh;
+ w32->prev_x = w32->window_x;
+ w32->prev_y = w32->window_y;
+ MP_VERBOSE(w32, "save window bounds: %d:%d:%d:%d\n",
+ w32->prev_x, w32->prev_y, w32->prev_width, w32->prev_height);
+ }
+
+ w32->window_x = w32->screenrc.x0;
+ w32->window_y = w32->screenrc.y0;
+ w32->dw = screen_w;
+ w32->dh = screen_h;
+ } else {
+ if (toggle_fs) {
+ // Restore window position and size when switching from fullscreen.
+ MP_VERBOSE(w32, "restore window bounds: %d:%d:%d:%d\n",
+ w32->prev_x, w32->prev_y, w32->prev_width, w32->prev_height);
+ w32->dw = w32->prev_width;
+ w32->dh = w32->prev_height;
+ w32->window_x = w32->prev_x;
+ w32->window_y = w32->prev_y;
+ }
+ }
+
+ r.left = w32->window_x;
+ r.right = r.left + w32->dw;
+ r.top = w32->window_y;
+ r.bottom = r.top + w32->dh;
+
+ SetWindowLongPtrW(w32->window, GWL_STYLE, style);
+
+ RECT cr = r;
+ add_window_borders(w32->window, &r);
+ // Check on client area size instead of window size on --fit-border=no
+ long o_w;
+ long o_h;
+ if( w32->opts->fit_border ) {
+ o_w = r.right - r.left;
+ o_h = r.bottom - r.top;
+ } else {
+ o_w = cr.right - cr.left;
+ o_h = cr.bottom - cr.top;
+ }
+
+ if ( !w32->current_fs && ( o_w > screen_w || o_h > screen_h ) )
+ {
+ MP_VERBOSE(w32, "requested window size larger than the screen\n");
+ // Use the aspect of the client area, not the full window size.
+ // Basically, try to compute the maximum window size.
+ long n_w;
+ long n_h;
+ if( w32->opts->fit_border ) {
+ n_w = screen_w - (r.right - cr.right) - (cr.left - r.left);
+ n_h = screen_h - (r.bottom - cr.bottom) - (cr.top - r.top);
+ } else {
+ n_w = screen_w;
+ n_h = screen_h;
+ }
+ // Letterbox
+ double asp = (cr.right - cr.left) / (double)(cr.bottom - cr.top);
+ double s_asp = n_w / (double)n_h;
+ if (asp > s_asp) {
+ n_h = n_w / asp;
+ } else {
+ n_w = n_h * asp;
+ }
+ // Save new size
+ w32->dw = n_w;
+ w32->dh = n_h;
+ // Get old window center
+ long o_cx = r.left + (r.right - r.left) / 2;
+ long o_cy = r.top + (r.bottom - r.top) / 2;
+ // Add window borders to the new window size
+ r = (RECT){.right = n_w, .bottom = n_h};
+ add_window_borders(w32->window, &r);
+ // Get top and left border size for client area position calculation
+ long b_top = -r.top;
+ long b_left = -r.left;
+ // Center the final window around the old window center
+ n_w = r.right - r.left;
+ n_h = r.bottom - r.top;
+ r.left = o_cx - n_w / 2;
+ r.top = o_cy - n_h / 2;
+ r.right = r.left + n_w;
+ r.bottom = r.top + n_h;
+ // Save new client area position
+ w32->window_x = r.left + b_left;
+ w32->window_y = r.top + b_top;
+ }
+
+ MP_VERBOSE(w32, "reset window bounds: %d:%d:%d:%d\n",
+ (int) r.left, (int) r.top, (int)(r.right - r.left),
+ (int)(r.bottom - r.top));
+
+ SetWindowPos(w32->window, layer, r.left, r.top, r.right - r.left,
+ r.bottom - r.top, SWP_FRAMECHANGED | SWP_SHOWWINDOW);
+
+ signal_events(w32, VO_EVENT_RESIZE);
}
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
@@ -745,7 +952,9 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
break;
case SC_RESTORE:
if (IsMaximized(w32->window) && w32->current_fs) {
- toggle_fullscreen(w32);
+ w32->toggle_fs = true;
+ reinit_window_state(w32);
+ signal_events(w32, VO_EVENT_FULLSCREEN_STATE);
return 0;
}
break;
@@ -999,219 +1208,6 @@ static void run_message_loop(struct vo_w32_state *w32)
mp_dispatch_queue_process(w32->dispatch, 1000);
}
-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 void update_screen_rect(struct vo_w32_state *w32)
-{
- struct mp_vo_opts *opts = w32->opts;
- int screen = w32->current_fs ? opts->fsscreen_id : opts->screen_id;
-
- // Handle --fs-screen=all
- if (w32->current_fs && screen == -2) {
- struct mp_rect rc = {
- GetSystemMetrics(SM_XVIRTUALSCREEN),
- GetSystemMetrics(SM_YVIRTUALSCREEN),
- GetSystemMetrics(SM_CXVIRTUALSCREEN),
- GetSystemMetrics(SM_CYVIRTUALSCREEN),
- };
- rc.x1 += rc.x0;
- rc.y1 += rc.y0;
- w32->screenrc = rc;
- return;
- }
-
- // When not using --fs-screen=all, mpv belongs to a specific HMONITOR
- HMONITOR mon;
- if (screen == -1) {
- // Handle --fs-screen=current and --screen=default
- mon = MonitorFromWindow(w32->window, MONITOR_DEFAULTTOPRIMARY);
- } else {
- mon = get_monitor(screen);
- if (!mon) {
- MP_INFO(w32, "Screen %d does not exist, falling back to primary\n",
- screen);
- mon = MonitorFromPoint((POINT){0, 0}, MONITOR_DEFAULTTOPRIMARY);
- }
- }
-
- MONITORINFO mi = { .cbSize = sizeof(mi) };
- GetMonitorInfoW(mon, &mi);
- w32->screenrc = (struct mp_rect){
- mi.rcMonitor.left, mi.rcMonitor.top,
- mi.rcMonitor.right, mi.rcMonitor.bottom,
- };
-}
-
-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;
-}
-
-// Update the window title, position, size, and border style.
-static void reinit_window_state(struct vo_w32_state *w32)
-{
- HWND layer = HWND_NOTOPMOST;
- RECT r;
-
- if (w32->parent)
- return;
-
- bool new_fs = w32->toggle_fs ? !w32->current_fs : w32->opts->fullscreen;
- bool toggle_fs = w32->current_fs != new_fs;
- w32->current_fs = new_fs;
- w32->toggle_fs = false;
-
- if (w32->taskbar_list) {
- ITaskbarList2_MarkFullscreenWindow(w32->taskbar_list,
- w32->window, w32->current_fs);
- }
-
- DWORD style = update_style(w32, GetWindowLongPtrW(w32->window, GWL_STYLE));
-
- if (w32->opts->ontop)
- layer = HWND_TOPMOST;
-
- // xxx not sure if this can trigger any unwanted messages (WM_MOVE/WM_SIZE)
- update_screen_rect(w32);
-
- int screen_w = w32->screenrc.x1 - w32->screenrc.x0;
- int screen_h = w32->screenrc.y1 - w32->screenrc.y0;
-
- if (w32->current_fs) {
- // Save window position and size when switching to fullscreen.
- if (toggle_fs) {
- w32->prev_width = w32->dw;
- w32->prev_height = w32->dh;
- w32->prev_x = w32->window_x;
- w32->prev_y = w32->window_y;
- MP_VERBOSE(w32, "save window bounds: %d:%d:%d:%d\n",
- w32->prev_x, w32->prev_y, w32->prev_width, w32->prev_height);
- }
-
- w32->window_x = w32->screenrc.x0;
- w32->window_y = w32->screenrc.y0;
- w32->dw = screen_w;
- w32->dh = screen_h;
- } else {
- if (toggle_fs) {
- // Restore window position and size when switching from fullscreen.
- MP_VERBOSE(w32, "restore window bounds: %d:%d:%d:%d\n",
- w32->prev_x, w32->prev_y, w32->prev_width, w32->prev_height);
- w32->dw = w32->prev_width;
- w32->dh = w32->prev_height;
- w32->window_x = w32->prev_x;
- w32->window_y = w32->prev_y;
- }
- }
-
- r.left = w32->window_x;
- r.right = r.left + w32->dw;
- r.top = w32->window_y;
- r.bottom = r.top + w32->dh;
-
- SetWindowLongPtrW(w32->window, GWL_STYLE, style);
-
- RECT cr = r;
- add_window_borders(w32->window, &r);
- // Check on client area size instead of window size on --fit-border=no
- long o_w;
- long o_h;
- if( w32->opts->fit_border ) {
- o_w = r.right - r.left;
- o_h = r.bottom - r.top;
- } else {
- o_w = cr.right - cr.left;
- o_h = cr.bottom - cr.top;
- }
-
- if ( !w32->current_fs && ( o_w > screen_w || o_h > screen_h ) )
- {
- MP_VERBOSE(w32, "requested window size larger than the screen\n");
- // Use the aspect of the client area, not the full window size.
- // Basically, try to compute the maximum window size.
- long n_w;
- long n_h;
- if( w32->opts->fit_border ) {
- n_w = screen_w - (r.right - cr.right) - (cr.left - r.left);
- n_h = screen_h - (r.bottom - cr.bottom) - (cr.top - r.top);
- } else {
- n_w = screen_w;
- n_h = screen_h;
- }
- // Letterbox
- double asp = (cr.right - cr.left) / (double)(cr.bottom - cr.top);
- double s_asp = n_w / (double)n_h;
- if (asp > s_asp) {
- n_h = n_w / asp;
- } else {
- n_w = n_h * asp;
- }
- // Save new size
- w32->dw = n_w;
- w32->dh = n_h;
- // Get old window center
- long o_cx = r.left + (r.right - r.left) / 2;
- long o_cy = r.top + (r.bottom - r.top) / 2;
- // Add window borders to the new window size
- r = (RECT){.right = n_w, .bottom = n_h};
- add_window_borders(w32->window, &r);
- // Get top and left border size for client area position calculation
- long b_top = -r.top;
- long b_left = -r.left;
- // Center the final window around the old window center
- n_w = r.right - r.left;
- n_h = r.bottom - r.top;
- r.left = o_cx - n_w / 2;
- r.top = o_cy - n_h / 2;
- r.right = r.left + n_w;
- r.bottom = r.top + n_h;
- // Save new client area position
- w32->window_x = r.left + b_left;
- w32->window_y = r.top + b_top;
- }
-
- MP_VERBOSE(w32, "reset window bounds: %d:%d:%d:%d\n",
- (int) r.left, (int) r.top, (int)(r.right - r.left),
- (int)(r.bottom - r.top));
-
- SetWindowPos(w32->window, layer, r.left, r.top, r.right - r.left,
- r.bottom - r.top, SWP_FRAMECHANGED | SWP_SHOWWINDOW);
-
- signal_events(w32, VO_EVENT_RESIZE);
-}
-
static void gui_thread_reconfig(void *ptr)
{
struct vo_w32_state *w32 = ptr;
@@ -1512,7 +1508,7 @@ static int gui_thread_control(struct vo_w32_state *w32, int request, void *arg)
reinit_window_state(w32);
return VO_TRUE;
case VOCTRL_GET_FULLSCREEN:
- *(bool *)arg = w32->toggle_fs != w32->current_fs;
+ *(bool *)arg = w32->current_fs;
return VO_TRUE;
case VOCTRL_GET_UNFS_WINDOW_SIZE: {
int *s = arg;
@@ -1613,8 +1609,6 @@ static void do_control(void *ptr)
w32->vo->dwidth = w32->dw;
w32->vo->dheight = w32->dh;
}
- if (*events & VO_EVENT_FULLSCREEN_STATE)
- reinit_window_state(w32);
}
int vo_w32_control(struct vo *vo, int *events, int request, void *arg)
@@ -1628,8 +1622,6 @@ int vo_w32_control(struct vo *vo, int *events, int request, void *arg)
vo->dheight = w32->dh;
mp_dispatch_unlock(w32->dispatch);
}
- if (*events & VO_EVENT_FULLSCREEN_STATE)
- reinit_window_state(w32);
return VO_TRUE;
} else {
int r;