Commit 8234814f authored by Peter Hutterer's avatar Peter Hutterer

gestures: add support for three-finger pinch gestures

https://bugs.freedesktop.org/show_bug.cgi?id=92834Signed-off-by: Peter Hutterer's avatarPeter Hutterer <peter.hutterer@who-t.net>
parent d9e1b260
......@@ -88,4 +88,20 @@ thus suggesting a window movement. libinput only has knowledge of the finger
coordinates (and even then only in device coordinates, not in screen
coordinates) and thus cannot differentiate the two.
@section gestures_softbuttons Gestures with enabled software buttons
If the touchpad device is a @ref touchpads_buttons_clickpads "Clickpad", it
is recommended that a caller switches to @ref clickfinger.
Usually fingers placed in a @ref software_buttons "software button area" is not
considered for gestures, resulting in some gestures to be interpreted as
pointer motion or two-finger scroll events.
@image html pinch-gestures-softbuttons.svg "Interference of software buttons and pinch gestures"
In the example above, the software button area is highlighted in red. The
user executes a three-finger pinch gesture, with the thumb remaining in the
software button area. libinput ignores fingers within the software button
areas, the movement of the remaining fingers is thus interpreted as a
two-finger scroll motion.
*/
This diff is collapsed.
......@@ -40,6 +40,7 @@ gesture_state_to_str(enum tp_gesture_state state)
CASE_RETURN_STRING(GESTURE_STATE_UNKNOWN);
CASE_RETURN_STRING(GESTURE_STATE_SCROLL);
CASE_RETURN_STRING(GESTURE_STATE_PINCH);
CASE_RETURN_STRING(GESTURE_STATE_SWIPE);
}
return NULL;
}
......@@ -94,34 +95,30 @@ tp_gesture_start(struct tp_dispatch *tp, uint64_t time)
if (tp->gesture.started)
return;
switch (tp->gesture.finger_count) {
case 2:
switch (tp->gesture.state) {
case GESTURE_STATE_NONE:
case GESTURE_STATE_UNKNOWN:
log_bug_libinput(libinput,
"%s in unknown gesture mode\n",
__func__);
break;
case GESTURE_STATE_SCROLL:
/* NOP */
break;
case GESTURE_STATE_PINCH:
gesture_notify_pinch(&tp->device->base, time,
LIBINPUT_EVENT_GESTURE_PINCH_BEGIN,
tp->gesture.finger_count,
&zero, &zero, 1.0, 0.0);
break;
}
switch (tp->gesture.state) {
case GESTURE_STATE_NONE:
case GESTURE_STATE_UNKNOWN:
log_bug_libinput(libinput,
"%s in unknown gesture mode\n",
__func__);
break;
case 3:
case 4:
case GESTURE_STATE_SCROLL:
/* NOP */
break;
case GESTURE_STATE_PINCH:
gesture_notify_pinch(&tp->device->base, time,
LIBINPUT_EVENT_GESTURE_PINCH_BEGIN,
tp->gesture.finger_count,
&zero, &zero, 1.0, 0.0);
break;
case GESTURE_STATE_SWIPE:
gesture_notify_swipe(&tp->device->base, time,
LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN,
tp->gesture.finger_count,
&zero, &zero);
break;
}
tp->gesture.started = true;
}
......@@ -247,19 +244,54 @@ tp_gesture_set_scroll_buildup(struct tp_dispatch *tp)
}
static enum tp_gesture_state
tp_gesture_twofinger_handle_state_none(struct tp_dispatch *tp, uint64_t time)
tp_gesture_handle_state_none(struct tp_dispatch *tp, uint64_t time)
{
struct tp_touch *first, *second;
struct tp_touch *touches[4];
unsigned int ntouches;
unsigned int i;
if (tp_gesture_get_active_touches(tp, tp->gesture.touches, 2) != 2)
ntouches = tp_gesture_get_active_touches(tp, touches, 4);
if (ntouches < 2)
return GESTURE_STATE_NONE;
first = tp->gesture.touches[0];
second = tp->gesture.touches[1];
first = touches[0];
second = touches[1];
/* For 3+ finger gestures we cheat. A human hand's finger
* arrangement means that for a 3 or 4 finger swipe gesture, the
* fingers are roughly arranged in a horizontal line.
* They will all move in the same direction, so we can simply look
* at the left and right-most ones only. If we have fake touches, we
* just take the left/right-most real touch position, since the fake
* touch has the same location as one of those.
*
* For a 3 or 4 finger pinch gesture, 2 or 3 fingers are roughly in
* a horizontal line, with the thumb below and left (right-handed
* users) or right (left-handed users). Again, the row of non-thumb
* fingers moves identically so we can look at the left and
* right-most only and then treat it like a two-finger
* gesture.
*/
if (ntouches > 2) {
second = touches[0];
for (i = 1; i < ntouches && i < tp->num_slots; i++) {
if (touches[i]->point.x < first->point.x)
first = touches[i];
else if (touches[i]->point.x > second->point.x)
second = touches[i];
}
if (first == second)
return GESTURE_STATE_NONE;
}
tp->gesture.initial_time = time;
first->gesture.initial = first->point;
second->gesture.initial = second->point;
tp->gesture.touches[0] = first;
tp->gesture.touches[1] = second;
return GESTURE_STATE_UNKNOWN;
}
......@@ -281,14 +313,16 @@ tp_gesture_same_directions(int dir1, int dir2)
}
static enum tp_gesture_state
tp_gesture_twofinger_handle_state_unknown(struct tp_dispatch *tp, uint64_t time)
tp_gesture_handle_state_unknown(struct tp_dispatch *tp, uint64_t time)
{
struct tp_touch *first = tp->gesture.touches[0],
*second = tp->gesture.touches[1];
int dir1, dir2;
/* if fingers stay unmoving for a while, assume (slow) scroll */
if (time > (tp->gesture.initial_time + DEFAULT_GESTURE_2FG_SCROLL_TIMEOUT)) {
/* for two-finger gestures, if the fingers stay unmoving for a
* while, assume (slow) scroll */
if (tp->gesture.finger_count == 2 &&
time > (tp->gesture.initial_time + DEFAULT_GESTURE_2FG_SCROLL_TIMEOUT)) {
tp_gesture_set_scroll_buildup(tp);
return GESTURE_STATE_SCROLL;
}
......@@ -299,10 +333,15 @@ tp_gesture_twofinger_handle_state_unknown(struct tp_dispatch *tp, uint64_t time)
if (dir1 == UNDEFINED_DIRECTION || dir2 == UNDEFINED_DIRECTION)
return GESTURE_STATE_UNKNOWN;
/* If both touches are moving in the same direction assume scroll */
/* If both touches are moving in the same direction assume
* scroll or swipe */
if (tp_gesture_same_directions(dir1, dir2)) {
tp_gesture_set_scroll_buildup(tp);
return GESTURE_STATE_SCROLL;
if (tp->gesture.finger_count == 2) {
tp_gesture_set_scroll_buildup(tp);
return GESTURE_STATE_SCROLL;
} else if (tp->gesture.enabled) {
return GESTURE_STATE_SWIPE;
}
} else if (tp->gesture.enabled) {
tp_gesture_get_pinch_info(tp,
&tp->gesture.initial_distance,
......@@ -316,7 +355,7 @@ tp_gesture_twofinger_handle_state_unknown(struct tp_dispatch *tp, uint64_t time)
}
static enum tp_gesture_state
tp_gesture_twofinger_handle_state_scroll(struct tp_dispatch *tp, uint64_t time)
tp_gesture_handle_state_scroll(struct tp_dispatch *tp, uint64_t time)
{
struct normalized_coords delta;
......@@ -350,7 +389,26 @@ tp_gesture_twofinger_handle_state_scroll(struct tp_dispatch *tp, uint64_t time)
}
static enum tp_gesture_state
tp_gesture_twofinger_handle_state_pinch(struct tp_dispatch *tp, uint64_t time)
tp_gesture_handle_state_swipe(struct tp_dispatch *tp, uint64_t time)
{
struct normalized_coords delta, unaccel;
unaccel = tp_get_average_touches_delta(tp);
delta = tp_filter_motion(tp, &unaccel, time);
if (!normalized_is_zero(delta) || !normalized_is_zero(unaccel)) {
tp_gesture_start(tp, time);
gesture_notify_swipe(&tp->device->base, time,
LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE,
tp->gesture.finger_count,
&delta, &unaccel);
}
return GESTURE_STATE_SWIPE;
}
static enum tp_gesture_state
tp_gesture_handle_state_pinch(struct tp_dispatch *tp, uint64_t time)
{
double angle, angle_delta, distance, scale;
struct device_float_coords center, fdelta;
......@@ -388,25 +446,29 @@ tp_gesture_twofinger_handle_state_pinch(struct tp_dispatch *tp, uint64_t time)
}
static void
tp_gesture_post_twofinger(struct tp_dispatch *tp, uint64_t time)
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_twofinger_handle_state_none(tp, time);
tp_gesture_handle_state_none(tp, time);
if (tp->gesture.state == GESTURE_STATE_UNKNOWN)
tp->gesture.state =
tp_gesture_twofinger_handle_state_unknown(tp, time);
tp_gesture_handle_state_unknown(tp, time);
if (tp->gesture.state == GESTURE_STATE_SCROLL)
tp->gesture.state =
tp_gesture_twofinger_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);
if (tp->gesture.state == GESTURE_STATE_PINCH)
tp->gesture.state =
tp_gesture_twofinger_handle_state_pinch(tp, time);
tp_gesture_handle_state_pinch(tp, time);
log_debug(tp_libinput_context(tp),
"gesture state: %s → %s\n",
......@@ -414,23 +476,6 @@ tp_gesture_post_twofinger(struct tp_dispatch *tp, uint64_t time)
gesture_state_to_str(tp->gesture.state));
}
static void
tp_gesture_post_swipe(struct tp_dispatch *tp, uint64_t time)
{
struct normalized_coords delta, unaccel;
unaccel = tp_get_average_touches_delta(tp);
delta = tp_filter_motion(tp, &unaccel, time);
if (!normalized_is_zero(delta) || !normalized_is_zero(unaccel)) {
tp_gesture_start(tp, time);
gesture_notify_swipe(&tp->device->base, time,
LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE,
tp->gesture.finger_count,
&delta, &unaccel);
}
}
void
tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time)
{
......@@ -453,11 +498,9 @@ tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time)
tp_gesture_post_pointer_motion(tp, time);
break;
case 2:
tp_gesture_post_twofinger(tp, time);
break;
case 3:
case 4:
tp_gesture_post_swipe(tp, time);
tp_gesture_post_gesture(tp, time);
break;
}
}
......@@ -484,32 +527,30 @@ tp_gesture_end(struct tp_dispatch *tp, uint64_t time, bool cancelled)
if (!tp->gesture.started)
return;
switch (tp->gesture.finger_count) {
case 2:
switch (state) {
case GESTURE_STATE_NONE:
case GESTURE_STATE_UNKNOWN:
log_bug_libinput(libinput,
"%s in unknown gesture mode\n",
__func__);
break;
case GESTURE_STATE_SCROLL:
tp_gesture_stop_twofinger_scroll(tp, time);
break;
case GESTURE_STATE_PINCH:
gesture_notify_pinch_end(&tp->device->base, time,
tp->gesture.finger_count,
tp->gesture.prev_scale,
cancelled);
break;
}
switch (state) {
case GESTURE_STATE_NONE:
case GESTURE_STATE_UNKNOWN:
log_bug_libinput(libinput,
"%s in unknown gesture mode\n",
__func__);
break;
case 3:
case 4:
gesture_notify_swipe_end(&tp->device->base, time,
tp->gesture.finger_count, cancelled);
case GESTURE_STATE_SCROLL:
tp_gesture_stop_twofinger_scroll(tp, time);
break;
case GESTURE_STATE_PINCH:
gesture_notify_pinch_end(&tp->device->base, time,
tp->gesture.finger_count,
tp->gesture.prev_scale,
cancelled);
break;
case GESTURE_STATE_SWIPE:
gesture_notify_swipe_end(&tp->device->base,
time,
tp->gesture.finger_count,
cancelled);
break;
}
tp->gesture.started = false;
}
......
......@@ -135,6 +135,7 @@ enum tp_gesture_state {
GESTURE_STATE_UNKNOWN,
GESTURE_STATE_SCROLL,
GESTURE_STATE_PINCH,
GESTURE_STATE_SWIPE,
};
enum tp_thumb_state {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment