summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarco Trevisan (Treviño) <mail@3v1n0.net>2018-11-14 00:08:34 +0100
committerMarco Trevisan (Treviño) <mail@3v1n0.net>2019-06-24 15:25:21 +0200
commit2db94e2e40e61e1224d7b3634ab0f63d81b46545 (patch)
tree7f335e678ddbf6112ee11611d87758dd69bdc454
parent866d6780c98276f187a5402adf2a25e7ea272a13 (diff)
downloadmutter-2db94e2e40e61e1224d7b3634ab0f63d81b46545.tar.gz
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)
-rw-r--r--src/tests/meson.build1
-rw-r--r--src/tests/stacking/closed-transient-no-input-parent-delayed-focus-default-cancelled.metatest36
-rw-r--r--src/tests/stacking/closed-transient-no-input-parent.metatest16
-rw-r--r--src/x11/window-x11.c98
4 files changed, 144 insertions, 7 deletions
diff --git a/src/tests/meson.build b/src/tests/meson.build
index d4ea97301..ba534b639 100644
--- a/src/tests/meson.build
+++ b/src/tests/meson.build
@@ -108,6 +108,7 @@ stacking_tests = files([
'stacking/closed-transient-no-input-no-take-focus-parent.metatest',
'stacking/closed-transient-no-input-no-take-focus-parents.metatest',
'stacking/closed-transient-no-input-parent.metatest',
+ 'stacking/closed-transient-no-input-parent-delayed-focus-default-cancelled.metatest',
'stacking/minimized.metatest',
'stacking/mixed-windows.metatest',
'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 6f3675cbc..695dbff25 100644
--- a/src/x11/window-x11.c
+++ b/src/x11/window-x11.c
@@ -50,6 +50,8 @@
#include "x11/window-props.h"
#include "x11/xprops.h"
+#define TAKE_FOCUS_FALLBACK_DELAY_MS 250
+
enum _MetaGtkEdgeConstraints
{
META_GTK_EDGE_CONSTRAINT_TOP_TILED = 1 << 0,
@@ -776,6 +778,64 @@ request_take_focus (MetaWindow *window,
send_icccm_message (window, display->x11_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);
+
+ g_clear_handle_id (&data->timeout_id, g_source_remove);
+ 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)
@@ -827,13 +887,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_x11_display_focus_the_no_focus_window (window->display->x11_display,
- timestamp);
+ {
+ MetaWindow *focus_window = window;
+ MetaX11Display *x11_display = window->display->x11_display;
+ 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 (focus_window->unmanaging)
+ continue;
+
+ if (focus_window->input)
+ break;
+
+ if (focus_window->shaded && focus_window->frame)
+ break;
+ }
+
+ meta_x11_display_focus_the_no_focus_window (x11_display,
+ timestamp);
+
+ if (focus_window)
+ meta_window_x11_maybe_focus_delayed (focus_window,
+ timestamp);
+ }
}
request_take_focus (window, timestamp);