[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