[PATCH libinput 4/5] fallback: change to handle the state at EV_SYN time

Peter Hutterer peter.hutterer at who-t.net
Tue Nov 14 04:13:00 UTC 2017


The previous approach was to remember the last event and flush it at the right
time. The new approach is to update the device state during the frame and send
out the events at EV_SYN time.

This gives us two advantages: we are not dependent on the kernel order of how
events come in and we can process events depending on other events in the same
frame. This will come in handy later for button debouncing.

This is also the approach we have in the touchpad and tablet backends.

Two FIXMEs are left in place, the button debouncing code and the lid switch
code. Both need to be handled in future patches.

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
 src/evdev-fallback.c | 404 ++++++++++++++++++++++++++++-----------------------
 src/evdev-fallback.h |   2 +
 src/evdev.h          |  24 ++-
 3 files changed, 245 insertions(+), 185 deletions(-)

diff --git a/src/evdev-fallback.c b/src/evdev-fallback.c
index e9fd7d62..70cbd3e1 100644
--- a/src/evdev-fallback.c
+++ b/src/evdev-fallback.c
@@ -45,6 +45,25 @@ hw_set_key_down(struct fallback_dispatch *dispatch, int code, int pressed)
 }
 
 static bool
+hw_key_has_changed(struct fallback_dispatch *dispatch, int code)
+{
+	return long_bit_is_set(dispatch->hw_key_mask, code) !=
+		long_bit_is_set(dispatch->last_hw_key_mask, code);
+}
+
+static void
+hw_key_update_last_state(struct fallback_dispatch *dispatch)
+{
+	static_assert(sizeof(dispatch->hw_key_mask) ==
+		      sizeof(dispatch->last_hw_key_mask),
+		      "Mismatching key mask size");
+
+	memcpy(dispatch->last_hw_key_mask,
+	       dispatch->hw_key_mask,
+	       sizeof(dispatch->hw_key_mask));
+}
+
+static bool
 hw_is_key_down(struct fallback_dispatch *dispatch, int code)
 {
 	return long_bit_is_set(dispatch->hw_key_mask, code);
@@ -228,6 +247,58 @@ fallback_flush_relative_motion(struct fallback_dispatch *dispatch,
 }
 
 static void
+fallback_flush_wheels(struct fallback_dispatch *dispatch,
+		      struct evdev_device *device,
+		      uint64_t time)
+{
+	struct normalized_coords wheel_degrees = { 0.0, 0.0 };
+	struct discrete_coords discrete = { 0.0, 0.0 };
+	enum libinput_pointer_axis_source source;
+
+	if (!(device->seat_caps & EVDEV_DEVICE_POINTER))
+		return;
+
+
+	if (dispatch->wheel.y != 0) {
+		wheel_degrees.y = -1 * dispatch->wheel.y *
+					device->scroll.wheel_click_angle.y;
+		discrete.y = -1 * dispatch->wheel.y;
+
+		source = device->scroll.is_tilt.vertical ?
+				LIBINPUT_POINTER_AXIS_SOURCE_WHEEL_TILT:
+				LIBINPUT_POINTER_AXIS_SOURCE_WHEEL;
+
+		evdev_notify_axis(
+			device,
+			time,
+			AS_MASK(LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL),
+			source,
+			&wheel_degrees,
+			&discrete);
+		dispatch->wheel.y = 0;
+	}
+
+	if (dispatch->wheel.x != 0) {
+		wheel_degrees.x = dispatch->wheel.x *
+					device->scroll.wheel_click_angle.x;
+		discrete.x = dispatch->wheel.x;
+
+		source = device->scroll.is_tilt.horizontal ?
+				LIBINPUT_POINTER_AXIS_SOURCE_WHEEL_TILT:
+				LIBINPUT_POINTER_AXIS_SOURCE_WHEEL;
+
+		evdev_notify_axis(
+			device,
+			time,
+			AS_MASK(LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL),
+			source,
+			&wheel_degrees,
+			&discrete);
+		dispatch->wheel.x = 0;
+	}
+}
+
+static void
 fallback_flush_absolute_motion(struct fallback_dispatch *dispatch,
 			       struct evdev_device *device,
 			       uint64_t time)
@@ -424,78 +495,6 @@ fallback_flush_st_up(struct fallback_dispatch *dispatch,
 	return true;
 }
 
-static enum evdev_event_type
-fallback_flush_pending_event(struct fallback_dispatch *dispatch,
-			     struct evdev_device *device,
-			     uint64_t time)
-{
-	enum evdev_event_type sent_event;
-	int slot_idx;
-
-	sent_event = dispatch->pending_event;
-
-	switch (dispatch->pending_event) {
-	case EVDEV_NONE:
-		break;
-	case EVDEV_RELATIVE_MOTION:
-		fallback_flush_relative_motion(dispatch, device, time);
-		break;
-	case EVDEV_ABSOLUTE_MT_DOWN:
-		slot_idx = dispatch->mt.slot;
-		if (!fallback_flush_mt_down(dispatch,
-					    device,
-					    slot_idx,
-					    time))
-			sent_event = EVDEV_NONE;
-		break;
-	case EVDEV_ABSOLUTE_MT_MOTION:
-		slot_idx = dispatch->mt.slot;
-		if (!fallback_flush_mt_motion(dispatch,
-					      device,
-					      slot_idx,
-					      time))
-			sent_event = EVDEV_NONE;
-		break;
-	case EVDEV_ABSOLUTE_MT_UP:
-		slot_idx = dispatch->mt.slot;
-		if (!fallback_flush_mt_up(dispatch,
-					  device,
-					  slot_idx,
-					  time))
-			sent_event = EVDEV_NONE;
-		break;
-	case EVDEV_ABSOLUTE_TOUCH_DOWN:
-		if (!fallback_flush_st_down(dispatch, device, time))
-			sent_event = EVDEV_NONE;
-		break;
-	case EVDEV_ABSOLUTE_MOTION:
-		if (device->seat_caps & EVDEV_DEVICE_TOUCH) {
-			if (fallback_flush_st_motion(dispatch,
-						     device,
-						     time))
-				sent_event = EVDEV_ABSOLUTE_MT_MOTION;
-			else
-				sent_event = EVDEV_NONE;
-		} else if (device->seat_caps & EVDEV_DEVICE_POINTER) {
-			fallback_flush_absolute_motion(dispatch,
-						       device,
-						       time);
-		}
-		break;
-	case EVDEV_ABSOLUTE_TOUCH_UP:
-		if (!fallback_flush_st_up(dispatch, device, time))
-			sent_event = EVDEV_NONE;
-		break;
-	default:
-		assert(0 && "Unknown pending event type");
-		break;
-	}
-
-	dispatch->pending_event = EVDEV_NONE;
-
-	return sent_event;
-}
-
 static enum key_type
 get_key_type(uint16_t code)
 {
@@ -536,13 +535,9 @@ fallback_process_touch_button(struct fallback_dispatch *dispatch,
 			      struct evdev_device *device,
 			      uint64_t time, int value)
 {
-	if (dispatch->pending_event != EVDEV_NONE &&
-	    dispatch->pending_event != EVDEV_ABSOLUTE_MOTION)
-		fallback_flush_pending_event(dispatch, device, time);
-
-	dispatch->pending_event = (value ?
+	dispatch->pending_event |= (value) ?
 				 EVDEV_ABSOLUTE_TOUCH_DOWN :
-				 EVDEV_ABSOLUTE_TOUCH_UP);
+				 EVDEV_ABSOLUTE_TOUCH_UP;
 }
 
 static inline void
@@ -562,6 +557,7 @@ fallback_flush_debounce(struct fallback_dispatch *dispatch,
 						     button,
 						     LIBINPUT_BUTTON_STATE_RELEASED);
 		hw_set_key_down(dispatch, code, 0);
+		hw_key_update_last_state(dispatch);
 	}
 
 	dispatch->debounce.state = DEBOUNCE_ON;
@@ -707,8 +703,6 @@ fallback_process_key(struct fallback_dispatch *dispatch,
 		return;
 	}
 
-	fallback_flush_pending_event(dispatch, device, time);
-
 	type = get_key_type(e->code);
 
 	/* Ignore key release events from the kernel for keys that libinput
@@ -721,40 +715,22 @@ fallback_process_key(struct fallback_dispatch *dispatch,
 		if ((e->value && hw_is_key_down(dispatch, e->code)) ||
 		    (e->value == 0 && !hw_is_key_down(dispatch, e->code)))
 			return;
+
+		dispatch->pending_event |= EVDEV_KEY;
 		break;
 	case KEY_TYPE_BUTTON:
+		/* FIXME: should move to handle_state */
 		if (fallback_filter_debounce(dispatch, device, e, time))
 			return;
 
 		if ((e->value && hw_is_key_down(dispatch, e->code)) ||
 		    (e->value == 0 && !hw_is_key_down(dispatch, e->code)))
 			return;
+		dispatch->pending_event |= EVDEV_KEY;
 		break;
 	}
 
 	hw_set_key_down(dispatch, e->code, e->value);
