From 9b9ce74afaafb29045fe3ab975eaaa9290eb3b7d Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Mon, 7 Sep 2020 18:22:25 +0200 Subject: [PATCH] command: add read-only focused property Add a property that returns whether the window is focused, currently only for X11 and Wayland. My use cause for this is having an equivalent of pause-when-minimize.lua for tiling window managers: make mpv play only while it's in the current workspace or is focused (I'm fine with either one but prefer focus). On X I do this by observing display-names, which is empty when the rectangles of the display and mpv don't intersect, but on Wayland its value doesn't change when mpv leaves the current workspace (and the same check doesn't work since the geometries still intersect). This could later be made writable as requested in #6252. Note that on Wayland se shouldn't consider an unactivated window with keyboard input focused. The wlroots compositors I tested set activated after changing the keyboard focus, so if you set wl->focused only in keyboard_handle_enter() and keyboard_handle_leave() to avoid adding the "has_keyboard_input" member, focused isn't set to true when first opening mpv until you focus another window and focus mpv again. Conversely, if that order can't be assumed for all compositors, we should toggle wl->focused when necessary in keyboard_handle_enter() and keyboard_handle_leave() as well as in handle_toplevel_config(). --- DOCS/man/input.rst | 3 +++ player/command.c | 17 +++++++++++++++++ player/command.h | 1 + player/playloop.c | 2 ++ video/out/vo.h | 5 ++++- video/out/wayland_common.c | 20 ++++++++++++++++++++ video/out/wayland_common.h | 3 +++ video/out/x11_common.c | 6 ++++++ 8 files changed, 56 insertions(+), 1 deletion(-) diff --git a/DOCS/man/input.rst b/DOCS/man/input.rst index 739f5f1022..18027ce17a 100644 --- a/DOCS/man/input.rst +++ b/DOCS/man/input.rst @@ -2439,6 +2439,9 @@ Property list since setting the option, and the window size was not restricted in other ways. The property is unavailable if no video is active. +``focused`` + Whether the window has focus. Currently works only on X11 and Wayland. + ``display-names`` Names of the displays that the mpv window covers. On X11, these are the xrandr names (LVDS1, HDMI1, DP1, VGA1, etc.). On Windows, these diff --git a/player/command.c b/player/command.c index 5b9e8bcbb4..723996d9be 100644 --- a/player/command.c +++ b/player/command.c @@ -2410,6 +2410,21 @@ static int mp_property_hidpi_scale(void *ctx, struct m_property *prop, return m_property_double_ro(action, arg, cmd->cached_window_scale); } +static int mp_property_focused(void *ctx, struct m_property *prop, + int action, void *arg) +{ + MPContext *mpctx = ctx; + struct vo *vo = mpctx->video_out; + if (!vo) + return M_PROPERTY_UNAVAILABLE; + + bool focused; + if (vo_control(vo, VOCTRL_GET_FOCUSED, &focused) < 1) + return M_PROPERTY_UNAVAILABLE; + + return m_property_flag_ro(action, arg, focused); +} + static int mp_property_display_names(void *ctx, struct m_property *prop, int action, void *arg) { @@ -3593,6 +3608,7 @@ static const struct m_property mp_properties_base[] = { PROPERTY_BITRATE("audio-bitrate", false, STREAM_AUDIO), PROPERTY_BITRATE("sub-bitrate", false, STREAM_SUB), + {"focused", mp_property_focused}, {"display-names", mp_property_display_names}, {"display-fps", mp_property_display_fps}, {"estimated-display-fps", mp_property_estimated_display_fps}, @@ -3676,6 +3692,7 @@ static const char *const *const mp_event_property_change[] = { "osd-par", "osd-dimensions"), E(MP_EVENT_WIN_STATE, "display-names", "display-fps"), E(MP_EVENT_WIN_STATE2, "display-hidpi-scale"), + E(MP_EVENT_FOCUS, "focused"), E(MP_EVENT_CHANGE_PLAYLIST, "playlist", "playlist-pos", "playlist-pos-1", "playlist-count", "playlist/count", "playlist-current-pos", "playlist-playing-pos"), diff --git a/player/command.h b/player/command.h index 17e0726b0b..c47ed40f1d 100644 --- a/player/command.h +++ b/player/command.h @@ -98,6 +98,7 @@ enum { MP_EVENT_WIN_RESIZE, MP_EVENT_WIN_STATE, MP_EVENT_WIN_STATE2, + MP_EVENT_FOCUS, MP_EVENT_CHANGE_PLAYLIST, MP_EVENT_CORE_IDLE, MP_EVENT_DURATION_UPDATE, diff --git a/player/playloop.c b/player/playloop.c index ddd3f6dbdb..44ccb4819b 100644 --- a/player/playloop.c +++ b/player/playloop.c @@ -844,6 +844,8 @@ static void handle_vo_events(struct MPContext *mpctx) mp_notify(mpctx, MP_EVENT_WIN_STATE, NULL); if (events & VO_EVENT_DPI) mp_notify(mpctx, MP_EVENT_WIN_STATE2, NULL); + if (events & VO_EVENT_FOCUS) + mp_notify(mpctx, MP_EVENT_FOCUS, NULL); } static void handle_sstep(struct MPContext *mpctx) diff --git a/video/out/vo.h b/video/out/vo.h index 1b7a239798..5deb99a6f1 100644 --- a/video/out/vo.h +++ b/video/out/vo.h @@ -48,10 +48,11 @@ enum { // Special thing for encode mode (vo_driver.initially_blocked). // Part of VO_EVENTS_USER to make vo_is_ready_for_frame() work properly. VO_EVENT_INITIAL_UNBLOCK = 1 << 7, + VO_EVENT_FOCUS = 1 << 8, // Set of events the player core may be interested in. VO_EVENTS_USER = VO_EVENT_RESIZE | VO_EVENT_WIN_STATE | VO_EVENT_DPI | - VO_EVENT_INITIAL_UNBLOCK, + VO_EVENT_INITIAL_UNBLOCK | VO_EVENT_FOCUS, }; enum mp_voctrl { @@ -99,6 +100,8 @@ enum mp_voctrl { VOCTRL_GET_UNFS_WINDOW_SIZE, // int[2] (w/h) VOCTRL_SET_UNFS_WINDOW_SIZE, // int[2] (w/h) + VOCTRL_GET_FOCUSED, // bool* + // char *** (NULL terminated array compatible with CONF_TYPE_STRING_LIST) // names for displays the window is on VOCTRL_GET_DISPLAY_NAMES, diff --git a/video/out/wayland_common.c b/video/out/wayland_common.c index a79c8aa8ca..c6c0e342c9 100644 --- a/video/out/wayland_common.c +++ b/video/out/wayland_common.c @@ -447,11 +447,15 @@ static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { + struct vo_wayland_state *wl = data; + wl->has_keyboard_input = true; } static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface) { + struct vo_wayland_state *wl = data; + wl->has_keyboard_input = false; } static bool create_input(struct vo_wayland_state *wl) @@ -951,6 +955,7 @@ static void handle_toplevel_config(void *data, struct xdg_toplevel *toplevel, bool is_maximized = false; bool is_fullscreen = false; + bool is_activated = false; enum xdg_toplevel_state *state; wl_array_for_each(state, states) { switch (*state) { @@ -961,6 +966,7 @@ static void handle_toplevel_config(void *data, struct xdg_toplevel *toplevel, wl->pending_vo_events |= VO_EVENT_LIVE_RESIZING; break; case XDG_TOPLEVEL_STATE_ACTIVATED: + is_activated = true; /* * If we get an ACTIVATED state, we know it cannot be * minimized, but it may not have been minimized @@ -992,6 +998,16 @@ static void handle_toplevel_config(void *data, struct xdg_toplevel *toplevel, m_config_cache_write_opt(wl->vo_opts_cache, &vo_opts->window_maximized); } + if (wl->activated != is_activated) { + wl->activated = is_activated; + if ((!wl->focused && wl->activated && wl->has_keyboard_input) || + (wl->focused && !wl->activated)) + { + wl->focused = !wl->focused; + wl->pending_vo_events |= VO_EVENT_FOCUS; + } + } + if (!(wl->pending_vo_events & VO_EVENT_LIVE_RESIZING)) vo_query_and_reset_events(wl->vo, VO_EVENT_LIVE_RESIZING); @@ -1544,6 +1560,10 @@ int vo_wayland_control(struct vo *vo, int *events, int request, void *arg) } return VO_TRUE; } + case VOCTRL_GET_FOCUSED: { + *(bool *)arg = wl->focused; + return VO_TRUE; + } case VOCTRL_GET_DISPLAY_NAMES: { *(char ***)arg = get_displays_spanned(wl); return VO_TRUE; diff --git a/video/out/wayland_common.h b/video/out/wayland_common.h index 65f7cbf3e5..7eec7be2d5 100644 --- a/video/out/wayland_common.h +++ b/video/out/wayland_common.h @@ -76,6 +76,9 @@ struct vo_wayland_state { bool frame_wait; bool state_change; bool toplevel_configured; + bool activated; + bool has_keyboard_input; + bool focused; int wakeup_pipe[2]; int pending_vo_events; int mouse_x; diff --git a/video/out/x11_common.c b/video/out/x11_common.c index 461ff378d0..92b3539aca 100644 --- a/video/out/x11_common.c +++ b/video/out/x11_common.c @@ -1128,11 +1128,13 @@ void vo_x11_check_events(struct vo *vo) case FocusIn: x11->has_focus = true; vo_update_cursor(vo); + x11->pending_vo_events |= VO_EVENT_FOCUS; break; case FocusOut: release_all_keys(vo); x11->has_focus = false; vo_update_cursor(vo); + x11->pending_vo_events |= VO_EVENT_FOCUS; break; case KeyRelease: release_all_keys(vo); @@ -1878,6 +1880,10 @@ int vo_x11_control(struct vo *vo, int *events, int request, void *arg) } return VO_TRUE; } + case VOCTRL_GET_FOCUSED: { + *(bool *)arg = x11->has_focus; + return VO_TRUE; + } case VOCTRL_GET_DISPLAY_NAMES: { if (!x11->pseudo_mapped) return VO_FALSE;