touchpad: if hold-and-tap is on, treat half-released taps as lesser taps

The expectation is that the user means the remaining fingers of the tap
to be holding fingers which just happened to be put down at roughly
the same time as the released tapping fingers.

This necessitates handling a tap with four or more fingers, because only
three of them might get lifted, making it actually a three-finger tap
and at least one more holding finger.

Though not strictly necessary, these states can also be reached
if hold-and-tap is off: it may catch a many-finger-tap to end drag-lock,
or a three-finger-tap joined by a palm, as demonstrated in the
previously existing test case touchpad_tap_palm_on_touch_4.

Signed-off-by: satrmb <10471-satrmb@users.noreply.gitlab.freedesktop.org>
This commit is contained in:
satrmb 2020-08-07 17:52:20 +02:00
parent 355a3e2f64
commit 696d3c71f3
4 changed files with 468 additions and 33 deletions

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 230 KiB

After

Width:  |  Height:  |  Size: 334 KiB

View file

@ -72,6 +72,11 @@ tap_state_to_str(enum tp_tap_state state)
CASE_RETURN_STRING(TAP_STATE_TOUCH_3_HOLD);
CASE_RETURN_STRING(TAP_STATE_TOUCH_3_RELEASE);
CASE_RETURN_STRING(TAP_STATE_TOUCH_3_RELEASE_2);
CASE_RETURN_STRING(TAP_STATE_TOUCH_4_PLUS);
CASE_RETURN_STRING(TAP_STATE_TOUCH_4_PLUS_HOLD);
CASE_RETURN_STRING(TAP_STATE_TOUCH_4_PLUS_RELEASE);
CASE_RETURN_STRING(TAP_STATE_TOUCH_4_PLUS_RELEASE_2);
CASE_RETURN_STRING(TAP_STATE_TOUCH_4_PLUS_RELEASE_3);
CASE_RETURN_STRING(TAP_STATE_BUTTON);
CASE_RETURN_STRING(TAP_STATE_DEAD);
}
@ -240,8 +245,10 @@ tp_drag_idle_handle_event(struct tp_dispatch *tp,
1,
LIBINPUT_BUTTON_STATE_PRESSED);
/* taps may end with fingers remaining down,
* those should not start a drag */
if (tp->tap.drag_enabled && tp->tap.nfingers_down == 0) {
* those should not start a drag;
* also, the touch in the process of being released
* is still counted for nfingers_down */
if (tp->tap.drag_enabled && tp->tap.nfingers_down == 1) {
tp->tap.drag_state = DRAG_STATE_1FGTAP_TAPPED;
tp_tap_set_drag_timer(tp, time, 1);
} else {
@ -256,7 +263,7 @@ tp_drag_idle_handle_event(struct tp_dispatch *tp,
tp->tap.saved_press_time,
2,
LIBINPUT_BUTTON_STATE_PRESSED);
if (tp->tap.drag_enabled && tp->tap.nfingers_down == 0) {
if (tp->tap.drag_enabled && tp->tap.nfingers_down == 1) {
tp->tap.drag_state = DRAG_STATE_2FGTAP_TAPPED;
tp_tap_set_drag_timer(tp, time, 2);
} else {
@ -271,7 +278,7 @@ tp_drag_idle_handle_event(struct tp_dispatch *tp,
tp->tap.saved_press_time,
3,
LIBINPUT_BUTTON_STATE_PRESSED);
if (tp->tap.drag_enabled && tp->tap.nfingers_down == 0) {
if (tp->tap.drag_enabled && tp->tap.nfingers_down == 1) {
tp->tap.drag_state = DRAG_STATE_3FGTAP_TAPPED;
tp_tap_set_drag_timer(tp, time, 3);
} else {
@ -434,7 +441,7 @@ tp_drag_dragging_handle_event(struct tp_dispatch *tp,
case TAP_EVENT_TOUCH:
break;
case TAP_EVENT_RELEASE:
if (tp->tap.nfingers_down == 0) {
if (tp->tap.nfingers_down == 1) {
if (tp->tap.drag_lock != LIBINPUT_CONFIG_DRAG_LOCK_DISABLED) {
enum tp_drag_state dest[3] = {
DRAG_STATE_1FGTAP_DRAGLOCK_WAIT,
@ -471,7 +478,7 @@ tp_drag_dragging_handle_event(struct tp_dispatch *tp,
case TAP_EVENT_THUMB:
break;
case TAP_EVENT_PALM:
if (tp->tap.nfingers_down == 0) {
if (tp->tap.nfingers_down == 1) {
tp_tap_notify(tp,
time,
nfingers_tapped,
@ -814,7 +821,7 @@ tp_tap_touch_handle_event(struct tp_dispatch *tp,
if (t->tap.state == TAP_TOUCH_STATE_TOUCH) {
tp->tap.saved_release_time = time;
tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time);
if (tp->tap.nfingers_down == 0)
if (tp->tap.nfingers_down == 1)
tp->tap.state = TAP_STATE_IDLE;
else
tp->tap.state = TAP_STATE_DEAD;
@ -847,7 +854,7 @@ tp_tap_touch_handle_event(struct tp_dispatch *tp,
break;
case TAP_EVENT_PALM:
if (t->tap.state == TAP_TOUCH_STATE_TOUCH) {
if (tp->tap.nfingers_down == 0)
if (tp->tap.nfingers_down == 1)
tp->tap.state = TAP_STATE_IDLE;
else
tp->tap.state = TAP_STATE_DEAD;
@ -1001,6 +1008,7 @@ tp_tap_touch2_release_handle_event(struct tp_dispatch *tp,
switch (event) {
case TAP_EVENT_TOUCH:
if (tp->tap.hold_tap_enabled) {
tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time);
tp_tap_kill_all_touches(tp, t);
tp->tap.state = TAP_STATE_TOUCH;
} else
@ -1011,23 +1019,29 @@ tp_tap_touch2_release_handle_event(struct tp_dispatch *tp,
case TAP_EVENT_RELEASE:
if (t->tap.state == TAP_TOUCH_STATE_TOUCH) {
tp_drag_handle_event(tp, t, TAP_EVENT_2FGTAP, time);
if (tp->tap.nfingers_down == 0)
if (tp->tap.nfingers_down == 1)
tp->tap.state = TAP_STATE_IDLE;
else
tp->tap.state = TAP_STATE_DEAD;
}
break;
case TAP_EVENT_MOTION:
if (tp->tap.hold_tap_enabled)
tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time);
tp->tap.state = TAP_STATE_DEAD;
break;
case TAP_EVENT_TIMEOUT:
if (!tp->tap.hold_tap_enabled &&
tp->tap.drag_state == DRAG_STATE_IDLE)
if (tp->tap.hold_tap_enabled) {
tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time);
tp->tap.state = TAP_STATE_DEAD;
} else if (tp->tap.drag_state == DRAG_STATE_IDLE)
tp->tap.state = TAP_STATE_HOLD;
else
tp->tap.state = TAP_STATE_DEAD;
break;
case TAP_EVENT_BUTTON:
if (tp->tap.hold_tap_enabled)
tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time);
tp->tap.state = TAP_STATE_BUTTON;
break;
case TAP_EVENT_BUTTON_UP:
@ -1046,7 +1060,7 @@ tp_tap_touch2_release_handle_event(struct tp_dispatch *tp,
* an issue.
*/
tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time);
if (tp->tap.nfingers_down == 0)
if (tp->tap.nfingers_down == 1)
tp->tap.state = TAP_STATE_IDLE;
else
tp->tap.state = TAP_STATE_DEAD;
@ -1068,10 +1082,9 @@ tp_tap_touch3_handle_event(struct tp_dispatch *tp,
switch (event) {
case TAP_EVENT_TOUCH:
tp->tap.state = TAP_STATE_DEAD;
/* this cannot be a tap anymore, get the dragging state machine
* out of DRAGGING_OR_DOUBLETAP or DRAGLOCK_CONTINUE */
tp_drag_handle_event(tp, t, TAP_EVENT_MOTION, time);
tp->tap.state = TAP_STATE_TOUCH_4_PLUS;
tp->tap.saved_press_time = time;
tp_tap_set_timer(tp, time);
break;
case TAP_EVENT_RELEASE:
if (t->tap.state == TAP_TOUCH_STATE_TOUCH) {
@ -1119,7 +1132,11 @@ tp_tap_touch3_hold_handle_event(struct tp_dispatch *tp,
switch (event) {
case TAP_EVENT_TOUCH:
tp->tap.state = TAP_STATE_DEAD;
/* Don't even try to revive the previous three touches
* for a tap like in the other hold states, because any tap
* with more than three fingers can only end drag-lock.
* If we got here the timeout for that has already elapsed. */
tp->tap.state = TAP_STATE_TOUCH_4_PLUS_HOLD;
break;
case TAP_EVENT_RELEASE:
tp->tap.state = TAP_STATE_TOUCH_2_HOLD;
@ -1157,6 +1174,7 @@ tp_tap_touch3_release_handle_event(struct tp_dispatch *tp,
switch (event) {
case TAP_EVENT_TOUCH:
if (tp->tap.hold_tap_enabled) {
tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time);
tp_tap_kill_all_touches(tp, t);
tp->tap.state = TAP_STATE_TOUCH;
} else {
@ -1173,14 +1191,17 @@ tp_tap_touch3_release_handle_event(struct tp_dispatch *tp,
}
break;
case TAP_EVENT_MOTION:
if (!tp->tap.hold_tap_enabled)
if (tp->tap.hold_tap_enabled)
tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time);
else
tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time);
tp->tap.state = TAP_STATE_DEAD;
break;
case TAP_EVENT_TIMEOUT:
if (tp->tap.hold_tap_enabled)
if (tp->tap.hold_tap_enabled) {
tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time);
tp->tap.state = TAP_STATE_DEAD;
else {
} else {
tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time);
if (tp->tap.drag_state == DRAG_STATE_IDLE)
tp->tap.state = TAP_STATE_TOUCH_2_HOLD;
@ -1189,7 +1210,9 @@ tp_tap_touch3_release_handle_event(struct tp_dispatch *tp,
}
break;
case TAP_EVENT_BUTTON:
if (!tp->tap.hold_tap_enabled)
if (tp->tap.hold_tap_enabled)
tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time);
else
tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time);
tp->tap.state = TAP_STATE_BUTTON;
break;
@ -1219,6 +1242,7 @@ tp_tap_touch3_release2_handle_event(struct tp_dispatch *tp,
switch (event) {
case TAP_EVENT_TOUCH:
if (tp->tap.hold_tap_enabled) {
tp_drag_handle_event(tp, t, TAP_EVENT_2FGTAP, time);
tp_tap_kill_all_touches(tp, t);
tp->tap.state = TAP_STATE_TOUCH;
} else {
@ -1231,21 +1255,24 @@ tp_tap_touch3_release2_handle_event(struct tp_dispatch *tp,
case TAP_EVENT_RELEASE:
if (t->tap.state == TAP_TOUCH_STATE_TOUCH) {
tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time);
if (tp->tap.nfingers_down == 0)
if (tp->tap.nfingers_down == 1)
tp->tap.state = TAP_STATE_IDLE;
else
tp->tap.state = TAP_STATE_DEAD;
}
break;
case TAP_EVENT_MOTION:
if (!tp->tap.hold_tap_enabled)
if (tp->tap.hold_tap_enabled)
tp_drag_handle_event(tp, t, TAP_EVENT_2FGTAP, time);
else
tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time);
tp->tap.state = TAP_STATE_DEAD;
break;
case TAP_EVENT_TIMEOUT:
if (tp->tap.hold_tap_enabled)
if (tp->tap.hold_tap_enabled) {
tp_drag_handle_event(tp, t, TAP_EVENT_2FGTAP, time);
tp->tap.state = TAP_STATE_DEAD;
else {
} else {
tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time);
if (tp->tap.drag_state == DRAG_STATE_IDLE)
tp->tap.state = TAP_STATE_HOLD;
@ -1254,7 +1281,9 @@ tp_tap_touch3_release2_handle_event(struct tp_dispatch *tp,
}
break;
case TAP_EVENT_BUTTON:
if (!tp->tap.hold_tap_enabled)
if (tp->tap.hold_tap_enabled)
tp_drag_handle_event(tp, t, TAP_EVENT_2FGTAP, time);
else
tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time);
tp->tap.state = TAP_STATE_BUTTON;
break;
@ -1266,7 +1295,7 @@ tp_tap_touch3_release2_handle_event(struct tp_dispatch *tp,
case TAP_EVENT_PALM:
if (t->tap.state == TAP_TOUCH_STATE_TOUCH) {
tp_drag_handle_event(tp, t, TAP_EVENT_2FGTAP, time);
if (tp->tap.nfingers_down == 0)
if (tp->tap.nfingers_down == 1)
tp->tap.state = TAP_STATE_IDLE;
else
tp->tap.state = TAP_STATE_DEAD;
@ -1280,6 +1309,384 @@ tp_tap_touch3_release2_handle_event(struct tp_dispatch *tp,
}
}
static int
tp_count_alive_touches(struct tp_dispatch *tp)
{
/* In the TOUCH_4_PLUS group of states we lose track of the exact
* number of fingers down which are still alive for tapping.
* There are several relatively unlikely events that may bring us back
* into the states corresponding to a smaller number of fingers,
* most prominently touches turning into palms.
* In these cases we need to figure out how many remaining
* alive touches there are. */
struct tp_touch *t;
int nfingers = 0;
/* When hold-and-tap is disabled, either all or no fingers are tapping,
* and the latter case occurs in the DEAD and BUTTON states only
* which don't have the need to ask for alive touches,
* so we have the count stored as the number of fingers. */
if (!tp->tap.hold_tap_enabled)
return tp->tap.nfingers_down;
/* If the setting is enabled however, tapping can happen with only some
* of the fingers down, i.e. there may be to-be-ignored holding touches,
* so we need to actually count the tapping ones. */
tp_for_each_touch(tp, t) {
if (t->tap.state == TAP_TOUCH_STATE_TOUCH)
nfingers++;
}
return nfingers;
}
static void
tp_tap_touch4plus_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum tap_event event, uint64_t time)
{
switch (event) {
case TAP_EVENT_TOUCH:
tp->tap.saved_press_time = time;
tp_tap_set_timer(tp, time);
break;
case TAP_EVENT_RELEASE:
if (t->tap.state == TAP_TOUCH_STATE_TOUCH) {
tp->tap.state = TAP_STATE_TOUCH_4_PLUS_RELEASE;
tp->tap.saved_release_time = time;
tp_tap_set_timer(tp, time);
}
break;
case TAP_EVENT_MOTION:
tp->tap.state = TAP_STATE_DEAD;
break;
case TAP_EVENT_TIMEOUT:
if (!tp->tap.hold_tap_enabled &&
tp->tap.drag_state == DRAG_STATE_IDLE)
tp->tap.state = TAP_STATE_TOUCH_4_PLUS_HOLD;
else
tp->tap.state = TAP_STATE_DEAD;
break;
case TAP_EVENT_BUTTON:
tp->tap.state = TAP_STATE_BUTTON;
break;
case TAP_EVENT_BUTTON_UP:
log_tap_bug(tp, t, event);
break;
case TAP_EVENT_THUMB:
break;
case TAP_EVENT_PALM:
if (t->tap.state == TAP_TOUCH_STATE_TOUCH) {
int remaining = tp_count_alive_touches(tp) - 1;
assert(remaining >= 3);
if (remaining == 3)
tp->tap.state = TAP_STATE_TOUCH_3;
}
break;
case TAP_EVENT_1FGTAP:
case TAP_EVENT_2FGTAP:
case TAP_EVENT_3FGTAP:
log_tap_bug(tp, t, event);
break;
}
}
static void
tp_tap_touch4plus_hold_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum tap_event event, uint64_t time)
{
switch (event) {
case TAP_EVENT_TOUCH:
break;
case TAP_EVENT_RELEASE: {
int remaining = tp_count_alive_touches(tp) - 1;
assert(remaining >= 3);
if (remaining == 3)
tp->tap.state = TAP_STATE_TOUCH_3_HOLD;
break;
}
case TAP_EVENT_MOTION:
tp->tap.state = TAP_STATE_DEAD;
break;
case TAP_EVENT_TIMEOUT:
break;
case TAP_EVENT_BUTTON:
tp->tap.state = TAP_STATE_BUTTON;
break;
case TAP_EVENT_BUTTON_UP:
log_tap_bug(tp, t, event);
break;
case TAP_EVENT_THUMB:
break;
case TAP_EVENT_PALM: {
int remaining = tp_count_alive_touches(tp) - 1;
assert(remaining >= 3);
if (remaining == 3)
tp->tap.state = TAP_STATE_TOUCH_3_HOLD;
break;
}
case TAP_EVENT_1FGTAP:
case TAP_EVENT_2FGTAP:
case TAP_EVENT_3FGTAP:
log_tap_bug(tp, t, event);
break;
}
}
static void
tp_tap_touch4plus_release_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum tap_event event, uint64_t time)
{
switch (event) {
case TAP_EVENT_TOUCH:
if (tp->tap.hold_tap_enabled) {
tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time);
tp_tap_kill_all_touches(tp, t);
tp->tap.state = TAP_STATE_TOUCH;
} else
tp->tap.state = TAP_STATE_TOUCH_4_PLUS;
tp->tap.saved_press_time = time;
tp_tap_set_timer(tp, time);
break;
case TAP_EVENT_RELEASE:
if (t->tap.state == TAP_TOUCH_STATE_TOUCH) {
tp->tap.state = TAP_STATE_TOUCH_4_PLUS_RELEASE_2;
tp_tap_set_timer(tp, time);
}
break;
case TAP_EVENT_MOTION:
if (tp->tap.hold_tap_enabled)
tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time);
tp->tap.state = TAP_STATE_DEAD;
break;
case TAP_EVENT_TIMEOUT:
if (tp->tap.hold_tap_enabled) {
tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time);
tp->tap.state = TAP_STATE_DEAD;
} else {
if (tp->tap.drag_state == DRAG_STATE_IDLE) {
int remaining = tp_count_alive_touches(tp);
assert(remaining >= 3);
if (remaining == 3)
tp->tap.state = TAP_STATE_TOUCH_3_HOLD;
else
tp->tap.state = TAP_STATE_TOUCH_4_PLUS_HOLD;
} else
tp->tap.state = TAP_STATE_DEAD;
}
break;
case TAP_EVENT_BUTTON:
if (tp->tap.hold_tap_enabled)
tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time);
tp->tap.state = TAP_STATE_BUTTON;
break;
case TAP_EVENT_BUTTON_UP:
log_tap_bug(tp, t, event);
break;
case TAP_EVENT_THUMB:
break;
case TAP_EVENT_PALM:
if (t->tap.state == TAP_TOUCH_STATE_TOUCH) {
int remaining = tp_count_alive_touches(tp) - 1;
assert(remaining >= 2);
if (remaining == 2)
tp->tap.state = TAP_STATE_TOUCH_3_RELEASE;
}
break;
case TAP_EVENT_1FGTAP:
case TAP_EVENT_2FGTAP:
case TAP_EVENT_3FGTAP:
log_tap_bug(tp, t, event);
break;
}
}
static void
tp_tap_touch4plus_release2_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum tap_event event, uint64_t time)
{
switch (event) {
case TAP_EVENT_TOUCH:
if (tp->tap.hold_tap_enabled) {
tp_drag_handle_event(tp, t, TAP_EVENT_2FGTAP, time);
tp_tap_kill_all_touches(tp, t);
tp->tap.state = TAP_STATE_TOUCH;
} else {
int remaining = tp_count_alive_touches(tp);
assert(remaining >= 3);
if (remaining == 3)
tp->tap.state = TAP_STATE_TOUCH_3;
else
tp->tap.state = TAP_STATE_TOUCH_4_PLUS;
}
tp->tap.saved_press_time = time;
tp_tap_set_timer(tp, time);
break;
case TAP_EVENT_RELEASE:
if (t->tap.state == TAP_TOUCH_STATE_TOUCH) {
tp->tap.state = TAP_STATE_TOUCH_4_PLUS_RELEASE_3;
tp_tap_set_timer(tp, time);
}
break;
case TAP_EVENT_MOTION:
if (tp->tap.hold_tap_enabled)
tp_drag_handle_event(tp, t, TAP_EVENT_2FGTAP, time);
tp->tap.state = TAP_STATE_DEAD;
break;
case TAP_EVENT_TIMEOUT:
if (tp->tap.hold_tap_enabled) {
tp_drag_handle_event(tp, t, TAP_EVENT_2FGTAP, time);
tp->tap.state = TAP_STATE_DEAD;
} else {
if (tp->tap.drag_state == DRAG_STATE_IDLE) {
int remaining = min(tp_count_alive_touches(tp),
4);
enum tp_tap_state dest[3] = {
TAP_STATE_TOUCH_2_HOLD,
TAP_STATE_TOUCH_3_HOLD,
TAP_STATE_TOUCH_4_PLUS_HOLD,
};
assert(remaining >= 2);
tp->tap.state = dest[remaining - 2];
} else
tp->tap.state = TAP_STATE_DEAD;
}
break;
case TAP_EVENT_BUTTON:
if (tp->tap.hold_tap_enabled)
tp_drag_handle_event(tp, t, TAP_EVENT_2FGTAP, time);
tp->tap.state = TAP_STATE_BUTTON;
break;
case TAP_EVENT_BUTTON_UP:
log_tap_bug(tp, t, event);
break;
case TAP_EVENT_THUMB:
break;
case TAP_EVENT_PALM:
if (t->tap.state == TAP_TOUCH_STATE_TOUCH) {
int remaining = tp_count_alive_touches(tp) - 1;
assert(remaining >= 1);
if (remaining == 1)
tp->tap.state = TAP_STATE_TOUCH_3_RELEASE_2;
}
break;
case TAP_EVENT_1FGTAP:
case TAP_EVENT_2FGTAP:
case TAP_EVENT_3FGTAP:
log_tap_bug(tp, t, event);
break;
}
}
static void
tp_tap_touch4plus_release3_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum tap_event event, uint64_t time)
{
switch (event) {
case TAP_EVENT_TOUCH:
if (tp->tap.hold_tap_enabled) {
tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time);
tp_tap_kill_all_touches(tp, t);
tp->tap.state = TAP_STATE_TOUCH;
} else {
int remaining = min(tp_count_alive_touches(tp),
4);
enum tp_tap_state dest[3] = {
TAP_STATE_TOUCH_2,
TAP_STATE_TOUCH_3,
TAP_STATE_TOUCH_4_PLUS,
};
assert(remaining >= 2);
tp->tap.state = dest[remaining - 2];
}
tp->tap.saved_press_time = time;
tp_tap_set_timer(tp, time);
break;
case TAP_EVENT_RELEASE:
if (t->tap.state == TAP_TOUCH_STATE_TOUCH) {
/* four-plus-finger taps are to be ignored,
* except for the purpose of stopping drag-lock.
* We also don't care about remaining fingers
* of this tap anymore */
if (tp->tap.nfingers_down == 1)
tp->tap.state = TAP_STATE_IDLE;
else
tp->tap.state = TAP_STATE_DEAD;
if (tp->tap.drag_state == DRAG_STATE_1FGTAP_DRAGLOCK_CONTINUE ||
tp->tap.drag_state == DRAG_STATE_2FGTAP_DRAGLOCK_CONTINUE ||
tp->tap.drag_state == DRAG_STATE_3FGTAP_DRAGLOCK_CONTINUE) {
/* all taps have the same result here,
* a one-finger tap does the trick without
* introducing another event just for this */
tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP,
time);
}
}
break;
case TAP_EVENT_MOTION:
if (tp->tap.hold_tap_enabled)
tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time);
tp->tap.state = TAP_STATE_DEAD;
break;
case TAP_EVENT_TIMEOUT:
if (tp->tap.hold_tap_enabled) {
tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time);
tp->tap.state = TAP_STATE_DEAD;
} else {
if (tp->tap.drag_state == DRAG_STATE_IDLE) {
int remaining = min(tp_count_alive_touches(tp),
4);
enum tp_tap_state dest[4] = {
TAP_STATE_HOLD,
TAP_STATE_TOUCH_2_HOLD,
TAP_STATE_TOUCH_3_HOLD,
TAP_STATE_TOUCH_4_PLUS_HOLD,
};
assert(remaining >= 1);
tp->tap.state = dest[remaining - 1];
} else
tp->tap.state = TAP_STATE_DEAD;
}
break;
case TAP_EVENT_BUTTON:
if (tp->tap.hold_tap_enabled)
tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time);
tp->tap.state = TAP_STATE_BUTTON;
break;
case TAP_EVENT_BUTTON_UP:
log_tap_bug(tp, t, event);
break;
case TAP_EVENT_THUMB:
break;
case TAP_EVENT_PALM:
if (t->tap.state == TAP_TOUCH_STATE_TOUCH) {
int remaining = tp_count_alive_touches(tp) - 1;
if (remaining == 0) {
tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP,
time);
if (tp->tap.nfingers_down == 1)
tp->tap.state = TAP_STATE_IDLE;
else
tp->tap.state = TAP_STATE_DEAD;
}
}
break;
case TAP_EVENT_1FGTAP:
case TAP_EVENT_2FGTAP:
case TAP_EVENT_3FGTAP:
log_tap_bug(tp, t, event);
break;
}
}
static void
tp_tap_button_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
@ -1329,7 +1736,7 @@ tp_tap_dead_handle_event(struct tp_dispatch *tp,
}
break;
case TAP_EVENT_RELEASE:
if (tp->tap.nfingers_down == 0)
if (tp->tap.nfingers_down == 1)
tp->tap.state = TAP_STATE_IDLE;
break;
case TAP_EVENT_MOTION:
@ -1344,7 +1751,7 @@ tp_tap_dead_handle_event(struct tp_dispatch *tp,
case TAP_EVENT_THUMB:
break;
case TAP_EVENT_PALM:
if (tp->tap.nfingers_down == 0)
if (tp->tap.nfingers_down == 1)
tp->tap.state = TAP_STATE_IDLE;
break;
case TAP_EVENT_1FGTAP:
@ -1396,6 +1803,21 @@ tp_tap_handle_event(struct tp_dispatch *tp,
case TAP_STATE_TOUCH_3_RELEASE_2:
tp_tap_touch3_release2_handle_event(tp, t, event, time);
break;
case TAP_STATE_TOUCH_4_PLUS:
tp_tap_touch4plus_handle_event(tp, t, event, time);
break;
case TAP_STATE_TOUCH_4_PLUS_HOLD:
tp_tap_touch4plus_hold_handle_event(tp, t, event, time);
break;
case TAP_STATE_TOUCH_4_PLUS_RELEASE:
tp_tap_touch4plus_release_handle_event(tp, t, event, time);
break;
case TAP_STATE_TOUCH_4_PLUS_RELEASE_2:
tp_tap_touch4plus_release2_handle_event(tp, t, event, time);
break;
case TAP_STATE_TOUCH_4_PLUS_RELEASE_3:
tp_tap_touch4plus_release3_handle_event(tp, t, event, time);
break;
case TAP_STATE_BUTTON:
tp_tap_button_handle_event(tp, t, event, time);
break;
@ -1511,9 +1933,9 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time)
assert(!t->tap.is_palm);
t->tap.is_palm = true;
if (t->state != TOUCH_BEGIN) {
assert(tp->tap.nfingers_down > 0);
tp->tap.nfingers_down--;
assert(tp->tap.nfingers_down >= 1);
tp_tap_handle_event(tp, t, TAP_EVENT_PALM, time);
tp->tap.nfingers_down--;
}
t->tap.state = TAP_TOUCH_STATE_DEAD;
} else if (t->state == TOUCH_BEGIN) {
@ -1532,8 +1954,8 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time)
} else if (t->state == TOUCH_END) {
if (t->was_down) {
assert(tp->tap.nfingers_down >= 1);
tp->tap.nfingers_down--;
tp_tap_handle_event(tp, t, TAP_EVENT_RELEASE, time);
tp->tap.nfingers_down--;
}
t->tap.state = TAP_TOUCH_STATE_IDLE;
} else if (tp->tap.state != TAP_STATE_IDLE &&

View file

@ -113,6 +113,11 @@ enum tp_tap_state {
TAP_STATE_TOUCH_3_HOLD,
TAP_STATE_TOUCH_3_RELEASE,
TAP_STATE_TOUCH_3_RELEASE_2,
TAP_STATE_TOUCH_4_PLUS,
TAP_STATE_TOUCH_4_PLUS_HOLD,
TAP_STATE_TOUCH_4_PLUS_RELEASE,
TAP_STATE_TOUCH_4_PLUS_RELEASE_2,
TAP_STATE_TOUCH_4_PLUS_RELEASE_3,
TAP_STATE_BUTTON, /**< clickpad button pressed */
TAP_STATE_DEAD, /**< clickpad button released, or finger moved */
};

View file

@ -5146,6 +5146,14 @@ START_TEST(touchpad_tap_palm_on_touch_4)
litest_touch_up(dev, (this + 2) % 4);
litest_touch_up(dev, (this + 3) % 4);
litest_assert_button_event(li,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_PRESSED);
litest_timeout_tap();
litest_assert_button_event(li,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_RELEASED);
litest_assert_empty_queue(li);
}
END_TEST