window-x11: Use any focusable window as fallback delayed focus window
As per commit f71151a5
we focus an input window if no take-focus-window accepts
it. This might lead to an infinite loop if there are various focusable but
non-input windows in the stack.
When the current focus window is unmanaging and we're trying to focus a
WM_TAKE_FOCUS window, we intent to give the focus to the first focusable input
window in the stack.
However, if an application (such as the Java ones) only uses non-input
WM_TAKE_FOCUS windows, are not requesting these ones to get the focus. This
might lead to a state where no window is focused, or a wrong one is.
So, instead of only focus the first eventually input window available, try to
request to all the take-focus windows that are in the stack between the
destroyed one and the first input one to acquire the input focus.
Use a queue to keep track of those windows, that is passed around stealing
ownership, while we protect for unmanaged queued windows.
Also, reduce the default timeout value, as the previous one might lead to an
excessive long wait.
Added metatests verifying these situations.
Closes: https://gitlab.gnome.org/GNOME/mutter/issues/660
https://gitlab.gnome.org/GNOME/mutter/merge_requests/669
This commit is contained in:
parent
b80250e483
commit
6d8293a422
@ -114,8 +114,10 @@ stacking_tests = [
|
|||||||
'closed-transient-no-input-no-take-focus-parent',
|
'closed-transient-no-input-no-take-focus-parent',
|
||||||
'closed-transient-no-input-no-take-focus-parents',
|
'closed-transient-no-input-no-take-focus-parents',
|
||||||
'closed-transient-no-input-parent',
|
'closed-transient-no-input-parent',
|
||||||
'closed-transient-no-input-parents',
|
|
||||||
'closed-transient-no-input-parent-delayed-focus-default-cancelled',
|
'closed-transient-no-input-parent-delayed-focus-default-cancelled',
|
||||||
|
'closed-transient-no-input-parents',
|
||||||
|
'closed-transient-no-input-parents-queued-default-focus-destroyed',
|
||||||
|
'closed-transient-only-take-focus-parents',
|
||||||
'minimized',
|
'minimized',
|
||||||
'mixed-windows',
|
'mixed-windows',
|
||||||
'set-parent',
|
'set-parent',
|
||||||
|
@ -25,6 +25,6 @@ dispatch
|
|||||||
assert_focused none
|
assert_focused none
|
||||||
assert_stacking 2/1 1/1 1/2
|
assert_stacking 2/1 1/1 1/2
|
||||||
|
|
||||||
sleep 250
|
sleep 150
|
||||||
assert_focused 1/1
|
assert_focused 1/1
|
||||||
assert_stacking 2/1 1/1 1/2
|
assert_stacking 2/1 1/1 1/2
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
new_client 0 x11
|
||||||
|
create 0/1
|
||||||
|
show 0/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
|
||||||
|
accept_focus 1/3 false
|
||||||
|
show 1/3
|
||||||
|
|
||||||
|
create 1/4 csd
|
||||||
|
set_parent 1/4 3
|
||||||
|
accept_focus 1/4 false
|
||||||
|
show 1/4
|
||||||
|
|
||||||
|
create 1/5 csd
|
||||||
|
set_parent 1/5 3
|
||||||
|
show 1/5
|
||||||
|
|
||||||
|
wait
|
||||||
|
assert_focused 1/5
|
||||||
|
assert_stacking 0/1 1/1 1/2 1/3 1/4 1/5
|
||||||
|
|
||||||
|
destroy 1/5
|
||||||
|
dispatch
|
||||||
|
|
||||||
|
assert_focused none
|
||||||
|
assert_stacking 0/1 1/1 1/2 1/3 1/4
|
||||||
|
|
||||||
|
destroy 1/2
|
||||||
|
dispatch
|
||||||
|
|
||||||
|
sleep 450
|
||||||
|
assert_focused 1/1
|
||||||
|
assert_stacking 0/1 1/1 1/3 1/4
|
@ -35,12 +35,12 @@ dispatch
|
|||||||
assert_focused none
|
assert_focused none
|
||||||
assert_stacking 0/1 1/1 1/2 1/3 1/4
|
assert_stacking 0/1 1/1 1/2 1/3 1/4
|
||||||
|
|
||||||
sleep 250
|
sleep 600
|
||||||
assert_focused none
|
assert_focused 1/1
|
||||||
assert_stacking 0/1 1/1 1/2 1/3 1/4
|
assert_stacking 0/1 1/1 1/2 1/3 1/4
|
||||||
|
|
||||||
destroy 1/3
|
destroy 1/3
|
||||||
wait
|
wait
|
||||||
|
|
||||||
assert_focused none
|
assert_focused 1/1
|
||||||
assert_stacking 0/1 1/1 1/2 1/4
|
assert_stacking 0/1 1/1 1/2 1/4
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
new_client 0 x11
|
||||||
|
create 0/1
|
||||||
|
show 0/1
|
||||||
|
|
||||||
|
new_client 1 x11
|
||||||
|
create 1/1
|
||||||
|
accept_focus 1/1 false
|
||||||
|
can_take_focus 1/1 true
|
||||||
|
accept_take_focus 1/1 true
|
||||||
|
show 1/1
|
||||||
|
|
||||||
|
create 1/2 csd
|
||||||
|
set_parent 1/2 1
|
||||||
|
accept_focus 1/2 false
|
||||||
|
can_take_focus 1/2 true
|
||||||
|
accept_take_focus 1/2 true
|
||||||
|
show 1/2
|
||||||
|
|
||||||
|
create 1/3
|
||||||
|
set_parent 1/3 2
|
||||||
|
show 1/3
|
||||||
|
|
||||||
|
assert_focused 1/3
|
||||||
|
assert_stacking 0/1 1/1 1/2 1/3
|
||||||
|
|
||||||
|
destroy 1/3
|
||||||
|
wait
|
||||||
|
|
||||||
|
assert_focused 1/2
|
||||||
|
assert_stacking 0/1 1/1 1/2
|
||||||
|
|
||||||
|
sleep 150
|
||||||
|
assert_focused 1/2
|
||||||
|
assert_stacking 0/1 1/1 1/2
|
@ -50,7 +50,7 @@
|
|||||||
#include "x11/window-props.h"
|
#include "x11/window-props.h"
|
||||||
#include "x11/xprops.h"
|
#include "x11/xprops.h"
|
||||||
|
|
||||||
#define TAKE_FOCUS_FALLBACK_DELAY_MS 250
|
#define TAKE_FOCUS_FALLBACK_DELAY_MS 150
|
||||||
|
|
||||||
enum _MetaGtkEdgeConstraints
|
enum _MetaGtkEdgeConstraints
|
||||||
{
|
{
|
||||||
@ -66,6 +66,11 @@ enum _MetaGtkEdgeConstraints
|
|||||||
|
|
||||||
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
|
||||||
|
meta_window_x11_maybe_focus_delayed (MetaWindow *window,
|
||||||
|
GQueue *other_focus_candidates,
|
||||||
|
guint32 timestamp);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
meta_window_x11_init (MetaWindowX11 *window_x11)
|
meta_window_x11_init (MetaWindowX11 *window_x11)
|
||||||
{
|
{
|
||||||
@ -781,22 +786,58 @@ request_take_focus (MetaWindow *window,
|
|||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
MetaWindow *window;
|
MetaWindow *window;
|
||||||
|
GQueue *pending_focus_candidates;
|
||||||
guint32 timestamp;
|
guint32 timestamp;
|
||||||
guint timeout_id;
|
guint timeout_id;
|
||||||
gulong unmanaged_id;
|
gulong unmanaged_id;
|
||||||
gulong focused_changed_id;
|
gulong focused_changed_id;
|
||||||
} MetaWindowX11DelayedFocusData;
|
} MetaWindowX11DelayedFocusData;
|
||||||
|
|
||||||
|
static void
|
||||||
|
disconnect_pending_focus_window_signals (MetaWindow *window,
|
||||||
|
GQueue *focus_candidates)
|
||||||
|
{
|
||||||
|
g_signal_handlers_disconnect_by_func (window, g_queue_remove,
|
||||||
|
focus_candidates);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
meta_window_x11_delayed_focus_data_free (MetaWindowX11DelayedFocusData *data)
|
meta_window_x11_delayed_focus_data_free (MetaWindowX11DelayedFocusData *data)
|
||||||
{
|
{
|
||||||
g_signal_handler_disconnect (data->window, data->unmanaged_id);
|
g_signal_handler_disconnect (data->window, data->unmanaged_id);
|
||||||
g_signal_handler_disconnect (data->window->display, data->focused_changed_id);
|
g_signal_handler_disconnect (data->window->display, data->focused_changed_id);
|
||||||
|
|
||||||
|
if (data->pending_focus_candidates)
|
||||||
|
{
|
||||||
|
g_queue_foreach (data->pending_focus_candidates,
|
||||||
|
(GFunc) disconnect_pending_focus_window_signals,
|
||||||
|
data->pending_focus_candidates);
|
||||||
|
g_queue_free (data->pending_focus_candidates);
|
||||||
|
}
|
||||||
|
|
||||||
g_clear_handle_id (&data->timeout_id, g_source_remove);
|
g_clear_handle_id (&data->timeout_id, g_source_remove);
|
||||||
g_free (data);
|
g_free (data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
focus_candidates_maybe_take_and_focus_next (GQueue **focus_candidates_ptr,
|
||||||
|
guint32 timestamp)
|
||||||
|
{
|
||||||
|
MetaWindow *focus_window;
|
||||||
|
GQueue *focus_candidates;
|
||||||
|
|
||||||
|
g_assert (*focus_candidates_ptr);
|
||||||
|
|
||||||
|
if (g_queue_is_empty (*focus_candidates_ptr))
|
||||||
|
return;
|
||||||
|
|
||||||
|
focus_candidates = g_steal_pointer (focus_candidates_ptr);
|
||||||
|
focus_window = g_queue_pop_head (focus_candidates);
|
||||||
|
|
||||||
|
disconnect_pending_focus_window_signals (focus_window, focus_candidates);
|
||||||
|
meta_window_x11_maybe_focus_delayed (focus_window, focus_candidates, timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
focus_window_delayed_timeout (gpointer user_data)
|
focus_window_delayed_timeout (gpointer user_data)
|
||||||
{
|
{
|
||||||
@ -804,6 +845,9 @@ focus_window_delayed_timeout (gpointer user_data)
|
|||||||
MetaWindow *window = data->window;
|
MetaWindow *window = data->window;
|
||||||
guint32 timestamp = data->timestamp;
|
guint32 timestamp = data->timestamp;
|
||||||
|
|
||||||
|
focus_candidates_maybe_take_and_focus_next (&data->pending_focus_candidates,
|
||||||
|
timestamp);
|
||||||
|
|
||||||
data->timeout_id = 0;
|
data->timeout_id = 0;
|
||||||
meta_window_x11_delayed_focus_data_free (data);
|
meta_window_x11_delayed_focus_data_free (data);
|
||||||
|
|
||||||
@ -814,6 +858,7 @@ focus_window_delayed_timeout (gpointer user_data)
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
meta_window_x11_maybe_focus_delayed (MetaWindow *window,
|
meta_window_x11_maybe_focus_delayed (MetaWindow *window,
|
||||||
|
GQueue *other_focus_candidates,
|
||||||
guint32 timestamp)
|
guint32 timestamp)
|
||||||
{
|
{
|
||||||
MetaWindowX11DelayedFocusData *data;
|
MetaWindowX11DelayedFocusData *data;
|
||||||
@ -821,6 +866,10 @@ meta_window_x11_maybe_focus_delayed (MetaWindow *window,
|
|||||||
data = g_new0 (MetaWindowX11DelayedFocusData, 1);
|
data = g_new0 (MetaWindowX11DelayedFocusData, 1);
|
||||||
data->window = window;
|
data->window = window;
|
||||||
data->timestamp = timestamp;
|
data->timestamp = timestamp;
|
||||||
|
data->pending_focus_candidates = other_focus_candidates;
|
||||||
|
|
||||||
|
meta_topic (META_DEBUG_FOCUS,
|
||||||
|
"Requesting delayed focus to %s\n", window->desc);
|
||||||
|
|
||||||
data->unmanaged_id =
|
data->unmanaged_id =
|
||||||
g_signal_connect_swapped (window, "unmanaged",
|
g_signal_connect_swapped (window, "unmanaged",
|
||||||
@ -836,6 +885,50 @@ meta_window_x11_maybe_focus_delayed (MetaWindow *window,
|
|||||||
focus_window_delayed_timeout, data);
|
focus_window_delayed_timeout, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
maybe_focus_default_window (MetaWorkspace *workspace,
|
||||||
|
MetaWindow *not_this_one,
|
||||||
|
guint32 timestamp)
|
||||||
|
{
|
||||||
|
MetaStack *stack = workspace->display->stack;
|
||||||
|
g_autoptr (GList) focusable_windows = NULL;
|
||||||
|
g_autoptr (GQueue) focus_candidates = NULL;
|
||||||
|
GList *l;
|
||||||
|
|
||||||
|
/* Go through all the focusable windows and try to focus them
|
||||||
|
* in order, waiting for a delay. The first one that replies to
|
||||||
|
* the request (in case of take focus windows) changing the display
|
||||||
|
* focused window, will stop the chained requests.
|
||||||
|
*/
|
||||||
|
focusable_windows =
|
||||||
|
meta_stack_get_default_focus_candidates (stack, workspace);
|
||||||
|
focus_candidates = g_queue_new ();
|
||||||
|
|
||||||
|
for (l = g_list_last (focusable_windows); l; l = l->prev)
|
||||||
|
{
|
||||||
|
MetaWindow *focus_window = l->data;
|
||||||
|
|
||||||
|
if (focus_window == not_this_one)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
g_queue_push_tail (focus_candidates, focus_window);
|
||||||
|
g_signal_connect_swapped (focus_window, "unmanaged",
|
||||||
|
G_CALLBACK (g_queue_remove),
|
||||||
|
focus_candidates);
|
||||||
|
|
||||||
|
if (!META_IS_WINDOW_X11 (focus_window))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (focus_window->input)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (focus_window->shaded && focus_window->frame)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
focus_candidates_maybe_take_and_focus_next (&focus_candidates, timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
meta_window_x11_focus (MetaWindow *window,
|
meta_window_x11_focus (MetaWindow *window,
|
||||||
guint32 timestamp)
|
guint32 timestamp)
|
||||||
@ -894,29 +987,8 @@ meta_window_x11_focus (MetaWindow *window,
|
|||||||
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 = workspace->display->stack;
|
|
||||||
|
|
||||||
while (TRUE)
|
|
||||||
{
|
|
||||||
focus_window = meta_stack_get_default_focus_window (stack,
|
|
||||||
workspace,
|
|
||||||
focus_window);
|
|
||||||
if (!focus_window)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (meta_window_is_focusable (focus_window))
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (focus_window->shaded && focus_window->frame)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
meta_display_unset_input_focus (window->display, timestamp);
|
meta_display_unset_input_focus (window->display, timestamp);
|
||||||
|
maybe_focus_default_window (window->workspace, window,
|
||||||
if (focus_window)
|
|
||||||
meta_window_x11_maybe_focus_delayed (focus_window,
|
|
||||||
timestamp);
|
timestamp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user