fallback: change to handle the state at EV_SYN time

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@who-t.net>
This commit is contained in:
Peter Hutterer 2017-11-13 12:19:44 +10:00
parent 8e86f28931
commit db3b6fe5f7
3 changed files with 245 additions and 185 deletions

View file

@ -44,6 +44,25 @@ hw_set_key_down(struct fallback_dispatch *dispatch, int code, int pressed)
long_set_bit_state(dispatch->hw_key_mask, code, pressed); long_set_bit_state(dispatch->hw_key_mask, code, 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 static bool
hw_is_key_down(struct fallback_dispatch *dispatch, int code) hw_is_key_down(struct fallback_dispatch *dispatch, int code)
{ {
@ -227,6 +246,58 @@ fallback_flush_relative_motion(struct fallback_dispatch *dispatch,
pointer_notify_motion(base, time, &accel, &raw); pointer_notify_motion(base, time, &accel, &raw);
} }
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 static void
fallback_flush_absolute_motion(struct fallback_dispatch *dispatch, fallback_flush_absolute_motion(struct fallback_dispatch *dispatch,
struct evdev_device *device, struct evdev_device *device,
@ -424,78 +495,6 @@ fallback_flush_st_up(struct fallback_dispatch *dispatch,
return true; 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 static enum key_type
get_key_type(uint16_t code) get_key_type(uint16_t code)
{ {
@ -536,13 +535,9 @@ fallback_process_touch_button(struct fallback_dispatch *dispatch,
struct evdev_device *device, struct evdev_device *device,
uint64_t time, int value) uint64_t time, int value)
{ {
if (dispatch->pending_event != EVDEV_NONE && dispatch->pending_event |= (value) ?
dispatch->pending_event != EVDEV_ABSOLUTE_MOTION)
fallback_flush_pending_event(dispatch, device, time);
dispatch->pending_event = (value ?
EVDEV_ABSOLUTE_TOUCH_DOWN : EVDEV_ABSOLUTE_TOUCH_DOWN :
EVDEV_ABSOLUTE_TOUCH_UP); EVDEV_ABSOLUTE_TOUCH_UP;
} }
static inline void static inline void
@ -562,6 +557,7 @@ fallback_flush_debounce(struct fallback_dispatch *dispatch,
button, button,
LIBINPUT_BUTTON_STATE_RELEASED); LIBINPUT_BUTTON_STATE_RELEASED);
hw_set_key_down(dispatch, code, 0); hw_set_key_down(dispatch, code, 0);
hw_key_update_last_state(dispatch);
} }
dispatch->debounce.state = DEBOUNCE_ON; dispatch->debounce.state = DEBOUNCE_ON;
@ -707,8 +703,6 @@ fallback_process_key(struct fallback_dispatch *dispatch,
return; return;
} }
fallback_flush_pending_event(dispatch, device, time);
type = get_key_type(e->code); type = get_key_type(e->code);
/* Ignore key release events from the kernel for keys that libinput /* 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)) || if ((e->value && hw_is_key_down(dispatch, e->code)) ||
(e->value == 0 && !hw_is_key_down(dispatch, e->code))) (e->value == 0 && !hw_is_key_down(dispatch, e->code)))
return; return;
dispatch->pending_event |= EVDEV_KEY;
break; break;
case KEY_TYPE_BUTTON: case KEY_TYPE_BUTTON:
/* FIXME: should move to handle_state */
if (fallback_filter_debounce(dispatch, device, e, time)) if (fallback_filter_debounce(dispatch, device, e, time))
return; return;
if ((e->value && hw_is_key_down(dispatch, e->code)) || if ((e->value && hw_is_key_down(dispatch, e->code)) ||
(e->value == 0 && !hw_is_key_down(dispatch, e->code))) (e->value == 0 && !hw_is_key_down(dispatch, e->code)))
return; return;
dispatch->pending_event |= EVDEV_KEY;
break; break;
} }
hw_set_key_down(dispatch, e->code, e->value); 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 static void
@ -763,8 +739,9 @@ fallback_process_touch(struct fallback_dispatch *dispatch,
struct input_event *e, struct input_event *e,
uint64_t time) uint64_t time)
{ {
switch (e->code) { struct mt_slot *slot = &dispatch->mt.slots[dispatch->mt.slot];
case ABS_MT_SLOT:
if (e->code == ABS_MT_SLOT) {
if ((size_t)e->value >= dispatch->mt.slots_len) { if ((size_t)e->value >= dispatch->mt.slots_len) {
evdev_log_bug_libinput(device, evdev_log_bug_libinput(device,
"exceeded slot count (%d of max %zd)\n", "exceeded slot count (%d of max %zd)\n",
@ -772,32 +749,36 @@ fallback_process_touch(struct fallback_dispatch *dispatch,
dispatch->mt.slots_len); dispatch->mt.slots_len);
e->value = dispatch->mt.slots_len - 1; e->value = dispatch->mt.slots_len - 1;
} }
fallback_flush_pending_event(dispatch, device, time);
dispatch->mt.slot = e->value; dispatch->mt.slot = e->value;
break; return;
}
switch (e->code) {
case ABS_MT_TRACKING_ID: case ABS_MT_TRACKING_ID:
if (dispatch->pending_event != EVDEV_NONE && if (e->value >= 0) {
dispatch->pending_event != EVDEV_ABSOLUTE_MT_MOTION) dispatch->pending_event |= EVDEV_ABSOLUTE_MT;
fallback_flush_pending_event(dispatch, device, time); slot->state = SLOT_STATE_BEGIN;
if (e->value >= 0) } else {
dispatch->pending_event = EVDEV_ABSOLUTE_MT_DOWN; dispatch->pending_event |= EVDEV_ABSOLUTE_MT;
else slot->state = SLOT_STATE_END;
dispatch->pending_event = EVDEV_ABSOLUTE_MT_UP; }
slot->dirty = true;
break; break;
case ABS_MT_POSITION_X: case ABS_MT_POSITION_X:
evdev_device_check_abs_axis_range(device, e->code, e->value); evdev_device_check_abs_axis_range(device, e->code, e->value);
dispatch->mt.slots[dispatch->mt.slot].point.x = e->value; dispatch->mt.slots[dispatch->mt.slot].point.x = e->value;
if (dispatch->pending_event == EVDEV_NONE) dispatch->pending_event |= EVDEV_ABSOLUTE_MT;
dispatch->pending_event = EVDEV_ABSOLUTE_MT_MOTION; slot->dirty = true;
break; break;
case ABS_MT_POSITION_Y: case ABS_MT_POSITION_Y:
evdev_device_check_abs_axis_range(device, e->code, e->value); evdev_device_check_abs_axis_range(device, e->code, e->value);
dispatch->mt.slots[dispatch->mt.slot].point.y = e->value; dispatch->mt.slots[dispatch->mt.slot].point.y = e->value;
if (dispatch->pending_event == EVDEV_NONE) dispatch->pending_event |= EVDEV_ABSOLUTE_MT;
dispatch->pending_event = EVDEV_ABSOLUTE_MT_MOTION; slot->dirty = true;
break; break;
} }
} }
static inline void static inline void
fallback_process_absolute_motion(struct fallback_dispatch *dispatch, fallback_process_absolute_motion(struct fallback_dispatch *dispatch,
struct evdev_device *device, struct evdev_device *device,
@ -807,14 +788,12 @@ fallback_process_absolute_motion(struct fallback_dispatch *dispatch,
case ABS_X: case ABS_X:
evdev_device_check_abs_axis_range(device, e->code, e->value); evdev_device_check_abs_axis_range(device, e->code, e->value);
dispatch->abs.point.x = 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; break;
case ABS_Y: case ABS_Y:
evdev_device_check_abs_axis_range(device, e->code, e->value); evdev_device_check_abs_axis_range(device, e->code, e->value);
dispatch->abs.point.y = 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; break;
} }
} }
@ -897,6 +876,8 @@ fallback_process_switch(struct fallback_dispatch *dispatch,
enum libinput_switch_state state; enum libinput_switch_state state;
bool is_closed; bool is_closed;
/* TODO: this should to move to handle_state */
switch (e->code) { switch (e->code) {
case SW_LID: case SW_LID:
is_closed = !!e->value; is_closed = !!e->value;
@ -947,61 +928,25 @@ fallback_process_relative(struct fallback_dispatch *dispatch,
struct evdev_device *device, struct evdev_device *device,
struct input_event *e, uint64_t time) 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)) if (fallback_reject_relative(device, e, time))
return; return;
switch (e->code) { switch (e->code) {
case REL_X: case REL_X:
if (dispatch->pending_event != EVDEV_RELATIVE_MOTION)
fallback_flush_pending_event(dispatch, device, time);
dispatch->rel.x += e->value; dispatch->rel.x += e->value;
dispatch->pending_event = EVDEV_RELATIVE_MOTION; dispatch->pending_event |= EVDEV_RELATIVE_MOTION;
break; break;
case REL_Y: case REL_Y:
if (dispatch->pending_event != EVDEV_RELATIVE_MOTION)
fallback_flush_pending_event(dispatch, device, time);
dispatch->rel.y += e->value; dispatch->rel.y += e->value;
dispatch->pending_event = EVDEV_RELATIVE_MOTION; dispatch->pending_event |= EVDEV_RELATIVE_MOTION;
break; break;
case REL_WHEEL: case REL_WHEEL:
fallback_flush_pending_event(dispatch, device, time); dispatch->wheel.y += e->value;
wheel_degrees.y = -1 * e->value * dispatch->pending_event |= EVDEV_WHEEL;
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);
break; break;
case REL_HWHEEL: case REL_HWHEEL:
fallback_flush_pending_event(dispatch, device, time); dispatch->wheel.x += e->value;
wheel_degrees.x = e->value * dispatch->pending_event |= EVDEV_WHEEL;
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);
break; break;
} }
} }
@ -1033,6 +978,122 @@ fallback_any_button_down(struct fallback_dispatch *dispatch,
return false; return false;
} }
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 static void
fallback_interface_process(struct evdev_dispatch *evdev_dispatch, fallback_interface_process(struct evdev_dispatch *evdev_dispatch,
struct evdev_device *device, struct evdev_device *device,
@ -1040,7 +1101,6 @@ fallback_interface_process(struct evdev_dispatch *evdev_dispatch,
uint64_t time) uint64_t time)
{ {
struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch); struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
enum evdev_event_type sent;
if (dispatch->ignore_events) if (dispatch->ignore_events)
return; return;
@ -1059,20 +1119,7 @@ fallback_interface_process(struct evdev_dispatch *evdev_dispatch,
fallback_process_switch(dispatch, device, event, time); fallback_process_switch(dispatch, device, event, time);
break; break;
case EV_SYN: case EV_SYN:
sent = fallback_flush_pending_event(dispatch, device, time); fallback_handle_state(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;
}
break; break;
} }
} }
@ -1164,6 +1211,7 @@ fallback_return_to_neutral_state(struct fallback_dispatch *dispatch,
release_touches(dispatch, device, time); release_touches(dispatch, device, time);
release_pressed_keys(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->hw_key_mask));
memset(dispatch->hw_key_mask, 0, sizeof(dispatch->last_hw_key_mask));
} }
static void static void

