[mutter/gnome-3-30] tests: Add "accept_take_focus" command



commit 288ab54ccf4c6f7fcd5bd826aecb00094e2acdd6
Author: Marco Trevisan (TreviƱo) <mail 3v1n0 net>
Date:   Wed Jul 3 16:47:54 2019 +0200

    tests: Add "accept_take_focus" command
    
    When used it setups an X11 event monitor that replies to WM_TAKE_FOCUS
    ClientMessage's with a XSetInputFocus request.
    
    It can only be used by x11 clients on windows that have WM_TAKE_FOCUS atom set
    and that does not accept input.
    
    https://gitlab.gnome.org/GNOME/mutter/merge_requests/669
    
    (cherry picked from commit b80250e483e20bc2b66aa0117af0c442875c1b74)

 src/tests/test-client.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++-
 src/tests/test-runner.c |  19 +++++++++
 2 files changed, 126 insertions(+), 1 deletion(-)
---
diff --git a/src/tests/test-client.c b/src/tests/test-client.c
index 61fc341bf..e5a211c6b 100644
--- a/src/tests/test-client.c
+++ b/src/tests/test-client.c
@@ -31,6 +31,7 @@ static gboolean wayland;
 GHashTable *windows;
 GQuark event_source_quark;
 GQuark event_handlers_quark;
+GQuark can_take_focus_quark;
 
 typedef void (*XEventHandler) (GtkWidget *window, XEvent *event);
 
@@ -61,6 +62,7 @@ lookup_window (const char *window_id)
 
 typedef struct {
   GSource base;
+  GSource **self_ref;
   GPollFD event_poll_fd;
   Display *xdisplay;
 } XClientEventSource;
@@ -118,10 +120,19 @@ x_event_source_dispatch (GSource     *source,
   return TRUE;
 }
 
+static void
+x_event_source_finalize (GSource *source)
+{
+  XClientEventSource *x_source = (XClientEventSource *) source;
+
+  *x_source->self_ref = NULL;
+}
+
 static GSourceFuncs x_event_funcs = {
   x_event_source_prepare,
   x_event_source_check,
   x_event_source_dispatch,
+  x_event_source_finalize,
 };
 
 static GSource*
@@ -136,6 +147,7 @@ ensure_xsource_handler (GdkDisplay *gdkdisplay)
 
   source = g_source_new (&x_event_funcs, sizeof (XClientEventSource));
   x_source = (XClientEventSource *) source;
+  x_source->self_ref = &source;
   x_source->xdisplay = xdisplay;
   x_source->event_poll_fd.fd = ConnectionNumber (xdisplay);
   x_source->event_poll_fd.events = G_IO_IN;
@@ -161,6 +173,15 @@ window_has_x11_event_handler (GtkWidget     *window,
   return g_list_find (handlers, handler) != NULL;
 }
 
+static void
+unref_and_maybe_destroy_gsource (GSource *source)
+{
+  g_source_unref (source);
+
+  if (source->ref_count == 1)
+    g_source_destroy (source);
+}
+
 static void
 window_add_x11_event_handler (GtkWidget     *window,
                               XEventHandler  handler)
@@ -173,7 +194,7 @@ window_add_x11_event_handler (GtkWidget     *window,
 
   source = ensure_xsource_handler (gtk_widget_get_display (window));
   g_object_set_qdata_full (G_OBJECT (window), event_source_quark, source,
-                           (GDestroyNotify) g_source_unref);
+                           (GDestroyNotify) unref_and_maybe_destroy_gsource);
 
   handlers = g_list_append (handlers, handler);
   g_object_set_qdata (G_OBJECT (window), event_handlers_quark, handlers);
@@ -194,6 +215,31 @@ window_remove_x11_event_handler (GtkWidget     *window,
   g_object_set_qdata (G_OBJECT (window), event_handlers_quark, handlers);
 }
 
+static void
+handle_take_focus (GtkWidget *window,
+                   XEvent    *xevent)
+{
+  GdkWindow *gdkwindow = gtk_widget_get_window (window);
+  GdkDisplay *display = gtk_widget_get_display (window);
+  Atom wm_protocols =
+    gdk_x11_get_xatom_by_name_for_display (display, "WM_PROTOCOLS");
+  Atom wm_take_focus =
+    gdk_x11_get_xatom_by_name_for_display (display, "WM_TAKE_FOCUS");
+
+  if (xevent->xany.type != ClientMessage ||
+      xevent->xany.window != GDK_WINDOW_XID (gdkwindow))
+    return;
+
+  if (xevent->xclient.message_type == wm_protocols &&
+      (Atom) xevent->xclient.data.l[0] == wm_take_focus)
+    {
+      XSetInputFocus (xevent->xany.display,
+                      GDK_WINDOW_XID (gdkwindow),
+                      RevertToParent,
+                      xevent->xclient.data.l[1]);
+    }
+}
+
 static void
 process_line (const char *line)
 {
@@ -262,6 +308,9 @@ process_line (const char *line)
       gtk_window_set_title (GTK_WINDOW (window), title);
       g_free (title);
 
+      g_object_set_qdata (G_OBJECT (window), can_take_focus_quark,
+                          GUINT_TO_POINTER (TRUE));
+
       gtk_widget_realize (window);
 
       if (!wayland)
@@ -348,6 +397,14 @@ process_line (const char *line)
           goto out;
         }
 
