mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2026-02-04 05:50:26 +01:00
touchpad: don't allow taps in the top half of the palm exclusion zone.
Touches in the exclusion zone are ignored for palm detection and don't move the cursor. Tapping however triggers before we know whether something is a palm or not, so we get erroneous button clickst. If a tap happens in the top half of the touchpad, within the palm exclusion zones, ignore it for tap purposes. To avoid further complicating the state machine simply pretend there was a movement > threshold on that finger. This advances the tap state machine properly that no button events are sent for this finger. https://bugs.freedesktop.org/show_bug.cgi?id=89625 Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net> Reviewed-by: Hans de Goede <hdegoede@redhat.com>
This commit is contained in:
parent
7552cd04f7
commit
39f1125347
6 changed files with 244 additions and 58 deletions
|
|
@ -23,6 +23,10 @@ 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
|
||||
tap (see @ref tapping).
|
||||
|
||||
In the diagram below, the exclusion zones are painted red.
|
||||
Touch 'A' starts inside the exclusion zone and moves
|
||||
almost vertically. It is considered a palm and ignored for cursor movement,
|
||||
|
|
@ -31,6 +35,11 @@ despite moving out of the exclusion zone.
|
|||
Touch 'B' starts inside the exclusion zone but moves horizontally out of the
|
||||
zone. It is considered a valid touch and controls the cursor.
|
||||
|
||||
Touch 'C' occurs in the top part of the exclusion zone. Despite being a
|
||||
tapping motion, it does not generate an emulated button event. Touch 'D'
|
||||
likewise occurs within the exclusion zone but in the bottom half. libinput
|
||||
will generate a button event for this touch.
|
||||
|
||||
@image html palm-detection.svg
|
||||
|
||||
@section trackpoint-disabling Palm detection during trackpoint use
|
||||
|
|
|
|||
|
|
@ -2,14 +2,66 @@
|
|||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
width="393.62857"
|
||||
height="268.62857"
|
||||
id="svg2">
|
||||
id="svg2"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="palm-detection.svg">
|
||||
<metadata
|
||||
id="metadata3479">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1136"
|
||||
id="namedview3477"
|
||||
showgrid="false"
|
||||
inkscape:zoom="3.5662625"
|
||||
inkscape:cx="199.35048"
|
||||
inkscape:cy="156.74673"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2" />
|
||||
<defs
|
||||
id="defs4">
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lstart"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="marker4663"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path4407"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
transform="scale(0.8) translate(12.5,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
refX="0"
|
||||
refY="0"
|
||||
|
|
@ -59,64 +111,111 @@
|
|||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
|
||||
</marker>
|
||||
</defs>
|
||||
<g
|
||||
transform="translate(343.95712,-527.33359)"
|
||||
id="layer3"
|
||||
style="display:inline">
|
||||
<rect
|
||||
width="386.42856"
|
||||
height="261.42856"
|
||||
x="-340.35712"
|
||||
y="530.93359"
|
||||
id="rect2858-0"
|
||||
style="color:#000000;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:7.20000076;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
|
||||
<rect
|
||||
width="65.281105"
|
||||
height="254.3844"
|
||||
x="-336.88608"
|
||||
y="534.46918"
|
||||
id="rect12924"
|
||||
style="color:#000000;fill:#ff0000;fill-opacity:0.32017547;fill-rule:nonzero;stroke:none;stroke-width:3.5;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
|
||||
<rect
|
||||
width="65.281105"
|
||||
height="254.3844"
|
||||
x="-22.72864"
|
||||
y="534.21661"
|
||||
id="rect13482"
|
||||
style="color:#000000;fill:#ff0000;fill-opacity:0.32017547;fill-rule:nonzero;stroke:none;stroke-width:3.5;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
|
||||
<path
|
||||
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"
|
||||
transform="translate(-343.95712,527.33359)"
|
||||
id="path13492"
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:6, 1;stroke-dashoffset:0;marker-mid:none;marker-end:url(#Arrow1Lend-2)" />
|
||||
<text
|
||||
x="-310.74283"
|
||||
y="590.96222"
|
||||
id="text13874"
|
||||
xml:space="preserve"
|
||||
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Utopia;-inkscape-font-specification:Utopia"><tspan
|
||||
x="-310.74283"
|
||||
y="590.96222"
|
||||
id="tspan13876"
|
||||
style="font-size:18px;font-family:Arial;-inkscape-font-specification:Arial">A</tspan></text>
|
||||
<text
|
||||
x="7.8971062"
|
||||
y="626.08258"
|
||||
id="text13874-8"
|
||||
xml:space="preserve"
|
||||
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Utopia;-inkscape-font-specification:Utopia"><tspan
|
||||
x="7.8971062"
|
||||
y="626.08258"
|
||||
id="tspan13876-7"
|
||||
style="font-size:18px;font-family:Arial;-inkscape-font-specification:Arial">B</tspan></text>
|
||||
<path
|
||||
d="m 347.5,90.414286 c 0,0 -28.20972,-6.408104 -85,-6.071429 -22.06971,0.130838 -66.07143,4.285715 -66.07143,4.285715"
|
||||
transform="translate(-343.95712,527.33359)"
|
||||
id="path13903"
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow1Lend-2)" />
|
||||
</g>
|
||||
<rect
|
||||
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:7.20000076;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
|
||||
id="rect2858-0"
|
||||
y="3.6000037"
|
||||
x="3.6000032"
|
||||
height="261.42856"
|
||||
width="386.42856" />
|
||||
<rect
|
||||
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ff0000;fill-opacity:0.32017547;fill-rule:nonzero;stroke:none;stroke-width:3.5;marker:none;enable-background:accumulate"
|
||||
id="rect12924"
|
||||
y="7.1355872"
|
||||
x="7.0710421"
|
||||
height="254.3844"
|
||||
width="65.281105" />
|
||||
<rect
|
||||
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ff0000;fill-opacity:0.32017547;fill-rule:nonzero;stroke:none;stroke-width:3.5;marker:none;enable-background:accumulate"
|
||||
id="rect13482"
|
||||
y="6.8830237"
|
||||
x="321.22849"
|
||||
height="254.3844"
|
||||
width="65.281105" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:6, 1;stroke-dashoffset:0;stroke-opacity:1;marker-mid:none;marker-end:url(#Arrow1Lend-2)"
|
||||
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"
|
||||
id="rect3490"
|
||||
width="65.272476"
|
||||
height="136.21509"
|
||||
x="7.0411549"
|
||||
y="7.0411549" />
|
||||
<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"
|
||||
xml:space="preserve"
|
||||
id="text13874"
|
||||
y="63.628628"
|
||||
x="33.214291"><tspan
|
||||
style="font-size:18px;font-family:Arial;-inkscape-font-specification:Arial"
|
||||
id="tspan13876"
|
||||
y="63.628628"
|
||||
x="33.214291">A</tspan></text>
|
||||
<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"
|
||||
id="rect3490-2"
|
||||
width="65.272476"
|
||||
height="136.21509"
|
||||
x="321.23563"
|
||||
y="6.7607527" />
|
||||
<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"
|
||||
xml:space="preserve"
|
||||
id="text13874-8"
|
||||
y="98.748993"
|
||||
x="351.85422"><tspan
|
||||
style="font-size:18px;font-family:Arial;-inkscape-font-specification:Arial"
|
||||
id="tspan13876-7"
|
||||
y="98.748993"
|
||||
x="351.85422">B</tspan></text>
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow1Lend-2)"
|
||||
id="path13903"
|
||||
d="m 347.5,90.414286 c 0,0 -28.20972,-6.408104 -85,-6.071429 -22.06971,0.130838 -66.07143,4.285715 -66.07143,4.285715" />
|
||||
<g
|
||||
transform="translate(343.95712,-527.33359)"
|
||||
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"
|
||||
xml:space="preserve"
|
||||
id="text13874-8-1"
|
||||
y="46.009491"
|
||||
x="342.27759"><tspan
|
||||
style="font-size:18px;font-family:Arial;-inkscape-font-specification:Arial"
|
||||
id="tspan13876-7-9"
|
||||
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"
|
||||
xml:space="preserve"
|
||||
id="text13874-8-1-4"
|
||||
y="215.65927"
|
||||
x="37.970726"><tspan
|
||||
style="font-size:18px;font-family:Arial;-inkscape-font-specification:Arial"
|
||||
id="tspan13876-7-9-5"
|
||||
y="215.65927"
|
||||
x="37.970726">D</tspan></text>
|
||||
<circle
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-opacity:1"
|
||||
id="path4401"
|
||||
cx="-360.181"
|
||||
cy="24.53549"
|
||||
r="4.0658817"
|
||||
transform="scale(-1,1)" />
|
||||
<circle
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-opacity:1"
|
||||
id="path4401-9"
|
||||
cx="-36.452721"
|
||||
cy="194.8819"
|
||||
r="4.0658817"
|
||||
transform="scale(-1,1)" />
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 8.3 KiB |
|
|
@ -571,6 +571,14 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time)
|
|||
t->tap.state = TAP_TOUCH_STATE_TOUCH;
|
||||
t->tap.initial = t->point;
|
||||
tp_tap_handle_event(tp, t, TAP_EVENT_TOUCH, time);
|
||||
|
||||
/* If we think this is a palm, pretend there's a
|
||||
* motion event which will prevent tap clicks
|
||||
* without requiring extra states in the FSM.
|
||||
*/
|
||||
if (tp_palm_tap_is_palm(tp, t))
|
||||
tp_tap_handle_event(tp, t, TAP_EVENT_MOTION, time);
|
||||
|
||||
} else if (t->state == TOUCH_END) {
|
||||
tp->tap.tap_finger_count--;
|
||||
tp_tap_handle_event(tp, t, TAP_EVENT_RELEASE, time);
|
||||
|
|
|
|||
|
|
@ -458,6 +458,24 @@ tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t)
|
|||
tp_edge_scroll_touch_active(tp, t);
|
||||
}
|
||||
|
||||
bool
|
||||
tp_palm_tap_is_palm(struct tp_dispatch *tp, 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)
|
||||
return false;
|
||||
|
||||
/* We're inside the left/right palm edge and in the northern half of
|
||||
* the touchpad - this tap is a palm */
|
||||
if (t->point.y < tp->palm.vert_center)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
|
||||
{
|
||||
|
|
@ -1098,13 +1116,16 @@ static int
|
|||
tp_init_palmdetect(struct tp_dispatch *tp,
|
||||
struct evdev_device *device)
|
||||
{
|
||||
int width;
|
||||
int width, height;
|
||||
|
||||
tp->palm.right_edge = INT_MAX;
|
||||
tp->palm.left_edge = INT_MIN;
|
||||
tp->palm.vert_center = INT_MIN;
|
||||
|
||||
width = abs(device->abs.absinfo_x->maximum -
|
||||
device->abs.absinfo_x->minimum);
|
||||
height = abs(device->abs.absinfo_y->maximum -
|
||||
device->abs.absinfo_y->minimum);
|
||||
|
||||
/* Apple touchpads are always big enough to warrant palm detection */
|
||||
if (evdev_device_get_id_vendor(device) != VENDOR_ID_APPLE) {
|
||||
|
|
@ -1121,6 +1142,7 @@ tp_init_palmdetect(struct tp_dispatch *tp,
|
|||
/* palm edges are 5% of the width on each side */
|
||||
tp->palm.right_edge = device->abs.absinfo_x->maximum - width * 0.05;
|
||||
tp->palm.left_edge = device->abs.absinfo_x->minimum + width * 0.05;
|
||||
tp->palm.vert_center = device->abs.absinfo_y->minimum + height/2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -261,6 +261,7 @@ struct tp_dispatch {
|
|||
struct {
|
||||
int32_t right_edge; /* in device coordinates */
|
||||
int32_t left_edge; /* in device coordinates */
|
||||
int32_t vert_center; /* in device coordinates */
|
||||
} palm;
|
||||
|
||||
struct {
|
||||
|
|
@ -387,4 +388,7 @@ tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time);
|
|||
void
|
||||
tp_gesture_stop_twofinger_scroll(struct tp_dispatch *tp, uint64_t time);
|
||||
|
||||
bool
|
||||
tp_palm_tap_is_palm(struct tp_dispatch *tp, struct tp_touch *t);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -2626,6 +2626,49 @@ START_TEST(touchpad_palm_detect_no_palm_moving_into_edges)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(touchpad_palm_detect_tap)
|
||||
{
|
||||
struct litest_device *dev = litest_current_device();
|
||||
struct libinput *li = dev->libinput;
|
||||
|
||||
if (!touchpad_has_palm_detect_size(dev))
|
||||
return;
|
||||
|
||||
libinput_device_config_tap_set_enabled(dev->libinput_device,
|
||||
LIBINPUT_CONFIG_TAP_ENABLED);
|
||||
|
||||
litest_drain_events(li);
|
||||
|
||||
litest_touch_down(dev, 0, 95, 5);
|
||||
litest_touch_up(dev, 0);
|
||||
litest_assert_empty_queue(li);
|
||||
|
||||
litest_touch_down(dev, 0, 5, 5);
|
||||
litest_touch_up(dev, 0);
|
||||
litest_assert_empty_queue(li);
|
||||
|
||||
litest_touch_down(dev, 0, 5, 90);
|
||||
litest_touch_up(dev, 0);
|
||||
litest_assert_button_event(li,
|
||||
BTN_LEFT,
|
||||
LIBINPUT_BUTTON_STATE_PRESSED);
|
||||
litest_assert_button_event(li,
|
||||
BTN_LEFT,
|
||||
LIBINPUT_BUTTON_STATE_RELEASED);
|
||||
litest_assert_empty_queue(li);
|
||||
|
||||
litest_touch_down(dev, 0, 95, 90);
|
||||
litest_touch_up(dev, 0);
|
||||
litest_assert_button_event(li,
|
||||
BTN_LEFT,
|
||||
LIBINPUT_BUTTON_STATE_PRESSED);
|
||||
litest_assert_button_event(li,
|
||||
BTN_LEFT,
|
||||
LIBINPUT_BUTTON_STATE_RELEASED);
|
||||
litest_assert_empty_queue(li);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(touchpad_left_handed)
|
||||
{
|
||||
struct litest_device *dev = litest_current_device();
|
||||
|
|
@ -3681,6 +3724,7 @@ int main(int argc, char **argv) {
|
|||
litest_add("touchpad:palm", touchpad_palm_detect_palm_becomes_pointer, LITEST_TOUCHPAD, LITEST_ANY);
|
||||
litest_add("touchpad:palm", touchpad_palm_detect_palm_stays_palm, LITEST_TOUCHPAD, LITEST_ANY);
|
||||
litest_add("touchpad:palm", touchpad_palm_detect_no_palm_moving_into_edges, LITEST_TOUCHPAD, LITEST_ANY);
|
||||
litest_add("touchpad:palm", touchpad_palm_detect_tap, LITEST_TOUCHPAD, LITEST_ANY);
|
||||
|
||||
litest_add("touchpad:left-handed", touchpad_left_handed, LITEST_TOUCHPAD|LITEST_BUTTON, LITEST_CLICKPAD);
|
||||
litest_add("touchpad:left-handed", touchpad_left_handed_clickpad, LITEST_CLICKPAD, LITEST_APPLE_CLICKPAD);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue