test-focus: test program for focus window management
https://bugzilla.gnome.org/show_bug.cgi?id=647706
This commit is contained in:
parent
e10804727d
commit
4f1d62170b
1
.gitignore
vendored
1
.gitignore
vendored
@ -62,6 +62,7 @@ mutter-message
|
||||
mutter-window-demo
|
||||
focus-window
|
||||
test-attached
|
||||
test-focus
|
||||
test-gravity
|
||||
test-resizing
|
||||
test-size-hints
|
||||
|
@ -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@
|
||||
|
362
src/wm-tester/test-focus.c
Normal file
362
src/wm-tester/test-focus.c
Normal file
@ -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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user