+      if (!wayland &&
+          window_has_x11_event_handler (window, handle_take_focus))
+        {
+          g_print ("Impossible to use %s for windows accepting take focus",
+                   argv[1]);
+          goto out;
+        }
+
       gboolean enabled = g_ascii_strcasecmp (argv[2], "true") == 0;
       gtk_window_set_accept_focus (GTK_WINDOW (window), enabled);
     }
@@ -372,6 +429,13 @@ process_line (const char *line)
           goto out;
         }
 
+      if (window_has_x11_event_handler (window, handle_take_focus))
+        {
+          g_print ("Impossible to change %s for windows accepting take focus",
+                   argv[1]);
+          goto out;
+        }
+
       GdkDisplay *display = gdk_display_get_default ();
       GdkWindow *gdkwindow = gtk_widget_get_window (window);
       Display *xdisplay = gdk_x11_display_get_xdisplay (display);
@@ -397,10 +461,51 @@ process_line (const char *line)
         new_protocols[n++] = wm_take_focus;
 
       XSetWMProtocols (xdisplay, xwindow, new_protocols, n);
+      g_object_set_qdata (G_OBJECT (window), can_take_focus_quark,
+                          GUINT_TO_POINTER (add));
 
       XFree (new_protocols);
       XFree (protocols);
     }
+  else if (strcmp (argv[0], "accept_take_focus") == 0)
+    {
+      if (argc != 3)
+        {
+          g_print ("usage: %s <window-id> [true|false]", argv[0]);
+          goto out;
+        }
+
+      GtkWidget *window = lookup_window (argv[1]);
+      if (!window)
+        {
+          g_print ("unknown window %s", argv[1]);
+          goto out;
+        }
+
+      if (wayland)
+        {
+          g_print ("%s not supported under wayland", argv[0]);
+          goto out;
+        }
+
+      if (gtk_window_get_accept_focus (GTK_WINDOW (window)))
+        {
+          g_print ("%s not supported for input windows", argv[0]);
+          goto out;
+        }
+
+      if (!g_object_get_qdata (G_OBJECT (window), can_take_focus_quark))
+        {
+          g_print ("%s not supported for windows with no WM_TAKE_FOCUS set",
+                   argv[0]);
+          goto out;
+        }
+
+      if (g_ascii_strcasecmp (argv[2], "true") == 0)
+        window_add_x11_event_handler (window, handle_take_focus);
+      else
+        window_remove_x11_event_handler (window, handle_take_focus);
+    }
   else if (strcmp (argv[0], "show") == 0)
     {
       if (argc != 2)
@@ -651,6 +756,7 @@ main(int argc, char **argv)
                                    g_free, NULL);
   event_source_quark = g_quark_from_static_string ("event-source");
   event_handlers_quark = g_quark_from_static_string ("event-handlers");
+  can_take_focus_quark = g_quark_from_static_string ("can-take-focus");
 
   GInputStream *raw_in = g_unix_input_stream_new (0, FALSE);
   GDataInputStream *in = g_data_input_stream_new (raw_in);
diff --git a/src/tests/test-runner.c b/src/tests/test-runner.c
index 5ec1c645f..4998d1d23 100644
--- a/src/tests/test-runner.c
+++ b/src/tests/test-runner.c
@@ -531,6 +531,25 @@ test_case_do (TestCase *test,
                            NULL))
         return FALSE;
     }
+  else if (strcmp (argv[0], "accept_take_focus") == 0)
+    {
+      if (argc != 3 ||
+          (g_ascii_strcasecmp (argv[2], "true") != 0 &&
+           g_ascii_strcasecmp (argv[2], "false") != 0))
+        BAD_COMMAND("usage: %s <client-id>/<window-id> [true|false]",
+                    argv[0]);
+
+      TestClient *client;
+      const char *window_id;
+      if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
+        return FALSE;
+
+      if (!test_client_do (client, error,
+                           argv[0], window_id,
+                           argv[2],
+                           NULL))
+        return FALSE;
+    }
   else if (strcmp (argv[0], "show") == 0)
     {
       if (argc != 2)


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