[PATCH libinput 2/2] touchpad: add upper edge into exclusion zone
Peter Hutterer
peter.hutterer at who-t.net
Thu Jul 6 03:49:58 UTC 2017
From: Ming-Yang Lu <op8867555 at gmail.com>
This reduces unexpected cursor moves when placing the thumb near the border
of trackpoint buttons and upper edge of touchpad.
https://bugs.freedesktop.org/show_bug.cgi?id=101574
Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
doc/palm-detection.dox | 22 +++++----
doc/svg/palm-detection.svg | 47 +++++++++++-------
src/evdev-mt-touchpad.c | 66 ++++++++++++++++++++-----
src/evdev-mt-touchpad.h | 1 +
test/test-touchpad.c | 120 +++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 218 insertions(+), 38 deletions(-)
diff --git a/doc/palm-detection.dox b/doc/palm-detection.dox
index c8ceb4eb..4dbd67c0 100644
--- a/doc/palm-detection.dox
+++ b/doc/palm-detection.dox
@@ -27,16 +27,20 @@ pressure ranges, see @ref touchpad_pressure.
@section palm_exclusion_zones Palm exclusion zones
-libinput enables palm detection on the edge of the touchpad. Two exclusion
-zones are defined on the left and right edge of the touchpad.
-If a touch starts in the exclusion zone, it is considered a palm and the
-touch point is ignored. However, for fast cursor movements across the
-screen, it is common for a finger to start inside an exclusion zone and move
-rapidly across the touchpad. libinput detects such movements and avoids palm
-detection on such touch sequences.
+libinput enables palm detection on the left, right and top edges of the
+touchpad. Two exclusion zones are defined on the left and right edge of the
+touchpad. If a touch starts in the exclusion zone, it is considered a palm
+and the touch point is ignored. However, for fast cursor movements across
+the screen, it is common for a finger to start inside an exclusion zone and
+move rapidly across the touchpad. libinput detects such movements and avoids
+palm detection on such touch sequences.
-Each exclusion zone is divided into a top part and a bottom part. A touch
-starting in the top part of the exclusion zone does not trigger a
+Another exclusion zone is defined on the top edge of the touchpad. As with
+the edge zones, libinput detects vertical movements out of the edge zone and
+avoids palm detection on such touch sequences.
+
+Each side edge exclusion zone is divided into a top part and a bottom part.
+A touch starting in the top part of the exclusion zone does not trigger a
tap (see @ref tapping).
In the diagram below, the exclusion zones are painted red.
diff --git a/doc/svg/palm-detection.svg b/doc/svg/palm-detection.svg
index c3e45f44..2849e265 100644
--- a/doc/svg/palm-detection.svg
+++ b/doc/svg/palm-detection.svg
@@ -36,16 +36,17 @@
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
- inkscape:window-height="1136"
+ inkscape:window-height="1016"
id="namedview3477"
showgrid="false"
inkscape:zoom="3.5662625"
- inkscape:cx="199.35048"
- inkscape:cy="156.74673"
+ inkscape:cx="180.54059"
+ inkscape:cy="269.48563"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
- inkscape:current-layer="svg2" />
+ inkscape:current-layer="svg2"
+ inkscape:document-rotation="0" />
<defs
id="defs4">
<marker
@@ -138,15 +139,14 @@
id="path13492"
d="m 38.928571,67.914286 c 0,0 3.508205,24.810617 9.642857,57.857144 6.134651,33.04652 23.277202,79.68584 89.642852,90.35714" />
<rect
- style="fill:#000000;fill-opacity:0.3559322;fill-rule:evenodd;stroke:none;stroke-width:3.30527353px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ style="fill:#000000;fill-opacity:0.3559322;fill-rule:evenodd;stroke:none;stroke-width:3.30510259px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="rect3490"
- width="65.272476"
- height="136.21509"
+ width="65.310997"
+ height="136.12065"
x="7.0411549"
- y="7.0411549" />
+ y="7.1355872" />
<text
- sodipodi:linespacing="100%"
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12px;line-height:100%;font-family:Utopia;-inkscape-font-specification:Utopia;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12px;line-height:0%;font-family:Utopia;-inkscape-font-specification:Utopia;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
xml:space="preserve"
id="text13874"
y="63.628628"
@@ -160,11 +160,10 @@
id="rect3490-2"
width="65.272476"
height="136.21509"
- x="321.23563"
- y="6.7607527" />
+ x="321.22849"
+ y="6.8830237" />
<text
- sodipodi:linespacing="100%"
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12px;line-height:100%;font-family:Utopia;-inkscape-font-specification:Utopia;text-align:start;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12px;line-height:0%;font-family:Utopia;-inkscape-font-specification:Utopia;text-align:start;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none"
xml:space="preserve"
id="text13874-8"
y="98.748993"
@@ -183,8 +182,7 @@
id="layer1"
style="display:inline" />
<text
- sodipodi:linespacing="100%"
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12px;line-height:100%;font-family:Utopia;-inkscape-font-specification:Utopia;text-align:start;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12px;line-height:0%;font-family:Utopia;-inkscape-font-specification:Utopia;text-align:start;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none"
xml:space="preserve"
id="text13874-8-1"
y="46.009491"
@@ -194,8 +192,7 @@
y="46.009491"
x="342.27759">C</tspan></text>
<text
- sodipodi:linespacing="100%"
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12px;line-height:100%;font-family:Utopia;-inkscape-font-specification:Utopia;text-align:start;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12px;line-height:0%;font-family:Utopia;-inkscape-font-specification:Utopia;text-align:start;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none"
xml:space="preserve"
id="text13874-8-1-4"
y="215.65927"
@@ -218,4 +215,18 @@
cy="194.8819"
r="4.0658817"
transform="scale(-1,1)" />
+ <rect
+ width="248.87633"
+ height="6.8111157"
+ x="72.35215"
+ y="7.1355872"
+ id="rect4355"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ff0000;fill-opacity:0.32017547;fill-rule:nonzero;stroke:none;stroke-width:1.11822701;marker:none;enable-background:accumulate" />
+ <rect
+ y="7.1355872"
+ x="72.35215"
+ height="6.8111153"
+ width="248.87634"
+ id="rect4353"
+ style="fill:#000000;fill-opacity:0.3559322;fill-rule:evenodd;stroke:none;stroke-width:1.44321382px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</svg>
diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c
index 2b9bc28b..b68d7669 100644
--- a/src/evdev-mt-touchpad.c
+++ b/src/evdev-mt-touchpad.c
@@ -550,14 +550,45 @@ tp_touch_active(const struct tp_dispatch *tp, const struct tp_touch *t)
tp_edge_scroll_touch_active(tp, t);
}
+static inline bool
+tp_palm_was_in_side_edge(const struct tp_dispatch *tp, const struct tp_touch *t)
+{
+ return t->palm.first.x < tp->palm.left_edge ||
+ t->palm.first.x > tp->palm.right_edge;
+}
+
+static inline bool
+tp_palm_was_in_top_edge(const struct tp_dispatch *tp, const struct tp_touch *t)
+{
+ return t->palm.first.y < tp->palm.upper_edge;
+}
+
+static inline bool
+tp_palm_in_side_edge(const struct tp_dispatch *tp, const struct tp_touch *t)
+{
+ return t->point.x < tp->palm.left_edge ||
+ t->point.x > tp->palm.right_edge;
+}
+
+static inline bool
+tp_palm_in_top_edge(const struct tp_dispatch *tp, const struct tp_touch *t)
+{
+ return t->point.y < tp->palm.upper_edge;
+}
+
+static inline bool
+tp_palm_in_edge(const struct tp_dispatch *tp, const struct tp_touch *t)
+{
+ return tp_palm_in_side_edge(tp, t) || tp_palm_in_top_edge(tp, t);
+}
+
bool
tp_palm_tap_is_palm(const struct tp_dispatch *tp, const struct tp_touch *t)
{
if (t->state != TOUCH_BEGIN)
return false;
- if (t->point.x > tp->palm.left_edge &&
- t->point.x < tp->palm.right_edge)
+ if (!tp_palm_in_edge(tp, t))
return false;
evdev_log_debug(tp->device, "palm: palm-tap detected\n");
@@ -654,16 +685,22 @@ tp_palm_detect_move_out_of_edge(struct tp_dispatch *tp,
uint64_t time)
{
const int PALM_TIMEOUT = ms2us(200);
- const int DIRECTIONS = NE|E|SE|SW|W|NW;
+ int directions = 0;
struct device_float_coords delta;
int dirs;
- if (time < t->palm.time + PALM_TIMEOUT &&
- (t->point.x > tp->palm.left_edge && t->point.x < tp->palm.right_edge)) {
- delta = device_delta(t->point, t->palm.first);
- dirs = phys_get_direction(tp_phys_delta(tp, delta));
- if ((dirs & DIRECTIONS) && !(dirs & ~DIRECTIONS))
- return true;
+ if (time < t->palm.time + PALM_TIMEOUT && !tp_palm_in_edge(tp, t)) {
+ if (tp_palm_was_in_side_edge(tp, t))
+ directions = NE|E|SE|SW|W|NW;
+ else if (tp_palm_was_in_top_edge(tp, t))
+ directions = S|SE|SW;
+
+ if (directions) {
+ delta = device_delta(t->point, t->palm.first);
+ dirs = phys_get_direction(tp_phys_delta(tp, delta));
+ if ((dirs & directions) && !(dirs & ~directions))
+ return true;
+ }
}
return false;
@@ -725,8 +762,7 @@ tp_palm_detect_edge(struct tp_dispatch *tp,
/* palm must start in exclusion zone, it's ok to move into
the zone without being a palm */
- if (t->state != TOUCH_BEGIN ||
- (t->point.x > tp->palm.left_edge && t->point.x < tp->palm.right_edge))
+ if (t->state != TOUCH_BEGIN || !tp_palm_in_edge(tp, t))
return false;
/* don't detect palm in software button areas, it's
@@ -2329,6 +2365,13 @@ tp_init_palmdetect_edge(struct tp_dispatch *tp,
mm.x = width * 0.92;
edges = evdev_device_mm_to_units(device, &mm);
tp->palm.right_edge = edges.x;
+
+ if (!tp->buttons.has_topbuttons) {
+ /* top edge is 5% of the height */
+ mm.y = height * 0.05;
+ edges = evdev_device_mm_to_units(device, &mm);
+ tp->palm.upper_edge = edges.y;
+ }
}
static int
@@ -2374,6 +2417,7 @@ tp_init_palmdetect(struct tp_dispatch *tp,
tp->palm.right_edge = INT_MAX;
tp->palm.left_edge = INT_MIN;
+ tp->palm.upper_edge = INT_MIN;
if (device->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD &&
!tp_is_tpkb_combo_below(device))
diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h
index 6d014607..d601f7e5 100644
--- a/src/evdev-mt-touchpad.h
+++ b/src/evdev-mt-touchpad.h
@@ -335,6 +335,7 @@ struct tp_dispatch {
struct {
int32_t right_edge; /* in device coordinates */
int32_t left_edge; /* in device coordinates */
+ int32_t upper_edge; /* in device coordinates */
bool trackpoint_active;
struct libinput_event_listener trackpoint_listener;
diff --git a/test/test-touchpad.c b/test/test-touchpad.c
index 913fad64..01d659e4 100644
--- a/test/test-touchpad.c
+++ b/test/test-touchpad.c
@@ -1000,6 +1000,26 @@ START_TEST(touchpad_palm_detect_at_edge)
}
END_TEST
+START_TEST(touchpad_palm_detect_at_top)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+
+ if (!touchpad_has_palm_detect_size(dev))
+ return;
+
+ litest_disable_tap(dev->libinput_device);
+
+ litest_drain_events(li);
+
+ litest_touch_down(dev, 0, 20, 1);
+ litest_touch_move_to(dev, 0, 20, 1, 70, 1, 10, 0);
+ litest_touch_up(dev, 0);
+
+ litest_assert_empty_queue(li);
+}
+END_TEST
+
START_TEST(touchpad_no_palm_detect_at_edge_for_edge_scrolling)
{
struct litest_device *dev = litest_current_device();
@@ -1102,6 +1122,26 @@ START_TEST(touchpad_palm_detect_palm_stays_palm)
}
END_TEST
+START_TEST(touchpad_palm_detect_top_palm_stays_palm)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+
+ if (!touchpad_has_palm_detect_size(dev))
+ return;
+
+ litest_disable_tap(dev->libinput_device);
+
+ litest_drain_events(li);
+
+ litest_touch_down(dev, 0, 20, 1);
+ litest_touch_move_to(dev, 0, 20, 1, 90, 30, 10, 0);
+ litest_touch_up(dev, 0);
+
+ litest_assert_empty_queue(li);
+}
+END_TEST
+
START_TEST(touchpad_palm_detect_palm_becomes_pointer)
{
struct litest_device *dev = litest_current_device();
@@ -1129,6 +1169,30 @@ START_TEST(touchpad_palm_detect_palm_becomes_pointer)
}
END_TEST
+START_TEST(touchpad_palm_detect_top_palm_becomes_pointer)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+
+ if (!touchpad_has_palm_detect_size(dev))
+ return;
+
+ litest_disable_tap(dev->libinput_device);
+
+ litest_drain_events(li);
+
+ litest_touch_down(dev, 0, 50, 1);
+ litest_touch_move_to(dev, 0, 50, 1, 50, 60, 20, 0);
+ litest_touch_up(dev, 0);
+
+ libinput_dispatch(li);
+
+ litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
+
+ litest_assert_empty_queue(li);
+}
+END_TEST
+
START_TEST(touchpad_palm_detect_no_palm_moving_into_edges)
{
struct litest_device *dev = litest_current_device();
@@ -1158,6 +1222,56 @@ START_TEST(touchpad_palm_detect_no_palm_moving_into_edges)
}
END_TEST
+START_TEST(touchpad_palm_detect_no_palm_moving_into_top)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+
+ if (!touchpad_has_palm_detect_size(dev))
+ return;
+
+ litest_disable_tap(dev->libinput_device);
+
+ /* moving non-palm into the edge does not label it as palm */
+ litest_drain_events(li);
+
+ litest_touch_down(dev, 0, 50, 50);
+ litest_touch_move_to(dev, 0, 50, 50, 0, 2, 10, 0);
+
+ litest_drain_events(li);
+
+ litest_touch_move_to(dev, 0, 0, 2, 50, 50, 10, 0);
+ libinput_dispatch(li);
+
+ litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
+
+ litest_touch_up(dev, 0);
+ libinput_dispatch(li);
+ litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_palm_detect_no_tap_top_edge)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+
+ if (!touchpad_has_palm_detect_size(dev))
+ return;
+
+ litest_enable_tap(dev->libinput_device);
+
+ litest_drain_events(li);
+
+ litest_touch_down(dev, 0, 50, 1);
+ litest_touch_up(dev, 0);
+ libinput_dispatch(li);
+
+ litest_timeout_tap();
+ litest_assert_empty_queue(li);
+}
+END_TEST
+
START_TEST(touchpad_palm_detect_tap_hardbuttons)
{
struct litest_device *dev = litest_current_device();
@@ -1344,6 +1458,7 @@ START_TEST(touchpad_palm_detect_both_edges)
litest_touch_move_to(dev, 0, 99, 50, 99, 40, 10, 0);
litest_touch_move_to(dev, 0, 99, 40, 99, 50, 10, 0);
litest_assert_empty_queue(li);
+ /* This set generates events */
litest_touch_down(dev, 1, 1, 50);
litest_touch_move_to(dev, 1, 1, 50, 1, 40, 10, 0);
litest_touch_move_to(dev, 1, 1, 40, 1, 50, 10, 0);
@@ -5152,11 +5267,16 @@ litest_setup_tests_touchpad(void)
litest_add("touchpad:scroll", touchpad_edge_scroll_into_area, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:palm", touchpad_palm_detect_at_edge, LITEST_TOUCHPAD, LITEST_ANY);
+ litest_add("touchpad:palm", touchpad_palm_detect_at_top, LITEST_TOUCHPAD, LITEST_TOPBUTTONPAD);
litest_add("touchpad:palm", touchpad_palm_detect_at_bottom_corners, LITEST_TOUCHPAD, LITEST_CLICKPAD);
litest_add("touchpad:palm", touchpad_palm_detect_at_top_corners, LITEST_TOUCHPAD, LITEST_TOPBUTTONPAD);
litest_add("touchpad:palm", touchpad_palm_detect_palm_becomes_pointer, LITEST_TOUCHPAD, LITEST_ANY);
+ litest_add("touchpad:palm", touchpad_palm_detect_top_palm_becomes_pointer, LITEST_TOUCHPAD, LITEST_TOPBUTTONPAD);
litest_add("touchpad:palm", touchpad_palm_detect_palm_stays_palm, LITEST_TOUCHPAD, LITEST_ANY);
+ litest_add("touchpad:palm", touchpad_palm_detect_top_palm_stays_palm, LITEST_TOUCHPAD, LITEST_TOPBUTTONPAD);
litest_add("touchpad:palm", touchpad_palm_detect_no_palm_moving_into_edges, LITEST_TOUCHPAD, LITEST_ANY);
+ litest_add("touchpad:palm", touchpad_palm_detect_no_palm_moving_into_top, LITEST_TOUCHPAD, LITEST_TOPBUTTONPAD);
+ litest_add("touchpad:palm", touchpad_palm_detect_no_tap_top_edge, LITEST_TOUCHPAD, LITEST_TOPBUTTONPAD);
litest_add("touchpad:palm", touchpad_palm_detect_tap_hardbuttons, LITEST_TOUCHPAD, LITEST_CLICKPAD);
litest_add("touchpad:palm", touchpad_palm_detect_tap_softbuttons, LITEST_CLICKPAD, LITEST_ANY);
litest_add("touchpad:palm", touchpad_palm_detect_tap_clickfinger, LITEST_CLICKPAD, LITEST_ANY);
--
2.13.0
More information about the wayland-devel
mailing list