Commit 5dc330bd authored by Ming-Yang Lu's avatar Ming-Yang Lu Committed by Peter Hutterer

touchpad: add upper edge into exclusion zone

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=101574Signed-off-by: Peter Hutterer's avatarPeter Hutterer <peter.hutterer@who-t.net>
parent 6a77cae8
......@@ -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.
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
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.
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.
......
......@@ -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>
......@@ -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))
......
......@@ -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;
......
......@@ -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);
......
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