From 19536c88351347b5509997100e7a9bdd3ba027ef Mon Sep 17 00:00:00 2001 From: Giovanni Campagna Date: Fri, 6 Sep 2013 15:59:21 +0200 Subject: [PATCH] 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 file changed, 69 insertions(+), 9 deletions(-) diff --git a/clutter/evdev/clutter-device-manager-evdev.c b/clutter/evdev/clutter-device-manager-evdev.c index 7f20ae0c5..dd6b5f6c2 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 } /**