[PATCH v2 7/9] evdev: queue events and process them only at EV_SYN time

Tiago Vignatti tiago.vignatti at intel.com
Wed Oct 26 05:55:27 PDT 2011


Input events have to be processed in the same order they come from the device
in order to work properly. This patch enqueue all the events until a SYN event
happens; meaning the delimiter of a logical event. A queue is used to batch
up the events and deliver them in the same order.

In some cases EV_KEY events come together in one single batch, so that is the
motivation of the queue's existence. For instance, clicking with two buttons at
the same time my mouse spits:

Event: time 1319619213.579492, -------------- SYN_REPORT ------------
Event: time 1319619213.747367, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90001
Event: time 1319619213.747382, type 1 (EV_KEY), code 272 (BTN_LEFT), value 0
Event: time 1319619213.747398, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90002
Event: time 1319619213.747403, type 1 (EV_KEY), code 273 (BTN_RIGHT), value 0
Event: time 1319619213.747572, -------------- SYN_REPORT ------------

sometimes it happens when three buttons are clicked as well. The keyboard might
have a similar output:

Event: time 1319619405.924676, -------------- SYN_REPORT ------------
Event: time 1319619406.148651, type 4 (EV_MSC), code 4 (MSC_SCAN), value 70007
Event: time 1319619406.148672, type 1 (EV_KEY), code 32 (KEY_D), value 1
Event: time 1319619406.148687, type 4 (EV_MSC), code 4 (MSC_SCAN), value 70009
Event: time 1319619406.148693, type 1 (EV_KEY), code 33 (KEY_F), value 1
Event: time 1319619406.148698, -------------- SYN_REPORT ------------

The patch fixes (at least) the bug of buttons events being queuing up before
motion ones, which caused the behaviour of taps/clicks not being caught in the
needed time. It was all tested with different input devices.

Tested-by: Tiago Vignatti <tiago.vignatti at intel.com>
Signed-off-by: Tiago Vignatti <tiago.vignatti at intel.com>
---
changes since v1:
- event queue now with size 4; and we're "safe" here cause it will output
  something if fills it up.

 compositor/evdev.c |  188 +++++++++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 165 insertions(+), 23 deletions(-)

diff --git a/compositor/evdev.c b/compositor/evdev.c
index 6b9c280..15052eb 100644
--- a/compositor/evdev.c
+++ b/compositor/evdev.c
@@ -30,10 +30,23 @@
 
 #include "compositor.h"
 
+#define EVDEV_MAXQUEUE 4
+
 struct evdev_input {
 	struct wlsc_input_device base;
 };
 
