[mutter] window-x11: Focus the default window with delay while waiting for take-focus



commit f71151a5dd990d935f3fbb39451f9b41f640b625
Author: Marco Trevisan (Treviño) <mail 3v1n0 net>
Date:   Wed Nov 14 00:08:34 2018 +0100

    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

 src/tests/meson.build                              |  1 +
 ...parent-delayed-focus-default-cancelled.metatest | 36 ++++++++
 .../closed-transient-no-input-parent.metatest      | 16 +++-
 src/x11/window-x11.c                               | 95 +++++++++++++++++++++-
 4 files changed, 142 insertions(+), 6 deletions(-)
---
diff --git a/src/tests/meson.build b/src/tests/meson.build
index 3d0a3f7a3..26bf3a8c1 100644
--- a/src/tests/meson.build
+++ b/src/tests/meson.build
@@ -114,6 +114,7 @@ stacking_tests = [
   'closed-transient-no-input-no-take-focus-parent',
   'closed-transient-no-input-no-take-focus-parents',
   'closed-transient-no-input-parent',
+  'closed-transient-no-input-parent-delayed-focus-default-cancelled',
   'minimized',
   'mixed-windows',
   'set-parent',
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 1f492984e..b1d115160 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,12 +887,41 @@ 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_unset_input_focus (window->display, timestamp);
+                {
+                  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 (focus_window->unmanaging)
+                        continue;
+
+                      if (focus_window->input)
+                        break;
+
+                      if (focus_window->shaded && focus_window->frame)
+                        break;
+                    }
+
+                  meta_display_unset_input_focus (window->display, timestamp);
+
+                  if (focus_window)
+                    meta_window_x11_maybe_focus_delayed (focus_window,
+                                                         timestamp);
+                }
             }
 
           request_take_focus (window, timestamp);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]