mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2025-12-28 23:40:08 +01:00
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=101574 Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
parent
6a77cae850
commit
5dc330bdea
5 changed files with 218 additions and 38 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.8 KiB |
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue