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 f71151a5dd)
This commit is contained in:
Marco Trevisan (Treviño) 2018-11-14 00:08:34 +01:00
parent 0060ddc5bf
commit b462e519e8
4 changed files with 146 additions and 8 deletions

View File

@ -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-parent.metatest \
tests/stacking/closed-transient-no-input-no-take-focus-parents.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.metatest \
tests/stacking/closed-transient-no-input-parent-delayed-focus-default-cancelled.metatest \
tests/stacking/minimized.metatest \ tests/stacking/minimized.metatest \
tests/stacking/mixed-windows.metatest \ tests/stacking/mixed-windows.metatest \
tests/stacking/set-parent.metatest \ tests/stacking/set-parent.metatest \

View File

@ -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

View File

@ -1,3 +1,7 @@
new_client 2 x11
create 2/1
show 2/1
new_client 1 x11 new_client 1 x11
create 1/1 create 1/1
show 1/1 show 1/1
@ -12,9 +16,15 @@ set_parent 1/3 2
show 1/3 show 1/3
wait 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 destroy 1/3
dispatch
wait assert_focused none
assert_stacking 1/1 1/2 assert_stacking 2/1 1/1 1/2
sleep 250
assert_focused 1/1
assert_stacking 2/1 1/1 1/2

View File

@ -53,6 +53,8 @@
#include "backends/meta-logical-monitor.h" #include "backends/meta-logical-monitor.h"
#include "backends/x11/meta-backend-x11.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) G_DEFINE_TYPE_WITH_PRIVATE (MetaWindowX11, meta_window_x11, META_TYPE_WINDOW)
static void static void
@ -722,6 +724,66 @@ request_take_focus (MetaWindow *window,
send_icccm_message (window, display->atom_WM_TAKE_FOCUS, timestamp); 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 static void
meta_window_x11_focus (MetaWindow *window, meta_window_x11_focus (MetaWindow *window,
guint32 timestamp) guint32 timestamp)
@ -771,14 +833,43 @@ meta_window_x11_focus (MetaWindow *window,
* Normally, we want to just leave the focus undisturbed until * Normally, we want to just leave the focus undisturbed until
* the window responds to WM_TAKE_FOCUS, but if we're unmanaging * the window responds to WM_TAKE_FOCUS, but if we're unmanaging
* the current focus window we *need* to move the focus away, so * the current focus window we *need* to move the focus away, so
* we focus the no_focus_window now (and set * we focus the no focus window before sending WM_TAKE_FOCUS,
* display->focus_window to that) 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 && if (window->display->focus_window != NULL &&
window->display->focus_window->unmanaging) window->display->focus_window->unmanaging)
{
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, meta_display_focus_the_no_focus_window (window->display,
window->screen, window->screen,
timestamp); timestamp);
if (focus_window)
meta_window_x11_maybe_focus_delayed (focus_window,
timestamp);
}
} }
request_take_focus (window, timestamp); request_take_focus (window, timestamp);