mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2026-05-08 12:28:10 +02:00
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:
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 |
|
|
@ -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 &&
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue