summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarco Trevisan (TreviƱo) <mail@3v1n0.net>2019-07-03 14:43:49 +0000
committerMarco Trevisan <mail@3v1n0.net>2019-07-08 14:32:32 +0000
commit80869e0737638fff1a785ff01392b7f977d352ee (patch)
treed5d508169b335b533ec8759dd81345232fcfc914
parent763092a4b6fd39d445d4ebadc8ae111cfad85d10 (diff)
downloadmutter-80869e0737638fff1a785ff01392b7f977d352ee.tar.gz
test-client: Add x11 events GSource handler
When using gtk under X11 some WM related events are always filtered and not delivered when using the gdk Window filters. So, add a new one with higher priority than the GTK events one so that we can pick those events before than Gtk itself. https://gitlab.gnome.org/GNOME/mutter/merge_requests/669 (cherry picked from commit bd0f1bd338d86b382ca34e659b6651e288eba2fd)
-rw-r--r--src/tests/test-client.c141
1 files changed, 141 insertions, 0 deletions
diff --git a/src/tests/test-client.c b/src/tests/test-client.c
index 7b3bf4de0..264be0489 100644
--- a/src/tests/test-client.c
+++ b/src/tests/test-client.c
@@ -31,6 +31,10 @@
const char *client_id = "0";
static gboolean wayland;
GHashTable *windows;
+GQuark event_source_quark;
+GQuark event_handlers_quark;
+
+typedef void (*XEventHandler) (GtkWidget *window, XEvent *event);
static void read_next_line (GDataInputStream *in);
@@ -57,6 +61,141 @@ lookup_window (const char *window_id)
return window;
}
+typedef struct {
+ GSource base;
+ GPollFD event_poll_fd;
+ Display *xdisplay;
+} XClientEventSource;
+
+static gboolean
+x_event_source_prepare (GSource *source,
+ int *timeout)
+{
+ XClientEventSource *x_source = (XClientEventSource *) source;
+
+ *timeout = -1;
+
+ return XPending (x_source->xdisplay);
+}
+
+static gboolean
+x_event_source_check (GSource *source)
+{
+ XClientEventSource *x_source = (XClientEventSource *) source;
+
+ return XPending (x_source->xdisplay);
+}
+
+static gboolean
+x_event_source_dispatch (GSource *source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ XClientEventSource *x_source = (XClientEventSource *) source;
+
+ while (XPending (x_source->xdisplay))
+ {
+ GHashTableIter iter;
+ XEvent event;
+ gpointer value;
+
+ XNextEvent (x_source->xdisplay, &event);
+
+ g_hash_table_iter_init (&iter, windows);
+ while (g_hash_table_iter_next (&iter, NULL, &value))
+ {
+ GList *l;
+ GtkWidget *window = value;
+ GList *handlers =
+ g_object_get_qdata (G_OBJECT (window), event_handlers_quark);
+
+ for (l = handlers; l; l = l->next)
+ {
+ XEventHandler handler = l->data;
+ handler (window, &event);
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static GSourceFuncs x_event_funcs = {
+ x_event_source_prepare,
+ x_event_source_check,
+ x_event_source_dispatch,
+};
+
+static GSource*
+ensure_xsource_handler (GdkDisplay *gdkdisplay)
+{
+ static GSource *source = NULL;
+ Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdkdisplay);
+ XClientEventSource *x_source;
+
+ if (source)
+ return g_source_ref (source);
+
+ source = g_source_new (&x_event_funcs, sizeof (XClientEventSource));
+ x_source = (XClientEventSource *) source;
+ x_source->xdisplay = xdisplay;
+ x_source->event_poll_fd.fd = ConnectionNumber (xdisplay);
+ x_source->event_poll_fd.events = G_IO_IN;
+ g_source_add_poll (source, &x_source->event_poll_fd);
+
+ g_source_set_priority (source, GDK_PRIORITY_EVENTS - 1);
+ g_source_set_can_recurse (source, TRUE);
+ g_source_attach (source, NULL);
+
+ return source;
+}
+
+static gboolean
+window_has_x11_event_handler (GtkWidget *window,
+ XEventHandler handler)
+{
+ GList *handlers =
+ g_object_get_qdata (G_OBJECT (window), event_handlers_quark);
+
+ g_return_val_if_fail (handler, FALSE);
+ g_return_val_if_fail (!wayland, FALSE);
+
+ return g_list_find (handlers, handler) != NULL;
+}
+
+static void
+window_add_x11_event_handler (GtkWidget *window,
+ XEventHandler handler)
+{
+ GSource *source;
+ GList *handlers =
+ g_object_get_qdata (G_OBJECT (window), event_handlers_quark);
+
+ g_return_if_fail (!window_has_x11_event_handler (window, handler));
+
+ 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);
+
+ handlers = g_list_append (handlers, handler);
+ g_object_set_qdata (G_OBJECT (window), event_handlers_quark, handlers);
+}
+
+static void
+window_remove_x11_event_handler (GtkWidget *window,
+ XEventHandler handler)
+{
+ GList *handlers =
+ g_object_get_qdata (G_OBJECT (window), event_handlers_quark);
+
+ g_return_if_fail (window_has_x11_event_handler (window, handler));
+
+ g_object_set_qdata (G_OBJECT (window), event_source_quark, NULL);
+
+ handlers = g_list_remove (handlers, handler);
+ g_object_set_qdata (G_OBJECT (window), event_handlers_quark, handlers);
+}
+
static void
process_line (const char *line)
{
@@ -528,6 +667,8 @@ main(int argc, char **argv)
windows = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, NULL);
+ event_source_quark = g_quark_from_static_string ("event-source");
+ event_handlers_quark = g_quark_from_static_string ("event-handlers");
GInputStream *raw_in = g_unix_input_stream_new (0, FALSE);
GDataInputStream *in = g_data_input_stream_new (raw_in);