[PATCH] evdev: Handle SYN_DROPPED, keep transaction buffer

Martin Minarik minarik11 at student.fiit.stuba.sk
Fri Apr 5 15:27:10 PDT 2013


Each evdev device keeps a key press bitmap,
the incoming events are filtered to the following constraints:

1. only notify releases for previously pressed keys
2. only notify presses for previously released keys
3. on device destroy, notify a releases for all pressed keys

Notes:

1. For example when switching VTs, we recieve only key releases
   Nothing special, the lost key presses were of course not
   recorded (happens outside weston). So key releases are simply
   dropped with warning (double release).
2. This is solved by injecting a fake key release notify, we
   assume evdev doesn't send double key press without key
   release in between.
3. Solved by broadcasting fake release events.
---
 src/evdev-touchpad.c |   57 +++++++++++-
 src/evdev.c          |  240 ++++++++++++++++++++++++++++++++++++++++++--------
 src/evdev.h          |   31 +++++++
 3 files changed, 289 insertions(+), 39 deletions(-)

diff --git a/src/evdev-touchpad.c b/src/evdev-touchpad.c
index c25a199..8f1e0c3 100644
--- a/src/evdev-touchpad.c
+++ b/src/evdev-touchpad.c
@@ -523,6 +523,27 @@ on_release(struct touchpad_dispatch *touchpad)
 	push_fsm_event(touchpad, FSM_EVENT_RELEASE);
 }
 
