[mutter] Nested X11: use KeymapNotify events to fix key state on FocusIn



commit 614d6bd0f80ce336df7dca64e04746b057753f3b
Author: Owen W. Taylor <otaylor fishsoup net>
Date:   Fri Aug 21 16:25:53 2015 -0400

    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(+), 0 deletions(-)
---
diff --git a/src/backends/x11/meta-backend-x11.c b/src/backends/x11/meta-backend-x11.c
index a76dbc5..641fb8e 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 <meta/util.h>
@@ -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 e4d4f22..8d136c9 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 b4a8c52..ea9db32 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 13709f1..65e05f6 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 ec12e06..7db5ca1 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,


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