[mutter] test-focus: test program for focus window management
- From: Jasper St. Pierre <jstpierre src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [mutter] test-focus: test program for focus window management
- Date: Wed, 22 May 2013 17:46:22 +0000 (UTC)
commit 4f1d62170b82adb06e0bb38bd1a2d7c059419787
Author: Dan Winship <danw gnome org>
Date: Tue Aug 2 17:35:36 2011 -0400
test-focus: test program for focus window management
https://bugzilla.gnome.org/show_bug.cgi?id=647706
.gitignore | 1 +
src/wm-tester/Makefile.am | 6 +-
src/wm-tester/test-focus.c | 362 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 368 insertions(+), 1 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 81a262c..298177b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -62,6 +62,7 @@ mutter-message
mutter-window-demo
focus-window
test-attached
+test-focus
test-gravity
test-resizing
test-size-hints
diff --git a/src/wm-tester/Makefile.am b/src/wm-tester/Makefile.am
index 1229f19..2bb29d9 100644
--- a/src/wm-tester/Makefile.am
+++ b/src/wm-tester/Makefile.am
@@ -19,7 +19,10 @@ test_size_hints_SOURCES= \
test_attached_SOURCES= \
test-attached.c
-noinst_PROGRAMS=wm-tester test-gravity test-resizing focus-window test-size-hints test-attached
+test_focus_SOURCES= \
+ test-focus.c
+
+noinst_PROGRAMS=wm-tester test-gravity test-resizing focus-window test-size-hints test-attached test-focus
wm_tester_LDADD= @MUTTER_LIBS@
test_gravity_LDADD= @MUTTER_LIBS@
@@ -27,3 +30,4 @@ test_resizing_LDADD= @MUTTER_LIBS@
test_size_hints_LDADD= @MUTTER_LIBS@
focus_window_LDADD= @MUTTER_LIBS@
test_attached_LDADD= @MUTTER_LIBS@
+test_focus_LDADD= @MUTTER_LIBS@
diff --git a/src/wm-tester/test-focus.c b/src/wm-tester/test-focus.c
new file mode 100644
index 0000000..2ff59c5
--- /dev/null
+++ b/src/wm-tester/test-focus.c
@@ -0,0 +1,362 @@
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <X11/Xatom.h>
+
+GtkWidget *main_window;
+GtkWidget *noinput_window, *passive_window, *local_window;
+GtkWidget *global_window, *lame_window, *grabby_window, *dying_window;
+
+static void
+clear_on_destroy (GtkWidget *widget, gpointer user_data)
+{
+ GtkWidget **widget_pointer = user_data;
+
+ *widget_pointer = NULL;
+}
+
+static void
+disable_take_focus (GtkWidget *window)
+{
+ GdkDisplay *display;
+ GdkWindow *gdkwindow;
+ Atom *protocols, wm_take_focus;
+ int n_protocols, i;
+
+ gtk_widget_realize (window);
+ gdkwindow = gtk_widget_get_window (window);
+ display = gdk_window_get_display (gdkwindow);
+
+ wm_take_focus = gdk_x11_get_xatom_by_name_for_display (display, "WM_TAKE_FOCUS");
+ XGetWMProtocols (GDK_DISPLAY_XDISPLAY (display),
+ GDK_WINDOW_XID (gdkwindow),
+ &protocols, &n_protocols);
+ for (i = 0; i < n_protocols; i++)
+ {
+ if (protocols[i] == wm_take_focus)
+ {
+ protocols[i] = protocols[n_protocols - 1];
+ n_protocols--;
+ break;
+ }
+ }
+ XSetWMProtocols (GDK_DISPLAY_XDISPLAY (display),
+ GDK_WINDOW_XID (gdkwindow),
+ protocols, n_protocols);
+ XFree (protocols);
+}
+
+static void
+clear_input_hint (GtkWidget *window)
+{
+ /* This needs to be called after gtk_widget_show, otherwise
+ * GTK+ will overwrite it. */
+ GdkWindow *gdkwindow = gtk_widget_get_window (window);
+ XWMHints *wm_hints;
+
+ wm_hints = XGetWMHints (GDK_DISPLAY_XDISPLAY (gdk_window_get_display (gdkwindow)),
+ GDK_WINDOW_XID (gdkwindow));
+
+ wm_hints->flags |= InputHint;
+ wm_hints->input = False;
+
+ XSetWMHints (GDK_DISPLAY_XDISPLAY (gdk_window_get_display (gdkwindow)),
+ GDK_WINDOW_XID (gdkwindow),
+ wm_hints);
+
+ XFree (wm_hints);
+}
+
+static void
+active_notify (GObject *obj,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ GtkLabel *label = user_data;
+
+ if (gtk_window_is_active (GTK_WINDOW (obj)))
+ gtk_label_set_text (label, "Focused");
+ else
+ gtk_label_set_text (label, "Not focused");
+}
+
+static void
+make_focused_label (GtkWidget *toplevel,
+ GtkWidget *parent)
+{
+ GtkWidget *label;
+
+ label = gtk_label_new ("");
+ gtk_widget_show (label);
+
+ gtk_container_add (GTK_CONTAINER (parent), label);
+
+ g_signal_connect (toplevel, "notify::is-active",
+ G_CALLBACK (active_notify), label);
+ active_notify (G_OBJECT (toplevel), NULL, label);
+}
+
+static void
+setup_test_dialog (GtkWidget *toplevel)
+{
+ make_focused_label (toplevel, toplevel);
+ gtk_widget_set_size_request (toplevel, 200, 200);
+}
+
+static void
+noinput_clicked (GtkButton *button, gpointer user_data)
+{
+ if (noinput_window)
+ gtk_window_present_with_time (GTK_WINDOW (noinput_window), gtk_get_current_event_time ());
+ else
+ {
+ noinput_window = g_object_new (GTK_TYPE_WINDOW,
+ "type", GTK_WINDOW_TOPLEVEL,
+ "title", "No Input",
+ "accept-focus", FALSE,
+ NULL);
+ setup_test_dialog (noinput_window);
+ g_signal_connect (noinput_window, "destroy",
+ G_CALLBACK (clear_on_destroy), &noinput_window);
+ disable_take_focus (noinput_window);
+ gtk_widget_show (noinput_window);
+ }
+}
+
+static void
+passive_clicked (GtkButton *button, gpointer user_data)
+{
+ if (passive_window)
+ gtk_window_present_with_time (GTK_WINDOW (passive_window), gtk_get_current_event_time ());
+ else
+ {
+ passive_window = g_object_new (GTK_TYPE_WINDOW,
+ "type", GTK_WINDOW_TOPLEVEL,
+ "title", "Passive Input",
+ "accept-focus", TRUE,
+ NULL);
+ setup_test_dialog (passive_window);
+ g_signal_connect (passive_window, "destroy",
+ G_CALLBACK (clear_on_destroy), &passive_window);
+ disable_take_focus (passive_window);
+ gtk_widget_show (passive_window);
+ }
+}
+
+static void
+local_clicked (GtkButton *button, gpointer user_data)
+{
+ if (local_window)
+ gtk_window_present_with_time (GTK_WINDOW (local_window), gtk_get_current_event_time ());
+ else
+ {
+ local_window = g_object_new (GTK_TYPE_WINDOW,
+ "type", GTK_WINDOW_TOPLEVEL,
+ "title", "Locally Active Input",
+ "accept-focus", TRUE,
+ NULL);
+ setup_test_dialog (local_window);
+ g_signal_connect (local_window, "destroy",
+ G_CALLBACK (clear_on_destroy), &local_window);
+ gtk_widget_show (local_window);
+ }
+}
+
+static void
+global_clicked (GtkButton *button, gpointer user_data)
+{
+ if (global_window)
+ gtk_window_present_with_time (GTK_WINDOW (global_window), gtk_get_current_event_time ());
+ else
+ {
+ /* gtk will only process WM_TAKE_FOCUS messages if accept-focus
+ * is TRUE. So we set that property and then manually clear the
+ * Input WMHint.
+ */
+ global_window = g_object_new (GTK_TYPE_WINDOW,
+ "type", GTK_WINDOW_TOPLEVEL,
+ "title", "Globally Active Input",
+ "accept-focus", TRUE,
+ NULL);
+ setup_test_dialog (global_window);
+ g_signal_connect (global_window, "destroy",
+ G_CALLBACK (clear_on_destroy), &global_window);
+ gtk_widget_show (global_window);
+ clear_input_hint (global_window);
+ }
+}
+
+static void
+lame_clicked (GtkButton *button, gpointer user_data)
+{
+ if (lame_window)
+ gtk_window_present_with_time (GTK_WINDOW (lame_window), gtk_get_current_event_time ());
+ else
+ {
+ lame_window = g_object_new (GTK_TYPE_WINDOW,
+ "type", GTK_WINDOW_TOPLEVEL,
+ "title", "Lame Globally Active Input",
+ "accept-focus", FALSE,
+ NULL);
+ setup_test_dialog (lame_window);
+ g_signal_connect (lame_window, "destroy",
+ G_CALLBACK (clear_on_destroy), &lame_window);
+ gtk_widget_show (lame_window);
+ }
+}
+
+static void
+grabby_active_changed (GObject *object, GParamSpec *param, gpointer user_data)
+{
+ if (gtk_window_is_active (GTK_WINDOW (grabby_window)))
+ {
+ GdkWindow *gdkwindow = gtk_widget_get_window (grabby_window);
+ guint32 now = gdk_x11_get_server_time (gdkwindow);
+
+ gtk_window_present_with_time (GTK_WINDOW (main_window), now - 1);
+ XSetInputFocus (GDK_DISPLAY_XDISPLAY (gdk_window_get_display (gdkwindow)),
+ GDK_WINDOW_XID (gdkwindow),
+ RevertToParent,
+ now);
+ }
+}
+
+static void
+grabby_clicked (GtkButton *button, gpointer user_data)
+{
+ if (grabby_window)
+ gtk_window_present_with_time (GTK_WINDOW (grabby_window), gtk_get_current_event_time ());
+ else
+ {
+ grabby_window = g_object_new (GTK_TYPE_WINDOW,
+ "type", GTK_WINDOW_TOPLEVEL,
+ "title", "Focus-grabbing Window",
+ "accept-focus", TRUE,
+ /* Because mutter maps windows
+ * asynchronously, our trick won't
+ * work if we try to do it when the
+ * window is first mapped.
+ */
+ "focus-on-map", FALSE,
+ NULL);
+ setup_test_dialog (grabby_window);
+ g_signal_connect (grabby_window, "destroy",
+ G_CALLBACK (clear_on_destroy), &grabby_window);
+ g_signal_connect (grabby_window, "notify::is-active",
+ G_CALLBACK (grabby_active_changed), NULL);
+ gtk_widget_show (grabby_window);
+ }
+}
+
+static void
+dying_clicked (GtkButton *button, gpointer user_data)
+{
+ if (dying_window)
+ {
+ gtk_window_present_with_time (GTK_WINDOW (dying_window), gtk_get_current_event_time ());
+ gtk_widget_destroy (dying_window);
+ }
+ else
+ {
+ GtkWidget *label;
+
+ dying_window = g_object_new (GTK_TYPE_WINDOW,
+ "type", GTK_WINDOW_TOPLEVEL,
+ "title", "Dying Window",
+ "accept-focus", TRUE,
+ /* As with grabby window */
+ "focus-on-map", FALSE,
+ NULL);
+ setup_test_dialog (dying_window);
+ g_signal_connect (dying_window, "destroy",
+ G_CALLBACK (clear_on_destroy), &dying_window);
+
+ label = gtk_label_new ("Click button again to test");
+ gtk_container_add (GTK_CONTAINER (dying_window), label);
+ gtk_widget_set_size_request (dying_window, 200, 200);
+
+ gtk_widget_show_all (dying_window);
+ }
+}
+
+static void
+main_window_destroyed (GtkWidget *widget, gpointer user_data)
+{
+ gtk_main_quit ();
+}
+
+int
+main (int argc, char **argv)
+{
+ GtkWidget *vbox, *button;
+
+ gtk_init (&argc, &argv);
+
+ main_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (main_window), "Focus Tester");
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 8);
+ gtk_box_set_homogeneous (GTK_BOX (vbox), 8);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
+ gtk_container_add (GTK_CONTAINER (main_window), vbox);
+
+ make_focused_label (main_window, vbox);
+
+ /* ICCCM "No Input" mode; Input hint False, WM_TAKE_FOCUS absent */
+ button = gtk_button_new_with_label ("No Input Window");
+ g_signal_connect (button, "clicked", G_CALLBACK (noinput_clicked), NULL);
+ gtk_container_add (GTK_CONTAINER (vbox), button);
+
+ /* ICCCM "Passive" mode; Input hint True, WM_TAKE_FOCUS absent */
+ button = gtk_button_new_with_label ("Passive Input Window");
+ g_signal_connect (button, "clicked", G_CALLBACK (passive_clicked), NULL);
+ gtk_container_add (GTK_CONTAINER (vbox), button);
+
+ /* ICCCM "Locally Active" mode; Input hint True, WM_TAKE_FOCUS present.
+ * This is the behavior of GtkWindows with accept_focus==TRUE.
+ */
+ button = gtk_button_new_with_label ("Locally Active Window");
+ g_signal_connect (button, "clicked", G_CALLBACK (local_clicked), NULL);
+ gtk_container_add (GTK_CONTAINER (vbox), button);
+
+ /* ICCCM "Globally Active" mode; Input hint False, WM_TAKE_FOCUS present,
+ * and the window responds to WM_TAKE_FOCUS by calling XSetInputFocus.
+ */
+ button = gtk_button_new_with_label ("Globally Active Window");
+ g_signal_connect (button, "clicked", G_CALLBACK (global_clicked), NULL);
+ gtk_container_add (GTK_CONTAINER (vbox), button);
+
+ /* "Lame" Globally Active mode; like "Globally Active", except that
+ * the window does not respond to WM_TAKE_FOCUS. This is the
+ * behavior of GtkWindows with accept_focus==FALSE.
+ */
+ button = gtk_button_new_with_label ("Globally Lame Window");
+ g_signal_connect (button, "clicked", G_CALLBACK (lame_clicked), NULL);
+ gtk_container_add (GTK_CONTAINER (vbox), button);
+
+ /* "Grabby" window; when you activate the window, it asks the wm to
+ * focus the main window, but then forcibly grabs focus back with
+ * a newer timestamp, causing the wm to be temporarily confused
+ * about what window is focused and triggering the "Earlier attempt
+ * to focus ... failed" codepath.
+ */
+ button = gtk_button_new_with_label ("Grabby Window");
+ g_signal_connect (button, "clicked", G_CALLBACK (grabby_clicked), NULL);
+ gtk_container_add (GTK_CONTAINER (vbox), button);
+
+ /* "Dying" window; we create the window on the first click, then
+ * activate it and destroy it on the second click, causing mutter to
+ * do an XSetInputFocus but not receive the corresponding FocusIn.
+ */
+ button = gtk_button_new_with_label ("Dying Window");
+ g_signal_connect (button, "clicked", G_CALLBACK (dying_clicked), NULL);
+ gtk_container_add (GTK_CONTAINER (vbox), button);
+
+ gtk_widget_show_all (main_window);
+
+ g_signal_connect (main_window, "destroy",
+ G_CALLBACK (main_window_destroyed), NULL);
+
+ gtk_main ();
+
+ return 0;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]