+/* Event queue used to defer keyboard/button events until EV_SYN time. */
+struct evdev_event_queue {
+	enum {
+		EV_QUEUE_KEY,
+		EV_QUEUE_BTN,
+		EV_QUEUE_PROXIMITY,
+	} type;
+	int key;	/* May be either a key code or button number. */
+	int val;	/* State of the key/button; pressed or released. */
+};
+
 struct evdev_input_device {
 	struct evdev_input *master;
 	struct wl_event_source *source;
@@ -50,17 +63,122 @@ struct evdev_input_device {
 	int is_touchpad, old_x_value, old_y_value, reset_x_value, reset_y_value;
 	uint32_t time;
 
+	/* Event queue used to defer keyboard/button events until EV_SYN time */
+	int num_queue;
+	struct evdev_event_queue queue[EVDEV_MAXQUEUE];
+
 	uint32_t abs_queued, rel_queued;
 };
 
-static inline void
+static struct evdev_event_queue *
+evdev_next_in_queue(struct evdev_input_device *device)
+{
+	if (device->num_queue >= EVDEV_MAXQUEUE)
+	{
+		fprintf(stderr, "dropping event due to full queue!\n");
+		return NULL;
+	}
+
+	device->num_queue++;
+	return &device->queue[device->num_queue - 1];
+}
+
+static void
+evdev_queue_kbd(struct evdev_input_device *device, struct input_event *ev,
+		int value)
+{
+	struct evdev_event_queue *queue;
+
+	/* Filter all repeated events from device */
+	if (value == 2)
+		return;
+
+	if ((queue = evdev_next_in_queue(device)))
+	{
+		queue->type = EV_QUEUE_KEY;
+		queue->key = ev->code;
+		queue->val = value;
+	}
+}
+
+static void
+evdev_queue_button(struct evdev_input_device *device, int button, int value)
+{
+	struct evdev_event_queue *queue;
+
+	if ((queue = evdev_next_in_queue(device)))
+	{
+		queue->type = EV_QUEUE_BTN;
+		queue->key = button;
+		queue->val = value;
+	}
+}
+
+/* Return an index value for a given button event code and returns 0 on
+ * non-button event.
+ */
+static unsigned int
+evdev_button_event_to_number(int code)
+{
+	switch (code) {
+	/* Mouse buttons */
+	case BTN_LEFT:
+		return 1;
+	case BTN_MIDDLE:
+		return 2;
+	case BTN_RIGHT:
+		return 3;
+	case BTN_SIDE ... BTN_JOYSTICK - 1:
+		return 8 + code - BTN_SIDE;
+
+	/* Generic buttons */
+	case BTN_0 ... BTN_2:
+		return 1 + code - BTN_0;
+	case BTN_3 ... BTN_MOUSE - 1:
+		return 8 + code - BTN_3;
+
+	/* Tablet stylus buttons */
+	case BTN_TOUCH ... BTN_STYLUS2:
+		return 1 + code - BTN_TOUCH;
+
+	/* The rest */
+	default:
+		/* Ignore */
+		return 0;
+	}
+}
+
+/**
+ * Take a button input event and process it accordingly.
+ */
+static void
+evdev_process_button_event(struct evdev_input_device *device,
+			   struct input_event *ev)
+{
+	unsigned int button;
+	int value;
+
+	button = evdev_button_event_to_number(ev->code);
+
+	/* Get the signed value, earlier kernels had this as unsigned */
+	value = ev->value;
+
+	if (button)
+		evdev_queue_button(device, ev->code, value);
+	else
+		evdev_queue_kbd(device, ev, value);
+}
+
+static void
 evdev_process_key(struct evdev_input_device *device, struct input_event *e)
 {
 	/* Get the signed value, earlier kernels had this as unsigned */
 	int value = e->value;
 
-	if (value == 2)
-		return;
+	/* don't repeat mouse buttons */
+	if (e->code >= BTN_MOUSE && e->code < KEY_OK)
+		if (value == 2)
+			return;
 
 	switch (e->code) {
 	case BTN_TOOL_PEN:
@@ -83,21 +201,8 @@ evdev_process_key(struct evdev_input_device *device, struct input_event *e)
 		 * BTN_LEFT */
 		e->code = BTN_LEFT;
 		/* Intentional fallthrough! */
-	case BTN_LEFT:
-	case BTN_RIGHT:
-	case BTN_MIDDLE:
-	case BTN_SIDE:
-	case BTN_EXTRA:
-	case BTN_FORWARD:
-	case BTN_BACK:
-	case BTN_TASK:
-		notify_button(&device->master->base.input_device,
-			      device->time, e->code, value);
-		break;
-
 	default:
-		notify_key(&device->master->base.input_device,
-			   device->time, e->code, value);
+		evdev_process_button_event(device, e);
 		break;
 	}
 }
@@ -181,8 +286,35 @@ evdev_process_relative_motion(struct evdev_input_device *device,
 	device->rel_queued = 1;
 }
 
-static inline void
-evdev_notify(struct evdev_input_device *device, struct input_event *e)
+static void
+evdev_process_queued(struct evdev_input_device *device)
+{
+	int i;
+
+	for (i = 0; i < device->num_queue; i++) {
+		switch (device->queue[i].type) {
+		case EV_QUEUE_KEY:
+			notify_key(&device->master->base.input_device,
+				   device->time, device->queue[i].key,
+				   device->queue[i].val);
+			break;
+		case EV_QUEUE_BTN:
+			notify_button(&device->master->base.input_device,
+				      device->time, device->queue[i].key,
+				      device->queue[i].val);
+			break;
+		case EV_QUEUE_PROXIMITY:
+			break;
+		}
+	}
+}
+
+/**
+ * Take the synchronization input event and process it accordingly; the motion
+ * notify events are sent first, then any button/key press/release events.
+ */
+static void
+evdev_process_sync(struct evdev_input_device *device, struct input_event *e)
 {
 	if (device->rel_queued)
                notify_motion(&device->master->base.input_device,
@@ -193,11 +325,19 @@ evdev_notify(struct evdev_input_device *device, struct input_event *e)
 		notify_motion(&device->master->base.input_device,
 			      device->time, device->x, device->y);
 
+	evdev_process_queued(device);
 
+	device->num_queue = 0;
 	device->rel_queued = 0;
 	device->abs_queued = 0;
+	device->dx = 0;
+	device->dy = 0;
 }
 
+/**
+ * Process the events from the device; nothing is actually posted to the server
+ * until an EV_SYN event is received.
+ */
 static void
 evdev_process_event(struct evdev_input_device *device, struct input_event *e)
 {
@@ -216,9 +356,10 @@ evdev_process_event(struct evdev_input_device *device, struct input_event *e)
 	case EV_KEY:
 		evdev_process_key(device, e);
 		break;
+	case EV_SYN:
+		evdev_process_sync(device, e);
+		break;
 	}
-
-	evdev_notify(device, e);
 }
 
 static int
@@ -234,8 +375,6 @@ evdev_input_device_data(int fd, uint32_t mask, void *data)
 	if (!ec->focus)
 		return 1;
 
-	device->dx = 0;
-	device->dy = 0;
 	device->x = device->master->base.input_device.x;
 	device->y = device->master->base.input_device.y;
 
@@ -330,6 +469,9 @@ evdev_input_device_create(struct evdev_input *master,
 	device = malloc(sizeof *device);
 	if (device == NULL)
 		return NULL;
+	device->dx = 0;
+	device->dy = 0;
+	device->num_queue = 0;
 
 	ec = (struct wlsc_compositor *) master->base.input_device.compositor;
 	device->output = 
-- 
1.7.5.4



More information about the wayland-devel mailing list