[mutter/gnome-3-32] window-x11: Use any focusable window as fallback delayed focus window



commit 61691bacc8be09e18eca6e9a7427f75761d5e532
Author: Marco Trevisan (TreviƱo) <mail 3v1n0 net>
Date:   Wed Jul 3 12:04:08 2019 +0200

    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
    
    (cherry picked from commit 6d8293a422b08af97d3da985d49448db32d59248)

 src/tests/meson.build                              |   2 +
 .../closed-transient-no-input-parent.metatest      |   2 +-
 ...parents-queued-default-focus-destroyed.metatest |  43 ++++++++
 .../closed-transient-no-input-parents.metatest     |   6 +-
 ...osed-transient-only-take-focus-parents.metatest |  34 ++++++
 src/x11/window-x11.c                               | 119 +++++++++++++++++----
 6 files changed, 179 insertions(+), 27 deletions(-)
---
diff --git a/src/tests/meson.build b/src/tests/meson.build
index 026e6a908..b3c1eab57 100644
--- a/src/tests/meson.build
+++ b/src/tests/meson.build
@@ -110,6 +110,8 @@ stacking_tests = files([
   'stacking/closed-transient-no-input-parent.metatest',
   'stacking/closed-transient-no-input-parent-delayed-focus-default-cancelled.metatest',
   'stacking/closed-transient-no-input-parents.metatest',
+  'stacking/closed-transient-no-input-parents-queued-default-focus-destroyed.metatest',
+  'stacking/closed-transient-only-take-focus-parents.metatest',
   'stacking/minimized.metatest',
   'stacking/mixed-windows.metatest',
   'stacking/set-parent.metatest',
diff --git a/src/tests/stacking/closed-transient-no-input-parent.metatest 
b/src/tests/stacking/closed-transient-no-input-parent.metatest
index e0f1dc1e2..d0f3228d5 100644
--- a/src/tests/stacking/closed-transient-no-input-parent.metatest
+++ b/src/tests/stacking/closed-transient-no-input-parent.metatest
@@ -25,6 +25,6 @@ dispatch
 assert_focused none
 assert_stacking 2/1 1/1 1/2
 
-sleep 250
+sleep 150
 assert_focused 1/1
 assert_stacking 2/1 1/1 1/2
diff --git a/src/tests/stacking/closed-transient-no-input-parents-queued-default-focus-destroyed.metatest 
b/src/tests/stacking/closed-transient-no-input-parents-queued-default-focus-destroyed.metatest
new file mode 100644
index 000000000..49ecc510f
--- /dev/null
+++ b/src/tests/stacking/closed-transient-no-input-parents-queued-default-focus-destroyed.metatest
@@ -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
diff --git a/src/tests/stacking/closed-transient-no-input-parents.metatest 
b/src/tests/stacking/closed-transient-no-input-parents.metatest
index e3ec2e84a..ee9984192 100644
--- a/src/tests/stacking/closed-transient-no-input-parents.metatest
+++ b/src/tests/stacking/closed-transient-no-input-parents.metatest
@@ -35,12 +35,12 @@ dispatch
 assert_focused none
 assert_stacking 0/1 1/1 1/2 1/3 1/4
 
-sleep 250
-assert_focused none
+sleep 600
+assert_focused 1/1
 assert_stacking 0/1 1/1 1/2 1/3 1/4
 
 destroy 1/3
 wait
 
-assert_focused none
+assert_focused 1/1
 assert_stacking 0/1 1/1 1/2 1/4
diff --git a/src/tests/stacking/closed-transient-only-take-focus-parents.metatest 
b/src/tests/stacking/closed-transient-only-take-focus-parents.metatest
new file mode 100644
index 000000000..8aa86700f
--- /dev/null
+++ b/src/tests/stacking/closed-transient-only-take-focus-parents.metatest
@@ -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
diff --git a/src/x11/window-x11.c b/src/x11/window-x11.c
index 34ec465e9..0ca651ba2 100644
--- a/src/x11/window-x11.c
+++ b/src/x11/window-x11.c
@@ -50,7 +50,7 @@
 #include "x11/window-props.h"
 #include "x11/xprops.h"
 
-#define TAKE_FOCUS_FALLBACK_DELAY_MS 250
+#define TAKE_FOCUS_FALLBACK_DELAY_MS 150
 
 enum _MetaGtkEdgeConstraints
 {
@@ -66,6 +66,11 @@ enum _MetaGtkEdgeConstraints
 
 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
 meta_window_x11_init (MetaWindowX11 *window_x11)
 {
@@ -781,22 +786,58 @@ request_take_focus (MetaWindow *window,
 typedef struct
 {
   MetaWindow *window;
+  GQueue *pending_focus_candidates;
   guint32 timestamp;
   guint timeout_id;
   gulong unmanaged_id;
   gulong focused_changed_id;
 } 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
 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->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_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
 focus_window_delayed_timeout (gpointer user_data)
 {
@@ -804,6 +845,9 @@ focus_window_delayed_timeout (gpointer user_data)
   MetaWindow *window = data->window;
   guint32 timestamp = data->timestamp;
 
+  focus_candidates_maybe_take_and_focus_next (&data->pending_focus_candidates,
+                                              timestamp);
+
   data->timeout_id = 0;
   meta_window_x11_delayed_focus_data_free (data);
 
@@ -814,6 +858,7 @@ focus_window_delayed_timeout (gpointer user_data)
 
 static void
 meta_window_x11_maybe_focus_delayed (MetaWindow *window,
+                                     GQueue     *other_focus_candidates,
                                      guint32     timestamp)
 {
   MetaWindowX11DelayedFocusData *data;
@@ -821,6 +866,10 @@ meta_window_x11_maybe_focus_delayed (MetaWindow *window,
   data = g_new0 (MetaWindowX11DelayedFocusData, 1);
   data->window = window;
   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 =
     g_signal_connect_swapped (window, "unmanaged",
@@ -836,6 +885,50 @@ meta_window_x11_maybe_focus_delayed (MetaWindow *window,
                                     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
 meta_window_x11_focus (MetaWindow *window,
                        guint32     timestamp)
@@ -894,32 +987,12 @@ meta_window_x11_focus (MetaWindow *window,
               if (window->display->focus_window != NULL &&
                   window->display->focus_window->unmanaging)
                 {
-                  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 (meta_window_is_focusable (focus_window))
-                        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);
+                  maybe_focus_default_window (window->workspace, window,
+                                              timestamp);
                 }
             }
 


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