View file

@ -58,6 +58,7 @@ struct fallback_dispatch {
} mt; } mt;
struct device_coords rel; struct device_coords rel;
struct device_coords wheel;
struct { struct {
/* The struct for the tablet mode switch device itself */ /* 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 /* Bitmask of pressed keys used to ignore initial release events from
* the kernel. */ * the kernel. */
unsigned long hw_key_mask[NLONGS(KEY_CNT)]; unsigned long hw_key_mask[NLONGS(KEY_CNT)];
unsigned long last_hw_key_mask[NLONGS(KEY_CNT)];
enum evdev_event_type pending_event; enum evdev_event_type pending_event;

View file

@ -41,13 +41,14 @@
enum evdev_event_type { enum evdev_event_type {
EVDEV_NONE, EVDEV_NONE,
EVDEV_ABSOLUTE_TOUCH_DOWN, EVDEV_ABSOLUTE_TOUCH_DOWN = (1 << 0),
EVDEV_ABSOLUTE_MOTION, EVDEV_ABSOLUTE_MOTION = (1 << 1),
EVDEV_ABSOLUTE_TOUCH_UP, EVDEV_ABSOLUTE_TOUCH_UP = (1 << 2),
EVDEV_ABSOLUTE_MT_DOWN, EVDEV_ABSOLUTE_MT= (1 << 3),
EVDEV_ABSOLUTE_MT_MOTION, EVDEV_WHEEL = (1 << 4),
EVDEV_ABSOLUTE_MT_UP, EVDEV_KEY = (1 << 5),
EVDEV_RELATIVE_MOTION, EVDEV_RELATIVE_MOTION = (1 << 6),
EVDEV_BUTTON = (1 << 7),
}; };
enum evdev_device_seat_capability { enum evdev_device_seat_capability {
@ -150,7 +151,16 @@ enum evdev_debounce_state {
DEBOUNCE_ACTIVE, DEBOUNCE_ACTIVE,
}; };
enum mt_slot_state {
SLOT_STATE_NONE,
SLOT_STATE_BEGIN,
SLOT_STATE_UPDATE,
SLOT_STATE_END,
};
struct mt_slot { struct mt_slot {
bool dirty;
enum mt_slot_state state;
int32_t seat_slot; int32_t seat_slot;
struct device_coords point; struct device_coords point;
struct device_coords hysteresis_center; struct device_coords hysteresis_center;