+struct evdev_dispatch_interface touchpad_interface;
+struct evdev_dispatch_interface touchpad_syn_drop_interface;
+
+static inline void
+process_syn(struct touchpad_dispatch *touchpad,
+		 struct evdev_device *device,
+		 struct input_event *e, uint32_t time)
+{
+	switch (e->code) {
+	case SYN_DROPPED:
+		if (device->dispatch->interface == &touchpad_interface)
+			device->dispatch->interface = &touchpad_syn_drop_interface;
+		weston_log("warning: evdev: Syn drop at %u on %s \n", time, device->devname);
+		break;
+	case SYN_REPORT:
+	default:
+		touchpad->event_mask |= TOUCHPAD_EVENT_REPORT;
+		break;
+	}
+}
+
 static inline void
 process_absolute(struct touchpad_dispatch *touchpad,
 		 struct evdev_device *device,
@@ -578,7 +599,8 @@ process_key(struct touchpad_dispatch *touchpad,
 	case BTN_FORWARD:
 	case BTN_BACK:
 	case BTN_TASK:
-		notify_button(device->seat,
+		if (evdev_tx(device, e->code, e->value))
+			notify_button(device->seat,
 			      time, e->code,
 			      e->value ? WL_POINTER_BUTTON_STATE_PRESSED :
 			                 WL_POINTER_BUTTON_STATE_RELEASED);
@@ -624,8 +646,7 @@ touchpad_process(struct evdev_dispatch *dispatch,
 
 	switch (e->type) {
 	case EV_SYN:
-		if (e->code == SYN_REPORT)
-			touchpad->event_mask |= TOUCHPAD_EVENT_REPORT;
+		process_syn(touchpad, device, e, time);
 		break;
 	case EV_ABS:
 		process_absolute(touchpad, device, e);
@@ -654,6 +679,32 @@ struct evdev_dispatch_interface touchpad_interface = {
 	touchpad_destroy
 };
 
+static void
+touchpad_syn_drop_process(struct evdev_dispatch *dispatch,
+		 struct evdev_device *device,
+		 struct input_event *event,
+		 uint32_t time)
+{
+	if ((event->code != EV_SYN) || (event->code != SYN_REPORT))
+		return;
+
+	if (device->dispatch->interface == &touchpad_syn_drop_interface)
+		device->dispatch->interface = &touchpad_interface;
+
+	evdev_tx_sync(device, time);
+}
+
+static void
+touchpad_syn_drop_destroy(struct evdev_dispatch *dispatch)
+{
+	free(dispatch);
+}
+
+struct evdev_dispatch_interface touchpad_syn_drop_interface = {
+	touchpad_syn_drop_process,
+	touchpad_syn_drop_destroy
+};
+
 static int
 touchpad_init(struct touchpad_dispatch *touchpad,
 	      struct evdev_device *device)
diff --git a/src/evdev.c b/src/evdev.c
index d2954b5..5417149 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -60,6 +60,130 @@ evdev_led_update(struct evdev_device *device, enum weston_led leds)
 	(void)i; /* no, we really don't care about the return value */
 }
 
+struct evdev_dispatch_interface fallback_interface;
+struct evdev_dispatch_interface syn_drop_interface;
+
+static inline void
+evdev_process_syn(struct evdev_device *device, struct input_event *e, int time)
+{
+	switch (e->code) {
+	case SYN_DROPPED:
+		if (device->dispatch->interface == &fallback_interface)
+			device->dispatch->interface = &syn_drop_interface;
+		weston_log("warning: evdev: Syn drop at %u on %s \n", time, device->devname);
+		break;
+	case SYN_REPORT:
+	default:
+		device->pending_events |= EVDEV_SYN;
+		break;
+	}
+}
+
+static void
+evdev_tx_fake_key_up(struct evdev_device *d, unsigned int time, unsigned int key)
+{
+	struct evdev_dispatch *dispatch = d->dispatch;
+	struct input_event fake;
+	fake.type = EV_KEY;
+	fake.code = key;
+	fake.value = 0;
+
+	dispatch->interface->process(dispatch, d, &fake, time);
+}
+
+
+static int
+evdev_tx_press(struct evdev_device *device, uint32_t key)
+{
+	if (device->tx.delivered_keys == NULL) {
+		device->tx.delivered_keys = calloc(NBITS(KEY_CNT), sizeof(unsigned long));
+	} else if (TEST_BIT(device->tx.delivered_keys, key)) {
+		weston_log("warning: evdev: double-press key%u\n", key);
+		return 0;
+	}
+	if (device->tx.delivered_keys == NULL) {
+		weston_log("warning: evdev: register alloc\n");
+		return 0;
+	} else {
+		device->tx.delivered_keys[LONG(key)] |= BIT(key);
+		return 1;
+	}
+}
+
+static int
+evdev_tx_release(struct evdev_device *device, uint32_t key)
+{
+	if (device->tx.delivered_keys == NULL) {
+		weston_log("warning: evdev: no register, release key%u\n", key);
+		return 0;
+	} else if (TEST_BIT(device->tx.delivered_keys, key)) {
+		device->tx.delivered_keys[LONG(key)] &= ~(BIT(key));
+		return 1;
+	} else {
+		weston_log("warning: evdev: double-release key%u\n", key);
+		return 0;
+	}
+}
+
+int
+evdev_tx(struct evdev_device *d, uint32_t key, uint32_t state)
+{
+	switch (state) {
+		case 0:
+			return evdev_tx_release(d, key);
+		case 1:
+			return evdev_tx_press(d, key);
+		default:
+			return 1;
+	}
+}
+
+int
+evdev_tx_sync(struct evdev_device *d, unsigned int time)
+{
+	unsigned long kernel_keys[NBITS(KEY_CNT)];
+	int ret;
+	unsigned int i;
+
+	if (d->tx.delivered_keys == NULL)
+		return -1;
+
+	memset(kernel_keys, 0, sizeof(kernel_keys));
+	ret = ioctl(d->fd, EVIOCGKEY(KEY_CNT), kernel_keys);
+	
+	if (ret < 0)
+		return -1;
+
+	for (i = 0; i < ARRAY_LENGTH(kernel_keys); i++) {
+		const unsigned long deliv = d->tx.delivered_keys[i];
+		const unsigned long released_keys = ~kernel_keys[i] & deliv;
+
+		if (released_keys) {
+			unsigned int j;
+			for (j = 0; j < BITS_PER_LONG; j++) {
+				if (released_keys & BIT(j)) {
+					const unsigned int key = BITS_PER_LONG * i + j;
+					evdev_tx_fake_key_up(d, time, key);
+				}
+			}
+		}
+	}
+
+	return 0;
+}
+
+static void
+evdev_tx_fake_terminate_all(struct evdev_device *device)
+{
+	unsigned int key;
+
+	for (key = 0; key < KEY_CNT; key++) {
+		if (TEST_BIT(device->tx.delivered_keys, key)) {
+			evdev_tx_fake_key_up(device, 0x1337, key);
+		}
+	}
+}
+
 static inline void
 evdev_process_key(struct evdev_device *device, struct input_event *e, int time)
 {
@@ -75,18 +199,21 @@ evdev_process_key(struct evdev_device *device, struct input_event *e, int time)
 	case BTN_FORWARD:
 	case BTN_BACK:
 	case BTN_TASK:
-		notify_button(device->seat,
-			      time, e->code,
-			      e->value ? WL_POINTER_BUTTON_STATE_PRESSED :
-					 WL_POINTER_BUTTON_STATE_RELEASED);
-		break;
+		if (evdev_tx(device, e->code, e->value))
+			notify_button(device->seat,
+				      time, e->code,
+				      e->value ? WL_POINTER_BUTTON_STATE_PRESSED :
+						 WL_POINTER_BUTTON_STATE_RELEASED);
 
+		break;
 	default:
-		notify_key(device->seat,
-			   time, e->code,
-			   e->value ? WL_KEYBOARD_KEY_STATE_PRESSED :
-				      WL_KEYBOARD_KEY_STATE_RELEASED,
-			   STATE_UPDATE_AUTOMATIC);
+		if (evdev_tx(device, e->code, e->value))
+			notify_key(device->seat,
+				   time, e->code,
+				   e->value ? WL_KEYBOARD_KEY_STATE_PRESSED :
+					      WL_KEYBOARD_KEY_STATE_RELEASED,
+				   STATE_UPDATE_AUTOMATIC);
+
 		break;
 	}
 }
@@ -308,7 +435,7 @@ fallback_process(struct evdev_dispatch *dispatch,
 		evdev_process_key(device, event, time);
 		break;
 	case EV_SYN:
-		device->pending_events |= EVDEV_SYN;
+		evdev_process_syn(device, event, time);
 		break;
 	}
 }
@@ -324,6 +451,32 @@ struct evdev_dispatch_interface fallback_interface = {
 	fallback_destroy
 };
 
+static void
+syn_drop_process(struct evdev_dispatch *dispatch,
+		 struct evdev_device *device,
+		 struct input_event *event,
+		 uint32_t time)
+{
+	if ((event->code != EV_SYN) || (event->code != SYN_REPORT))
+		return;
+
+	if (device->dispatch->interface == &syn_drop_interface)
+		device->dispatch->interface = &fallback_interface;
+
+	evdev_tx_sync(device, time);
+}
+
+static void
+syn_drop_destroy(struct evdev_dispatch *dispatch)
+{
+	free(dispatch);
+}
+
+struct evdev_dispatch_interface syn_drop_interface = {
+	syn_drop_process,
+	syn_drop_destroy
+};
+
 static struct evdev_dispatch *
 fallback_dispatch_create(void)
 {
@@ -543,6 +696,7 @@ evdev_device_create(struct weston_seat *seat, const char *path, int device_fd)
 	device->rel.dy = 0;
 	device->dispatch = NULL;
 	device->fd = device_fd;
+	device->tx.delivered_keys = NULL;
 
 	ioctl(device->fd, EVIOCGNAME(sizeof(devname)), devname);
 	device->devname = strdup(devname);
@@ -592,6 +746,11 @@ evdev_device_destroy(struct evdev_device *device)
 {
 	struct evdev_dispatch *dispatch;
 
+	if (device->tx.delivered_keys) {
+		evdev_tx_fake_terminate_all(device);
+		free(device->tx.delivered_keys);
+	}
+
 	dispatch = device->dispatch;
 	if (dispatch)
 		dispatch->interface->destroy(dispatch);
@@ -600,51 +759,60 @@ evdev_device_destroy(struct evdev_device *device)
 	wl_list_remove(&device->link);
 	if (device->mtdev)
 		mtdev_close_delete(device->mtdev);
+
 	close(device->fd);
 	free(device->devname);
 	free(device->devnode);
 	free(device);
 }
 
+
 void
-evdev_notify_keyboard_focus(struct weston_seat *seat,
-			    struct wl_list *evdev_devices)
+evdev_keys_states_query(struct wl_list *evdev_devices,
+			unsigned long all_keys[NBITS(KEY_CNT)])
 {
 	struct evdev_device *device;
-	struct wl_array keys;
-	unsigned int i, set;
-	char evdev_keys[(KEY_CNT + 7) / 8];
-	char all_keys[(KEY_CNT + 7) / 8];
-	uint32_t *k;
-	int ret;
-
-	if (!seat->seat.keyboard)
-		return;
 
-	memset(all_keys, 0, sizeof all_keys);
 	wl_list_for_each(device, evdev_devices, link) {
-		memset(evdev_keys, 0, sizeof evdev_keys);
-		ret = ioctl(device->fd,
-			    EVIOCGKEY(sizeof evdev_keys), evdev_keys);
-		if (ret < 0) {
-			weston_log("failed to get keys for device %s\n",
-				device->devnode);
+		unsigned long evdev_keys[NBITS(KEY_CNT)];
+		unsigned int i;
+
+		if (evdev_tx_sync(device, 0x1337))
 			continue;
-		}
+
+		memset(evdev_keys, 0, sizeof(evdev_keys));
+
 		for (i = 0; i < ARRAY_LENGTH(evdev_keys); i++)
-			all_keys[i] |= evdev_keys[i];
+			all_keys[i] |= device->tx.delivered_keys[i];
 	}
+}
+
+void
+evdev_keys_depressed_array(struct wl_list *evdev_devices, struct wl_array *keys)
+{
+	unsigned long all_keys[NBITS(KEY_CNT)];
+	uint32_t i;
+	uint32_t *k;
+
+	memset(all_keys, 0, sizeof(all_keys));
+	evdev_keys_states_query(evdev_devices, all_keys);
 
-	wl_array_init(&keys);
 	for (i = 0; i < KEY_CNT; i++) {
-		set = all_keys[i >> 3] & (1 << (i & 7));
-		if (set) {
-			k = wl_array_add(&keys, sizeof *k);
+		if (TEST_BIT(all_keys,i)) {
+			k = wl_array_add(keys, sizeof *k);
 			*k = i;
 		}
 	}
+}
 
-	notify_keyboard_focus_in(seat, &keys, STATE_UPDATE_AUTOMATIC);
+void
+evdev_notify_keyboard_focus(struct weston_seat *seat,
+			    struct wl_list *evdev_devices)
+{
+	struct wl_array keys;
 
+	wl_array_init(&keys);
+	evdev_keys_depressed_array(evdev_devices, &keys);
+	notify_keyboard_focus_in(seat, &keys, STATE_UPDATE_AUTOMATIC);
 	wl_array_release(&keys);
 }
diff --git a/src/evdev.h b/src/evdev.h
index eb5c868..a0b6639 100644
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -45,6 +45,15 @@ enum evdev_device_capability {
 	EVDEV_TOUCH = (1 << 4),
 };
 
+struct evdev_stale_keypress {
+	uint16_t code;
+	uint16_t count;
+};
+
+struct evdev_transactional {
+	unsigned long *delivered_keys;
+};
+
 struct evdev_device {
 	struct weston_seat *seat;
 	struct wl_list link;
@@ -77,6 +86,8 @@ struct evdev_device {
 	enum evdev_device_capability caps;
 
 	int is_mt;
+
+	struct evdev_transactional tx;
 };
 
 /* copied from udev/extras/input_id/input_id.c */
@@ -121,7 +132,27 @@ void
 evdev_device_destroy(struct evdev_device *device);
 
 void
+evdev_keys_states_count(struct wl_list *evdev_devices, uint8_t *keys_counts,
+			uint32_t key_min, uint32_t key_max);
+void
+evdev_keys_states_query(struct wl_list *evdev_devices,
+			unsigned long all_keys[NBITS(KEY_CNT)]);
+void
+evdev_keys_depressed_array(struct wl_list *evdev_devices,struct wl_array *keys);
+
+void
 evdev_notify_keyboard_focus(struct weston_seat *seat,
 			    struct wl_list *evdev_devices);
 
+void
+evdev_notify_key_status(struct weston_seat *seat,
+			struct wl_list *evdev_devices,
+			uint32_t key_min, uint32_t key_max);
+
+int
+evdev_tx(struct evdev_device *d, uint32_t key, uint32_t state);
+
+int
+evdev_tx_sync(struct evdev_device *d, unsigned int time);
+
 #endif /* EVDEV_H */
-- 
1.7.10.4



More information about the wayland-devel mailing list