---
src/Makefile.am | 1 +
src/evdev-mt-touchpad-buttons.c | 1 -
src/evdev-mt-touchpad-edge-scroll.c | 1 -
src/evdev-mt-touchpad-gestures.c | 146 ++++++++++++++++++++++++++++++++++++
src/evdev-mt-touchpad.c | 129 ++++++-------------------------
src/evdev-mt-touchpad.h | 47 +++++++++---
test/litest.c | 6 ++
test/litest.h | 1 +
test/touchpad.c | 12 +++
9 files changed, 225 insertions(+), 119 deletions(-)
create mode 100644 src/evdev-mt-touchpad-gestures.c
diff --git a/src/Makefile.am b/src/Makefile.am
index b5eba73..ff65ff7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -15,6 +15,7 @@ libinput_la_SOURCES = \
evdev-mt-touchpad-tap.c \
evdev-mt-touchpad-buttons.c \
evdev-mt-touchpad-edge-scroll.c \
+ evdev-mt-touchpad-gestures.c \
filter.c \
filter.h \
filter-private.h \
diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c
index 9dbb513..12f8023 100644
--- a/src/evdev-mt-touchpad-buttons.c
+++ b/src/evdev-mt-touchpad-buttons.c
@@ -156,7 +156,6 @@ tp_button_set_state(struct tp_dispatch *tp, struct tp_touch
*t,
break;
case BUTTON_STATE_AREA:
t->button.curr = BUTTON_EVENT_IN_AREA;
- tp_set_pointer(tp, t);
break;
case BUTTON_STATE_BOTTOM:
t->button.curr = event;
diff --git a/src/evdev-mt-touchpad-edge-scroll.c
b/src/evdev-mt-touchpad-edge-scroll.c
index df181d5..28f29c2 100644
--- a/src/evdev-mt-touchpad-edge-scroll.c
+++ b/src/evdev-mt-touchpad-edge-scroll.c
@@ -86,7 +86,6 @@ tp_edge_scroll_set_state(struct tp_dispatch *tp,
break;
case EDGE_SCROLL_TOUCH_STATE_AREA:
t->scroll.edge = EDGE_NONE;
- tp_set_pointer(tp, t);
break;
}
}
diff --git a/src/evdev-mt-touchpad-gestures.c b/src/evdev-mt-touchpad-gestures.c
new file mode 100644
index 0000000..28ac74b
--- /dev/null
+++ b/src/evdev-mt-touchpad-gestures.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright © 2015 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <math.h>
+#include <stdbool.h>
+#include <limits.h>
+
+#include "evdev-mt-touchpad.h"
+
+#define DEFAULT_GESTURE_SWITCH_TIMEOUT 100 /* ms */
+
+void
+tp_gesture_start(struct tp_dispatch *tp, uint64_t time)
+{
+ if (tp->gesture.started)
+ return;
+
+ switch (tp->gesture.finger_mode) {
+ case 2:
+ /* NOP */
+ break;
+ }
+ tp->gesture.started = true;
+}
+
+void
+tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time)
+{
+ if (tp->gesture.finger_mode == 0)
+ return;
+
+ /* When tap-and-dragging, or a clickpad is clicked force 1fg mode */
+ if (tp_tap_dragging(tp) || (tp->buttons.is_clickpad &&
tp->buttons.state)) {
+ tp_gesture_stop(tp, time);
+ tp->gesture.finger_mode = 1;
+ tp->gesture.finger_mode_pending = 0;
+ }
+
+ /* Don't send events when we're unsure in which mode we are */
+ if (tp->gesture.finger_mode_pending)
+ return;
+
+ switch (tp->gesture.finger_mode) {
+ case 1:
+ tp_gesture_post_pointer_motion(tp, time);
+ break;
+ case 2:
+ tp_gesture_post_twofinger_scroll(tp, time);
+ break;
+ }
+}
+
+void
+tp_gesture_stop(struct tp_dispatch *tp, uint64_t time)
+{
+ if (!tp->gesture.started)
+ return;
+
+ switch (tp->gesture.finger_mode) {
+ case 2:
+ tp_gesture_stop_twofinger_scroll(tp, time);
+ break;
+ }
+ tp->gesture.started = false;
+}
+
+static void
+tp_gesture_finger_mode_switch_timeout(uint64_t now, void *data)
+{
+ struct tp_dispatch *tp = data;
+
+ if (!tp->gesture.finger_mode_pending)
+ return;
+
+ tp_gesture_stop(tp, now); /* End current gesture */
+ tp->gesture.finger_mode = tp->gesture.finger_mode_pending;
+ tp->gesture.finger_mode_pending = 0;
+}
+
+void
+tp_gesture_handle_state(struct tp_dispatch *tp, uint64_t time)
+{
+ unsigned int active_touches = 0;
+ struct tp_touch *t;
+
+ tp_for_each_touch(tp, t)
+ if (tp_touch_active(tp, t))
+ active_touches++;
+
+ if (active_touches != tp->gesture.finger_mode) {
+ /* If all fingers are lifted immediately end the gesture */
+ if (active_touches == 0) {
+ tp_gesture_stop(tp, time);
+ tp->gesture.finger_mode = 0;
+ tp->gesture.finger_mode_pending = 0;
+ /* Immediately switch to new mode to avoid initial latency */
+ } else if (!tp->gesture.started) {
+ tp->gesture.finger_mode = active_touches;
+ tp->gesture.finger_mode_pending = 0;
+ /* Else debounce finger changes */
+ } else if (active_touches != tp->gesture.finger_mode_pending) {
+ tp->gesture.finger_mode_pending = active_touches;
+
libinput_timer_set(&tp->gesture.finger_mode_switch_timer,
+ time + DEFAULT_GESTURE_SWITCH_TIMEOUT);
+ }
+ } else {
+ tp->gesture.finger_mode_pending = 0;
+ }
+}
+
+int
+tp_init_gesture(struct tp_dispatch *tp)
+{
+ libinput_timer_init(&tp->gesture.finger_mode_switch_timer,
+ tp->device->base.seat->libinput,
+ tp_gesture_finger_mode_switch_timeout, tp);
+ return 0;
+}
+
+void
+tp_remove_gesture(struct tp_dispatch *tp)
+{
+ libinput_timer_cancel(&tp->gesture.finger_mode_switch_timer);
+}
diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c
index c6c48d3..947678a 100644
--- a/src/evdev-mt-touchpad.c
+++ b/src/evdev-mt-touchpad.c
@@ -227,7 +227,6 @@ tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t,
uint64_t time)
}
t->dirty = true;
- t->is_pointer = false;
t->palm.is_palm = false;
t->state = TOUCH_END;
t->pinned.is_pinned = false;
@@ -424,7 +423,6 @@ tp_unpin_finger(struct tp_dispatch *tp, struct tp_touch *t)
if (xdist * xdist + ydist * ydist >=
tp->buttons.motion_dist * tp->buttons.motion_dist) {
t->pinned.is_pinned = false;
- tp_set_pointer(tp, t);
return;
}
@@ -439,14 +437,13 @@ tp_pin_fingers(struct tp_dispatch *tp)
struct tp_touch *t;
tp_for_each_touch(tp, t) {
- t->is_pointer = false;
t->pinned.is_pinned = true;
t->pinned.center_x = t->x;
t->pinned.center_y = t->y;
}
}
-static int
+int
tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t)
{
return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) &&
@@ -456,21 +453,6 @@ tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t)
tp_edge_scroll_touch_active(tp, t);
}
-void
-tp_set_pointer(struct tp_dispatch *tp, struct tp_touch *t)
-{
- struct tp_touch *tmp = NULL;
-
- /* Only set the touch as pointer if we don't have one yet */
- tp_for_each_touch(tp, tmp) {
- if (tmp->is_pointer)
- return;
- }
-
- if (tp_touch_active(tp, t))
- t->is_pointer = true;
-}
-
static void
tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
{
@@ -487,7 +469,6 @@ tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t,
uint64_t time)
int dirs = vector_get_direction(t->x - t->palm.x, t->y -
t->palm.y);
if ((dirs & DIRECTIONS) && !(dirs & ~DIRECTIONS)) {
t->palm.is_palm = false;
- tp_set_pointer(tp, t);
}
}
return;
@@ -530,8 +511,6 @@ tp_get_average_touches_delta(struct tp_dispatch *tp, double
*dx, double *dy)
*dx += tmpx;
*dy += tmpy;
}
- /* Stop spurious MOTION events at the end of scrolling */
- t->is_pointer = false;
}
if (nchanged == 0)
@@ -541,80 +520,30 @@ tp_get_average_touches_delta(struct tp_dispatch *tp,
double *dx, double *dy)
*dy /= nchanged;
}
-static void
-tp_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time)
+void
+tp_gesture_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time)
{
double dx = 0, dy =0;
tp_get_average_touches_delta(tp, &dx, &dy);
tp_filter_motion(tp, &dx, &dy, NULL, NULL, time);
+ if (dx == 0.0 && dy == 0.0)
+ return;
+
+ tp_gesture_start(tp, time);
evdev_post_scroll(tp->device,
time,
LIBINPUT_POINTER_AXIS_SOURCE_FINGER,
dx, dy);
- tp->scroll.twofinger_state = TWOFINGER_SCROLL_STATE_ACTIVE;
}
-static void
-tp_twofinger_stop_scroll(struct tp_dispatch *tp, uint64_t time)
+void
+tp_gesture_stop_twofinger_scroll(struct tp_dispatch *tp, uint64_t time)
{
- struct tp_touch *t, *ptr = NULL;
- int nfingers_down = 0;
-
evdev_stop_scroll(tp->device,
time,
LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
-
- /* If we were scrolling and now there's exactly 1 active finger,
- switch back to pointer movement */
- if (tp->scroll.twofinger_state == TWOFINGER_SCROLL_STATE_ACTIVE) {
- tp_for_each_touch(tp, t) {
- if (tp_touch_active(tp, t)) {
- nfingers_down++;
- if (ptr == NULL)
- ptr = t;
- }
- }
-
- if (nfingers_down == 1)
- tp_set_pointer(tp, ptr);
- }
-
- tp->scroll.twofinger_state = TWOFINGER_SCROLL_STATE_NONE;
-}
-
-static int
-tp_twofinger_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
-{
- struct tp_touch *t;
- int nfingers_down = 0;
-
- if (tp->scroll.method != LIBINPUT_CONFIG_SCROLL_2FG)
- return 0;
-
- /* No 2fg scrolling during tap-n-drag */
- if (tp_tap_dragging(tp))
- return 0;
-
- /* No 2fg scrolling while a clickpad is clicked */
- if (tp->buttons.is_clickpad && tp->buttons.state)
- return 0;
-
- /* Only count active touches for 2 finger scrolling */
- tp_for_each_touch(tp, t) {
- if (tp_touch_active(tp, t))
- nfingers_down++;
- }
-
- if (nfingers_down == 2) {
- tp_post_twofinger_scroll(tp, time);
- return 1;
- }
-
- tp_twofinger_stop_scroll(tp, time);
-
- return 0;
}
static void
@@ -718,6 +647,8 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time)
if ((tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS) &&
tp->buttons.is_clickpad)
tp_pin_fingers(tp);
+
+ tp_gesture_handle_state(tp, time);
}
static void
@@ -749,24 +680,6 @@ tp_post_process_state(struct tp_dispatch *tp, uint64_t
time)
}
static void
-tp_get_pointer_delta(struct tp_dispatch *tp, double *dx, double *dy)
-{
- struct tp_touch *t = tp_current_touch(tp);
-
- if (!t->is_pointer) {
- tp_for_each_touch(tp, t) {
- if (t->is_pointer)
- break;
- }
- }
-
- if (!t->is_pointer || !t->dirty)
- return;
-
- tp_get_delta(t, dx, dy);
-}
-
-static void
tp_get_combined_touches_delta(struct tp_dispatch *tp, double *dx, double *dy)
{
struct tp_touch *t;
@@ -785,8 +698,8 @@ tp_get_combined_touches_delta(struct tp_dispatch *tp,
double *dx, double *dy)
}
}
-static void
-tp_post_pointer_motion(struct tp_dispatch *tp, uint64_t time)
+void
+tp_gesture_post_pointer_motion(struct tp_dispatch *tp, uint64_t time)
{
double dx = 0.0, dy = 0.0;
double dx_unaccel, dy_unaccel;
@@ -795,7 +708,7 @@ tp_post_pointer_motion(struct tp_dispatch *tp, uint64_t
time)
if (tp->buttons.is_clickpad && tp->buttons.state)
tp_get_combined_touches_delta(tp, &dx, &dy);
else
- tp_get_pointer_delta(tp, &dx, &dy);
+ tp_get_average_touches_delta(tp, &dx, &dy);
tp_filter_motion(tp, &dx, &dy, &dx_unaccel, &dy_unaccel, time);
@@ -821,16 +734,14 @@ tp_post_events(struct tp_dispatch *tp, uint64_t time)
if (filter_motion || tp->sendevents.trackpoint_active) {
tp_edge_scroll_stop_events(tp, time);
- tp_twofinger_stop_scroll(tp, time);
+ tp_gesture_stop(tp, time);
return;
}
if (tp_edge_scroll_post_events(tp, time) != 0)
return;
- if (tp_twofinger_scroll_post_events(tp, time) != 0)
- return;
- tp_post_pointer_motion(tp, time);
+ tp_gesture_post_events(tp, time);
}
static void
@@ -887,6 +798,7 @@ tp_remove(struct evdev_dispatch *dispatch)
tp_remove_buttons(tp);
tp_remove_sendevents(tp);
tp_remove_edge_scroll(tp);
+ tp_remove_gesture(tp);
}
static void
@@ -980,7 +892,7 @@ tp_trackpoint_event(uint64_t time, struct libinput_event
*event, void *data)
if (!tp->sendevents.trackpoint_active) {
tp_edge_scroll_stop_events(tp, time);
- tp_twofinger_stop_scroll(tp, time);
+ tp_gesture_stop(tp, time);
tp_tap_suspend(tp, time);
tp->sendevents.trackpoint_active = true;
}
@@ -1221,7 +1133,7 @@ tp_scroll_config_scroll_method_set_method(struct
libinput_device *device,
return LIBINPUT_CONFIG_STATUS_SUCCESS;
tp_edge_scroll_stop_events(tp, time);
- tp_twofinger_stop_scroll(tp, time);
+ tp_gesture_stop_twofinger_scroll(tp, time);
tp->scroll.method = method;
@@ -1359,6 +1271,9 @@ tp_init(struct tp_dispatch *tp,
if (tp_init_scroll(tp, device) != 0)
return -1;
+ if (tp_init_gesture(tp) != 0)
+ return -1;
+
device->seat_caps |= EVDEV_DEVICE_POINTER;
return 0;
diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h
index 3479e5e..4d8e875 100644
--- a/src/evdev-mt-touchpad.h
+++ b/src/evdev-mt-touchpad.h
@@ -118,11 +118,6 @@ enum tp_edge_scroll_touch_state {
EDGE_SCROLL_TOUCH_STATE_AREA,
};
-enum tp_twofinger_scroll_state {
- TWOFINGER_SCROLL_STATE_NONE,
- TWOFINGER_SCROLL_STATE_ACTIVE,
-};
-
struct tp_motion {
int32_t x;
int32_t y;
@@ -133,7 +128,6 @@ struct tp_touch {
enum touch_state state;
bool has_ended; /* TRACKING_ID == -1 */
bool dirty;
- bool is_pointer; /* the pointer-controlling
touch */
int32_t x;
int32_t y;
uint64_t millis;
@@ -217,6 +211,13 @@ struct tp_dispatch {
} accel;
struct {
+ bool started;
+ unsigned int finger_mode;
+ unsigned int finger_mode_pending;
+ struct libinput_timer finger_mode_switch_timer;
+ } gesture;
+
+ struct {
bool is_clickpad; /* true for clickpads */
bool has_topbuttons;
bool use_clickfinger; /* number of fingers decides
button number */
@@ -253,7 +254,6 @@ struct tp_dispatch {
enum libinput_config_scroll_method method;
int32_t right_edge;
int32_t bottom_edge;
- enum tp_twofinger_scroll_state twofinger_state;
} scroll;
enum touchpad_event queued;
@@ -288,15 +288,15 @@ void
tp_get_delta(struct tp_touch *t, double *dx, double *dy);
void
-tp_set_pointer(struct tp_dispatch *tp, struct tp_touch *t);
-
-void
tp_filter_motion(struct tp_dispatch *tp,
double *dx, double *dy,
double *dx_unaccel, double *dy_unaccel,
uint64_t time);
int
+tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t);
+
+int
tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time);
int
@@ -368,4 +368,31 @@ tp_edge_scroll_stop_events(struct tp_dispatch *tp,
uint64_t time);
int
tp_edge_scroll_touch_active(struct tp_dispatch *tp, struct tp_touch *t);
+int
+tp_init_gesture(struct tp_dispatch *tp);
+
+void
+tp_remove_gesture(struct tp_dispatch *tp);
+
+void
+tp_gesture_start(struct tp_dispatch *tp, uint64_t time);
+
+void
+tp_gesture_stop(struct tp_dispatch *tp, uint64_t time);
+
+void
+tp_gesture_handle_state(struct tp_dispatch *tp, uint64_t time);
+
+void
+tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time);
+
+void
+tp_gesture_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time);
+
+void
+tp_gesture_stop_twofinger_scroll(struct tp_dispatch *tp, uint64_t time);
+
+void
+tp_gesture_post_pointer_motion(struct tp_dispatch *tp, uint64_t time);
+
#endif
diff --git a/test/litest.c b/test/litest.c
index b938cce..cf12424 100644
--- a/test/litest.c
+++ b/test/litest.c
@@ -1385,6 +1385,12 @@ litest_timeout_buttonscroll(void)
}
void
+litest_timeout_finger_switch(void)
+{
+ msleep(120);
+}
+
+void
litest_push_event_frame(struct litest_device *dev)
{
assert(!dev->skip_ev_syn);
diff --git a/test/litest.h b/test/litest.h
index 60ec458..d78bf0e 100644
--- a/test/litest.h
+++ b/test/litest.h
@@ -180,6 +180,7 @@ struct libevdev_uinput *
litest_create_uinput_abs_device(const char *name,
void litest_timeout_tap(void);
void litest_timeout_softbuttons(void);
void litest_timeout_buttonscroll(void);
+void litest_timeout_finger_switch(void);
void litest_push_event_frame(struct litest_device *dev);
void litest_pop_event_frame(struct litest_device *dev);
diff --git a/test/touchpad.c b/test/touchpad.c
index 9c34b50..3627352 100644
--- a/test/touchpad.c
+++ b/test/touchpad.c
@@ -1871,9 +1871,15 @@ START_TEST(touchpad_2fg_scroll_return_to_motion)
/* 2fg scroll */
litest_touch_down(dev, 1, 53, 50);
+ libinput_dispatch(li);
+ litest_timeout_finger_switch();
+ libinput_dispatch(li);
litest_touch_move_to(dev, 0, 47, 50, 47, 70, 5, 0);
litest_touch_move_to(dev, 1, 53, 50, 53, 70, 5, 0);
litest_touch_up(dev, 1);
+ libinput_dispatch(li);
+ litest_timeout_finger_switch();
+ libinput_dispatch(li);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_AXIS);
litest_touch_move_to(dev, 0, 47, 70, 47, 50, 10, 0);
@@ -1881,9 +1887,15 @@ START_TEST(touchpad_2fg_scroll_return_to_motion)
/* back to 2fg scroll, lifting the other finger */
litest_touch_down(dev, 1, 50, 50);
+ libinput_dispatch(li);
+ litest_timeout_finger_switch();
+ libinput_dispatch(li);
litest_touch_move_to(dev, 0, 47, 50, 47, 70, 5, 0);
litest_touch_move_to(dev, 1, 53, 50, 53, 70, 5, 0);
litest_touch_up(dev, 0);
+ libinput_dispatch(li);
+ litest_timeout_finger_switch();
+ libinput_dispatch(li);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_AXIS);
/* move with second finger */
--
2.1.0
_______________________________________________
wayland-devel mailing list
wayland-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/wayland-devel