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



commit b462e519e82be6c7cbba9ac9434ce3ad9d6bb519
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
    (cherry picked from commit f71151a5dd990d935f3fbb39451f9b41f640b625)

 src/Makefile-tests.am                              |   1 +
 ...parent-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(-)
---
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);


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