[PATCH libinput 3/4] touchpad: impose maximum distance limits on clickfingers
Hans de Goede
hdegoede at redhat.com
Wed Jun 3 00:29:52 PDT 2015
Hi,
On 03-06-15 07:51, Peter Hutterer wrote:
> A common use-case for clickfinger is to use the index finger for moving the
> pointer, then triggering the click with a thumb. If the index finger isn't
> lifted before the click this counted as two-finger click.
>
> To avoid this, check the distance between touches on the touchpad (on
> touchpads reporting resolution values anyway). If the touches are too far
> apart, don't count them together (or specifically only count those close
> enough together as multi-finger).
>
> The touch area is uneven, it's wider than high. Spreading fingers horizontally
> is more common and this also makes it easier to rule out thumbs which tend to
> be well below the fingers.
>
> http://bugs.freedesktop.org/show_bug.cgi?id=90526
>
> Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
LGTM: Reviewed-by: Hans de Goede <hdegoede at redhat.com>
Regards,
Hans
> ---
> doc/clickpad-softbuttons.dox | 13 +++--
> doc/svg/clickfinger-distance.svg | 106 +++++++++++++++++++++++++++++++++++++++
> src/evdev-mt-touchpad-buttons.c | 71 +++++++++++++++++++++++++-
> test/touchpad.c | 48 +++++++++++++++++-
> 4 files changed, 233 insertions(+), 5 deletions(-)
> create mode 100644 doc/svg/clickfinger-distance.svg
>
> diff --git a/doc/clickpad-softbuttons.dox b/doc/clickpad-softbuttons.dox
> index e7c4e54..a4f2e44 100644
> --- a/doc/clickpad-softbuttons.dox
> +++ b/doc/clickpad-softbuttons.dox
> @@ -64,9 +64,16 @@ software-defined button areas.
>
> @image html clickfinger.svg "One, two and three-finger click with Clickfinger behavior"
>
> -The Xorg synaptics driver uses 30% of the touchpad dimensions as threshold,
> -libinput does not have this restriction. If two fingers are on the pad
> -while clicking, that is a two-finger click.
> +On some touchpads, libinput imposes a limit on how the fingers may be placed
> +on the touchpad. In the most common use-case this allows for a user to
> +trigger a click with the thumb while leaving the pointer-moving finger on
> +the touchpad.
> +
> + at image html clickfinger-distance.svg "Illustration of the distance detection algorithm"
> +
> +In the illustration above the red area marks the proximity area around the
> +first finger. Since the thumb is outside of that area libinput considers the
> +click a single-finger click rather than a two-finger click.
>
> Clickfinger configuration can be enabled through the
> libinput_device_config_click_set_method() call. If clickfingers are
> diff --git a/doc/svg/clickfinger-distance.svg b/doc/svg/clickfinger-distance.svg
> new file mode 100644
> index 0000000..ac659cf
> --- /dev/null
> +++ b/doc/svg/clickfinger-distance.svg
> @@ -0,0 +1,106 @@
> +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
> +<!-- 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"
> + width="89.829216mm"
> + height="59.06765mm"
> + viewBox="0 0 318.2925 209.29482"
> + id="svg4184"
> + version="1.1"
> + inkscape:version="0.91 r13725"
> + sodipodi:docname="clickfinger-distance.svg">
> + <defs
> + id="defs4186" />
> + <sodipodi:namedview
> + id="base"
> + pagecolor="#ffffff"
> + bordercolor="#666666"
> + borderopacity="1.0"
> + inkscape:pageopacity="0.0"
> + inkscape:pageshadow="2"
> + inkscape:zoom="1.4"
> + inkscape:cx="235.68795"
> + inkscape:cy="163.39995"
> + inkscape:document-units="px"
> + inkscape:current-layer="layer1"
> + showgrid="false"
> + inkscape:window-width="1920"
> + inkscape:window-height="1136"
> + inkscape:window-x="0"
> + inkscape:window-y="27"
> + inkscape:window-maximized="1"
> + fit-margin-top="0"
> + fit-margin-left="0"
> + fit-margin-right="0"
> + fit-margin-bottom="0" />
> + <metadata
> + id="metadata4189">
> + <rdf:RDF>
> + <cc:Work
> + rdf:about="">
> + <dc:format>image/svg+xml</dc:format>
> + <dc:type
> + rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
> + <dc:title></dc:title>
> + </cc:Work>
> + </rdf:RDF>
> + </metadata>
> + <g
> + inkscape:label="Layer 1"
> + inkscape:groupmode="layer"
> + id="layer1"
> + transform="translate(-257.99662,-299.41313)">
> + <rect
> + width="313.09872"
> + height="167.89594"
> + x="260.59351"
> + y="302.01001"
> + id="rect2858-0"
> + style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.19376326;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate" />
> + <rect
> + style="opacity:0.92000002;fill:#7b0000;fill-opacity:0.2983426;stroke:#000000;stroke-width:1.00100005;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
> + id="rect4788"
> + width="188.57143"
> + height="79.285713"
> + x="355"
> + y="309.50507" />
> + <g
> + transform="matrix(0.79657897,0.11742288,-0.14814182,0.631399,276.6631,-158.96703)"
> + id="g3663-9-5">
> + <path
> + d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z"
> + id="path2820-6-6"
> + style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
> + inkscape:connector-curvature="0" />
> + <path
> + d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z"
> + id="path2824-1-1"
> + style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
> + inkscape:connector-curvature="0" />
> + <path
> + d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
> + id="path2824-7-1-4"
> + style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
> + inkscape:connector-curvature="0" />
> + </g>
> + <path
> + inkscape:connector-curvature="0"
> + style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.00100005;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
> + id="path2824-1-1-3"
> + d="m 353.70196,495.15765 c -24.01774,-7.29937 -29.0012,-10.10221 -30.51977,-10.54973 -10.67294,-3.14527 -18.27051,-5.54063 -23.77758,-13.4704 -5.50707,-7.92977 -5.34967,-20.78347 8.87612,-26.31604 14.2258,-5.53257 39.34351,8.79597 60.13061,16.16341 20.7871,7.36744 33.04563,11.44545 39.33422,13.87551 -8.10022,18.05041 -7.22129,21.15857 -10.11054,33.34117 -0.0481,0.20261 -17.87459,-5.12433 -43.93306,-13.04392 z"
> + sodipodi:nodetypes="sszzzcss" />
> + <path
> + inkscape:connector-curvature="0"
> + style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
> + id="path2824-7-1-4-3"
> + d="m 324.44991,483.39364 c -10.67294,-1.94747 -17.88441,-5.64478 -21.62691,-8.75386 -8.11652,-9.03765 -6.31775,-15.03428 -3.3272,-13.99784 8.90495,-0.9097 30.20384,9.01528 33.86042,10.17935 -5.80268,11.37909 -1.08919,13.70271 -8.90631,12.57235 z"
> + sodipodi:nodetypes="ccccc" />
> + </g>
> +</svg>
> diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c
> index f0c39b6..469f0fa 100644
> --- a/src/evdev-mt-touchpad-buttons.c
> +++ b/src/evdev-mt-touchpad-buttons.c
> @@ -784,12 +784,81 @@ tp_post_physical_buttons(struct tp_dispatch *tp, uint64_t time)
> return 0;
> }
>
> +static inline int
> +tp_check_clickfinger_distance(struct tp_dispatch *tp,
> + struct tp_touch *t1,
> + struct tp_touch *t2)
> +{
> + int res_x, res_y;
> + double x, y;
> +
> + if (!t1 || !t2)
> + return 0;
> +
> + /* no resolution, so let's assume they're close enough together */
> + if (tp->device->abs.fake_resolution)
> + return 1;
> +
> + res_x = tp->device->abs.absinfo_x->resolution;
> + res_y = tp->device->abs.absinfo_y->resolution;
> +
> + x = abs(t1->point.x - t2->point.x)/res_x;
> + y = abs(t1->point.y - t2->point.y)/res_y;
> +
> + /* maximum spread is 40mm horiz, 20mm vert. Anything wider than that
> + * is probably a gesture. The y spread is small so we ignore clicks
> + * with thumbs at the bottom of the touchpad while the pointer
> + * moving finger is still on the pad */
> + return (x < 40 && y < 20) ? 1 : 0;
> +}
> +
> static uint32_t
> tp_clickfinger_set_button(struct tp_dispatch *tp)
> {
> uint32_t button;
> + unsigned int nfingers = tp->nfingers_down;
> + struct tp_touch *t;
> + struct tp_touch *first = NULL,
> + *second = NULL,
> + *third = NULL;
> + uint32_t close_touches = 0;
>
> - switch (tp->nfingers_down) {
> + if (nfingers < 2 || nfingers > 3)
> + goto out;
> +
> + /* two or three fingers down on the touchpad. Check for distance
> + * between the fingers. */
> + tp_for_each_touch(tp, t) {
> + if (t->state != TOUCH_BEGIN && t->state != TOUCH_UPDATE)
> + continue;
> +
> + if (!first)
> + first = t;
> + else if (!second)
> + second = t;
> + else if (!third) {
> + third = t;
> + break;
> + }
> + }
> +
> + if (!first || !second) {
> + nfingers = 1;
> + goto out;
> + }
> +
> + close_touches |= tp_check_clickfinger_distance(tp, first, second) << 0;
> + close_touches |= tp_check_clickfinger_distance(tp, second, third) << 1;
> + close_touches |= tp_check_clickfinger_distance(tp, first, third) << 2;
> +
> + switch(__builtin_popcount(close_touches)) {
> + case 0: nfingers = 1; break;
> + case 1: nfingers = 2; break;
> + default: nfingers = 3; break;
> + }
> +
> +out:
> + switch (nfingers) {
> case 0:
> case 1: button = BTN_LEFT; break;
> case 2: button = BTN_RIGHT; break;
> diff --git a/test/touchpad.c b/test/touchpad.c
> index a747910..d9daf43 100644
> --- a/test/touchpad.c
> +++ b/test/touchpad.c
> @@ -1808,6 +1808,51 @@ START_TEST(touchpad_2fg_clickfinger)
> }
> END_TEST
>
> +START_TEST(touchpad_2fg_clickfinger_distance)
> +{
> + struct litest_device *dev = litest_current_device();
> + struct libinput *li = dev->libinput;
> +
> + libinput_device_config_click_set_method(dev->libinput_device,
> + LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER);
> + litest_drain_events(li);
> +
> + litest_touch_down(dev, 0, 90, 50);
> + litest_touch_down(dev, 1, 10, 50);
> + litest_event(dev, EV_KEY, BTN_LEFT, 1);
> + litest_event(dev, EV_SYN, SYN_REPORT, 0);
> + litest_event(dev, EV_KEY, BTN_LEFT, 0);
> + litest_event(dev, EV_SYN, SYN_REPORT, 0);
> + litest_touch_up(dev, 0);
> + litest_touch_up(dev, 1);
> +
> + 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, 50, 5);
> + litest_touch_down(dev, 1, 50, 95);
> + litest_event(dev, EV_KEY, BTN_LEFT, 1);
> + litest_event(dev, EV_SYN, SYN_REPORT, 0);
> + litest_event(dev, EV_KEY, BTN_LEFT, 0);
> + litest_event(dev, EV_SYN, SYN_REPORT, 0);
> + litest_touch_up(dev, 0);
> + litest_touch_up(dev, 1);
> +
> + litest_assert_button_event(li,
> + BTN_LEFT,
> + LIBINPUT_BUTTON_STATE_PRESSED);
> + litest_assert_button_event(li,
> + BTN_LEFT,
> + LIBINPUT_BUTTON_STATE_RELEASED);
> +}
> +END_TEST
> +
> START_TEST(touchpad_clickfinger_to_area_method)
> {
> struct litest_device *dev = litest_current_device();
> @@ -2636,7 +2681,7 @@ START_TEST(clickpad_topsoftbuttons_clickfinger)
> litest_assert_empty_queue(li);
>
> litest_touch_down(dev, 0, 90, 5);
> - litest_touch_down(dev, 1, 10, 5);
> + litest_touch_down(dev, 1, 80, 5);
> litest_event(dev, EV_KEY, BTN_LEFT, 1);
> litest_event(dev, EV_SYN, SYN_REPORT, 0);
> litest_event(dev, EV_KEY, BTN_LEFT, 0);
> @@ -5084,6 +5129,7 @@ litest_setup_tests(void)
> litest_add("touchpad:clickfinger", touchpad_1fg_clickfinger, LITEST_CLICKPAD, LITEST_ANY);
> litest_add("touchpad:clickfinger", touchpad_1fg_clickfinger_no_touch, LITEST_CLICKPAD, LITEST_ANY);
> litest_add("touchpad:clickfinger", touchpad_2fg_clickfinger, LITEST_CLICKPAD, LITEST_ANY);
> + litest_add("touchpad:clickfinger", touchpad_2fg_clickfinger_distance, LITEST_CLICKPAD, LITEST_APPLE_CLICKPAD);
> litest_add("touchpad:clickfinger", touchpad_clickfinger_to_area_method, LITEST_CLICKPAD, LITEST_ANY);
> litest_add("touchpad:clickfinger",
> touchpad_clickfinger_to_area_method_while_down, LITEST_CLICKPAD, LITEST_ANY);
>
More information about the wayland-devel
mailing list