#define PINCH_DISAMBIGUATION_MOVE_THRESHOLD 1.5 /* mm */
+enum gesture_event {
+ GESTURE_EVENT_RESET,
+ GESTURE_EVENT_FINGER_DETECTED,
+ GESTURE_EVENT_POINTER_MOTION,
+ GESTURE_EVENT_SCROLL,
+ GESTURE_EVENT_SWIPE,
+ GESTURE_EVENT_PINCH,
+};
+
static inline const char*
gesture_state_to_str(enum tp_gesture_state state)
{
return NULL;
}
+static inline const char*
+gesture_event_to_str(enum gesture_event event)
+{
+ switch(event) {
+ CASE_RETURN_STRING(GESTURE_EVENT_RESET);
+ CASE_RETURN_STRING(GESTURE_EVENT_FINGER_DETECTED);
+ CASE_RETURN_STRING(GESTURE_EVENT_POINTER_MOTION);
+ CASE_RETURN_STRING(GESTURE_EVENT_SCROLL);
+ CASE_RETURN_STRING(GESTURE_EVENT_SWIPE);
+ CASE_RETURN_STRING(GESTURE_EVENT_PINCH);
+ }
+ return NULL;
+}
+
static struct device_float_coords
tp_get_touches_delta(struct tp_dispatch *tp, bool average)
{
}
}
-static enum tp_gesture_state
+static inline void
+log_gesture_bug(struct tp_dispatch *tp, enum gesture_event event)
+{
+ evdev_log_bug_libinput(tp->device,
+ "invalid gesture event %s in state %s\n",
+ gesture_event_to_str(event),
+ gesture_state_to_str(tp->gesture.state));
+}
+
+static void
+tp_gesture_none_handle_event(struct tp_dispatch *tp,
+ enum gesture_event event,
+ uint64_t time)
+{
+ switch(event) {
+ case GESTURE_EVENT_FINGER_DETECTED:
+ tp->gesture.state = GESTURE_STATE_UNKNOWN;
+ break;
+ case GESTURE_EVENT_POINTER_MOTION:
+ tp->gesture.state = GESTURE_STATE_POINTER_MOTION;
+ break;
+ case GESTURE_EVENT_SCROLL:
+ tp->gesture.state = GESTURE_STATE_SCROLL;
+ break;
+ case GESTURE_EVENT_RESET:
+ case GESTURE_EVENT_SWIPE:
+ case GESTURE_EVENT_PINCH:
+ log_gesture_bug(tp, event);
+ break;
+ }
+}
+
+static void
+tp_gesture_unknown_handle_event(struct tp_dispatch *tp,
+ enum gesture_event event,
+ uint64_t time)
+{
+ switch(event) {
+ case GESTURE_EVENT_RESET:
+ tp->gesture.state = GESTURE_STATE_NONE;
+ break;
+ case GESTURE_EVENT_POINTER_MOTION:
+ tp->gesture.state = GESTURE_STATE_POINTER_MOTION;
+ break;
+ case GESTURE_EVENT_SCROLL:
+ tp_gesture_set_scroll_buildup(tp);
+ tp->gesture.state = GESTURE_STATE_SCROLL;
+ break;
+ case GESTURE_EVENT_SWIPE:
+ tp->gesture.state = GESTURE_STATE_SWIPE;
+ break;
+ case GESTURE_EVENT_PINCH:
+ tp_gesture_init_pinch(tp);
+ tp->gesture.state = GESTURE_STATE_PINCH;
+ break;
+ case GESTURE_EVENT_FINGER_DETECTED:
+ log_gesture_bug(tp, event);
+ break;
+ }
+}
+
+static void
+tp_gesture_pointer_motion_handle_event(struct tp_dispatch *tp,
+ enum gesture_event event,
+ uint64_t time)
+{
+ switch(event) {
+ case GESTURE_EVENT_RESET:
+ tp->gesture.state = GESTURE_STATE_NONE;
+ break;
+ case GESTURE_EVENT_FINGER_DETECTED:
+ case GESTURE_EVENT_POINTER_MOTION:
+ case GESTURE_EVENT_SCROLL:
+ case GESTURE_EVENT_SWIPE:
+ case GESTURE_EVENT_PINCH:
+ log_gesture_bug(tp, event);
+ break;
+ }
+}
+
+static void
+tp_gesture_scroll_handle_event(struct tp_dispatch *tp,
+ enum gesture_event event,
+ uint64_t time)
+{
+ switch(event) {
+ case GESTURE_EVENT_RESET:
+ tp->gesture.state = GESTURE_STATE_NONE;
+ break;
+ case GESTURE_EVENT_FINGER_DETECTED:
+ case GESTURE_EVENT_POINTER_MOTION:
+ case GESTURE_EVENT_SCROLL:
+ case GESTURE_EVENT_SWIPE:
+ case GESTURE_EVENT_PINCH:
+ log_gesture_bug(tp, event);
+ break;
+ }
+}
+
+static void
+tp_gesture_pinch_handle_event(struct tp_dispatch *tp,
+ enum gesture_event event,
+ uint64_t time)
+{
+ switch(event) {
+ case GESTURE_EVENT_RESET:
+ tp->gesture.state = GESTURE_STATE_NONE;
+ break;
+ case GESTURE_EVENT_FINGER_DETECTED:
+ case GESTURE_EVENT_POINTER_MOTION:
+ case GESTURE_EVENT_SCROLL:
+ case GESTURE_EVENT_SWIPE:
+ case GESTURE_EVENT_PINCH:
+ log_gesture_bug(tp, event);
+ break;
+ }
+}
+
+static void
+tp_gesture_swipe_handle_event(struct tp_dispatch *tp,
+ enum gesture_event event,
+ uint64_t time)
+{
+ switch(event) {
+ case GESTURE_EVENT_RESET:
+ tp->gesture.state = GESTURE_STATE_NONE;
+ break;
+ case GESTURE_EVENT_FINGER_DETECTED:
+ case GESTURE_EVENT_POINTER_MOTION:
+ case GESTURE_EVENT_SCROLL:
+ case GESTURE_EVENT_SWIPE:
+ case GESTURE_EVENT_PINCH:
+ log_gesture_bug(tp, event);
+ break;
+ }
+}
+
+static void
+tp_gesture_handle_event(struct tp_dispatch *tp,
+ enum gesture_event event,
+ uint64_t time)
+{
+ enum tp_gesture_state oldstate;
+
+ oldstate = tp->gesture.state;
+
+ switch(tp->gesture.state) {
+ case GESTURE_STATE_NONE:
+ tp_gesture_none_handle_event(tp, event, time);
+ break;
+ case GESTURE_STATE_UNKNOWN:
+ tp_gesture_unknown_handle_event(tp, event, time);
+ break;
+ case GESTURE_STATE_POINTER_MOTION:
+ tp_gesture_pointer_motion_handle_event(tp, event, time);
+ break;
+ case GESTURE_STATE_SCROLL:
+ tp_gesture_scroll_handle_event(tp, event, time);
+ break;
+ case GESTURE_STATE_PINCH:
+ tp_gesture_pinch_handle_event(tp, event, time);
+ break;
+ case GESTURE_STATE_SWIPE:
+ tp_gesture_swipe_handle_event(tp, event, time);
+ break;
+ }
+
+ if (oldstate != tp->gesture.state) {
+ evdev_log_debug(tp->device,
+ "gesture state %s → %s → %s\n",
+ gesture_state_to_str(oldstate),
+ gesture_event_to_str(event),
+ gesture_state_to_str(tp->gesture.state));
+ }
+}
+
+static void
tp_gesture_detect_motion_gestures(struct tp_dispatch *tp, uint64_t time)
{
struct tp_touch *first = tp->gesture.touches[0],
double max_move = 4.0; /* max movement threshold in mm - ignore other touch */
if (tp->gesture.finger_count == 1) {
- if (tp_has_pending_pointer_motion(tp, time))
- return GESTURE_STATE_POINTER_MOTION;
-
- return GESTURE_STATE_UNKNOWN;
+ if (tp_has_pending_pointer_motion(tp, time)) {
+ tp_gesture_handle_event(tp,
+ GESTURE_EVENT_POINTER_MOTION,
+ time);
+ }
+ return;
}
/* If we have more fingers than slots, we don't know where the
* fingers are. Default to swipe */
if (tp->gesture.enabled && tp->gesture.finger_count > 2 &&
- tp->gesture.finger_count > tp->num_slots)
- return GESTURE_STATE_SWIPE;
+ tp->gesture.finger_count > tp->num_slots) {
+ tp_gesture_handle_event(tp, GESTURE_EVENT_SWIPE, time);
+ return;
+ }
/* Need more margin for error when there are more fingers */
max_move += 2.0 * (tp->gesture.finger_count - 2);
/* If both touches moved less than a mm, we cannot decide yet */
if (first_mm < 1 && second_mm < 1)
- return GESTURE_STATE_UNKNOWN;
+ return;
/* Pick the thumb as the lowest point on the touchpad */
if (first->point.y > second->point.y) {
if ((!tp->gesture.enabled ||
(distance_mm.x < 40.0 && distance_mm.y < 7.0)) &&
time > (tp->gesture.initial_time + DEFAULT_GESTURE_SWIPE_TIMEOUT)) {
- if (tp->gesture.finger_count == 2) {
- tp_gesture_set_scroll_buildup(tp);
- return GESTURE_STATE_SCROLL;
- }
- return GESTURE_STATE_SWIPE;
+ if (tp->gesture.finger_count == 2)
+ tp_gesture_handle_event(tp, GESTURE_EVENT_SCROLL, time);
+ else
+ tp_gesture_handle_event(tp, GESTURE_EVENT_SWIPE, time);
+
+ return;
}
/* If one touch exceeds the max_move threshold while the other has not
*/
if (tp->thumb.detect_thumbs && thumb_mm < min_move) {
tp_thumb_suppress(tp, thumb);
- return GESTURE_STATE_NONE;
+ tp_gesture_handle_event(tp, GESTURE_EVENT_RESET, time);
+ return;
}
/* If gestures detection is disabled, or if finger is still
*/
if ((!tp->gesture.enabled || finger_mm < min_move) &&
tp->gesture.finger_count == 2) {
- tp_gesture_set_scroll_buildup(tp);
- return GESTURE_STATE_SCROLL;
+ tp_gesture_handle_event(tp, GESTURE_EVENT_SCROLL, time);
+ return;
}
/* If more than 2 fingers are involved, and the thumb moves
tp->gesture.finger_count > 2 &&
tp->gesture.enabled &&
tp->thumb.pinch_eligible) {
- tp_gesture_init_pinch(tp);
- return GESTURE_STATE_PINCH;
+ tp_gesture_handle_event(tp, GESTURE_EVENT_PINCH, time);
+ return;
}
}
* tell what kind of gesture this is.
*/
if ((first_mm < min_move) || (second_mm < min_move))
- return GESTURE_STATE_UNKNOWN;
+ return;
/* Both touches have exceeded the min_move threshold, so we have a
* valid gesture. Update gesture initial time and get directions so
if (tp->gesture.finger_count > tp->num_slots ||
tp_gesture_same_directions(dir1, dir2)) {
if (tp->gesture.finger_count == 2) {
- tp_gesture_set_scroll_buildup(tp);
- return GESTURE_STATE_SCROLL;
+ tp_gesture_handle_event(tp, GESTURE_EVENT_SCROLL, time);
+ return;
}
if (tp->gesture.enabled) {
- return GESTURE_STATE_SWIPE;
+ tp_gesture_handle_event(tp, GESTURE_EVENT_SWIPE, time);
+ return;
}
}
/* If the touches are moving away from each other, this is a pinch */
- tp_gesture_init_pinch(tp);
- return GESTURE_STATE_PINCH;
+ tp_gesture_handle_event(tp, GESTURE_EVENT_PINCH, time);
}
static bool
return true;
}
-static enum tp_gesture_state
+static void
tp_gesture_handle_state_none(struct tp_dispatch *tp, uint64_t time)
{
struct tp_touch *first, *second;
unsigned int i;
ntouches = tp_gesture_get_active_touches(tp, touches, 4);
- if (ntouches < 2)
- return GESTURE_STATE_UNKNOWN;
+
+ if (ntouches == 0)
+ return;
+
+ if (ntouches == 1) {
+ tp_gesture_handle_event(tp,
+ GESTURE_EVENT_FINGER_DETECTED,
+ time);
+ return;
+ }
if (!tp->gesture.enabled) {
if (ntouches == 2)
- return GESTURE_STATE_SCROLL;
- return GESTURE_STATE_NONE;
+ tp_gesture_handle_event(tp, GESTURE_EVENT_SCROLL, time);
+
+ return;
}
first = touches[0];
}
if (first == second)
- return GESTURE_STATE_NONE;
+ return;
}
tp->gesture.touches[0] = first;
tp->gesture.touches[1] = second;
- return GESTURE_STATE_UNKNOWN;
+ tp_gesture_handle_event(tp, GESTURE_EVENT_FINGER_DETECTED, time);
}
-
-static enum tp_gesture_state
+static void
tp_gesture_handle_state_unknown(struct tp_dispatch *tp, uint64_t time)
{
- return tp_gesture_detect_motion_gestures(tp, time);
+ tp_gesture_detect_motion_gestures(tp, time);
}
-static enum tp_gesture_state
+static void
tp_gesture_handle_state_pointer_motion(struct tp_dispatch *tp, uint64_t time)
{
if (tp->queued & TOUCHPAD_EVENT_MOTION)
tp_gesture_post_pointer_motion(tp, time);
- return GESTURE_STATE_POINTER_MOTION;
}
-static enum tp_gesture_state
+static void
tp_gesture_handle_state_scroll(struct tp_dispatch *tp, uint64_t time)
{
struct device_float_coords raw;
struct normalized_coords delta;
if (tp->scroll.method != LIBINPUT_CONFIG_SCROLL_2FG)
- return GESTURE_STATE_SCROLL;
+ return;
/* We may confuse a pinch for a scroll initially,
* allow ourselves to correct our guess.
if (time < (tp->gesture.initial_time + DEFAULT_GESTURE_PINCH_TIMEOUT) &&
tp_gesture_is_pinch(tp)) {
tp_gesture_cancel(tp, time);
- tp_gesture_init_pinch(tp);
- return GESTURE_STATE_PINCH;
+ tp_gesture_handle_event(tp, GESTURE_EVENT_PINCH, time);
+ return;
}
raw = tp_get_average_touches_delta(tp);
delta = tp_filter_motion_unaccelerated(tp, &raw, time);
if (normalized_is_zero(delta))
- return GESTURE_STATE_SCROLL;
+ return;
tp_gesture_start(tp, time);
tp_gesture_apply_scroll_constraints(tp, &raw, &delta, time);
time,
LIBINPUT_POINTER_AXIS_SOURCE_FINGER,
&delta);
-
- return GESTURE_STATE_SCROLL;
}
-static enum tp_gesture_state
+static void
tp_gesture_handle_state_swipe(struct tp_dispatch *tp, uint64_t time)
{
struct device_float_coords raw;
tp->gesture.finger_count,
&delta, &unaccel);
}
-
- return GESTURE_STATE_SWIPE;
}
-static enum tp_gesture_state
+static void
tp_gesture_handle_state_pinch(struct tp_dispatch *tp, uint64_t time)
{
double angle, angle_delta, distance, scale;
if (normalized_is_zero(delta) && device_float_is_zero(fdelta) &&
scale == tp->gesture.prev_scale && angle_delta == 0.0)
- return GESTURE_STATE_PINCH;
+ return;
unaccel = tp_filter_motion_unaccelerated(tp, &fdelta, time);
tp_gesture_start(tp, time);
&delta, &unaccel, scale, angle_delta);
tp->gesture.prev_scale = scale;
-
- return GESTURE_STATE_PINCH;
}
static void
tp_gesture_post_gesture(struct tp_dispatch *tp, uint64_t time)
{
- enum tp_gesture_state oldstate = tp->gesture.state;
-
if (tp->gesture.state == GESTURE_STATE_NONE)
- tp->gesture.state =
- tp_gesture_handle_state_none(tp, time);
+ tp_gesture_handle_state_none(tp, time);
if (tp->gesture.state == GESTURE_STATE_UNKNOWN)
- tp->gesture.state =
- tp_gesture_handle_state_unknown(tp, time);
+ tp_gesture_handle_state_unknown(tp, time);
if (tp->gesture.state == GESTURE_STATE_POINTER_MOTION)
- tp->gesture.state =
- tp_gesture_handle_state_pointer_motion(tp, time);
+ tp_gesture_handle_state_pointer_motion(tp, time);
if (tp->gesture.state == GESTURE_STATE_SCROLL)
- tp->gesture.state =
- tp_gesture_handle_state_scroll(tp, time);
+ tp_gesture_handle_state_scroll(tp, time);
if (tp->gesture.state == GESTURE_STATE_SWIPE)
- tp->gesture.state =
- tp_gesture_handle_state_swipe(tp, time);
+ tp_gesture_handle_state_swipe(tp, time);
if (tp->gesture.state == GESTURE_STATE_PINCH)
- tp->gesture.state =
- tp_gesture_handle_state_pinch(tp, time);
-
- if (oldstate != tp->gesture.state)
- evdev_log_debug(tp->device,
- "gesture state: %s → %s\n",
- gesture_state_to_str(oldstate),
- gesture_state_to_str(tp->gesture.state));
+ tp_gesture_handle_state_pinch(tp, time);
}
static bool
tp->thumb.state == THUMB_STATE_FINGER)) {
if (tp->gesture.state != GESTURE_STATE_POINTER_MOTION) {
tp_gesture_cancel(tp, time);
- tp->gesture.state = GESTURE_STATE_POINTER_MOTION;
+ tp_gesture_handle_event(tp,
+ GESTURE_EVENT_POINTER_MOTION,
+ time);
}
tp->gesture.finger_count = 1;
tp->gesture.finger_count_pending = 0;
{
enum tp_gesture_state state = tp->gesture.state;
- tp->gesture.state = GESTURE_STATE_NONE;
-
- if (!tp->gesture.started)
+ if (!tp->gesture.started) {
+ tp_gesture_handle_event(tp, GESTURE_EVENT_RESET, time);
return;
+ }
switch (state) {
case GESTURE_STATE_NONE:
}
tp->gesture.started = false;
+ tp_gesture_handle_event(tp, GESTURE_EVENT_RESET, time);
}
void
} else if (!tp->gesture.started) {
tp->gesture.finger_count = active_touches;
tp->gesture.finger_count_pending = 0;
- /* If in UNKNOWN state, go back to NONE to
- * re-evaluate leftmost and rightmost touches
+ /* If in UNKNOWN or POINTER_MOTION state, go back to
+ * NONE to re-evaluate leftmost and rightmost touches
*/
- tp->gesture.state = GESTURE_STATE_NONE;
+ if (tp->gesture.state == GESTURE_STATE_UNKNOWN ||
+ tp->gesture.state == GESTURE_STATE_POINTER_MOTION) {
+ tp_gesture_handle_event(tp,
+ GESTURE_EVENT_RESET,
+ time);
+ }
/* Else debounce finger changes */
} else if (active_touches != tp->gesture.finger_count_pending) {
tp->gesture.finger_count_pending = active_touches;