From 1fe0969c237ac2d85898a97d8b7445ab43de0a98 Mon Sep 17 00:00:00 2001 From: Crend King <975235+CrendKing@users.noreply.github.com> Date: Mon, 1 Jun 2026 06:05:41 -0700 Subject: [PATCH 1/2] video/out/w32_common: add --focus-on support for Windows --- DOCS/man/options.rst | 6 +++++- video/out/w32_common.c | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst index d8c11bd995bcd..d4be4791d7973 100644 --- a/DOCS/man/options.rst +++ b/DOCS/man/options.rst @@ -3488,9 +3488,13 @@ Window :level: A level as integer. ``--focus-on=``, - (X11 and macOS only) + (Windows, X11 and macOS only) Focus the video window and make it the front most window on specific events (default: open). + On Windows, due to the system's focus stealing prevention mechanism, + ``--force-window=immediate`` is recommended for ``open`` and ``all`` + to work reliably. + :never: Never focus the window on open or new file load events. :open: Focus the window on creation, eg when a vo is initialised. :all: Focus the window on open and new file load event. diff --git a/video/out/w32_common.c b/video/out/w32_common.c index ba4f3825ec176..75e8567bae76f 100644 --- a/video/out/w32_common.c +++ b/video/out/w32_common.c @@ -1987,6 +1987,9 @@ static void window_resize(struct vo_w32_state *w32) ShowWindow(w32->window, SW_SHOWMINNOACTIVE); } else if (w32->opts->window_maximized && !w32->current_fs) { ShowWindow(w32->window, SW_SHOWMAXIMIZED); + } else if (w32->opts->focus_on == 0) { + ShowWindow(w32->window, SW_SHOWNOACTIVATE); + SetWindowPos(w32->window, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); } else { ShowWindow(w32->window, SW_SHOW); } @@ -2017,6 +2020,9 @@ void vo_w32_config(struct vo *vo) { struct vo_w32_state *w32 = vo->w32; mp_dispatch_run(w32->dispatch, gui_thread_reconfig, w32); + + if (w32->opts->focus_on == 2) + SetForegroundWindow(w32->window); } static void w32_api_load(struct vo_w32_state *w32) From a959c17165d5d9eaa7b3c297503c9faba6a0c2ed Mon Sep 17 00:00:00 2001 From: Crend King <975235+CrendKing@users.noreply.github.com> Date: Tue, 2 Jun 2026 04:22:37 -0700 Subject: [PATCH 2/2] video/out/w32_common: experiment with VOCTRL_BRING_FRONT --- player/loadfile.c | 5 ++++- video/out/vo.c | 28 ++++++++++++++++++++++++++-- video/out/vo.h | 3 +++ video/out/w32_common.c | 11 +++++++---- 4 files changed, 40 insertions(+), 7 deletions(-) diff --git a/player/loadfile.c b/player/loadfile.c index 0b3e70d550d59..129425e4e4ba4 100644 --- a/player/loadfile.c +++ b/player/loadfile.c @@ -2052,8 +2052,11 @@ static void play_current_file(struct MPContext *mpctx) current && current->reloading; if (current) current->reloading = false; - if (!reloading) + if (!reloading) { + if (mpctx->video_out) + vo_set_changing_file(mpctx->video_out); m_config_restore_backups(mpctx->mconfig); + } TA_FREEP(&mpctx->filter_root); talloc_free(mpctx->filtered_tags); diff --git a/video/out/vo.c b/video/out/vo.c index c75edbd6e56b4..c355283793f7a 100644 --- a/video/out/vo.c +++ b/video/out/vo.c @@ -139,6 +139,7 @@ struct vo_internal { bool paused; bool visible; bool wakeup_on_done; + bool changing_file; int queued_events; // event mask for the user int internal_events; // event mask for us @@ -237,11 +238,12 @@ static void read_opts(struct vo *vo) static void update_opts(void *p) { struct vo *vo = p; + struct vo_internal *in = vo->in; if (m_config_cache_update(vo->opts_cache)) { read_opts(vo); - if (vo->driver->control) { - vo->driver->control(vo, VOCTRL_VO_OPTS_CHANGED, NULL); + vo->driver->control(vo, VOCTRL_VO_OPTS_CHANGED, NULL); + if (!in->changing_file) { // "Legacy" update of video position related options. // Unlike VOCTRL_VO_OPTS_CHANGED, often not propagated to backends. vo->driver->control(vo, VOCTRL_SET_PANSCAN, NULL); @@ -249,6 +251,19 @@ static void update_opts(void *p) } } +static void handle_file_change(struct vo *vo) +{ + struct vo_internal *in = vo->in; + + if (in->changing_file) { + in->changing_file = false; + vo->driver->control(vo, VOCTRL_SET_PANSCAN, NULL); + + if (vo->opts->focus_on == 2) + vo->driver->control(vo, VOCTRL_BRING_FRONT, NULL); + } +} + // Does not include thread- and VO uninit. static void dealloc_vo(struct vo *vo) { @@ -1014,6 +1029,8 @@ static bool render_frame(struct vo *vo) stats_time_start(in->stats, "video-flip"); + handle_file_change(vo); + vo->driver->flip_page(vo); struct vo_vsync_info vsync = { @@ -1071,6 +1088,13 @@ static bool render_frame(struct vo *vo) return more_frames; } +void vo_set_changing_file(struct vo *vo) +{ + mp_mutex_lock(&vo->in->lock); + vo->in->changing_file = true; + mp_mutex_unlock(&vo->in->lock); +} + static void do_redraw(struct vo *vo) { struct vo_internal *in = vo->in; diff --git a/video/out/vo.h b/video/out/vo.h index a98b0f9496853..9d3a4d0091bba 100644 --- a/video/out/vo.h +++ b/video/out/vo.h @@ -133,6 +133,8 @@ enum mp_voctrl { // Clipboard VOCTRL_GET_CLIPBOARD, // struct voctrl_clipboard* VOCTRL_SET_CLIPBOARD, + + VOCTRL_BRING_FRONT, }; // Helper to expose what kind of content is currently playing to the VO. @@ -542,6 +544,7 @@ bool vo_want_redraw(struct vo *vo); void vo_seek_reset(struct vo *vo); void vo_destroy(struct vo *vo); void vo_set_paused(struct vo *vo, bool paused); +void vo_set_changing_file(struct vo *vo); int64_t vo_get_drop_count(struct vo *vo); void vo_increment_drop_count(struct vo *vo, int64_t n); int64_t vo_get_delayed_count(struct vo *vo); diff --git a/video/out/w32_common.c b/video/out/w32_common.c index 75e8567bae76f..d7de7b71a491a 100644 --- a/video/out/w32_common.c +++ b/video/out/w32_common.c @@ -2020,9 +2020,6 @@ void vo_w32_config(struct vo *vo) { struct vo_w32_state *w32 = vo->w32; mp_dispatch_run(w32->dispatch, gui_thread_reconfig, w32); - - if (w32->opts->focus_on == 2) - SetForegroundWindow(w32->window); } static void w32_api_load(struct vo_w32_state *w32) @@ -2311,6 +2308,7 @@ static bool gui_thread_control_supports(int request) case VOCTRL_BEGIN_DRAGGING: case VOCTRL_SHOW_MENU: case VOCTRL_UPDATE_MENU: + case VOCTRL_BRING_FRONT: return true; } @@ -2505,12 +2503,17 @@ static int gui_thread_control(struct vo_w32_state *w32, int request, void *arg) case VOCTRL_SHOW_MENU: PostMessageW(w32->window, WM_SHOWMENU, 0, 0); return VO_TRUE; - case VOCTRL_UPDATE_MENU:; + case VOCTRL_UPDATE_MENU: const m_option_t menu_data_type = {.type = CONF_TYPE_NODE}; m_option_free(&menu_data_type, &w32->menu_data); m_option_copy(&menu_data_type, &w32->menu_data, arg); talloc_steal(w32, node_get_alloc(&w32->menu_data)); return VO_TRUE; + case VOCTRL_BRING_FRONT: + if (SetForegroundWindow(w32->window) != FALSE) + return VO_TRUE; + + return VO_FALSE; } // Keep gui_thread_control_supports() in sync