-
-	switch (type) {
-	case KEY_TYPE_NONE:
-		break;
-	case KEY_TYPE_KEY:
-		fallback_keyboard_notify_key(
-			dispatch,
-			device,
-			time,
-			e->code,
-			e->value ? LIBINPUT_KEY_STATE_PRESSED :
-				   LIBINPUT_KEY_STATE_RELEASED);
-		break;
-	case KEY_TYPE_BUTTON:
-		evdev_pointer_notify_physical_button(
-			device,
-			time,
-			evdev_to_left_handed(device, e->code),
-			e->value ? LIBINPUT_BUTTON_STATE_PRESSED :
-				   LIBINPUT_BUTTON_STATE_RELEASED);
-		break;
-	}
 }
 
 static void
@@ -763,8 +739,9 @@ fallback_process_touch(struct fallback_dispatch *dispatch,
 		       struct input_event *e,
 		       uint64_t time)
 {
-	switch (e->code) {
-	case ABS_MT_SLOT:
+	struct mt_slot *slot = &dispatch->mt.slots[dispatch->mt.slot];
+
+	if (e->code == ABS_MT_SLOT) {
 		if ((size_t)e->value >= dispatch->mt.slots_len) {
 			evdev_log_bug_libinput(device,
 					 "exceeded slot count (%d of max %zd)\n",
@@ -772,32 +749,36 @@ fallback_process_touch(struct fallback_dispatch *dispatch,
 					 dispatch->mt.slots_len);
 			e->value = dispatch->mt.slots_len - 1;
 		}
-		fallback_flush_pending_event(dispatch, device, time);
 		dispatch->mt.slot = e->value;
-		break;
+		return;
+	}
+
+	switch (e->code) {
 	case ABS_MT_TRACKING_ID:
-		if (dispatch->pending_event != EVDEV_NONE &&
-		    dispatch->pending_event != EVDEV_ABSOLUTE_MT_MOTION)
-			fallback_flush_pending_event(dispatch, device, time);
-		if (e->value >= 0)
-			dispatch->pending_event = EVDEV_ABSOLUTE_MT_DOWN;
-		else
-			dispatch->pending_event = EVDEV_ABSOLUTE_MT_UP;
+		if (e->value >= 0) {
+			dispatch->pending_event |= EVDEV_ABSOLUTE_MT;
+			slot->state = SLOT_STATE_BEGIN;
+		} else {
+			dispatch->pending_event |= EVDEV_ABSOLUTE_MT;
+			slot->state = SLOT_STATE_END;
+		}
+		slot->dirty = true;
 		break;
 	case ABS_MT_POSITION_X:
 		evdev_device_check_abs_axis_range(device, e->code, e->value);
 		dispatch->mt.slots[dispatch->mt.slot].point.x = e->value;
-		if (dispatch->pending_event == EVDEV_NONE)
-			dispatch->pending_event = EVDEV_ABSOLUTE_MT_MOTION;
+		dispatch->pending_event |= EVDEV_ABSOLUTE_MT;
+		slot->dirty = true;
 		break;
 	case ABS_MT_POSITION_Y:
 		evdev_device_check_abs_axis_range(device, e->code, e->value);
 		dispatch->mt.slots[dispatch->mt.slot].point.y = e->value;
-		if (dispatch->pending_event == EVDEV_NONE)
-			dispatch->pending_event = EVDEV_ABSOLUTE_MT_MOTION;
+		dispatch->pending_event |= EVDEV_ABSOLUTE_MT;
+		slot->dirty = true;
 		break;
 	}
 }
+
 static inline void
 fallback_process_absolute_motion(struct fallback_dispatch *dispatch,
 				 struct evdev_device *device,
@@ -807,14 +788,12 @@ fallback_process_absolute_motion(struct fallback_dispatch *dispatch,
 	case ABS_X:
 		evdev_device_check_abs_axis_range(device, e->code, e->value);
 		dispatch->abs.point.x = e->value;
-		if (dispatch->pending_event == EVDEV_NONE)
-			dispatch->pending_event = EVDEV_ABSOLUTE_MOTION;
+		dispatch->pending_event |= EVDEV_ABSOLUTE_MOTION;
 		break;
 	case ABS_Y:
 		evdev_device_check_abs_axis_range(device, e->code, e->value);
 		dispatch->abs.point.y = e->value;
-		if (dispatch->pending_event == EVDEV_NONE)
-			dispatch->pending_event = EVDEV_ABSOLUTE_MOTION;
+		dispatch->pending_event |= EVDEV_ABSOLUTE_MOTION;
 		break;
 	}
 }
@@ -897,6 +876,8 @@ fallback_process_switch(struct fallback_dispatch *dispatch,
 	enum libinput_switch_state state;
 	bool is_closed;
 
+	/* TODO: this should to move to handle_state */
+
 	switch (e->code) {
 	case SW_LID:
 		is_closed = !!e->value;
@@ -947,61 +928,25 @@ fallback_process_relative(struct fallback_dispatch *dispatch,
 			  struct evdev_device *device,
 			  struct input_event *e, uint64_t time)
 {
-	struct normalized_coords wheel_degrees = { 0.0, 0.0 };
-	struct discrete_coords discrete = { 0.0, 0.0 };
-	enum libinput_pointer_axis_source source;
-
 	if (fallback_reject_relative(device, e, time))
 		return;
 
 	switch (e->code) {
 	case REL_X:
-		if (dispatch->pending_event != EVDEV_RELATIVE_MOTION)
-			fallback_flush_pending_event(dispatch, device, time);
 		dispatch->rel.x += e->value;
-		dispatch->pending_event = EVDEV_RELATIVE_MOTION;
+		dispatch->pending_event |= EVDEV_RELATIVE_MOTION;
 		break;
 	case REL_Y:
-		if (dispatch->pending_event != EVDEV_RELATIVE_MOTION)
-			fallback_flush_pending_event(dispatch, device, time);
 		dispatch->rel.y += e->value;
-		dispatch->pending_event = EVDEV_RELATIVE_MOTION;
+		dispatch->pending_event |= EVDEV_RELATIVE_MOTION;
 		break;
 	case REL_WHEEL:
-		fallback_flush_pending_event(dispatch, device, time);
-		wheel_degrees.y = -1 * e->value *
-					device->scroll.wheel_click_angle.y;
-		discrete.y = -1 * e->value;
-
-		source = device->scroll.is_tilt.vertical ?
-				LIBINPUT_POINTER_AXIS_SOURCE_WHEEL_TILT:
-				LIBINPUT_POINTER_AXIS_SOURCE_WHEEL;
-
-		evdev_notify_axis(
-			device,
-			time,
-			AS_MASK(LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL),
-			source,
-			&wheel_degrees,
-			&discrete);
+		dispatch->wheel.y += e->value;
+		dispatch->pending_event |= EVDEV_WHEEL;
 		break;
 	case REL_HWHEEL:
-		fallback_flush_pending_event(dispatch, device, time);
-		wheel_degrees.x = e->value *
-					device->scroll.wheel_click_angle.x;
-		discrete.x = e->value;
-
-		source = device->scroll.is_tilt.horizontal ?
-				LIBINPUT_POINTER_AXIS_SOURCE_WHEEL_TILT:
-				LIBINPUT_POINTER_AXIS_SOURCE_WHEEL;
-
-		evdev_notify_axis(
-			device,
-			time,
-			AS_MASK(LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL),
-			source,
-			&wheel_degrees,
-			&discrete);
+		dispatch->wheel.x += e->value;
+		dispatch->pending_event |= EVDEV_WHEEL;
 		break;
 	}
 }
@@ -1034,13 +979,128 @@ fallback_any_button_down(struct fallback_dispatch *dispatch,
 }
 
 static void
+fallback_handle_state(struct fallback_dispatch *dispatch,
+		      struct evdev_device *device,
+		      uint64_t time)
+{
+	bool need_touch_frame = false;
+
+	/* Relative motion */
+	if (dispatch->pending_event & EVDEV_RELATIVE_MOTION)
+		fallback_flush_relative_motion(dispatch, device, time);
+
+	/* Single touch or absolute pointer devices */
+	if (dispatch->pending_event & EVDEV_ABSOLUTE_TOUCH_DOWN) {
+		if (fallback_flush_st_down(dispatch, device, time))
+			need_touch_frame = true;
+	}
+
+	if (dispatch->pending_event & EVDEV_ABSOLUTE_MOTION) {
+		if (device->seat_caps & EVDEV_DEVICE_TOUCH) {
+			if (fallback_flush_st_motion(dispatch,
+						     device,
+						     time))
+				need_touch_frame = true;
+		} else if (device->seat_caps & EVDEV_DEVICE_POINTER) {
+			fallback_flush_absolute_motion(dispatch,
+						       device,
+						       time);
+		}
+	}
+
+	if (dispatch->pending_event & EVDEV_ABSOLUTE_TOUCH_UP) {
+		if (fallback_flush_st_up(dispatch, device, time))
+			need_touch_frame = true;
+	}
+
+	/* Multitouch devices */
+	if (dispatch->pending_event & EVDEV_ABSOLUTE_MT) {
+		bool sent = false;
+		for (size_t i = 0; i < dispatch->mt.slots_len; i++) {
+			struct mt_slot *slot = &dispatch->mt.slots[i];
+
+			if (!slot->dirty)
+				continue;
+
+			if (slot->state == SLOT_STATE_BEGIN) {
+				sent = fallback_flush_mt_down(dispatch,
+							      device,
+							      i,
+							      time);
+				slot->state = SLOT_STATE_UPDATE;
+			} else if (slot->state == SLOT_STATE_UPDATE) {
+				sent = fallback_flush_mt_motion(dispatch,
+								device,
+								i,
+								time);
+			} else if (slot->state == SLOT_STATE_END) {
+				sent = fallback_flush_mt_up(dispatch,
+							    device,
+							    i,
+							    time);
+				slot->state = SLOT_STATE_NONE;
+			}
+
+
+			slot->dirty = false;
+		}
+
+		need_touch_frame = sent;
+	}
+
+	if (need_touch_frame)
+		touch_notify_frame(&device->base, time);
+
+	fallback_flush_wheels(dispatch, device, time);
+
+	/* Buttons and keys */
+	if (dispatch->pending_event & EVDEV_KEY) {
+		for (unsigned int code = 0; code <= KEY_MAX; code++) {
+			bool new_state;
+
+			if (!hw_key_has_changed(dispatch, code))
+				continue;
+
+			new_state = hw_is_key_down(dispatch, code);
+
+			switch (get_key_type(code)) {
+			case KEY_TYPE_NONE:
+				break;
+			case KEY_TYPE_KEY:
+				fallback_keyboard_notify_key(
+						     dispatch,
+						     device,
+						     time,
+						     code,
+						     new_state ?
+							     LIBINPUT_KEY_STATE_PRESSED :
+							     LIBINPUT_KEY_STATE_RELEASED);
+				break;
+			case KEY_TYPE_BUTTON:
+				evdev_pointer_notify_physical_button(
+						     device,
+						     time,
+						     evdev_to_left_handed(device, code),
+						     new_state ?
+							     LIBINPUT_BUTTON_STATE_PRESSED :
+							     LIBINPUT_BUTTON_STATE_RELEASED);
+				break;
+			}
+
+		}
+		hw_key_update_last_state(dispatch);
+	}
+
+	dispatch->pending_event = EVDEV_NONE;
+}
+
+static void
 fallback_interface_process(struct evdev_dispatch *evdev_dispatch,
 			   struct evdev_device *device,
 			   struct input_event *event,
 			   uint64_t time)
 {
 	struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
-	enum evdev_event_type sent;
 
 	if (dispatch->ignore_events)
 		return;
@@ -1059,20 +1119,7 @@ fallback_interface_process(struct evdev_dispatch *evdev_dispatch,
 		fallback_process_switch(dispatch, device, event, time);
 		break;
 	case EV_SYN:
-		sent = fallback_flush_pending_event(dispatch, device, time);
-		switch (sent) {
-		case EVDEV_ABSOLUTE_TOUCH_DOWN:
-		case EVDEV_ABSOLUTE_TOUCH_UP:
-		case EVDEV_ABSOLUTE_MT_DOWN:
-		case EVDEV_ABSOLUTE_MT_MOTION:
-		case EVDEV_ABSOLUTE_MT_UP:
-			touch_notify_frame(&device->base, time);
-			break;
-		case EVDEV_ABSOLUTE_MOTION:
-		case EVDEV_RELATIVE_MOTION:
-		case EVDEV_NONE:
-			break;
-		}
+		fallback_handle_state(dispatch, device, time);
 		break;
 	}
 }
@@ -1164,6 +1211,7 @@ fallback_return_to_neutral_state(struct fallback_dispatch *dispatch,
 	release_touches(dispatch, device, time);
 	release_pressed_keys(dispatch, device, time);
 	memset(dispatch->hw_key_mask, 0, sizeof(dispatch->hw_key_mask));
+	memset(dispatch->hw_key_mask, 0, sizeof(dispatch->last_hw_key_mask));
 }
 
 static void
diff --git a/src/evdev-fallback.h b/src/evdev-fallback.h
index e8783d4b..ba121c08 100644
--- a/src/evdev-fallback.h
+++ b/src/evdev-fallback.h
@@ -58,6 +58,7 @@ struct fallback_dispatch {
 	} mt;
 
 	struct device_coords rel;
+	struct device_coords wheel;
 
 	struct {
 		/* The struct for the tablet mode switch device itself */
@@ -75,6 +76,7 @@ struct fallback_dispatch {
 	/* Bitmask of pressed keys used to ignore initial release events from
 	 * the kernel. */
 	unsigned long hw_key_mask[NLONGS(KEY_CNT)];
+	unsigned long last_hw_key_mask[NLONGS(KEY_CNT)];
 
 	enum evdev_event_type pending_event;
 
diff --git a/src/evdev.h b/src/evdev.h
index 7d5e6efb..11ed7da5 100644
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -41,13 +41,14 @@
 
 enum evdev_event_type {
 	EVDEV_NONE,
-	EVDEV_ABSOLUTE_TOUCH_DOWN,
-	EVDEV_ABSOLUTE_MOTION,
-	EVDEV_ABSOLUTE_TOUCH_UP,
-	EVDEV_ABSOLUTE_MT_DOWN,
-	EVDEV_ABSOLUTE_MT_MOTION,
-	EVDEV_ABSOLUTE_MT_UP,
-	EVDEV_RELATIVE_MOTION,
+	EVDEV_ABSOLUTE_TOUCH_DOWN = (1 << 0),
+	EVDEV_ABSOLUTE_MOTION = (1 << 1),
+	EVDEV_ABSOLUTE_TOUCH_UP = (1 << 2),
+	EVDEV_ABSOLUTE_MT= (1 << 3),
+	EVDEV_WHEEL = (1 << 4),
+	EVDEV_KEY = (1 << 5),
+	EVDEV_RELATIVE_MOTION = (1 << 6),
+	EVDEV_BUTTON = (1 << 7),
 };
 
 enum evdev_device_seat_capability {
@@ -150,7 +151,16 @@ enum evdev_debounce_state {
 	DEBOUNCE_ACTIVE,
 };
 
+enum mt_slot_state {
+	SLOT_STATE_NONE,
+	SLOT_STATE_BEGIN,
+	SLOT_STATE_UPDATE,
+	SLOT_STATE_END,
+};
+
 struct mt_slot {
+	bool dirty;
+	enum mt_slot_state state;
 	int32_t seat_slot;
 	struct device_coords point;
 	struct device_coords hysteresis_center;
-- 
2.13.6



More information about the wayland-devel mailing list