[clutter/clutter-1.16] evdev: sync the keyboard state when releasing and reclaiming devices



commit 19536c88351347b5509997100e7a9bdd3ba027ef
Author: Giovanni Campagna <gcampagn redhat com>
Date:   Fri Sep 6 15:59:21 2013 +0200

    evdev: sync the keyboard state when releasing and reclaiming devices
    
    When we release a device, we lose all the events after that point,
    so our state can become stale. Similarly, we need to sync the
    state with the effectively pressed keys when we reclaim.
    This ensures that modifier keys don't get stuck when switching
    VTs using a keybinding.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=706494

 clutter/evdev/clutter-device-manager-evdev.c |   78 +++++++++++++++++++++++---
 1 files changed, 69 insertions(+), 9 deletions(-)
---
diff --git a/clutter/evdev/clutter-device-manager-evdev.c b/clutter/evdev/clutter-device-manager-evdev.c
index 7f20ae0..dd6b5f6 100644
--- a/clutter/evdev/clutter-device-manager-evdev.c
+++ b/clutter/evdev/clutter-device-manager-evdev.c
@@ -188,12 +188,12 @@ remove_key (GArray  *keys,
 }
 
 static void
-notify_key (ClutterEventSource *source,
-            guint32             time_,
-            guint32             key,
-            guint32             state)
+notify_key_device (ClutterInputDevice *input_device,
+                  guint32             time_,
+                  guint32             key,
+                  guint32             state,
+                  gboolean            update_keys)
 {
-  ClutterInputDevice *input_device = (ClutterInputDevice *) source->device;
   ClutterDeviceManagerEvdev *manager_evdev;
   ClutterStage *stage;
   ClutterEvent *event = NULL;
@@ -223,16 +223,29 @@ notify_key (ClutterEventSource *source,
        {
          xkb_state_update_key (manager_evdev->priv->xkb, event->key.hardware_keycode, state ? XKB_KEY_DOWN : 
XKB_KEY_UP);
 
-         if (state)
-           add_key (manager_evdev->priv->keys, event->key.hardware_keycode);
-         else
-           remove_key (manager_evdev->priv->keys, event->key.hardware_keycode);
+         if (update_keys)
+           {
+             if (state)
+               add_key (manager_evdev->priv->keys, event->key.hardware_keycode);
+             else
+               remove_key (manager_evdev->priv->keys, event->key.hardware_keycode);
+           }
        }
 
       queue_event (event);
     }
 }
 
+static void
+notify_key (ClutterEventSource *source,
+            guint32             time_,
+            guint32             key,
+            guint32             state)
+{
+  ClutterInputDevice *input_device = (ClutterInputDevice *) source->device;
+
+  notify_key_device (input_device, time_, key, state, TRUE);
+}
 
 static void
 notify_relative_motion (ClutterEventSource *source,
@@ -1197,6 +1210,8 @@ clutter_evdev_release_devices (void)
   ClutterDeviceManagerEvdev *evdev_manager;
   ClutterDeviceManagerEvdevPrivate *priv;
   GSList *l, *next;
+  uint32_t time_;
+  unsigned i;
 
   if (!manager)
     {
@@ -1218,6 +1233,13 @@ clutter_evdev_release_devices (void)
       return;
     }
 
+  /* Fake release events for all currently pressed keys */
+  time_ = g_get_monotonic_time () / 1000;
+  for (i = 0; i < priv->keys->len; i++)
+    notify_key_device (priv->core_keyboard, time_,
+                      g_array_index (priv->keys, uint32_t, i) - 8, 0, FALSE);
+  g_array_set_size (priv->keys, 0);
+
   for (l = priv->devices; l; l = next)
     {
       ClutterInputDevice *device = l->data;
@@ -1251,6 +1273,13 @@ clutter_evdev_reclaim_devices (void)
   ClutterDeviceManager *manager = clutter_device_manager_get_default ();
   ClutterDeviceManagerEvdev *evdev_manager;
   ClutterDeviceManagerEvdevPrivate *priv;
+#define LONG_BITS (sizeof(long) * 8)
+#define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS)
+  unsigned long key_bits[NLONGS(KEY_CNT)];
+  unsigned long source_key_bits[NLONGS(KEY_CNT)];
+  GSList *iter;
+  int i, j, rc;
+  guint32 time_;
 
   if (!manager)
     {
@@ -1273,6 +1302,37 @@ clutter_evdev_reclaim_devices (void)
 
   priv->released = FALSE;
   clutter_device_manager_evdev_probe_devices (evdev_manager);
+
+  memset (key_bits, 0, sizeof (key_bits));
+  for (iter = priv->event_sources; iter; iter++)
+    {
+      ClutterEventSource *source = iter->data;
+      ClutterInputDevice *slave = CLUTTER_INPUT_DEVICE (source->device);
+
+      if (clutter_input_device_get_device_type (slave) == CLUTTER_KEYBOARD_DEVICE)
+       {
+         rc = ioctl (source->event_poll_fd.fd, EVIOCGBIT(EV_KEY, sizeof (source_key_bits)), source_key_bits);
+         if (rc < 0)
+           continue;
+
+         for (i = 0; i < NLONGS(KEY_CNT); i++)
+           key_bits[i] |= source_key_bits[i];
+       }
+    }
+
+  /* Fake press events for all currently pressed keys */
+  time_ = g_get_monotonic_time () / 1000;
+  for (i = 0; i < NLONGS(KEY_CNT); i++)
+    {
+      for (j = 0; j < 8; j++)
+       {
+         if (key_bits[i] & (1 << j))
+           notify_key_device (priv->core_keyboard, time_, i * 8 + j, 1, TRUE);
+       }
+    }
+
+#undef LONG_BITS
+#undef NLONGS
 }
 
 /**


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