From 614d6bd0f80ce336df7dca64e04746b057753f3b Mon Sep 17 00:00:00 2001 From: "Owen W. Taylor" Date: Fri, 21 Aug 2015 16:25:53 -0400 Subject: [PATCH] Nested X11: use KeymapNotify events to fix key state on FocusIn If the user Alt-Tabs out of the window, we will be left thinking the Alt key is still pressed since we don't see a release for it. Solve this and other related issues for the nested X11 compositor by selecting for KeymapStateMask which causes a KeymapNotify event to be sent after each FocusIn, and when we get these events, update the internal XKB state and send any necessary modifiers events to clients. https://bugzilla.gnome.org/show_bug.cgi?id=753948 --- src/backends/x11/meta-backend-x11.c | 30 +++++++++++++++++++++++++++++ src/wayland/meta-wayland-keyboard.c | 27 ++++++++++++++++++++++++++ src/wayland/meta-wayland-keyboard.h | 4 ++++ src/wayland/meta-wayland.c | 22 +++++++++++++++++++++ src/wayland/meta-wayland.h | 4 ++++ 5 files changed, 87 insertions(+) diff --git a/src/backends/x11/meta-backend-x11.c b/src/backends/x11/meta-backend-x11.c index a76dbc5c0..641fb8e79 100644 --- a/src/backends/x11/meta-backend-x11.c +++ b/src/backends/x11/meta-backend-x11.c @@ -40,6 +40,7 @@ #include "meta-idle-monitor-xsync.h" #include "meta-monitor-manager-xrandr.h" #include "backends/meta-monitor-manager-dummy.h" +#include "wayland/meta-wayland.h" #include "meta-cursor-renderer-x11.h" #include @@ -269,6 +270,21 @@ handle_host_xevent (MetaBackend *backend, } } + if (priv->mode == META_BACKEND_X11_MODE_NESTED && event->type == FocusIn) + { + Window xwin = meta_backend_x11_get_xwindow(x11); + XEvent xev; + + if (event->xfocus.window == xwin) + { + /* Since we've selected for KeymapStateMask, every FocusIn is followed immediately + * by a KeymapNotify event */ + XMaskEvent(priv->xdisplay, KeymapStateMask, &xev); + MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); + meta_wayland_compositor_update_key_state (compositor, xev.xkeymap.key_vector, 32, 8); + } + } + if (event->type == (priv->xsync_event_base + XSyncAlarmNotify)) handle_alarm_notify (backend, event); @@ -800,6 +816,20 @@ meta_backend_x11_select_stage_events (MetaBackend *backend) } XISelectEvents (priv->xdisplay, xwin, &mask, 1); + + if (priv->mode == META_BACKEND_X11_MODE_NESTED) + { + /* We have no way of tracking key changes when the stage doesn't have + * focus, so we select for KeymapStateMask so that we get a complete + * dump of the keyboard state in a KeymapNotify event that immediately + * follows each FocusIn (and EnterNotify, but we ignore that.) + */ + XWindowAttributes xwa; + + XGetWindowAttributes(priv->xdisplay, xwin, &xwa); + XSelectInput(priv->xdisplay, xwin, + xwa.your_event_mask | FocusChangeMask | KeymapStateMask); + } } static void diff --git a/src/wayland/meta-wayland-keyboard.c b/src/wayland/meta-wayland-keyboard.c index e4d4f2212..8d136c9b9 100644 --- a/src/wayland/meta-wayland-keyboard.c +++ b/src/wayland/meta-wayland-keyboard.c @@ -477,6 +477,33 @@ meta_wayland_keyboard_handle_event (MetaWaylandKeyboard *keyboard, return handled; } +void +meta_wayland_keyboard_update_key_state (MetaWaylandKeyboard *keyboard, + char *key_vector, + int key_vector_len, + int offset) +{ + gboolean mods_changed = FALSE; + + for (gint i = offset; i < key_vector_len * 8; i++) + { + gboolean set = (key_vector[i/8] & (1 << (i % 8))) != 0; + + /* The 'offset' parameter allows the caller to have the indices + * into key_vector to either be X-style (base 8) or evdev (base 0), or + * something else (unlikely). We subtract 'offset' to convert to evdev + * style, then add 8 to convert the "evdev" style keycode back to + * the X-style that xkbcommon expects. + */ + mods_changed |= xkb_state_update_key (keyboard->xkb_info.state, + i - offset + 8, + set ? XKB_KEY_DOWN : XKB_KEY_UP); + } + + if (mods_changed) + notify_modifiers (keyboard); +} + static void move_resources (struct wl_list *destination, struct wl_list *source) { diff --git a/src/wayland/meta-wayland-keyboard.h b/src/wayland/meta-wayland-keyboard.h index b4a8c5280..ea9db32d1 100644 --- a/src/wayland/meta-wayland-keyboard.h +++ b/src/wayland/meta-wayland-keyboard.h @@ -85,6 +85,10 @@ void meta_wayland_keyboard_update (MetaWaylandKeyboard *keyboard, gboolean meta_wayland_keyboard_handle_event (MetaWaylandKeyboard *keyboard, const ClutterKeyEvent *event); +void meta_wayland_keyboard_update_key_state (MetaWaylandKeyboard *compositor, + char *key_vector, + int key_vector_len, + int offset); void meta_wayland_keyboard_set_focus (MetaWaylandKeyboard *keyboard, MetaWaylandSurface *surface); diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c index 13709f15c..65e05f629 100644 --- a/src/wayland/meta-wayland.c +++ b/src/wayland/meta-wayland.c @@ -207,6 +207,28 @@ meta_wayland_compositor_handle_event (MetaWaylandCompositor *compositor, return meta_wayland_seat_handle_event (compositor->seat, event); } +/* meta_wayland_compositor_update_key_state: + * @compositor: the #MetaWaylandCompositor + * @key_vector: bit vector of key states + * @key_vector_len: length of @key_vector + * @offset: the key for the first evdev keycode is found at this offset in @key_vector + * + * This function is used to resynchronize the key state that Mutter + * is tracking with the actual keyboard state. This is useful, for example, + * to handle changes in key state when a nested compositor doesn't + * have focus. We need to fix up the XKB modifier tracking and deliver + * any modifier changes to clients. + */ +void +meta_wayland_compositor_update_key_state (MetaWaylandCompositor *compositor, + char *key_vector, + int key_vector_len, + int offset) +{ + meta_wayland_keyboard_update_key_state (&compositor->seat->keyboard, + key_vector, key_vector_len, offset); +} + void meta_wayland_compositor_destroy_frame_callbacks (MetaWaylandCompositor *compositor, MetaWaylandSurface *surface) diff --git a/src/wayland/meta-wayland.h b/src/wayland/meta-wayland.h index ec12e06c5..7db5ca159 100644 --- a/src/wayland/meta-wayland.h +++ b/src/wayland/meta-wayland.h @@ -39,6 +39,10 @@ void meta_wayland_compositor_update (MetaWaylandComp const ClutterEvent *event); gboolean meta_wayland_compositor_handle_event (MetaWaylandCompositor *compositor, const ClutterEvent *event); +void meta_wayland_compositor_update_key_state (MetaWaylandCompositor *compositor, + char *key_vector, + int key_vector_len, + int offset); void meta_wayland_compositor_repick (MetaWaylandCompositor *compositor); void meta_wayland_compositor_set_input_focus (MetaWaylandCompositor *compositor,