From b462e519e82be6c7cbba9ac9434ce3ad9d6bb519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 14 Nov 2018 00:08:34 +0100 Subject: [PATCH] window-x11: Focus the default window with delay while waiting for take-focus When requesting to a take-focus window to acquire the input, the client may or may not respond with a SetInputFocus (this doesn't happen for no-input gtk windows in fact [to be fixed there too]), in such case we were unsetting the focus while waiting the reply. In case the client won't respond, we wait for a small delay (set to 250 ms) for the take-focus window to grab the input focus before setting it to the default window. Added a test for this behavior and for the case in which a window takes the focus meanwhile we're waiting to focus the default window. https://gitlab.gnome.org/GNOME/mutter/merge_requests/307 (cherry picked from commit f71151a5dd990d935f3fbb39451f9b41f640b625) --- src/Makefile-tests.am | 1 + ...t-delayed-focus-default-cancelled.metatest | 36 +++++++ .../closed-transient-no-input-parent.metatest | 16 ++- src/x11/window-x11.c | 101 +++++++++++++++++- 4 files changed, 146 insertions(+), 8 deletions(-) create mode 100644 src/tests/stacking/closed-transient-no-input-parent-delayed-focus-default-cancelled.metatest diff --git a/src/Makefile-tests.am b/src/Makefile-tests.am index 376491799..eca6a7b90 100644 --- a/src/Makefile-tests.am +++ b/src/Makefile-tests.am @@ -18,6 +18,7 @@ dist_stacking_DATA = \ tests/stacking/closed-transient-no-input-no-take-focus-parent.metatest \ tests/stacking/closed-transient-no-input-no-take-focus-parents.metatest \ tests/stacking/closed-transient-no-input-parent.metatest \ + tests/stacking/closed-transient-no-input-parent-delayed-focus-default-cancelled.metatest \ tests/stacking/minimized.metatest \ tests/stacking/mixed-windows.metatest \ tests/stacking/set-parent.metatest \ diff --git a/src/tests/stacking/closed-transient-no-input-parent-delayed-focus-default-cancelled.metatest b/src/tests/stacking/closed-transient-no-input-parent-delayed-focus-default-cancelled.metatest new file mode 100644 index 000000000..38897e388 --- /dev/null +++ b/src/tests/stacking/closed-transient-no-input-parent-delayed-focus-default-cancelled.metatest @@ -0,0 +1,36 @@ +new_client 2 x11 +create 2/1 +show 2/1 + +new_client 1 x11 +create 1/1 +show 1/1 + +create 1/2 csd +set_parent 1/2 1 +accept_focus 1/2 false +show 1/2 + +create 1/3 csd +set_parent 1/3 2 +show 1/3 + +wait +assert_focused 1/3 +assert_stacking 2/1 1/1 1/2 1/3 + +destroy 1/3 +sleep 10 + +assert_focused none +assert_stacking 2/1 1/1 1/2 + +activate 2/1 +wait + +assert_focused 2/1 +assert_stacking 1/1 1/2 2/1 + +sleep 250 +assert_focused 2/1 +assert_stacking 1/1 1/2 2/1 diff --git a/src/tests/stacking/closed-transient-no-input-parent.metatest b/src/tests/stacking/closed-transient-no-input-parent.metatest index 4cadb2350..e0f1dc1e2 100644 --- a/src/tests/stacking/closed-transient-no-input-parent.metatest +++ b/src/tests/stacking/closed-transient-no-input-parent.metatest @@ -1,3 +1,7 @@ +new_client 2 x11 +create 2/1 +show 2/1 + new_client 1 x11 create 1/1 show 1/1 @@ -12,9 +16,15 @@ set_parent 1/3 2 show 1/3 wait -assert_stacking 1/1 1/2 1/3 +assert_focused 1/3 +assert_stacking 2/1 1/1 1/2 1/3 destroy 1/3 +dispatch -wait -assert_stacking 1/1 1/2 +assert_focused none +assert_stacking 2/1 1/1 1/2 + +sleep 250 +assert_focused 1/1 +assert_stacking 2/1 1/1 1/2 diff --git a/src/x11/window-x11.c b/src/x11/window-x11.c index e1c34412d..29ea3a6be 100644 --- a/src/x11/window-x11.c +++ b/src/x11/window-x11.c @@ -53,6 +53,8 @@ #include "backends/meta-logical-monitor.h" #include "backends/x11/meta-backend-x11.h" +#define TAKE_FOCUS_FALLBACK_DELAY_MS 250 + G_DEFINE_TYPE_WITH_PRIVATE (MetaWindowX11, meta_window_x11, META_TYPE_WINDOW) static void @@ -722,6 +724,66 @@ request_take_focus (MetaWindow *window, send_icccm_message (window, display->atom_WM_TAKE_FOCUS, timestamp); } +typedef struct +{ + MetaWindow *window; + guint32 timestamp; + guint timeout_id; + gulong unmanaged_id; + gulong focused_changed_id; +} MetaWindowX11DelayedFocusData; + +static void +meta_window_x11_delayed_focus_data_free (MetaWindowX11DelayedFocusData *data) +{ + g_signal_handler_disconnect (data->window, data->unmanaged_id); + g_signal_handler_disconnect (data->window->display, data->focused_changed_id); + + if (data->timeout_id) + g_source_remove (data->timeout_id); + + g_free (data); +} + +static gboolean +focus_window_delayed_timeout (gpointer user_data) +{ + MetaWindowX11DelayedFocusData *data = user_data; + MetaWindow *window = data->window; + guint32 timestamp = data->timestamp; + + data->timeout_id = 0; + meta_window_x11_delayed_focus_data_free (data); + + meta_window_focus (window, timestamp); + + return G_SOURCE_REMOVE; +} + +static void +meta_window_x11_maybe_focus_delayed (MetaWindow *window, + guint32 timestamp) +{ + MetaWindowX11DelayedFocusData *data; + + data = g_new0 (MetaWindowX11DelayedFocusData, 1); + data->window = window; + data->timestamp = timestamp; + + data->unmanaged_id = + g_signal_connect_swapped (window, "unmanaged", + G_CALLBACK (meta_window_x11_delayed_focus_data_free), + data); + + data->focused_changed_id = + g_signal_connect_swapped (window->display, "notify::focus-window", + G_CALLBACK (meta_window_x11_delayed_focus_data_free), + data); + + data->timeout_id = g_timeout_add (TAKE_FOCUS_FALLBACK_DELAY_MS, + focus_window_delayed_timeout, data); +} + static void meta_window_x11_focus (MetaWindow *window, guint32 timestamp) @@ -771,14 +833,43 @@ meta_window_x11_focus (MetaWindow *window, * Normally, we want to just leave the focus undisturbed until * the window responds to WM_TAKE_FOCUS, but if we're unmanaging * the current focus window we *need* to move the focus away, so - * we focus the no_focus_window now (and set - * display->focus_window to that) before sending WM_TAKE_FOCUS. + * we focus the no focus window before sending WM_TAKE_FOCUS, + * and eventually the default focus windwo excluding this one, + * if meanwhile we don't get any focus request. */ if (window->display->focus_window != NULL && window->display->focus_window->unmanaging) - meta_display_focus_the_no_focus_window (window->display, - window->screen, - timestamp); + { + MetaWindow *focus_window = window; + MetaWorkspace *workspace = window->workspace; + MetaStack *stack = window->screen->stack; + + while (TRUE) + { + focus_window = meta_stack_get_default_focus_window (stack, + workspace, + focus_window); + if (!focus_window) + break; + + if (focus_window->unmanaging) + continue; + + if (focus_window->input) + break; + + if (focus_window->shaded && focus_window->frame) + break; + } + + meta_display_focus_the_no_focus_window (window->display, + window->screen, + timestamp); + + if (focus_window) + meta_window_x11_maybe_focus_delayed (focus_window, + timestamp); + } } request_take_focus (window, timestamp);