EVDEV_KEY_TYPE_BUTTON,
};
+static void
+set_key_down(struct evdev_device *device, int code, int pressed)
+{
+ long_set_bit_state(device->key_mask, code, pressed);
+}
+
+static int
+is_key_down(struct evdev_device *device, int code)
+{
+ return long_bit_is_set(device->key_mask, code);
+}
+
void
evdev_device_led_update(struct evdev_device *device, enum libinput_led leds)
{
evdev_process_key(struct evdev_device *device,
struct input_event *e, uint64_t time)
{
+ enum evdev_key_type type;
+
/* ignore kernel key repeat */
if (e->value == 2)
return;
evdev_flush_pending_event(device, time);
- switch (get_key_type(e->code)) {
+ type = get_key_type(e->code);
+
+ /* Ignore key release events from the kernel for keys that libinput
+ * never got a pressed event for. */
+ if (e->value == 0) {
+ switch (type) {
+ case EVDEV_KEY_TYPE_NONE:
+ break;
+ case EVDEV_KEY_TYPE_KEY:
+ case EVDEV_KEY_TYPE_BUTTON:
+ if (!is_key_down(device, e->code))
+ return;
+ }
+ }
+
+ set_key_down(device, e->code, e->value);
+
+ switch (type) {
case EVDEV_KEY_TYPE_NONE:
break;
case EVDEV_KEY_TYPE_KEY:
int
evdev_device_get_keys(struct evdev_device *device, char *keys, size_t size)
{
- int len;
-
memset(keys, 0, size);
- len = ioctl(device->fd, EVIOCGKEY(size), keys);
-
- return (len == -1) ? -errno : len;
+ return 0;
}
const char *
struct {
struct motion_filter *filter;
} pointer;
+
+ /* Bitmask of pressed keys used to ignore initial release events from
+ * the kernel. */
+ unsigned long key_mask[NLONGS(KEY_CNT)];
};
#define EVDEV_UNHANDLED_DEVICE ((struct evdev_device *) 1)
pos = tmp, \
tmp = container_of(pos->member.next, tmp, member))
+#define LONG_BITS (sizeof(long) * 8)
+#define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS)
#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
#define ARRAY_FOR_EACH(_arr, _elem) \
for (size_t _i = 0; (_elem = &_arr[_i]) && _i < ARRAY_LENGTH(_arr); _i++)
return dir;
}
+static inline int
+long_bit_is_set(const unsigned long *array, int bit)
+{
+ return !!(array[bit / LONG_BITS] & (1LL << (bit % LONG_BITS)));
+}
+
+static inline void
+long_set_bit(unsigned long *array, int bit)
+{
+ array[bit / LONG_BITS] |= (1LL << (bit % LONG_BITS));
+}
+
+static inline void
+long_clear_bit(unsigned long *array, int bit)
+{
+ array[bit / LONG_BITS] &= ~(1LL << (bit % LONG_BITS));
+}
+
+static inline void
+long_set_bit_state(unsigned long *array, int bit, int state)
+{
+ if (state)
+ long_set_bit(array, bit);
+ else
+ long_clear_bit(array, bit);
+}
+
#endif /* LIBINPUT_UTIL_H */
*/
int
libinput_device_get_keys(struct libinput_device *device,
- char *keys, size_t size);
+ char *keys, size_t size)
+ LIBINPUT_ATTRIBUTE_DEPRECATED;
/**
* @ingroup device
}
END_TEST
+START_TEST(keyboard_ignore_no_pressed_release)
+{
+ struct litest_device *dev;
+ struct libinput *unused_libinput;
+ struct libinput *libinput;
+ struct libinput_event *event;
+ struct libinput_event_keyboard *kevent;
+ int events[] = {
+ EV_KEY, KEY_A,
+ -1, -1,
+ };
+ enum libinput_key_state *state;
+ enum libinput_key_state expected_states[] = {
+ LIBINPUT_KEY_STATE_PRESSED,
+ LIBINPUT_KEY_STATE_RELEASED,
+ };
+
+ /* We can't send pressed -> released -> pressed events using uinput
+ * as such non-symmetric events are dropped. Work-around this by first
+ * adding the test device to the tested context after having sent an
+ * initial pressed event. */
+ unused_libinput = litest_create_context();
+ dev = litest_add_device_with_overrides(unused_libinput,
+ LITEST_KEYBOARD,
+ "Generic keyboard",
+ NULL, NULL, events);
+
+ litest_keyboard_key(dev, KEY_A, true);
+ litest_drain_events(unused_libinput);
+
+ libinput = litest_create_context();
+ libinput_path_add_device(libinput,
+ libevdev_uinput_get_devnode(dev->uinput));
+ litest_drain_events(libinput);
+
+ litest_keyboard_key(dev, KEY_A, false);
+ litest_keyboard_key(dev, KEY_A, true);
+ litest_keyboard_key(dev, KEY_A, false);
+
+ libinput_dispatch(libinput);
+
+ ARRAY_FOR_EACH(expected_states, state) {
+ event = libinput_get_event(libinput);
+ ck_assert_notnull(event);
+ ck_assert_int_eq(libinput_event_get_type(event),
+ LIBINPUT_EVENT_KEYBOARD_KEY);
+ kevent = libinput_event_get_keyboard_event(event);
+ ck_assert_int_eq(libinput_event_keyboard_get_key(kevent),
+ KEY_A);
+ ck_assert_int_eq(libinput_event_keyboard_get_key_state(kevent),
+ *state);
+ libinput_event_destroy(event);
+ libinput_dispatch(libinput);
+ }
+
+ litest_assert_empty_queue(libinput);
+ litest_delete_device(dev);
+ libinput_unref(libinput);
+ libinput_unref(unused_libinput);
+}
+END_TEST
+
int
main(int argc, char **argv)
{
litest_add_no_device("keyboard:seat key count", keyboard_seat_key_count);
+ litest_add_no_device("keyboard:key counting", keyboard_ignore_no_pressed_release);
return litest_run(argc, argv);
}